ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

text-to-cad:用一句话生成可编辑CAD模型的落地实践与避坑指南

text-to-cad:用一句话生成可编辑CAD模型的落地实践与避坑指南

上个月,一个做结构设计的朋友跟我抱怨,说他们公司每天都在画相似的支架、壳体、安装板,参数换来换去,全是重复劳动,问我能不能让AI照着一段描述直接生成CAD模型。我当时的直觉是,这事对应的方向就是text-to-cad,把自然语言变成可编辑、可加工的三维模型。断断续续试了两周之后,我的结论很明确:这件事不仅能跑通,而且已经能放进实际工作流,关键看你怎么设计链路,以及怎么管住那些"看着能出图、实际没法用"的坑。

text-to-cad解决的核心问题,是把人在脑子里构思的几何信息,用一段普通的话表达出来,再由机器完成建模。它不是要取代设计师,而是把"描述需求→画出草图→反复改尺寸"这条链路上最耗时间的部分自动化。所以这篇文章适合三类人:想给产品加设计自动化能力的开发者、整天被改版折磨的机械结构工程师、做3D打印想快速起型的创客。我会从方案选型讲到核心原理,再给一套可以直接照抄的落地流程,最后把我踩过的坑全部摊开。

1. 整体设计思路与方案选型

1.1 先想清楚:text-to-cad真正要替代的是哪一段

很多人一听到"自然语言生成CAD",就以为是让AI直接交付一个完整产品模型。我的建议是先把这句话降温。传统设计流程从需求到出货,至少要经过需求沟通、概念草图、参数计算、详细建模、出图评审、加工准备六个环节。text-to-cad目前最擅长的是其中一小段,也就是"概念草图加初步参数"这一段。它把你口头描述的形状、尺寸、装配关系,快速变成可视化的几何实体,供人确认、修改、迭代。

这就像写文章时先用口语把大纲讲给助理听,助理整理成初稿后你负责审改。你省掉的是从空白画布开始建模的时间,而不是放弃对设计的判断。一个人说"做一个带四个沉孔的L形支架",系统能马上给出一个大致尺寸正确的支架模型,你再在上面改孔位、调整圆角,比从零画快得多。如果你指望一句话直接替代整套详细设计和工艺分析,那目前还没有哪个方案能做稳。明确边界之后,项目才不会越做越模糊。

1.2 三条常见技术路线,我为什么选"程序化生成"

我在动手之前先调研了三类实现思路,这也是目前text-to-cad方向最常见的分岔路口。

第一类是让AI像生成图片一样直接输出三维网格模型,常见的产物是STL或OBJ文件。这类方案的优点是门槛低、速度快,模型看起来也像那么回事。但它的问题对工程来说几乎是致命的:生成的是"像素级"表面网格,没有特征树、没有参数、没有约束关系。你想改一个孔的位置,等于重新生成一次;你想导出成STEP文件进加工流程,网格数据也基本派不上用场。

第二类是文本检索已有模型库,根据描述匹配最接近的模型。准确率和稳定性都还不错,但本质是"查库",不是"设计"。库里没有能组合出新功能的模型,它就无能为力。这类方案适合做推荐,不适合做创作。

第三类方案是用语言模型把自然语言解析成参数化建模指令,再交给CAD内核去执行。建模指令本质上是一段结构化的程序,LLM只负责"翻译",几何计算由内核完成。最终生成的模型自带特征树,拉伸就是拉伸,打孔就是打孔,参数可以改,步骤可以回退。我最终选的就是这条路线,具体对比如下。

技术路线可控性几何精度可修改性创新性实现成本
直接生成网格低低低中低
文本检索模型库高高低低低
文本生成参数化脚本高高高高中高

选择第三路线还有一个很实际的考虑:稳定性。让LLM直接操作CAD内核的底层API,它会频繁调用不存在的函数、传错参数类型、写出自相矛盾的几何操作,排查起来非常痛苦。把LLM限制在一个小的指令集里,它只能做"意图识别和槽位填充",出错概率会低一个数量级,出了问题也好定位。

1.3 四层架构:从文本到实体,链路越短越容易崩

我最终采用的系统分为四层:解析层、校验层、执行层、导出层。解析层由LLM负责,把用户文本转成结构化指令;校验层用规则代码检查指令是否合法,缺失参数补默认值,越界参数自动调整;执行层调用参数化CAD内核生成实体;导出层负责输出STEP、STL或原生格式文件。

为什么必须分层,而不是让LLM一步到位直接输出最终文件?因为每一层都可以单独测试、单独替换。解析层出了问题,看JSON输出就知道它哪里理解错了;执行层出了问题,把JSON重新喂给内核就能复现;将来你想换一个更快的CAD内核,也不影响前面的解析逻辑。我实际操作中还会在每层写入日志,这样用户说了一句"生成失败",我能直接看到是解析失败、校验失败还是内核执行失败,而不是对着黑盒瞎猜。

2. 核心技术细节解析

2.1 先给CAD建模"做减法"

CAD软件的功能极其庞大,但常规零件真正高频使用的特征并没有多少。我统计了一下自己过去处理过的模型,拉伸、旋转、布尔运算、打孔、倒角、阵列、扫掠这七类操作,覆盖了绝大多数机械零件和消费电子产品结构件。所以text-to-cad系统的第一步,不是想方设法让LLM学会所有功能,而是把可执行的建模指令限制在这几个类别里。

这步"做减法"非常重要。一个自由发挥的LLM会给你生成各种匪夷所思的建模脚本,但你给了它一个固定的指令清单之后,它要做的事情就变成了选择题加填空题。"L形支架"对应一个拉伸草图加一次布尔或圆角,"四个沉孔"对应四个孔特征,"圆盘中心孔"对应旋转体加打孔特征。人的语言里描述形状的词汇虽然多,但落到几何特征层面,基本都能映射到这几类操作上。指令集可控,解析准确率自然就上去了。

2.2 从一句人话到一个JSON:语义解析

理解文本并转成结构,是整个链路中最关键的一步。我拿一个简单例子说明。用户说"做一个直径10的圆盘,中心开一个直径4的孔"。一个有经验的工程师会这样解析:对象是圆盘,旋转体,直径10,高度未说明就按默认值处理;随后在圆心位置打一个直径4的通孔。LLM要做的也是这件事,业界通常称之为意图识别加槽位填充,意图是识别出"创建拉伸体"和"创建孔特征",槽位是提取直径、高度、位置这些参数。

我的建议是让LLM以JSON格式输出解析结果。还是这个例子,理想的输出是这样的:

{ "steps": [ { "type": "extrude", "sketch": { "shape": "circle", "diameter": 10, "plane": "XY" }, "depth": 3, "unit": "mm" }, { "type": "hole", "target": "face_center", "diameter": 4, "through": true, "unit": "mm" } ] }

这里有个经验:不要让LLM去做数值计算。比如用户说"直径是半径的两倍,半径是5",你让它自己算出直径等于10,这通常会出错。更稳妥的做法是让它直接输出"diameter_expression": "r*2",把具体计算留给后面的代码层。LLM擅长的是提取语义,不擅长算数,把计算从它手里拿走,准确率会立刻提升。

2.3 DSL设计:给模型一张"填空题"而不是"作文题"

既然LLM不能直接自由操作CAD内核,那它们之间就需要一个中间语言,也就是领域特定语言DSL。我这里用的DSL是JSON,因为JSON天然可校验、可枚举、可版本管理,也方便调试。设计DSL时,我会为每个指令规定好允许的参数类型和取值范围,比如extrude指令必须有sketch对象和depth数值,hole指令必须有diameter和position字段。

这等于给LLM一张"填空题"而不是"作文题"。它只能在限定的框架里输出,不小心说错也会被校验层拦下来。比如它生成一个不存在的指令类型"make_thing",校验层会直接拒绝,而不会让内核崩溃。JSON的另一个好处是方便做后续的自动化处理,你可以把一个JSON放进参数模板批量替换,也可以把多个JSON合并成一个装配体脚本。整条链路因为有这个中间格式,变得非常透明。

2.4 单位、默认值和参数推导

自然语言里充满了"厚一点""大概""比这个大一点"这类模糊表达,这些不能让LLM硬猜,也不能直接扔给内核。我的做法是在校验层前面加一个参数补全模块,由预设规则生成默认值。比如默认板厚3毫米、默认壁厚2毫米、默认倒角0.5毫米、默认孔间距0.2毫米。这些规则完全基于常见加工经验,不同行业可以自己调整。

另一种情况是语言里隐含了常识,例如"一个A4纸大小的托盘,四周有2厘米高的边沿"。LLM知道A4纸的标准尺寸是210乘297毫米,它可以把底座参数提取为255乘180并标记为"托盘外沿",边高20毫米,再交给补全规则填充默认壁厚。这就是参数推导的实际效果。顺序上,先让LLM提取明确的数值和单位,再把空值交给默认规则,最后做范围校验,全部通过再进入内核执行。单位换算也要在这一层统一成毫米,否则后面会出现尺寸放大十倍的惨剧。

3. 实操:从零搭一个能跑的text-to-cad工作流

3.1 环境准备与工具选型

我用的环境是Python 3.10以上,搭配一个开源参数化CAD内核,再接入支持JSON输出的对话模型API。目前主流的开源参数化CAD内核有好几个,接口风格大同小异,下面示例里的函数名来自我这边的常用封装,你换成别的内核只需要改函数名。为什么选Python?因为这类库的生态最全,后续做网格检查、布尔运算、格式转换都有现成库可以接。

# 简化示例,展示内核执行时的核心概念 from cad_backend import Document doc = Document() sk = doc.add_sketch(plane="XY") sk.add_rect(width=80, height=50, center=(0, 0)) part = doc.extrude(sk, depth=10) doc.export("demo.step")

这段代码做的事情是:创建一个草图平面,画一个80乘50的矩形,拉伸10毫米,导出STEP文件。它代表的是text-to-cad执行层的全部逻辑:拿着预先设计好的JSON指令,把它变成真实的几何实体。实际项目里,JSON里的每一步都会对应到类似的调用。先把这条"最底层通路"跑通,再去接LLM,出问题时你才知道到底是模型理解错了,还是接口调用错了。

3.2 设计最小指令集与Schema

我在系统里定义了七个核心指令,全部以JSON格式表达。表格如下:

指令名作用关键参数
create_sketch创建2D草图shape, size, plane
extrude拉伸草图depth, direction
revolve旋转生成axis, angle
boolean布尔运算operation, target, tool
hole打孔特征diameter, depth, position
fillet倒角edges, radius
pattern阵列复制copies, spacing

每个指令在LLM的prompt里都有明确的Schema说明,例如create_sketch只允许填rect、circle、polygon三种图形,extrude的depth必须是正数。这样LLM的输出空间就被限制住了。实际测试下来,指令集越小,LLM输出的合法率越高。我最初版本有二十多个指令,合法率只有六成;收敛到七个核心指令之后,合法率提升到九成以上。多一些规则校验,少一些自由发挥,这是text-to-cad系统稳定性的根本。

3.3 Prompt设计:像给实习生派活,而不是考它

Prompt是解析层的灵魂。我的设计思路是"给实习生派活",明确告诉他任务边界、输出格式、常见错误和示例。下面是核心模板,你可以直接改造:

你是一位CAD建模助手。你的任务是根据用户描述,输出一段结构化建模指令JSON。规则: 1. 只输出JSON,不要解释。 2. 所有长度单位默认毫米。 3. 所有数值必须在0.1到2000之间,超出范围的数值填null。 4. 不要计算任何数值,只需要提取;如果描述中包含逻辑关系,用表达式输出。 5. 可用指令类型包括:create_sketch, extrude, revolve, boolean, hole, fillet, pattern。 示例: 用户:做一个直径60的圆盘,中心开直径20的孔 输出:{...}

注意规则4,我之前一直强调不让LLM做计算,在这条规则里落实了。你可能会问,为什么不直接让LLM生成CAD内核代码?因为代码的自由度太高,容易出现非法调用;而JSON经过校验后,十有八九能直接进内核执行。Prompt里给示例也极其重要,LLM的few-shot能力非常依赖示范,示例里的单位写"mm",它就会跟着写mm;示例里数值带小数点,它也会模仿。

3.4 参数校验与自动修复

LLM输出JSON后会进校验层。这里我会做四层检查:类型检查、范围检查、拓扑检查、语义检查。类型检查确保depth是数字不是字符串;范围检查确保尺寸在合理区间;拓扑检查确保布尔操作的两个实体存在且不相重合;语义检查确保没有"把孔打在孔上"这类明显错误。能自动修复的错误就地修掉,不能修复的就返回错误信息并附上原因。

# 校验与自动修复的简化逻辑 def check_and_fix(step): if step["type"] == "extrude": depth = step.get("depth") if depth is None: step["depth"] = 3 # 默认拉伸深度 if not isinstance(depth, (int, float)): raise ValueError("深度必须是数值") if depth < 0: step["depth"] = abs(depth) if depth > 2000: step["depth"] = 2000 return step

这段代码看起来简单,但在整个系统里扮演的角色非常重要。我实测过,加了这层之后,内核被执行时报错率降低了七成。很多看起来是LLM的问题,其实是上游参数不合法被内核暴露出问题,而这层完全可以兜住。

3.5 完整案例:一行话生成一颗M5螺丝

把各环节串起来,我用一句常见的描述演示:输入"一颗M5的十字盘头螺丝,头部直径9,头部厚度3,螺杆长度25,螺距0.8"。解析层输出的JSON大致是:

{ "parts": [ { "type": "revolve", "profile": "screw_head", "angle": 360 }, { "type": "cylinder", "diameter": 5, "length": 25 }, { "type": "boolean", "operation": "union", "target": "head", "tool": "shaft" }, { "type": "helix_thread", "pitch": 0.8, "length": 25 }, { "type": "fillet", "edges": "head_cross", "radius": 0.5 } ] }

校验层确认尺寸都在合理范围后,执行层把这五个步骤依次交给内核。头部用旋转体生成,螺杆用圆柱生成,两个实体做合并,螺纹用扫掠切割模拟,最后给头部边缘倒圆角。最终得到一个带特征树的STEP文件。你怎么验证它是对的?可以检查体积是否符合原料估算,可以导出STL进切片软件看是否流形,也可以直接放进商用CAD软件里打开看一眼,这些都比对着屏幕猜靠谱。

4. 常见问题与排查技巧实录

4.1 尺寸离谱怎么排查

这是所有text-to-cad项目里最容易踩的坑。用户说直径120,模型生成出来却是个1200毫米的庞然大物。我排查后发现,问题出在LLM把单位认知搞混了,它在输出里写"120",但下游某一步把它当成了米。从那之后,我在DSL层固定所有单位换算,LLM只输出数值和单位字符串,解析层统一转成毫米。同时给数值范围加硬边界,超过边界的直接拒绝或调整。

另一个经验是给LLM的示例全部带上"mm"字样。模型会模仿你的示例格式,你给足了带单位的示范,它输出的自然就是带单位的结构化数字,转换层的负担会小很多。排查尺寸问题时,先看JSON里的原始数值,再看单位字段,最后才看内核执行结果,三步基本能定位。

4.2 生成的模型"能看不能造"

有一次我生成一个安装支架,屏幕上看起来完全正常,壁厚、孔位、外形都合理。但导进切片软件时,软件提示存在非流形边,模型无法打印。原因是布尔运算产生了悬面,简单说就是两个实体合并后,内部残留了一片没有厚度的面,这个在工程上是致命的。

我的解决办法是引入制造可行性检查,也叫设计规则检查。在导出前跑一遍所有实体是否闭合、边是否非流形、最小壁厚是否大于0.5毫米、最小倒角是否大于0.2毫米。这些规则不用很复杂,但对"能看不能造"的模型拦截效果立竿见影。执行层生成完模型不是终点,验证"人类能不能加工它"才是终点。

4.3 复杂装配体怎么拆

直接让LLM一次性生成一个十零件的装配体,错误率高得惊人。我的做法是先拆分后装配,让LLM为每个零件输出独立的JSON,再用固定的命名规范把它们关联起来。例如一个"桌面加四条腿"的桌子,拆成part_desk_top、part_leg_1到part_leg_4,每条腿的JSON结构一致,只是位置参数不同。这大大降低了单次输出的复杂度,也方便后续批量替换和参数调整。

成本方面也有讲究。同一个LLM调用,让它输出越多内容,失败率和token消耗都越高。我建议一次调用最多对应一个零件,宁可多次调用也不要贪多。整体装配可以放在执行层完成,内核的装配功能远比LLM生成装配关系稳定。

4.4 延迟和成本控制

实际使用中,token消耗是个绕不开的话题。我的做法是给系统加一层缓存,把用户的输入文本计算哈希,相同描述直接返回上次生成的JSON和模型文件。对高频的"支架、壳体、安装板"这类模板化需求,命中率非常高。另外可以先用一个轻量模型做意图分类,只把复杂的新设计交给更强的模型生成细节,这种两级方案能省下不少成本。

现象可能原因解决办法
模型尺寸变大十倍单位换算错误DSL层统一转毫米,LLM只给数值和单位
生成成功但无法打印非流形边、悬面导出前跑网格检查和制造规则检查
多零件位置全跑偏装配参数依赖LLM拆分零件JSON,用固定命名和位置规则
频繁超时单次生成内容过多一次只生成一个零件,增加缓存
JSON频繁非法指令集太宽收敛到最小指令集,增强Prompt示例

5. 从demo到落地:几个我认为值得做的扩展方向

5.1 反向链路:让参数化模型"开口说话"

既然已经建立了"自然语言到参数"的映射,反向链路其实是顺理成章的。一个参数化模型加一套文本模板,就能自动生成"这是一颗M5十字盘头螺丝,头部直径9,长度25"这种描述。这件事的价值在于设计文档自动化、三维模型检索和跨部门沟通。结构工程师不用再逐帧截图写说明,产品经理可以直接读到模型语义。

5.2 批量变体生成

text-to-cad最有用的场景不是从零设计,而是快速生成变体。只要有了参数化模板,一句"生成五种腿长不同但风格一致的桌子",系统就能在预设的尺寸范围内批量采样出五个模型。LLM在这里只负责把需求映射到模板,采样交给程序,效率和稳定性都远高于让LLM直接想象五个模型。

5.3 工具是放大器,不是许愿机,这是我最大的体会

搞了大半年这类系统,我的体会是text-to-cad最大的价值不是"说一句话就得到完美产品",而是把设计师从参数搬运工的角色里解放出来。我自己现在的习惯是,动手画图之前先拿一两句话描述需求,让系统给出一版可以看的模型,再在这个基础上去改细节,比从空白画布开始快了一大截。下一步我打算把材料、加工工艺和成本估算也接进来,让文本从建模阶段直接驱动到报价阶段。这条路还很长,但方向已经验证过,可以继续走。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进