
1. 从一句话到可编辑模型Text-to-CAD 到底在做什么第一次听到“Text-to-CAD”这个词很多人脑子里浮现的画面大概是对着电脑敲一句“给我画一个带四个安装孔的方形法兰盘”然后屏幕上就自动长出一个能直接拿去加工的三维模型。这个想象方向没错但中间隔着的工程量比大多数人以为的要大得多。Text-to-CAD 本质上是把自然语言提示词翻译成参数化模型的一套系统它的输出不是一张图片也不是一个死掉的网格而是一棵带尺寸、带约束、可以继续改的特征树。这一点是它和普通“文生图”最根本的分水岭。我接触这类工具是从一个很具体的需求开始的手头有一批标准件要反复改尺寸每次改都要重新拉伸、打孔、倒角烦得不行。当时就想能不能用一句话描述需求让程序自己把模型建出来。后来顺着这个思路把提示词工程、参数化建模、几何内核这几块串起来看才算把 Text-to-CAD 的完整链路摸清楚。它解决的问题很明确——把“描述需求”和“生成可制造模型”之间的手工建模环节压缩掉让不会 CAD 的人也能出模型让会 CAD 的人少做重复劳动。这篇文章适合三类人看一是做机械设计、想提效的工程师二是做 AI 应用、想把大模型接到 CAD 上的开发者三是纯粹好奇“AI 怎么画三维图”的爱好者。我会从整体设计思路讲起把提示词怎么解析、参数怎么提取、模型怎么生成、坑在哪里一层层拆开。你看完之后至少能自己搭一个最小可用的 Text-to-CAD 流程也能判断市面上的工具哪些是真参数化、哪些只是套壳出网格。先说结论Text-to-CAD 的核心难点从来不是“生成形状”而是“生成带正确约束和尺寸的参数化特征树”。形状谁都能糊出来但一个孔打偏了 0.5 毫米、一个倒角方向反了模型就是废的。所以整篇文章的重点会放在参数提取、约束求解和特征序列生成这三块上。2. 整体架构拆解一条提示词要经过几道手2.1 为什么不能一步到位直接出模型很多人第一反应是既然大模型这么强直接让它输出一个三维模型文件不就行了我试过这条路结论是走不通。原因有三个。第一三维模型的数据量太大一个中等复杂度的零件顶点坐标动辄几万个浮点数让语言模型逐点生成既慢又不准。第二语言模型擅长的是符号和结构不擅长连续数值的精确计算你让它算一个 M8 螺栓孔的坐标它大概率给你一个“差不多”的数。第三也是最关键的直接生成的网格模型没有参数、没有特征历史改一个尺寸整个模型就崩了完全丧失了 CAD 的价值。所以正确的架构一定是分层的自然语言先被解析成结构化的中间表示再由中间表示驱动几何内核去建模。这个中间表示就是整个系统的灵魂。它通常是一段结构化的描述比如 JSON 或者某种 DSL领域特定语言里面写清楚了“要建什么特征、每个特征的参数是多少、特征之间的依赖关系是什么”。2.2 四层流水线解析、规划、求解、建模把整条链路拆开我习惯分成四层来看每一层各司其职层与层之间用明确定义的数据结构传递。层级职责输入输出关键技术语义解析层理解提示词意图自然语言意图实体大模型、NER、意图分类特征规划层决定建哪些特征、什么顺序意图实体特征序列任务分解、模板匹配参数求解层算出每个特征的具体数值特征序列带参数的约束图约束求解、单位换算几何建模层真正生成三维实体约束图参数化模型几何内核、特征操作这四层里语义解析层是大家最熟悉的也是大模型最擅长的。参数求解层是最容易被低估的很多开源方案在这里偷懒导致模型看着对、尺寸全错。几何建模层反而是最成熟的因为 CAD 内核已经发展了几十年只要给它正确的指令它就能稳定输出。2.3 中间表示为什么选结构化文本而不是直接调 API有人会问解析完直接调用 CAD 的 API 不就行了为什么还要搞一个中间表示我的经验是中间表示带来两个不可替代的好处。一是可调试模型建错了你可以直接看中间表示里哪个参数不对而不是对着一堆 API 调用日志发呆。二是可复用同一份中间表示可以喂给不同的几何内核今天用这个内核明天换那个上层逻辑不用动。这就像写代码先编译成中间语言再转机器码多一层反而更灵活。我实际用下来中间表示用 JSON 是最省事的结构清晰、好解析、好生成。下面是一个简化例子描述一个带中心孔和四个角孔的方板{ units: mm, features: [ {type: sketch_rectangle, id: base, width: 100, height: 60}, {type: extrude, id: body, sketch: base, distance: 10}, {type: hole, id: center_hole, face: body.top, diameter: 20, position: [50, 30]}, {type: hole_pattern, id: corner_holes, face: body.top, diameter: 6, positions: [[10,10],[90,10],[10,50],[90,50]]} ] }这段 JSON 就是提示词和几何内核之间的“合同”。只要这份合同写对了后面建模基本不会出大问题。3. 提示词解析让大模型听懂“人话”里的工程参数3.1 提示词里到底藏着哪些信息一句看起来简单的提示词比如“做一个 100×60×10 的铝板中间开个直径 20 的孔四角各开一个 M6 的沉头孔”里面其实塞了至少五类信息。第一类是几何类型板、孔、沉头孔。第二类是尺寸数值100、60、10、20、M6。第三类是位置关系中间、四角。第四类是材料或工艺铝板、沉头。第五类是隐含约束四角孔通常是对称分布的沉头孔有标准的角度和深度。大模型要做的就是把这五类信息全部抽出来一个都不能漏。漏了尺寸模型尺寸就错漏了位置孔就打偏漏了工艺沉头就变成通孔。我见过太多失败案例问题都出在解析阶段漏信息而不是建模阶段。3.2 用结构化提示词把大模型“框住”直接丢一句自然语言给大模型让它输出 JSON成功率其实不高尤其是参数一多就容易乱。我的做法是用结构化提示词把输出格式死死框住同时给出明确的字段定义和示例。这就是所谓的提示词工程在 Text-to-CAD 里的具体应用。一个我常用的解析提示词模板长这样你是一个 CAD 参数解析器。请把用户的自然语言描述转换成如下 JSON 结构。 字段定义 - units: 单位默认 mm - features: 特征数组每个特征包含 type 和参数 - type 可选值: sketch_rectangle, sketch_circle, extrude, hole, hole_pattern, fillet, chamfer - 所有尺寸必须转成数字单位统一为 mm - 如果用户没说位置按对称或居中处理并在 assumptions 字段里说明 用户描述{user_input} 只输出 JSON不要输出任何解释。这个模板的关键在于三点字段枚举明确、默认规则写死、强制只输出 JSON。尤其是最后一条能省掉大量后处理清洗的工作。我试过不加“只输出 JSON”模型总喜欢在前面加一句“好的这是解析结果”解析器直接报错。3.3 单位换算和数值归一化工程里单位是个大坑。用户可能说“10 公分”“4 英寸”“M8”这些都得在解析阶段统一成毫米。我的处理方式是维护一张换算表在提示词里明确告诉模型换算规则同时在代码里再做一次校验。用户输入含义换算为 mm10 公分厘米1004 英寸inch101.6M8公称直径 8 的螺纹孔径约 6.8底孔1 分英制管径约 3.1755 丝0.05 mm0.05注意M 系列螺纹在建模时通常建底孔而不是公称直径M8 的底孔约 6.8mmM6 约 5.0mm。如果直接按公称直径建孔装配时会装不进去。这个细节很多自动建模工具都忽略了。3.4 处理模糊描述默认规则要写清楚用户不会每次都把话说全。“开几个散热孔”这种描述孔的数量、直径、排列方式全是空的。这时候系统必须有一套默认规则并且把假设明确告诉用户。我的默认规则是这样的散热孔默认直径 5mm、阵列排布、间距等于 2 倍孔径、居中对称。系统生成模型后会附带一句“已按默认规则处理孔径 5mm阵列间距 10mm如需调整请说明”。这套机制的价值在于它让系统在信息不全时也能给出一个合理可用的结果而不是直接报错。用户看到默认值不对再补一句“孔径改成 8mm”就行交互成本很低。4. 参数化建模的核心特征树、约束与几何内核4.1 参数化模型和普通网格模型的本质区别这里必须把概念讲透否则后面全是糊涂账。普通三维网格模型比如 STL、OBJ记录的是一堆三角面片的顶点坐标它只知道“表面长这样”不知道“这是一个孔”。你想把孔改大只能重新生成整个网格。而参数化模型记录的是一串特征操作和它们的参数先画一个矩形草图拉伸 10mm再在顶面打一个直径 20 的孔。孔是一个独立特征改直径只需要改一个数字几何内核会自动重算。这个区别决定了 Text-to-CAD 的输出必须是参数化的。因为用户的需求天然是参数化的——“把孔改大点”“板加厚 2mm”这些操作只有在参数化模型上才能优雅实现。如果输出的是网格那 Text-to-CAD 就退化成了一个一次性的形状生成器价值大打折扣。4.2 特征树是怎么长出来的特征树是一棵有向无环图每个节点是一个特征边表示依赖关系。比如“打孔”这个特征依赖于“拉伸出的顶面”而“顶面”又依赖于“矩形草图”和“拉伸距离”。改任何一个上游参数下游全部自动重算。这就是参数化的威力。生成特征树的关键是顺序。同样一组特征顺序错了就建不出来。比如你必须先有实体才能在上面打孔先有边才能倒角。所以特征规划层要做的就是把解析出来的特征按依赖关系排好序。我一般用拓扑排序把“被依赖”的特征排在前面。# 简化的特征排序逻辑 def sort_features(features): # 建立依赖关系face 引用指向产生该面的特征 graph {} for f in features: deps [] if sketch in f: deps.append(f[sketch]) if face in f: # face 形如 body.top依赖 body 特征 deps.append(f[face].split(.)[0]) graph[f[id]] deps # 拓扑排序 return topological_sort(graph)这段逻辑看着简单但实际项目里依赖关系会复杂得多尤其是涉及阵列、镜像、布尔运算的时候。我的建议是先把依赖关系显式建模出来别指望几何内核帮你猜。4.3 约束求解尺寸不是孤立的数字参数化建模里尺寸之间往往有约束关系。比如“四个角孔关于中心对称”这意味着你只需要定义两个坐标另外两个是推导出来的。再比如“孔到边的距离相等”这是一个等式约束。这些约束如果不处理用户改一个尺寸其他尺寸不会跟着变模型就散了。约束求解一般交给几何内核或者专门的约束求解器。Text-to-CAD 系统要做的是把自然语言里的约束关系翻译成求解器能懂的方程。比如“对称”翻译成x1 x2 width“等距”翻译成d1 d2。这块是难点也是区分工具好坏的分水岭。我见过一些工具生成的模型看着没问题但你一改尺寸就发现孔跑偏了就是因为约束没建对。4.4 几何内核选型开源和商业怎么选几何内核是真正干活的底层引擎负责把特征操作变成实体。选型上有几个方向内核类型特点适用场景OpenCASCADE开源功能全学习曲线陡自研系统、预算有限Parasolid商业工业级稳定精度高商业产品、高精度需求ACIS商业老牌内核生态成熟传统 CAD 集成Manifold开源轻量适合网格布尔快速原型、网格为主我的经验是如果只是做原型验证OpenCASCADE 完全够用Python 有 pythonocc 封装上手快。如果要做产品精度和稳定性要求高商业内核更稳妥。选型时重点看两点布尔运算的鲁棒性和特征操作的完整性。布尔运算不稳模型稍微复杂点就崩这是最常见的坑。5. 完整实操从提示词到可导出模型的落地流程5.1 环境准备与依赖安装我以 Python 技术栈为例走一遍完整流程。核心依赖是 pythonoccOpenCASCADE 的 Python 封装和一个大模型接口。安装 pythonocc 在 Windows 上稍微麻烦点推荐用 condaconda create -n text2cad python3.10 conda activate text2cad conda install -c conda-forge pythonocc-core pip install openai pydantic提示pythonocc 用 pip 直接装经常失败因为底层 C 库的编译依赖很复杂。conda-forge 的预编译包最省心实测下来很稳。5.2 第一步提示词解析成中间表示先写解析函数调用大模型把自然语言转成前面说的 JSON 结构。这里用 Pydantic 做校验防止模型输出缺字段。from pydantic import BaseModel, Field from typing import List, Optional class Feature(BaseModel): type: str id: str width: Optional[float] None height: Optional[float] None distance: Optional[float] None diameter: Optional[float] None position: Optional[List[float]] None positions: Optional[List[List[float]]] None sketch: Optional[str] None face: Optional[str] None class CADSpec(BaseModel): units: str mm features: List[Feature] assumptions: List[str] [] def parse_prompt(user_input: str) - CADSpec: prompt build_parser_prompt(user_input) raw call_llm(prompt) return CADSpec.model_validate_json(raw)Pydantic 的好处是模型输出格式不对会直接抛异常你能第一时间发现而不是等到建模阶段才报错。5.3 第二步特征排序与参数校验解析出来的特征顺序不一定对需要重新排序。同时要做参数校验比如尺寸不能为负、孔径不能大于板宽。def validate_and_sort(spec: CADSpec) - CADSpec: for f in spec.features: if f.diameter is not None and f.diameter 0: raise ValueError(f特征 {f.id} 的直径非法: {f.diameter}) spec.features sort_features(spec.features) return spec参数校验这一步千万别省。我踩过的坑是模型偶尔会把“直径 20”解析成“半径 20”如果不校验建出来的孔大一倍而且不容易发现。5.4 第三步驱动几何内核建模这是最核心的一步把中间表示翻译成 OpenCASCADE 的操作。下面是一个建方板加中心孔的简化实现from OCC.Core.BRepPrimAPI import BRepPrimAPI_MakeBox, BRepPrimAPI_MakeCylinder from OCC.Core.BRepAlgoAPI import BRepAlgoAPI_Cut from OCC.Core.gp import gp_Pnt, gp_Ax2, gp_Dir def build_model(spec: CADSpec): solids {} for f in spec.features: if f.type sketch_rectangle: # 记录草图尺寸等拉伸时用 solids[f.id] {w: f.width, h: f.height} elif f.type extrude: base solids[f.sketch] box BRepPrimAPI_MakeBox(base[w], base[h], f.distance).Shape() solids[f.id] box elif f.type hole: body solids[f.face.split(.)[0]] axis gp_Ax2(gp_Pnt(f.position[0], f.position[1], -1), gp_Dir(0, 0, 1)) cyl BRepPrimAPI_MakeCylinder(axis, f.diameter/2, 100).Shape() solids[f.face.split(.)[0]] BRepAlgoAPI_Cut(body, cyl).Shape() return solids这段代码是简化版实际项目里要处理坐标系变换、面的选取、多实体管理等问题。但核心逻辑就是这样每个特征对应一次几何操作操作之间通过实体引用串联。5.5 第四步导出与验证模型建好后导出成 STEP 或 STL。STEP 保留参数化信息适合后续编辑STL 适合 3D 打印。from OCC.Core.STEPControl import STEPControl_Writer, STEPControl_AsIs def export_step(shape, path): writer STEPControl_Writer() writer.Transfer(shape, STEPControl_AsIs) writer.Write(path)导出后一定要做验证。我的习惯是导出 STEP 再用另一个工具打开检查尺寸对不对、孔位准不准。自动化验证可以算体积、包围盒尺寸跟预期值对比。6. 常见问题与排查技巧实录6.1 模型建出来是空的或者缺特征这是最常见的问题八成出在特征依赖上。比如打孔特征引用的面不存在或者拉伸的草图没定义。排查思路是打印中间表示逐个特征检查依赖是否满足。我一般会在建模前先做一次依赖检查把不满足的特征标出来。现象可能原因排查方法模型为空草图尺寸为 0 或负检查 width/height缺孔孔特征引用的面不存在检查 face 字段孔位置错坐标系理解偏差打印 position 值布尔失败实体不相交或重合检查拉伸距离和孔深6.2 布尔运算失败导致模型崩溃布尔运算是几何内核最容易出问题的地方。两个实体如果只是相切或者面重合布尔运算可能失败。解决办法是给一点容差比如孔深比板厚多 1mm确保完全穿透。这个技巧我用了很多次能解决大部分布尔失败。注意孔深不要刚好等于板厚留 0.5 到 1mm 余量。相切是布尔运算的天敌宁可多切一点。6.3 大模型解析不稳定同样的提示词结果不一样这是大模型的通病。解决办法有三个一是降低温度参数把 temperature 调到 0 到 0.2输出更确定二是加 few-shot 示例在提示词里给几个输入输出对模型会照着格式来三是加校验和重试解析失败就重试最多三次。def parse_with_retry(user_input, max_retry3): for i in range(max_retry): try: return parse_prompt(user_input) except Exception as e: if i max_retry - 1: raise continue6.4 尺寸单位混乱单位问题前面提过这里再强调一次。用户说“10 公分”模型可能理解成 10mm。我的做法是在提示词里强制要求输出单位字段并且在代码里做二次校验。如果 units 不是 mm统一换算。另外导出前再检查一遍包围盒尺寸跟提示词里的数值对一下对不上就报警。6.5 复杂模型生成质量下降提示词一长特征一多模型就容易漏特征或者参数错乱。这时候要把任务拆开不要指望一次生成整个复杂零件。我的策略是分步生成先生成主体确认没问题再追加特征。每一步都验证比一次性生成再debug省时间得多。7. 提示词工程在 Text-to-CAD 里的实战心得7.1 好提示词的三个特征用了这么久我总结出好提示词有三个特征尺寸明确、位置明确、工艺明确。“做一个板”是坏提示词“做一个 100×60×10 的铝板四角开 M6 沉头孔孔中心距边 10mm”是好提示词。前者系统只能猜后者系统能精确执行。如果你想让 Text-to-CAD 出好结果先把需求描述清楚这一步省不了。7.2 用“角色规则示例”三段式写系统提示词系统提示词我一般写成三段。第一段定角色“你是一个专业的 CAD 参数解析器”。第二段定规则“所有尺寸转 mm缺失位置按对称处理只输出 JSON”。第三段给示例一到两个就够。这个结构实测比一大段散文式的提示词稳定得多。7.3 迭代式交互比一次性生成更靠谱别指望一句话生成完美模型。我的工作流是先生成初版看哪里不对再用自然语言追加修改。“把中心孔改成 25”“四个角孔改成 M8”“板厚加到 15”每次改一个点系统在已有模型上增量修改。这种交互方式既符合工程习惯又能保证每步都可控。8. 这套东西还能往哪些方向延伸Text-to-CAD 目前最成熟的场景是标准件和简单结构件的快速生成比如法兰、支架、安装板。再往上走有几个方向值得关注。一是装配体生成从“把这两个零件装在一起”这种描述直接生成装配约束这块难度大很多因为涉及配合关系。二是工程图联动模型生成后自动出二维工程图标注尺寸和公差。三是制造约束注入在生成时就考虑加工工艺比如最小壁厚、刀具半径避免生成无法加工的结构。我自己最看好的方向是参数化模板加自然语言微调。纯从零生成不稳定但如果有一个参数化模板库用户用自然语言描述要改哪些参数系统在模板上做增量修改稳定性和实用性都会高很多。这其实就是把 Text-to-CAD 和传统参数化设计的优势结合起来也是我接下来打算重点折腾的方向。最后分享一个我踩过的坑一开始我总想让系统支持任意复杂的提示词结果发现维护成本高得离谱。后来改成限定支持的特征类型只做拉伸、打孔、阵列、倒角这几种反而把成功率做到了 90% 以上。做这类系统克制比贪心重要先把高频场景做扎实再慢慢扩。