做CAD开发这几年,我每周几乎都会碰到同一种需求:对方甩过来一句“做一个底座,120乘80,厚度10,四角打四个直径10的孔”,然后等你把三维模型交过去。以前人得老老实实打开CAD软件,画草图、标约束、拉深度,运气好十分钟,运气不好重新改尺寸就是一轮返工。现在text-to-cad要解决的就是这件事——让这一段自然语言直接变成可编辑的三维CAD实体,而不是一张渲染图,更不是一堆没法改的网格面片。
这篇文章我把这个方向的思路、技术选型、最小可跑通的方案,以及踩过的坑都整理一遍。适合三类人看:机械结构工程师,想做设计自动化的开发者,还有那些被重复建模烦到想偷懒的朋友。
1. 先把text-to-cad这件事想清楚
1.1 它到底在解决什么问题
text-to-cad的完整含义是“从自然语言文本生成CAD模型”。听起来和“文生图”“文生3D”很像,但骨子里完全不是一回事。文生图要的是视觉上的像,文生3D要的是看一眼能用的白模,而CAD要的是精确的几何尺寸、特征树、约束关系、可修改的参数,最终还要能出工程图、加工制造。
一句话概括:text-to-cad的核心目标不是“看起来对”,而是“制造上能用”。
我见过不少朋友一开始理解就跑偏,以为输入“一个花瓶”它就应该吐出一个水灵灵的花瓶模型。真实场景里更常见的输入是“M6沉头孔,四个,均匀分布在直径60的圆上”,或者“法兰外径80,内径40,厚度8,六个均布孔直径6.5”。这种描述充满了精确数字和工程语义,不是感性描述,而是参数表。
所以要解决的需求本质上是两件事。第一是理解:从模糊的、夹杂口语和工程术语的自然语言里提取出实体类型、尺寸、位置、约束关系。第二是生成:把这些提取出来的信息转成CAD系统能识别的建模操作,最好是带参数历史的那种,而非一次性网格。
这两件事单独看都不算新,但组合在一起就比较麻烦。语言理解是自然语言处理的活儿,几何建模是CAD内核的活儿,中间还隔着一层“如何把语义变成几何操作”的鸿沟。text-to-cad正是为这道鸿沟搭桥。
1.2 为什么“生成代码”比“直接生成模型”更靠谱
当年这个想法刚出来的时候,主流直觉是训练一个神经网络,从文本直接回归出体素、点云或者SDF场。后来大家发现这条路越走越窄。原因很简单:端到端生成的东西通常是一堆离散点或者一个带噪网格,来之不易,也无法在CAD软件里编辑。你拿一个STL三角网格去做工程变更,等于让裁缝拿着一块石头改西装。
真正过得硬的做法是让模型生成程序化建模指令——也就是CAD脚本或者参数化建模操作序列。
这个选择背后的逻辑很实际。至少有三点让我坚持这条路线:
第一,可编辑性。脚本是文本,参数是变量,想改尺寸就改数字,想改特征就改代码,改完重新执行一次即可。工程上最珍贵的就是“改得动”。
第二,精度。脚本让CAD内核去算几何,圆弧就是圆弧、平面就是平面、圆柱面就是圆柱面,全部是精确的解析曲面,而不是成千上万个三角形模拟出来的假曲面。
第三,LLM的特性。大语言模型训练的时候见过大量的代码和结构化文本,让它们输出JSON、脚本、函数调用,远比让它们从无到有发明一串顶点坐标靠谱。换句话说,模型做“翻译”很擅长,做“几何发明家”很不擅长。
我常拿一个生活类比:文生3D像是让AI用笔画一张桌子,看着神似但尺寸靠猜;text-to-cad是让AI写一张木工下料单,老师傅照单子就能加工出一张能用的桌子。CAD领域要的是下料单,不是画。
2. 核心技术路线拆解
2.1 一条能落地的处理链路:自然语言 → 中间表征 → CAD脚本
我的习惯是先画一条清晰的处理链路,再动手写代码。目前我个人认为最稳的text-to-cad落地链路是四段式:
自然语言输入,先交给大语言模型做语义抽取,得到一份结构化的中间表征。这份中间表征建议用JSON,因为它结构清晰、LLM不容易胡写、程序也好解析。然后把这份JSON交给一个自己写的执行器,翻译成目标CAD软件的动作序列,最后在CAD软件里执行并导出标准格式。
为什么要加一道“中间表征”?直接让大语言模型输出CAD脚本不行吗?我试过,效果时好时坏。问题在于CAD API往往很长很绕,模型记不住精确的函数签名,也要碰运气才能蒙对参数顺序。稍微错一个括号、写错一个属性名,脚本直接跑不起来。而且不同CAD软件脚本长得天差地别,绑定太死没有复用价值。
如果让模型只输出一份结构简单的JSON,事情就变得可控多了。JSON里只需要“有哪些实体、尺寸多少、放在哪里、做什么布尔运算”,这些是人类语义层面的东西,大语言模型学得很好。真正和CAD API打交道的脏活交给执行器,这一层代码是死的、可测的、保证不会语法错。
这套方案最明显的胜出点在于分工:大模型负责“懂人话”,代码负责“懂CAD”。两边各做各擅长的部分,中间用JSON当翻译本,谁也不用强行越界。
2.2 中间表征怎么设计才不容易翻车
中间表征设计是整个链路最关键的地方。设计得好,后面执行器写起来省一半力气;设计得不好,你就会整天收到“非法JSON”、“缺少必要字段”、“布尔运算目标引用不到”之类的报警。
我的设计原则可以概括成三句话:先原子后组合、尺寸显式化、运算顺序显式化。
先原子后组合的意思是,不要指望模型一次“想出来”一个复杂的带圆角带孔带加强筋的零件。而是让它先定义出若干基础实体,比如块、圆柱、圆锥、圆环,再定义一堆布尔运算把它们合并或者切掉。这个思路对应的是最经典的参数化建模流程:先拉伸出一个毛坯,再在上面打孔、挖槽、加筋。
尺寸显式化,就是要避免“大一点”、“薄一点”这种模糊词出现在JSON里。所有几何参数必须给具体数值。遇到用户说“薄一点”怎么办?我目前的处理方式是让模型给一个合理默认值,并在JSON里加一个confidence字段,标记该尺寸是用户明确给的是推导出来的。这样一来,后处理的人一眼看得出来哪里可能需要二次确认。
运算顺序显式化则是很多人容易忽略的坑。模型输出的JSON如果先执行了差集,再执行并集,和先并集再差集,结果可能完全不同。所以中间表征里必须包括一个有序的operations列表,每一步是加料还是减料,顺序必须严格按照建模逻辑。
这里给你一份我常用的最小JSON Schema,不算严谨但足够干活:
{ "units": "mm", "primitives": [ {"id": "base", "type": "box", "size": [120, 80, 10]}, {"id": "hole_1", "type": "cylinder", "radius": 5, "height": 10} ], "operations": [ {"op": "union", "target": "scene", "source": "base"}, {"op": "difference", "target": "scene", "source": "hole_1", "location": [10, 10, 0]} ] }正常情况下,我们会在原始描述里多给一条约束:所有未指定的圆孔方向,默认为垂直向下或垂直于对应平面。因为工程对象里绝大多数孔都是垂直于加工面的,把它做成默认规则,能减少一半的语义歧义。
2.3 校验回路:让模型自己先审一遍
大语言模型这东西,谁用谁知道,它总会偶尔给你来点惊喜。比如把直径60写成半径60,把“四个孔均布”理解成排成一排。我的做法是加一道双向校验回路。
第一道是对大模型输出的JSON做语法和合法性校验。解析失败就打回去让模型重新生成。缺失关键字段,比如没有operations,也要打回去。这一步干的就是“挡掉语法错误和低级幻觉”。
第二道是几何层面的理性校验。执行器解析JSON后,先估算体积,和用户描述的常识对照一下,比如一个120x80x10的板,体积应该是96000立方毫米,减去四个孔之后大约是92858立方毫米。如果生成模型实测体积差得离谱,那一定哪里出了问题,直接中断并交给模型自纠。
这一步听起来麻烦,但实际收益很大。CAD模型生成不像网页生成,错一毫米不会显示成乱码,而是默默变成一个错误零件。如果能在建模阶段就把量级错误拦住,后面就能少摔好多次跟头。
3. 实操:搭一个能跑通的text-to-cad最小方案
3.1 工具选型与整体结构
“最小方案”的意思是用最少的代码先把链路跑通,以后再加功能。我的建议是:大语言模型API选一个支持结构化输出的当前主流模型就好,执行器用Python写,三维内核用某款开源的脚本化CAD软件来承载。
我这么说是有原因的。大模型承担的是语义解析,每次调用输出JSON,不需要做真正的几何计算。执行器做的是把JSON翻译成脚本化CAD软件能听懂的命令,这个时候Python的灵活性就很关键。开源脚本化CAD软件则负责最终把脚本变成精确的三维实体。这一套下来,每一层都各司其职,整体验证起来非常快。
整体结构大概是这样:
def text_to_cad(user_input): cad_json = parse_to_json(user_input) # 调大模型,得到结构化描述 validate_json(cad_json) # 语法和字段校验 script = generate_script(cad_json) # Python执行器,转成CAD脚本 execute_in_cad(script) # 在脚本化CAD工具里建模并导出这段伪代码对应了整条链路的核心骨架。我自己开发的时候会在generate_script这个函数里加很多日志,打印出每一步生成的几何操作。因为一旦出问题,你能立刻知道是模型理解错了,还是执行器翻译错了,排查效率高出不少。
3.2 用结构化提示词约束大模型输出
我见过很多人让大模型自由发挥,结果当然是花样百出。既然前面已经定了中间表征格式,提示词就应该把模型“关”在一个固定的JSON框架里。
我通常会在系统提示词里写四类内容:角色定义、输出格式、单位约定、兜底规则。
角色定义一般是“你是一个机械设计助手,负责把用户的需求解析为结构化JSON”。输出格式直接贴上JSON模板,并且强调只能输出JSON,不许解释、不许加Markdown代码块标记。单位约定设为毫米,并且提示所有尺寸默认是毫米。兜底规则则是“如果用户描述缺少尺寸,给出一个合理的工程默认值,并在对应字段里标注来源”。
提示词里我对尺寸来源字段的要求最细。我们取一个字段名,比如size_source,它有“user”和“inferred”两个值。用户明确说的填user,模型猜的填inferred。这招在后续人工复核时极其好用。有一次批量建模,很多孔的深度是模型猜的,后处理的人只筛选inferred字段,瞬间就定位到所有需要确认项,省了不少事。
另外还有个小技巧:让模型在返回JSON前先“自我复述”一遍关键尺寸。比如在JSON里增加一个summary字段,把“底板120x80x10,四个孔直径10,位于四角”这种话写出来。因为复述的过程会强迫模型重新过一遍语义,能明显降低尺寸漏掉和错位的概率。
3.3 执行器与几何验算
当大模型吐出JSON之后,就轮到执行器干活了。执行器的逻辑其实很简单:遍历primitives,创建基础实体;遍历operations,按照顺序做布尔运算或者移动操作。
我推荐把执行器写成“图元解释器+运算执行器”两个类。图元解释器负责把JSON里的box、cylinder、cone翻译成脚本化CAD软件的基础图元调用。运算执行器负责把union和difference翻译成对应的布尔运算。这样不管将来换CAD内核还是换JSON格式,都只改一层,不会牵一发动全身。
下面是我常用的一段核心逻辑示意:
def generate_script(cad_json): lines = [] for prim in cad_json["primitives"]: lines.append(create_primitive(prim)) for op in cad_json["operations"]: lines.append(create_boolean(op)) return "\n".join(lines)这里面的create_primitive和create_boolean,每个工具各不相同,但接口只要稳定,后面非常省心。
几何验算是很多demo里被忽略的一步,但恰恰是最值得做的。以开头那个例子的底板为例,毛坯体积120乘80乘10等于96000立方毫米;每个孔直径10、半径5、深度10,体积就是π乘5的平方乘10,约785.4立方毫米;4个孔合计约3141.6;最后模型的实际体积大约是92858立方毫米。执行器跑完,我们可以让脚本化CAD软件返回实体的体积属性,把这个值和理论值对比。误差在1%以内,说明布尔运算执行成功;误差大了,几乎可以断定某个孔没切上或者切漏了。
这个验算动作看起来死板,但它能自动筛掉大量低级错误,比人眼盯屏幕可靠多了。
4. 常见翻车现场与排查经验
4.1 尺寸老是错,不是数值错而是单位错
我在实测中遇到最多的一类问题是尺寸整体差了1000倍。比如用户说“外径80毫米”,生成的模型实测尺寸是0.08毫米。为什么会这样?因为默认情况下,有相当一部分开源建模工具把数值理解成“单位制内部量”,而大模型有时会在JSON里漏掉units字段,或者跑偏成米制。
我的排查方法很简单:第一件事永远看JSON里的units字段。第二件事看summary复述里有没有“毫米”字样。两处都没问题再看执行器里有没有偷偷做单位换算。单位错误是低级错误,但低级错误最坑人,因为它不报错,只是成品静悄悄小一千倍。
我的对策是,在提示词里写死“所有输出单位为毫米”,并在JSON Schema里把units设置为必填字段,合法的值只有mm这一种。校验时如果units不是mm就直接打回。粗鲁但有效。
4.2 坐标和布尔运算引发的消失面与飞孔
还有一种极常见的问题:孔没打在板上,飞在半空中,或者布尔运算失败导致整个零件消失。
原因多半出在坐标基准不一致。大模型理解的“孔位于左上角位置”和CAD脚本化工具默认的坐标系原点,根本不是一个概念。你说“左上角”,它认为那是X负Y正;你说“四角均布”,它不知道角上还要留出边距。
后来我在prompt里强制增加一条规则:所有几何位置必须以零件最小包围盒的XY平面为基准,原点取零件中心,并且孔位坐标必须是相对零件的坐标,不是绝对世界坐标。这样一改,大量位置错误瞬间消失。
布尔运算出问题也同样常见。原因通常是操作顺序不对,或者是差集的目标引用错了。比如你想切孔,JSON写成了union,或者把hole的实体直接加进去,孔就变成一个凸起圆柱。我建议把布尔运算和几何实体的定义彻底分离,这样模型只需要关心“哪些部分留着、哪些部分干掉”,不用去猜具体API的运算名。
4.3 生成结果没法编辑,导出格式不对
有个朋友拿我给的方案去试,兴奋地跑出一个零件,接着就傻眼了:导出的是STL网格,拿到CAD软件里只能当哑巴模型看,尺寸也改不了。这个问题根子上是因为脚本化CAD工具默认导出格式往往是STL,但你真正交给生产线的应当是STEP或者原生参数化文件。
解决办法是执行器负责一键导出多种格式,至少包含STEP和原生脚本格式。STEP是国际通用的CAD交换格式,能保留B-rep精确曲面,别人拿过去能切开、能延伸。原生脚本格式则保留了建模全过程,随时可以改参数重新生成。只有STL,只适合3D打印或者做渲染预览。
所以排查任何“不能编辑”问题,第一步就是看导出后缀。是STL就别抱怨不能编辑,换STEP再看。
4.4 问题速查表备查
我把自己踩过的坑整理成一张速查表,挂在项目文档里,协作的时候很管用。
| 异常现象 | 最可能原因 | 排查方向 | 对应解法 |
|---|---|---|---|
| 零件整体小/大1000倍 | 单位制冲突 | 查看units字段 | 强制mm并校验必填 |
| 孔不在实体上 | 坐标基准不一致 | 检查位置字段定义 | 改用相对坐标+默认平面 |
| 零件变成两个体 | 布尔运算失效 | 检查布尔类型 | 运算实体与目标实体分离 |
| 孔变凸台 | union/difference写反 | 检查operation顺序 | 用模板限制可用op类型 |
| JSON解析失败 | 模型输出多余文本 | 检查返回内容 | 系统提示词强制纯JSON |
| 网格模型不可编辑 | 导出了STL | 查看导出文件后缀 | 改为STEP+原生脚本 |
| 尺寸是猜的心里没底 | 缺少来源标注 | 检查size_source | 标注inferred字段方便复核 |
这张表不解决所有问题,但能把你从深夜调试的崩溃边缘拉回来。
5. 应用场景、人群与边界
5.1 哪些场景真的受益
我发现text-to-cad最立竿见影的场景不是复杂精密设计,而是“标准件+变参数”这种重复劳动。比如法兰盘、安装支架、基座板、电机座这类零件,结构形式固定,只有尺寸和孔位变化。传统流程是每次重新建模,现在一句话改参数就能出新模型,效率完全不在一个量级。
创客和3D打印用户也很适合。很多玩家脑海里有个大概形状,直接描述出来,几分钟后就能得到可打印的STL。不用学繁琐的建模软件,需要的只是把需求说清楚。
早期概念设计阶段也很实用。工程师在方案阶段往往只需要一个大致的装配布局,确定空间够不够、孔位对不对得齐。这个阶段用text-to-cad快速出草模,比手动建模快得多,而且后续还能继续迭代参数。
5.2 短期内别指望它干什么
看得清边界,才不会被失望绊倒。现在的text-to-cad在下面几类问题上还远谈不上替代人工:
第一,自由曲面和艺术造型。“做一个像水滴一样的曲面外壳”这种需求,靠程序化形状拼装很难做出来,还是要靠曲面设计软件手工操作。
第二,大型装配体。比如整台设备几十上百个零件之间的装配关系、运动约束、干涉检查,这已经超出“从文本生成模型”的范畴,属于系统工程,不是一句话能解决的。
第三,有严苛制造约束的零件。生成的模型可能几何上正确,但CAM工艺上不合理:比如孔间距太近导致壁厚不足,或者加强筋位置不利于模具脱模。这类问题需要制造知识把关,目前模型显然还做不到。
text-to-cad的定位更像是一位“手速很快的建模助理”,而不是“资深结构工程师”。它能帮你把头脑里的想法快速变成三维草稿,但最终能不能下车床、能不能开模具,仍然需要人来判断。
按照我个人的使用习惯,现在最顺手的工作流是:口述需求生成第一版模型,然后打开脚本化CAD工具调整关键参数,确认没问题后再导STEP给结构同事评审。一次两次可能没有感觉,用多了你会发现,真正为你省时间的不是建模那十分钟,而是后续少改的那好几轮。如果你的工作里也有大量“换个尺寸重新画一遍”的需求,text-to-cad这个方向非常值得你花一个周末把最小链路搭起来再下结论。