戰(zhàn):從自然語(yǔ)言到三維CAD模型的生成系統(tǒng)設(shè)計(jì)與實(shí)現(xiàn))
1. 從一句話到三維模型text-to-cad 到底在解決什么問(wèn)題第一次聽(tīng)到“text-to-cad”這個(gè)說(shuō)法是在一個(gè)做機(jī)械設(shè)計(jì)的朋友那里。他當(dāng)時(shí)正對(duì)著屏幕上一堆草圖發(fā)愁嘴里念叨著“要是能直接打字生成模型就好了?!蔽耶?dāng)時(shí)覺(jué)得這想法挺天真的畢竟CAD建模在大多數(shù)人印象里就是鼠標(biāo)點(diǎn)來(lái)點(diǎn)去、參數(shù)調(diào)來(lái)調(diào)去的手工活。但后來(lái)接觸了一些生成式模型的項(xiàng)目之后我發(fā)現(xiàn)這個(gè)方向并不是空想它背后有一套相當(dāng)清晰的邏輯。text-to-cad直譯過(guò)來(lái)就是“文本到計(jì)算機(jī)輔助設(shè)計(jì)”。它的核心目標(biāo)很直接你輸入一段自然語(yǔ)言描述比如“一個(gè)邊長(zhǎng)50毫米、壁厚3毫米、帶四個(gè)安裝孔的法蘭盤”系統(tǒng)輸出一個(gè)可編輯的三維CAD模型文件。這件事如果做成影響的是整個(gè)產(chǎn)品設(shè)計(jì)的前端環(huán)節(jié)——從“腦子里有個(gè)想法”到“屏幕上有個(gè)能看的模型”之間的時(shí)間可能從幾小時(shí)壓縮到幾十秒。它解決的核心痛點(diǎn)有三個(gè)。第一是建模門檻。傳統(tǒng)CAD軟件如SolidWorks、Fusion 360、FreeCAD學(xué)習(xí)曲線陡峭一個(gè)沒(méi)接觸過(guò)參數(shù)化建模的人光搞清楚草圖約束和特征樹(shù)的關(guān)系就要花好幾天。第二是重復(fù)勞動(dòng)。很多標(biāo)準(zhǔn)件、常見(jiàn)結(jié)構(gòu)設(shè)計(jì)師每次都要重新畫一遍雖然可以用模板但模板的靈活性有限。第三是早期溝通效率。產(chǎn)品經(jīng)理或客戶說(shuō)“我想要一個(gè)圓角一點(diǎn)的、看起來(lái)穩(wěn)一點(diǎn)的支架”設(shè)計(jì)師得先理解再建模來(lái)回改幾輪才能對(duì)齊預(yù)期。text-to-cad試圖把“語(yǔ)言”直接變成“幾何”跳過(guò)中間的手工翻譯環(huán)節(jié)。適合關(guān)注這個(gè)方向的人我大致分了三類。一類是CAD二次開(kāi)發(fā)者想在自己的工具鏈里嵌入文本生成模型的能力一類是AI應(yīng)用工程師在探索大語(yǔ)言模型與三維幾何的結(jié)合點(diǎn)還有一類是產(chǎn)品設(shè)計(jì)從業(yè)者想了解這個(gè)技術(shù)到底能不能用在日常工作中現(xiàn)在到了什么程度。不管你是哪一類下面我會(huì)從整體設(shè)計(jì)思路開(kāi)始一步步拆到實(shí)操層面把我知道的、試過(guò)的、踩過(guò)的坑都攤開(kāi)來(lái)講。2. 整體設(shè)計(jì)思路拆解為什么不能直接讓語(yǔ)言模型畫圖2.1 核心矛盾語(yǔ)言是離散的幾何是連續(xù)的text-to-cad最根本的難點(diǎn)在于語(yǔ)言和幾何之間的表示鴻溝。自然語(yǔ)言是離散的符號(hào)序列而三維模型是連續(xù)的坐標(biāo)、曲線、曲面。你讓一個(gè)語(yǔ)言模型直接輸出“一個(gè)邊長(zhǎng)50毫米的立方體”它可能會(huì)輸出一堆數(shù)字但這些數(shù)字怎么變成CAD軟件能識(shí)別的邊界表示B-rep或網(wǎng)格中間缺了一座橋。我見(jiàn)過(guò)一些早期嘗試思路是讓模型直接生成頂點(diǎn)坐標(biāo)和面索引也就是網(wǎng)格數(shù)據(jù)。這種做法在簡(jiǎn)單形狀上勉強(qiáng)能用但一旦涉及圓角、倒角、孔位陣列這些CAD里最常見(jiàn)的特征網(wǎng)格表示就力不從心了。因?yàn)镃AD的核心是參數(shù)化特征歷史不是一堆三角面片。一個(gè)孔不是一個(gè)圓柱面的網(wǎng)格而是一個(gè)“拉伸切除”特征加上直徑、深度、位置這些參數(shù)。所以合理的思路不是讓語(yǔ)言模型直接“畫”而是讓它生成建模指令序列。這就像你告訴一個(gè)熟練的CAD操作員“先畫一個(gè)矩形拉伸50毫米然后在四個(gè)角打直徑8毫米的孔”他聽(tīng)到的是操作步驟不是最終的坐標(biāo)點(diǎn)。text-to-cad系統(tǒng)要做的就是把自然語(yǔ)言翻譯成這種操作步驟序列再由一個(gè)確定性的幾何內(nèi)核去執(zhí)行。2.2 方案選型三種主流路線的取舍目前能看到的text-to-cad實(shí)現(xiàn)大致分三條路線。我在不同項(xiàng)目里都試過(guò)或評(píng)估過(guò)各有各的適用場(chǎng)景。第一條是基于腳本生成。系統(tǒng)讓語(yǔ)言模型輸出一段CAD腳本比如OpenSCAD的代碼或者CadQuery的Python代碼然后調(diào)用對(duì)應(yīng)的解釋器生成模型。這條路線的好處是幾何內(nèi)核成熟OpenSCAD和CadQuery背后是CGAL和OpenCASCADE布爾運(yùn)算、圓角、倒角這些都有現(xiàn)成實(shí)現(xiàn)。缺點(diǎn)是模型必須能表達(dá)成腳本對(duì)于自由曲面、復(fù)雜有機(jī)形狀腳本寫起來(lái)非常吃力。而且語(yǔ)言模型生成的腳本經(jīng)常有語(yǔ)法錯(cuò)誤或邏輯錯(cuò)誤需要額外的校驗(yàn)和修復(fù)環(huán)節(jié)。第二條是基于特征序列生成。系統(tǒng)定義一套領(lǐng)域特定的建模指令集比如“創(chuàng)建草圖”“拉伸”“旋轉(zhuǎn)”“打孔”“陣列”然后讓語(yǔ)言模型輸出這些指令的序列和參數(shù)。這條路線更貼近CAD的原生邏輯生成的模型天然具有參數(shù)化特征樹(shù)后續(xù)修改方便。缺點(diǎn)是需要自己實(shí)現(xiàn)一個(gè)幾何執(zhí)行引擎或者對(duì)接現(xiàn)有CAD軟件的API。開(kāi)發(fā)成本比第一條路線高不少。第三條是基于隱式表示生成。系統(tǒng)讓模型輸出一個(gè)隱式函數(shù)或符號(hào)距離場(chǎng)SDF然后用 marching cubes 之類的算法提取網(wǎng)格。這條路線在學(xué)術(shù)研究里比較常見(jiàn)能生成復(fù)雜形狀但輸出的模型沒(méi)有特征歷史不可參數(shù)化編輯而且網(wǎng)格質(zhì)量往往一般。對(duì)于工程應(yīng)用來(lái)說(shuō)實(shí)用性打折扣。我個(gè)人的判斷是如果你的目標(biāo)是做工程可用的CAD模型第二條路線最靠譜。第一條路線適合快速原型驗(yàn)證第三條路線適合形狀探索和概念設(shè)計(jì)。下面我主要以第二條路線為基準(zhǔn)來(lái)展開(kāi)因?yàn)樗募夹g(shù)挑戰(zhàn)最全面也最能體現(xiàn)text-to-cad的核心價(jià)值。2.3 系統(tǒng)架構(gòu)四個(gè)模塊的協(xié)作方式一個(gè)完整的text-to-cad系統(tǒng)我把它拆成四個(gè)模塊。第一個(gè)是語(yǔ)言理解模塊負(fù)責(zé)把自然語(yǔ)言解析成結(jié)構(gòu)化的建模意圖。第二個(gè)是指令生成模塊把建模意圖翻譯成具體的建模指令序列。第三個(gè)是幾何執(zhí)行模塊調(diào)用幾何內(nèi)核執(zhí)行指令生成三維模型。第四個(gè)是校驗(yàn)與反饋模塊檢查生成的模型是否滿足約束如果不滿足回傳給語(yǔ)言模型重新生成。這四個(gè)模塊里語(yǔ)言理解模塊和指令生成模塊通常由一個(gè)大語(yǔ)言模型承擔(dān)但需要針對(duì)CAD領(lǐng)域做微調(diào)或提示工程。幾何執(zhí)行模塊是確定性的不涉及AI它的可靠性直接決定了最終輸出的質(zhì)量。校驗(yàn)與反饋模塊是最容易被忽視但最關(guān)鍵的一環(huán)因?yàn)檎Z(yǔ)言模型生成的指令經(jīng)常有各種問(wèn)題沒(méi)有校驗(yàn)就等于把錯(cuò)誤直接暴露給用戶。我試過(guò)一個(gè)簡(jiǎn)化版的架構(gòu)用提示工程讓通用語(yǔ)言模型輸出CadQuery代碼然后本地執(zhí)行。結(jié)果大概有30%的生成結(jié)果因?yàn)檎Z(yǔ)法錯(cuò)誤或幾何錯(cuò)誤無(wú)法執(zhí)行。加上一個(gè)基于規(guī)則的校驗(yàn)層之后成功率提升到70%左右。這個(gè)數(shù)字說(shuō)明text-to-cad不是“模型一跑就完事”的事情工程化的工作量比想象中大。3. 核心細(xì)節(jié)解析與實(shí)操要點(diǎn)3.1 語(yǔ)言理解模塊怎么讓模型聽(tīng)懂“人話”語(yǔ)言理解模塊的任務(wù)是把用戶那句可能很模糊的話變成明確的建模意圖。這里最大的挑戰(zhàn)是歧義消解和缺省補(bǔ)全。用戶說(shuō)“一個(gè)圓角盒子”圓角半徑是多少盒子尺寸是多少壁厚有沒(méi)有這些信息語(yǔ)言模型不知道但它不能隨便猜也不能一直追問(wèn)。我的做法是設(shè)計(jì)一套默認(rèn)值策略加槽位填充的機(jī)制。系統(tǒng)預(yù)定義一套建模意圖模板每個(gè)模板有若干槽位比如“創(chuàng)建盒子”這個(gè)意圖有長(zhǎng)、寬、高、圓角半徑、壁厚五個(gè)槽位。語(yǔ)言模型的任務(wù)是從用戶輸入里抽取槽位值抽不到的用默認(rèn)值填充。默認(rèn)值不是隨便定的而是根據(jù)常見(jiàn)工程實(shí)踐來(lái)設(shè)。比如圓角半徑默認(rèn)2毫米壁厚默認(rèn)3毫米這些數(shù)值在大多數(shù)場(chǎng)景下不會(huì)出大錯(cuò)。提示工程在這里很關(guān)鍵。我用的提示詞大致是這樣的結(jié)構(gòu)先給模型幾個(gè)示例展示從自然語(yǔ)言到槽位填充的映射然后給出當(dāng)前用戶輸入最后要求模型以JSON格式輸出意圖和槽位。示例的選擇要覆蓋常見(jiàn)表達(dá)方式比如“帶四個(gè)孔”和“四角打孔”要映射到同一個(gè)意圖。實(shí)測(cè)下來(lái)給5到8個(gè)高質(zhì)量示例通用語(yǔ)言模型在簡(jiǎn)單場(chǎng)景下的槽位抽取準(zhǔn)確率能到85%以上。注意不要指望語(yǔ)言模型一次就能抽對(duì)所有槽位。我的經(jīng)驗(yàn)是對(duì)于尺寸、數(shù)量這類數(shù)值槽位模型容易搞混單位或漏掉。最好在提示詞里明確要求輸出單位并且在后續(xù)模塊里做單位統(tǒng)一。還有一個(gè)容易被忽略的點(diǎn)是否定表達(dá)的處理。用戶說(shuō)“不要圓角”和“圓角半徑為零”是同一個(gè)意思但模型可能理解成“沒(méi)有提到圓角”。我在提示詞里專門加了一類示例覆蓋“不要”“去掉”“取消”這些否定詞讓模型學(xué)會(huì)把它們映射到槽位的零值或空值。3.2 指令生成模塊從意圖到可執(zhí)行代碼拿到結(jié)構(gòu)化的建模意圖之后指令生成模塊要把它變成幾何內(nèi)核能執(zhí)行的代碼。我選的是CadQuery因?yàn)樗贠penCASCADE幾何運(yùn)算可靠而且Python語(yǔ)法對(duì)語(yǔ)言模型比較友好。生成的代碼大致長(zhǎng)這樣import cadquery as cq result ( cq.Workplane(XY) .box(50, 50, 20) .edges(|Z) .fillet(2) .faces(Z) .workplane() .rect(30, 30, forConstructionTrue) .vertices() .hole(8) )這段代碼創(chuàng)建一個(gè)50x50x20的盒子Z方向邊倒2毫米圓角頂面打四個(gè)直徑8毫米的孔。語(yǔ)言模型要生成的就是這種風(fēng)格的代碼。但直接讓模型生成完整代碼出錯(cuò)率很高。我的做法是分步生成先生成特征序列的偽代碼再翻譯成CadQuery代碼。偽代碼的格式很簡(jiǎn)單每行一個(gè)操作比如create_box length50 width50 height20 fillet_edges directionZ radius2 create_holes facetop diameter8 count4 patternrectangular這樣做的好處是偽代碼的語(yǔ)法空間比完整Python代碼小得多模型更容易生成正確。而且偽代碼和CadQuery之間的映射是確定性的可以用規(guī)則引擎完成翻譯不依賴模型。實(shí)測(cè)下來(lái)分步生成的成功率比直接生成代碼高出不少。提示在提示詞里給模型提供CadQuery的API文檔摘要比讓它自己回憶API要可靠。我通常會(huì)把常用的十幾個(gè)API函數(shù)的簽名和說(shuō)明放在提示詞里模型生成代碼時(shí)參考這些信息錯(cuò)誤率明顯下降。另一個(gè)實(shí)操要點(diǎn)是參數(shù)傳遞的格式。語(yǔ)言模型生成的數(shù)值經(jīng)常是字符串而CadQuery需要浮點(diǎn)數(shù)。我在翻譯層里加了一個(gè)類型轉(zhuǎn)換和單位換算的步驟把所有長(zhǎng)度單位統(tǒng)一到毫米角度統(tǒng)一到度。這個(gè)步驟看起來(lái)簡(jiǎn)單但少了它后面幾何執(zhí)行就會(huì)報(bào)一堆類型錯(cuò)誤。3.3 幾何執(zhí)行模塊確定性內(nèi)核的可靠性保障幾何執(zhí)行模塊是整個(gè)系統(tǒng)里唯一不涉及AI的部分但它的重要性怎么強(qiáng)調(diào)都不過(guò)分。語(yǔ)言模型可以犯錯(cuò)但幾何內(nèi)核不能。一個(gè)布爾運(yùn)算失敗或者圓角半徑過(guò)大導(dǎo)致幾何無(wú)效整個(gè)生成流程就斷了。我用的CadQuery底層是OpenCASCADE這是一個(gè)經(jīng)過(guò)幾十年驗(yàn)證的幾何內(nèi)核可靠性很高。但即便如此還是有一些常見(jiàn)的坑。比如圓角半徑超過(guò)相鄰邊長(zhǎng)度的一半會(huì)導(dǎo)致圓角失敗。打孔位置太靠近邊緣可能導(dǎo)致孔穿透到外面。布爾運(yùn)算的兩個(gè)實(shí)體沒(méi)有交集會(huì)返回空結(jié)果。這些情況在手工建模時(shí)設(shè)計(jì)師會(huì)自然避免但語(yǔ)言模型不知道這些約束生成的參數(shù)可能越界。我的做法是在幾何執(zhí)行之前加一個(gè)參數(shù)合法性檢查層。這個(gè)檢查層根據(jù)建模意圖的類型驗(yàn)證參數(shù)是否在合理范圍內(nèi)。比如對(duì)于圓角操作檢查半徑是否小于相鄰邊最短長(zhǎng)度的一半對(duì)于打孔操作檢查孔中心到邊緣的距離是否大于孔徑。如果檢查不通過(guò)不是直接報(bào)錯(cuò)而是把問(wèn)題回傳給語(yǔ)言模型讓它調(diào)整參數(shù)重新生成。這個(gè)反饋循環(huán)我設(shè)了最多三輪三輪還不行就返回失敗讓用戶手動(dòng)調(diào)整。def validate_fillet(shape, edge_selector, radius): edges shape.edges(edge_selector).vals() for edge in edges: length edge.Length() if radius length / 2: return False, f圓角半徑{radius}超過(guò)邊長(zhǎng)度{length}的一半 return True, OK這段校驗(yàn)代碼很簡(jiǎn)單但能攔住大部分幾何錯(cuò)誤。我統(tǒng)計(jì)過(guò)加上校驗(yàn)層之后幾何執(zhí)行失敗率從25%降到了5%以下。剩下的5%主要是OpenCASCADE內(nèi)部的數(shù)值精度問(wèn)題比較難完全避免。3.4 校驗(yàn)與反饋模塊讓系統(tǒng)自己發(fā)現(xiàn)錯(cuò)誤校驗(yàn)與反饋模塊是text-to-cad系統(tǒng)里最像“工程”而不是“研究”的部分。它的任務(wù)是在模型生成之后檢查模型是否滿足用戶的原始意圖。這里分兩個(gè)層次幾何有效性校驗(yàn)和語(yǔ)義一致性校驗(yàn)。幾何有效性校驗(yàn)比較簡(jiǎn)單調(diào)用幾何內(nèi)核的檢查函數(shù)就行。比如檢查模型是否是有效的實(shí)體solid有沒(méi)有自相交體積是否大于零。這些檢查CadQuery和OpenCASCADE都有現(xiàn)成的API。語(yǔ)義一致性校驗(yàn)就復(fù)雜一些。用戶說(shuō)“帶四個(gè)孔”生成的模型是不是真的有四個(gè)孔用戶說(shuō)“邊長(zhǎng)50毫米”模型的包圍盒尺寸是不是50毫米這些檢查需要從生成的模型中提取幾何特征然后和原始意圖做比對(duì)。我的做法是寫一組特征提取器比如統(tǒng)計(jì)孔的數(shù)量、測(cè)量包圍盒尺寸、檢測(cè)圓角是否存在。然后把這些提取結(jié)果和意圖槽位做比對(duì)不一致就觸發(fā)重新生成。注意語(yǔ)義一致性校驗(yàn)不要做得太嚴(yán)格。比如用戶說(shuō)“大約50毫米”模型生成49.8毫米這不應(yīng)該算錯(cuò)誤。我在比對(duì)時(shí)設(shè)了一個(gè)容差尺寸類參數(shù)容差5%數(shù)量類參數(shù)要求精確匹配。這個(gè)容差范圍可以根據(jù)實(shí)際需求調(diào)整。反饋模塊的提示詞設(shè)計(jì)也有講究。不能只說(shuō)“錯(cuò)了重新生成”而要告訴模型具體哪里錯(cuò)了、期望值是多少。比如“生成的模型有3個(gè)孔但用戶要求4個(gè)孔請(qǐng)調(diào)整陣列參數(shù)”。這種具體的反饋能讓模型更快收斂。我試過(guò)帶具體反饋的重新生成成功率比不帶反饋高出40%左右。4. 實(shí)操過(guò)程與核心環(huán)節(jié)實(shí)現(xiàn)4.1 環(huán)境搭建從零開(kāi)始的最小可行系統(tǒng)如果你想自己搭一個(gè)text-to-cad的驗(yàn)證系統(tǒng)我建議從最小可行版本開(kāi)始。不需要一上來(lái)就搞微調(diào)用提示工程加通用語(yǔ)言模型就能跑通基本流程。下面是我實(shí)際用的一套環(huán)境配置。首先是Python環(huán)境建議3.9以上。核心依賴是CadQuery和OpenAI的API客戶端或者任何兼容的本地模型接口。CadQuery的安裝用conda比較省事pip安裝有時(shí)候會(huì)遇到OpenCASCADE的二進(jìn)制依賴問(wèn)題。conda create -n text2cad python3.10 conda activate text2cad conda install -c conda-forge cadquery pip install openai然后是項(xiàng)目結(jié)構(gòu)。我習(xí)慣分成四個(gè)文件intent_parser.py負(fù)責(zé)語(yǔ)言理解code_generator.py負(fù)責(zé)指令生成geometry_executor.py負(fù)責(zé)幾何執(zhí)行main.py是入口。每個(gè)模塊之間用JSON格式的數(shù)據(jù)結(jié)構(gòu)傳遞信息這樣方便調(diào)試和替換。# intent_parser.py 的核心邏輯 def parse_intent(user_input): prompt build_prompt(user_input) response call_llm(prompt) intent json.loads(response) intent fill_defaults(intent) return intentbuild_prompt函數(shù)里放示例和格式要求fill_defaults函數(shù)里放默認(rèn)值策略。這兩個(gè)函數(shù)是語(yǔ)言理解模塊的核心值得花時(shí)間打磨。4.2 提示詞工程讓通用模型輸出結(jié)構(gòu)化結(jié)果提示詞的質(zhì)量直接決定了語(yǔ)言理解模塊的準(zhǔn)確率。我經(jīng)過(guò)多次迭代總結(jié)出一個(gè)比較穩(wěn)定的提示詞模板。它的結(jié)構(gòu)分四部分角色設(shè)定、任務(wù)說(shuō)明、示例、輸出格式要求。角色設(shè)定部分告訴模型它是一個(gè)CAD建模助手負(fù)責(zé)把自然語(yǔ)言轉(zhuǎn)成建模意圖。任務(wù)說(shuō)明部分解釋槽位填充的規(guī)則包括哪些槽位是必填的、哪些有默認(rèn)值、數(shù)值的單位是什么。示例部分給5到8個(gè)覆蓋常見(jiàn)場(chǎng)景的輸入輸出對(duì)。輸出格式要求部分明確指定JSON的schema。你是一個(gè)CAD建模意圖解析助手。用戶會(huì)用自然語(yǔ)言描述一個(gè)三維模型你需要把它解析成結(jié)構(gòu)化的建模意圖。 可用的意圖類型 - create_box: 創(chuàng)建長(zhǎng)方體槽位有l(wèi)ength, width, height, fillet_radius - create_cylinder: 創(chuàng)建圓柱體槽位有radius, height - create_hole: 打孔槽位有face, diameter, count, pattern 默認(rèn)值 - fillet_radius: 2 - 單位: 毫米 示例1 輸入一個(gè)50毫米長(zhǎng)、30毫米寬、20毫米高的盒子帶2毫米圓角 輸出{intent: create_box, slots: {length: 50, width: 30, height: 20, fillet_radius: 2}} 示例2 輸入頂面打四個(gè)直徑8毫米的孔 輸出{intent: create_hole, slots: {face: top, diameter: 8, count: 4, pattern: rectangular}} 現(xiàn)在請(qǐng)解析以下輸入 {user_input} 請(qǐng)只輸出JSON不要有其他內(nèi)容。這個(gè)模板的關(guān)鍵在于示例的質(zhì)量。示例要覆蓋不同的表達(dá)方式比如“盒子”和“長(zhǎng)方體”要映射到同一個(gè)意圖“打孔”和“鉆孔”也要映射到同一個(gè)意圖。我通常會(huì)準(zhǔn)備20個(gè)左右的示例根據(jù)實(shí)際測(cè)試中出現(xiàn)的錯(cuò)誤不斷補(bǔ)充。提示如果你的模型支持系統(tǒng)提示詞和用戶提示詞分離把角色設(shè)定和任務(wù)說(shuō)明放在系統(tǒng)提示詞里示例和用戶輸入放在用戶提示詞里。這樣模型對(duì)角色和規(guī)則的記憶更穩(wěn)定。4.3 代碼生成與執(zhí)行從偽代碼到三維模型指令生成模塊的輸入是結(jié)構(gòu)化的建模意圖輸出是CadQuery代碼。我采用兩階段生成先生成偽代碼再翻譯成CadQuery。偽代碼的生成也用提示詞工程但比意圖解析簡(jiǎn)單因?yàn)閭未a的語(yǔ)法空間很小。偽代碼的格式我定義得很簡(jiǎn)單每行一個(gè)操作操作名和參數(shù)用空格分隔。比如create_box length50 width30 height20 fillet_edges directionZ radius2 create_holes facetop diameter8 count4 patternrectangular生成偽代碼的提示詞里我會(huì)給出所有可用操作的列表和參數(shù)說(shuō)明。模型的任務(wù)就是根據(jù)建模意圖選擇合適的操作并填參數(shù)。因?yàn)椴僮鲾?shù)量有限模型的選擇空間不大準(zhǔn)確率比較高。翻譯層是一個(gè)純規(guī)則的Python函數(shù)把每行偽代碼映射到對(duì)應(yīng)的CadQuery調(diào)用。這個(gè)函數(shù)不需要AI寫起來(lái)很直接。def execute_pseudo_code(pseudo_code): result None for line in pseudo_code.strip().split(\n): parts line.split() op parts[0] params dict(p.split() for p in parts[1:]) if op create_box: result cq.Workplane(XY).box( float(params[length]), float(params[width]), float(params[height]) ) elif op fillet_edges: result result.edges(params[direction]).fillet(float(params[radius])) elif op create_holes: result result.faces(params[face]).workplane().rect( 30, 30, forConstructionTrue ).vertices().hole(float(params[diameter])) return result這段代碼是簡(jiǎn)化版實(shí)際項(xiàng)目中需要處理更多操作類型和參數(shù)組合。但核心邏輯就是這樣順序執(zhí)行偽代碼指令每一步在上一步的結(jié)果上操作。4.4 完整流程走一遍一個(gè)法蘭盤的生成實(shí)例我用一個(gè)具體例子把整個(gè)流程串起來(lái)。用戶輸入是“一個(gè)外徑100毫米、內(nèi)徑60毫米、厚度10毫米的法蘭盤帶六個(gè)直徑8毫米的安裝孔孔均勻分布在一個(gè)直徑80毫米的圓上?!钡谝徊揭鈭D解析。語(yǔ)言模型輸出{ intent: create_flange, slots: { outer_diameter: 100, inner_diameter: 60, thickness: 10, hole_diameter: 8, hole_count: 6, hole_circle_diameter: 80 } }第二步偽代碼生成。模型輸出create_cylinder radius50 height10 create_hole diameter60 create_holes diameter8 count6 patterncircular circle_diameter80第三步代碼翻譯和執(zhí)行。翻譯層把偽代碼轉(zhuǎn)成CadQuery代碼result ( cq.Workplane(XY) .circle(50) .extrude(10) .faces(Z) .workplane() .hole(60) .faces(Z) .workplane() .polarArray(40, 0, 360, 6) .hole(8) )第四步校驗(yàn)。幾何有效性檢查通過(guò)模型是一個(gè)有效的實(shí)體。語(yǔ)義一致性檢查外徑100毫米內(nèi)徑60毫米厚度10毫米孔數(shù)6個(gè)孔分布圓直徑80毫米全部匹配。生成成功。整個(gè)流程從輸入到輸出在我本地環(huán)境上大概需要5到10秒主要時(shí)間花在語(yǔ)言模型的API調(diào)用上。如果換成更小的本地模型時(shí)間可以壓縮到2秒以內(nèi)但準(zhǔn)確率會(huì)下降一些。注意polarArray的參數(shù)是半徑、起始角度、終止角度、數(shù)量。這里半徑是40毫米因?yàn)榭追植紙A直徑是80毫米。這個(gè)換算關(guān)系容易搞錯(cuò)我在翻譯層里加了一個(gè)檢查如果偽代碼里給的是直徑自動(dòng)除以2再傳給polarArray。5. 常見(jiàn)問(wèn)題與排查技巧實(shí)錄5.1 語(yǔ)言模型輸出格式錯(cuò)誤怎么辦這是最常見(jiàn)的問(wèn)題。你要求模型輸出JSON它可能輸出一段解釋文字加JSON或者JSON的鍵名拼錯(cuò)或者數(shù)值帶了單位字符串。我的處理策略是多層容錯(cuò)。第一層在提示詞里反復(fù)強(qiáng)調(diào)“只輸出JSON”。第二層用正則表達(dá)式從模型輸出里提取JSON部分忽略前后的解釋文字。第三層用JSON schema校驗(yàn)如果鍵名不對(duì)或類型不對(duì)嘗試自動(dòng)修復(fù)。比如把length: 50mm修復(fù)成length: 50.0。第四層如果修復(fù)失敗把錯(cuò)誤信息回傳給模型讓它重新生成。import re import json def extract_json(text): match re.search(r\{.*\}, text, re.DOTALL) if not match: return None try: return json.loads(match.group()) except json.JSONDecodeError: return None這個(gè)函數(shù)能處理大部分格式問(wèn)題。實(shí)測(cè)下來(lái)加上這層提取之后格式錯(cuò)誤率從15%降到了3%以下。5.2 幾何執(zhí)行失敗怎么排查幾何執(zhí)行失敗的原因很多我整理了一個(gè)排查順序。先看錯(cuò)誤信息OpenCASCADE的報(bào)錯(cuò)通常比較具體比如“BRep_API: command not done”通常意味著布爾運(yùn)算失敗“Standard_ConstructionError”通常是參數(shù)越界。然后檢查參數(shù)合法性用前面說(shuō)的校驗(yàn)層過(guò)一遍。最后檢查操作順序有些操作必須在特定操作之前或之后執(zhí)行比如打孔必須在拉伸之后圓角必須在打孔之前否則圓角可能被孔打斷。我遇到最多的幾何失敗是圓角半徑過(guò)大。語(yǔ)言模型經(jīng)常生成一個(gè)看起來(lái)合理但實(shí)際超過(guò)邊長(zhǎng)的圓角半徑。比如一個(gè)10毫米高的盒子模型生成5毫米的圓角這剛好等于高度的一半OpenCASCADE會(huì)報(bào)錯(cuò)。我的校驗(yàn)層里設(shè)的閾值是邊長(zhǎng)的0.4倍超過(guò)就觸發(fā)重新生成。另一個(gè)常見(jiàn)問(wèn)題是孔的位置越界。語(yǔ)言模型生成孔陣列時(shí)可能把孔放在實(shí)體外面。比如一個(gè)50x50的盒子模型生成孔分布圓直徑60毫米孔就跑到外面去了。校驗(yàn)層里檢查孔中心到實(shí)體邊緣的距離小于孔徑就報(bào)錯(cuò)。5.3 生成結(jié)果和預(yù)期不符怎么調(diào)整有時(shí)候幾何執(zhí)行成功了但模型不是用戶想要的。比如用戶說(shuō)“一個(gè)圓角盒子”模型生成了一個(gè)帶圓角的盒子但圓角在錯(cuò)誤的邊上?;蛘哂脩粽f(shuō)“四個(gè)孔”模型打了四個(gè)孔但位置不對(duì)。這類問(wèn)題的根源通常在意圖解析階段。語(yǔ)言模型對(duì)“圓角”的理解可能是在所有邊上倒圓角而用戶想要的是只在垂直邊上倒圓角。解決辦法是在意圖解析的槽位里增加更細(xì)粒度的控制比如fillet_edges槽位指定哪些邊需要倒圓角。然后在提示詞里增加示例展示不同邊選擇對(duì)應(yīng)的輸出。還有一種情況是默認(rèn)值不符合用戶預(yù)期。用戶說(shuō)“一個(gè)盒子”沒(méi)有指定尺寸系統(tǒng)用了默認(rèn)的50x50x20。但用戶心里想的是100x100x50。這種情況沒(méi)法完全避免但可以在生成之后給用戶一個(gè)反饋界面讓用戶看到實(shí)際使用的參數(shù)方便手動(dòng)調(diào)整。5.4 常見(jiàn)問(wèn)題速查表問(wèn)題現(xiàn)象可能原因排查方法解決策略模型輸出不是JSON提示詞不夠明確檢查提示詞是否強(qiáng)調(diào)只輸出JSON加正則提取層回傳錯(cuò)誤重新生成JSON鍵名拼寫錯(cuò)誤模型幻覺(jué)對(duì)比schema檢查鍵名加鍵名映射表自動(dòng)糾正幾何執(zhí)行報(bào)錯(cuò)參數(shù)越界或操作順序錯(cuò)誤查看OpenCASCADE錯(cuò)誤信息加參數(shù)校驗(yàn)層調(diào)整操作順序圓角失敗半徑超過(guò)邊長(zhǎng)一半檢查圓角半徑和相鄰邊長(zhǎng)度設(shè)閾值0.4倍邊長(zhǎng)超限重新生成孔位置越界陣列參數(shù)錯(cuò)誤檢查孔中心到邊緣距離校驗(yàn)距離大于孔徑否則重新生成生成結(jié)果不符預(yù)期意圖解析偏差對(duì)比用戶輸入和解析結(jié)果增加示例細(xì)化槽位粒度生成速度慢語(yǔ)言模型API延遲測(cè)量各模塊耗時(shí)換本地小模型或緩存常用意圖提示這張表是我在實(shí)際調(diào)試中慢慢積累的建議你在自己的項(xiàng)目里也維護(hù)一份。每次遇到新問(wèn)題就加一行時(shí)間長(zhǎng)了就是一份很有價(jià)值的排查手冊(cè)。5.5 幾個(gè)容易被忽視的實(shí)操心得第一個(gè)心得是緩存常用意圖的解析結(jié)果。很多用戶輸入是重復(fù)的比如“一個(gè)50x50x20的盒子”可能被不同用戶反復(fù)輸入。把意圖解析的結(jié)果緩存起來(lái)相同的輸入直接返回緩存能省不少API調(diào)用。我用一個(gè)簡(jiǎn)單的字典做緩存命中率大概有20%左右。第二個(gè)心得是給語(yǔ)言模型提供幾何約束的反饋。在重新生成的提示詞里不僅告訴模型“錯(cuò)了”還要告訴它“為什么錯(cuò)了”。比如“圓角半徑5毫米超過(guò)了邊長(zhǎng)10毫米的一半請(qǐng)把半徑改到4毫米以下”。這種具體的約束信息能讓模型更快找到可行解。第三個(gè)心得是不要追求一次生成完美。text-to-cad系統(tǒng)的最佳交互模式是“生成-預(yù)覽-調(diào)整”而不是“一次生成最終模型”。用戶看到初步結(jié)果后可以用自然語(yǔ)言繼續(xù)調(diào)整比如“把孔的數(shù)量改成8個(gè)”“圓角再大一點(diǎn)”。這種迭代式的交互比一次性生成更符合實(shí)際設(shè)計(jì)流程。6. 這個(gè)方向后續(xù)可以怎么擴(kuò)展我在實(shí)際項(xiàng)目里跑通基本流程之后發(fā)現(xiàn)text-to-cad的擴(kuò)展空間比預(yù)想的大。一個(gè)方向是多模態(tài)輸入用戶不僅可以打字還可以畫一個(gè)粗略的草圖或者拍一張照片系統(tǒng)結(jié)合文本和圖像一起理解意圖。另一個(gè)方向是裝配體生成不只是單個(gè)零件而是多個(gè)零件的裝配關(guān)系比如“一個(gè)齒輪箱包含輸入軸、輸出軸和殼體”。這個(gè)難度比單零件高一個(gè)量級(jí)因?yàn)樯婕傲慵g的配合約束。還有一個(gè)我覺(jué)得很有價(jià)值的方向是與現(xiàn)有CAD軟件深度集成?,F(xiàn)在的實(shí)現(xiàn)大多是生成獨(dú)立的模型文件用戶還得導(dǎo)入到CAD軟件里繼續(xù)編輯。如果能做成CAD軟件的一個(gè)插件生成的模型直接出現(xiàn)在當(dāng)前的設(shè)計(jì)樹(shù)里并且保留完整的特征歷史那實(shí)用性會(huì)大大提升。我試過(guò)用FreeCAD的Python API做類似的事情技術(shù)上可行但穩(wěn)定性和性能還需要打磨。最后分享一個(gè)小技巧。如果你也在做類似的項(xiàng)目建議從標(biāo)準(zhǔn)件庫(kù)開(kāi)始驗(yàn)證。標(biāo)準(zhǔn)件形狀固定、參數(shù)明確、生成邏輯簡(jiǎn)單是驗(yàn)證text-to-cad流程的理想場(chǎng)景。等標(biāo)準(zhǔn)件跑通了再逐步擴(kuò)展到自由形狀和復(fù)雜裝配。我在項(xiàng)目初期花了兩周時(shí)間只做螺栓和法蘭盤把整個(gè)流程的坑都踩了一遍后面擴(kuò)展到其他零件時(shí)順利很多。