
1. 从一句话到三维模型text-to-cad 到底在解决什么问题第一次听到 text-to-cad 这个词我脑子里蹦出来的画面是对着电脑敲一行字屏幕上直接长出一个能用的三维零件。这个方向这两年确实火但真正动手做过的人都知道它远没有宣传里那么一键生成。我前后折腾了大半年从最早的纯文本生成网格到后来接 STEP、GLB、STL 各种格式的导出链路踩的坑比想象中多得多。先把概念说清楚。text-to-cad 指的是用自然语言描述一个零件或结构由程序自动生成对应的 CAD 模型文件。这里的 CAD 不是指某个具体软件而是指参数化、可编辑、带精确尺寸的几何模型最终落地成 STEP、GLB、STL 这类通用格式。它要解决的问题很实际传统建模需要人手动拉伸、打孔、倒角一个简单支架可能就要画半小时而 text-to-cad 想做到的是你说一个 80x60x5 的底板四角各一个 M4 通孔中心一个直径 30 的凸台高 10 毫米程序直接把模型吐出来。适合看这篇内容的人我大致分三类。第一类是做机械设计或产品结构的工程师想把手头重复性的建模工作自动化第二类是做 AI 应用开发的程序员想在自己的产品里集成模型生成能力第三类是创客和 3D 打印玩家手里有打印机想快速把脑子里的想法变成 STL 去切片。这三类人的诉求不一样但底层要打通的链路是同一套文本解析、几何构造、格式导出、精度校验。我自己的项目背景是这样的一开始只是想做个批量生成标准件的小工具比如法兰、支架、垫片这些结构固定的东西。后来发现光生成网格mesh不够用因为网格没法精确改尺寸加工厂也不认。于是转向参数化建模再往后才接触到用大模型做文本到代码的转换。整个过程走下来我对 text-to-cad 的理解从魔法变成了一套需要精心设计的工程管线。下面我把这套管线拆开讲包括选型逻辑、核心实现、实操步骤和那些文档里不会写的坑。2. 整体方案设计为什么不能只靠一个大模型2.1 三种主流技术路线的取舍做 text-to-cad绕不开的第一个决策就是走哪条技术路线。我实测下来市面上能落地的方案基本归为三类各有各的适用边界。第一类是文本直接生成网格代表思路是用扩散模型或点云生成网络输入文本描述输出三角网格。这条路生成速度快形状自由度高适合做概念草图、艺术造型。但问题也很致命生成的网格拓扑混乱尺寸不可控你让它生成直径 30 的孔它可能给你个 28.7 的近似圆。对于需要装配的零件这基本没法用。第二类是文本转代码再执行也就是让大模型输出建模脚本比如 CadQuery、OpenSCAD 的代码再由脚本引擎执行生成精确几何。这条路是我最终选定的方向因为它的尺寸精度完全可控生成的模型天然带参数改一个数字就能重新生成。代价是对模型的代码能力要求高描述稍微复杂一点就容易生成语法错误或逻辑错误的代码。第三类是文本转参数表预先定义好一批参数化模板大模型只负责从文本里抽取尺寸和特征填进模板。这条路最稳但灵活性最差只能覆盖预设好的结构类型。我把三类的核心差异整理成一张表方便你对照自己的需求选路线精度灵活性开发成本适合场景文本生成网格低高中概念设计、艺术造型文本转代码执行高中高高工程零件、可编辑模型文本转参数表高低低标准件批量生成提示如果你的目标是做能直接送去加工或 3D 打印的零件别在网格生成上浪费时间直接上文本转代码这条路。我早期在网格方案上耗了两个月最后发现精度问题根本绕不过去。2.2 为什么选 CadQuery 而不是 OpenSCAD确定走代码路线后下一个问题是选哪个建模引擎。主流的有两个OpenSCAD 和 CadQuery。我两个都深度用过最后选了 CadQuery理由值得说清楚。OpenSCAD 的优势是语法简单像写伪代码大模型生成起来错误率低。但它的底层是 CSG构造实体几何所有操作都是布尔运算的堆叠做复杂曲面和倒角非常吃力而且导出的 STEP 质量一般。更麻烦的是OpenSCAD 的坐标系和特征定位全靠手动算写一个带多个孔的板子代码里全是 translate 和 rotate 的嵌套可读性差。CadQuery 基于 OCCTOpen CASCADE Technology内核这是工业级的几何内核导出的 STEP 精度高支持真正的 B-rep边界表示建模。它的 API 更接近工程思维比如选面、选边、在面上打孔这些操作都很自然。缺点是学习曲线陡大模型生成 CadQuery 代码的出错率比 OpenSCAD 高一些。我的做法是用大模型生成 CadQuery 代码但加一层校验和重试机制。具体来说生成的代码先在沙箱里执行捕获异常把错误信息回传给模型让它自我修正最多重试三次。这套机制把成功率从最初的六成左右提到了九成以上。这个思路后面在实操部分会详细展开。2.3 格式导出的链路设计模型生成出来最终要落地成文件。text-to-cad 涉及的主要格式有三个STEP、GLB、STL它们各自解决不同的问题不能混用。STEP 是精确的 B-rep 格式保留了完整的几何定义和拓扑关系是 CAD 软件之间交换的标准。你要把模型导进 SolidWorks、中望 CAD 或者 FreeCAD 继续编辑必须用 STEP。它的缺点是文件大且不含材质和颜色信息。GLB 是glTF 的二进制版本主打轻量和 Web 友好带材质、颜色、光照信息。你要在网页里做 3D 预览或者导入 Blender 做渲染GLB 是最佳选择。它本质上是网格格式精度不如 STEP。STL 是3D 打印的事实标准只描述三角面片没有单位、没有颜色、没有拓扑。切片软件认它但它是最笨的格式一旦导出就基本没法再精确编辑。我的导出链路是这样设计的以 CadQuery 的模型对象为唯一数据源同时导出三种格式。STEP 直接由 CadQuery 导出GLB 通过中间转换成网格再打包STL 也是从网格导出。这样保证三种格式的几何是一致的不会出现 STEP 和 STL 对不上的情况。这里有个细节从 B-rep 转网格时弦高容差tolerance这个参数直接决定网格精度和文件大小设太小文件爆炸设太大圆孔变多边形后面会讲怎么调。3. 核心实现细节文本怎么变成精确几何3.1 文本解析与特征抽取的关键设计text-to-cad 的第一步是把人话翻译成结构化的建模意图。这一步做不好后面全白搭。我的做法是分两层先做意图分类再做参数抽取。意图分类是判断用户想要什么类型的操作。是生成一个新零件还是修改已有模型还是查询某个尺寸这个用大模型做 few-shot 分类就能搞定准确率很高。参数抽取才是难点因为工程描述里的尺寸、位置、特征关系非常密集。举个例子用户说一块 100 长 50 宽 8 厚的板左边两个 M5 沉头孔孔心距边 15间距 30。这句话里包含了基体尺寸100x50x8、特征类型沉头孔、规格M5、数量2、定位约束距边 15、间距 30、方位左边。如果直接让大模型生成代码它很可能把距边 15理解成距某一条边而实际工程里这个边是有歧义的。我的解决方案是在提示词里强制模型输出结构化的 JSON 中间表示而不是直接输出代码。JSON 里明确定义每个特征的坐标系、参考基准、尺寸值。然后再用一个确定性的代码生成器把 JSON 转成 CadQuery 代码。这样做的好处是JSON 层可以做校验比如检查孔是否超出板边界、尺寸是否矛盾把错误拦在生成代码之前。# 中间表示的结构示例 { base: {type: box, length: 100, width: 50, thickness: 8}, features: [ { type: counterbore_hole, spec: M5, count: 2, positions: [ {x: 15, y: 15}, {x: 15, y: 45} ], reference: bottom_left_corner } ] }这个中间层是我整个项目里最有价值的设计。它把理解和生成解耦了理解错了可以单独调提示词生成错了可以单独调代码模板排查问题的时候不会一团乱麻。3.2 参数化建模的坐标系与基准选择做精确建模坐标系是命根子。我见过太多项目因为坐标系定义混乱导致生成的模型位置飘忽不定。CadQuery 默认用全局坐标系原点在 (0,0,0)Z 轴向上。但工程描述里的左边顶部中心这些词必须映射到明确的基准上。我的做法是强制约定一套基准规则所有零件默认以底面中心为原点Z 轴向上X 轴指向零件长度方向Y 轴指向宽度方向。用户描述里的左边默认指 X 负方向顶部指 Z 正方向。这套规则写进提示词让模型在抽取参数时就按这个基准换算。为什么选底面中心而不是角点因为中心基准在做对称特征时最省事。比如四角各一个孔用中心基准就是 (±L/2∓offset, ±W/2∓offset)对称性一目了然。用角点基准的话每个孔都要单独算容易出错。注意如果你的用户群体习惯用角点基准很多机械图纸是这么标的那就在中间表示里加一个 reference 字段明确标注基准类型代码生成器根据这个字段做换算。别指望用户改习惯让程序去适配人。3.3 从 B-rep 到网格的容差控制前面提到 STEP 转 GLB 和 STL 需要网格化这里的容差参数是精度和体积的平衡点。CadQuery 底层用 OCCT 的网格化算法核心参数有两个线性偏差linear deflection和角度偏差angular deflection。线性偏差控制的是曲面离散成三角面片时面片和真实曲面的最大距离。设 0.1 毫米意味着曲面上的点最多偏离真实曲面 0.1 毫米。角度偏差控制相邻面片法向的最大夹角影响曲率变化剧烈区域的细分程度。我的经验值是做 3D 打印预览线性偏差设 0.05 到 0.1 毫米足够做网页展示可以放宽到 0.2 毫米文件能小一半做精密装配检查得压到 0.01 毫米但文件会大得离谱。角度偏差一般设 0.1 到 0.5 弧度圆孔多的话调小一点否则孔会变成明显的多边形。这里有个坑容差设太小会导致网格化时间爆炸。我试过一个带 200 个孔的板子线性偏差设 0.001 毫米网格化跑了将近两分钟。后来改成 0.05时间降到三秒肉眼几乎看不出差别。所以别盲目追求高精度按实际用途定。4. 完整实操流程从零搭一套可用的生成管线4.1 环境准备与依赖安装先把环境搭起来。我用的技术栈是 Python CadQuery 一个大模型 API。CadQuery 的安装是第一个坎因为它依赖 OCCT在不同系统上的安装方式不一样。# 推荐用 conda 装pip 装 CadQuery 经常出 OCCT 链接错误 conda create -n text2cad python3.10 conda activate text2cad conda install -c conda-forge cadquery # 网格处理和格式转换 pip install trimesh numpy为什么强调用 conda因为 CadQuery 的 OCCT 依赖在 pip 下经常出现动态库找不到的问题尤其是 Windows 上。我一开始用 pip 装报了一堆 DLL 错误换成 conda 一次过。如果你非要用 pip记得先装好系统级的 OCCT 库。大模型这块我用的是支持函数调用和代码生成的通用模型通过 API 调用。这里不绑定具体厂商因为不同模型的提示词要微调但整体流程是一样的。关键是要选支持长上下文和结构化输出的模型因为提示词里要塞建模规则、示例、格式约束短上下文模型扛不住。4.2 提示词工程让模型稳定输出可用代码提示词是 text-to-cad 的灵魂。我前后改了十几版总结出几个关键原则。第一给足示例。在系统提示词里放三到五个完整的文本描述到 JSON 中间表示的示例覆盖板类、轴类、壳体类零件。示例要包含边界情况比如带倒角、带阵列孔的描述。模型看到示例后输出格式的稳定性会大幅提升。第二明确禁止项。告诉模型不要输出解释性文字不要用未定义的变量不要假设单位统一用毫米。这些约束看起来琐碎但能省掉大量后处理。第三分步引导。让模型先输出中间表示确认无误后再生成代码。我在实际管线里是分两次调用的第一次调用只生成 JSON程序校验 JSON 的合法性第二次调用把 JSON 和代码模板一起给模型让它填充生成 CadQuery 代码。两次调用比一次调用慢但成功率高一截。# 提示词的核心结构简化版 SYSTEM_PROMPT 你是一个 CAD 建模助手。用户会用自然语言描述一个零件。 你需要输出 JSON 格式的中间表示包含基体和特征列表。 所有尺寸单位为毫米。坐标系约定底面中心为原点Z 轴向上。 不要输出任何解释文字只输出 JSON。 示例 输入一个直径 50 高 20 的圆柱中心一个直径 10 的通孔 输出{base: {type: cylinder, diameter: 50, height: 20}, features: [{type: through_hole, diameter: 10, position: {x: 0, y: 0}}]} 4.3 代码生成与沙箱执行拿到 JSON 后代码生成器把它转成 CadQuery 脚本。我一开始让大模型直接生成代码但发现它经常在 API 细节上出错比如把Workplane的方法名记混。后来改成模板加填充的方式我预先写好各类特征的代码模板模型只负责把 JSON 里的参数填进去。这样代码的正确率接近百分之百。import cadquery as cq def build_from_spec(spec): # 基体 base spec[base] if base[type] box: wp cq.Workplane(XY).box( base[length], base[width], base[thickness]) elif base[type] cylinder: wp cq.Workplane(XY).circle( base[diameter] / 2).extrude(base[height]) # 特征 for feat in spec[features]: if feat[type] through_hole: wp wp.faces(Z).workplane().pushPoints( [(feat[position][x], feat[position][y])] ).hole(feat[diameter]) elif feat[type] counterbore_hole: # 沉头孔需要两步先钻通孔再铣沉头 wp wp.faces(Z).workplane().pushPoints( [(p[x], p[y]) for p in feat[positions]] ).cboreHole(feat[spec][shaft], feat[spec][head], feat[spec][depth]) return wp执行环节一定要放沙箱。生成的代码在独立进程里跑设超时我设 30 秒捕获所有异常。如果执行失败把错误信息回传给模型让它修正 JSON 或代码最多重试三次。这套重试机制是稳定性的关键实测能把端到端成功率从六成提到九成以上。4.4 多格式导出与精度校验模型建好后导出三种格式。CadQuery 导出 STEP 很简单一行代码。导出 STL 和 GLB 需要先转网格。# 导出 STEP cq.exporters.export(wp, part.step) # 导出 STL指定容差 cq.exporters.export(wp, part.stl, tolerance0.05, angularTolerance0.1) # 导出 GLB 需要先转成 trimesh import trimesh mesh wp.val().tessellate(0.05) tm trimesh.Trimesh(verticesmesh[0], facesmesh[1]) tm.export(part.glb)导出后必须做精度校验。我的做法是读取导出的 STL计算它的包围盒尺寸和原始模型的包围盒对比误差超过 0.1 毫米就报警。这一步能抓出网格化参数设错、单位换算错误这类问题。另外对于带孔的零件我会检查孔的数量和直径确保特征没丢。提示STL 没有单位信息导出时一定要在文件名或元数据里标注单位是毫米。我吃过亏导出的 STL 被切片软件当成英寸零件直接大了 25 倍。5. 常见问题与排查技巧实录5.1 生成失败与代码报错排查text-to-cad 最常见的失败就是代码执行报错。我把踩过的坑整理成一张速查表基本覆盖了九成以上的报错场景。报错现象根本原因解决方法AttributeError: Workplane has no attribute模型记错了 API 方法名用模板填充代替自由生成孔位置偏移或超出边界坐标系基准理解错误在提示词里强制基准约定布尔运算失败特征之间干涉或相切加最小间隙检查避免零间隙网格化超时容差设太小或特征太复杂放宽容差或分区域网格化STEP 导出后打不开几何有自交或非流形边导出前做几何有效性检查其中布尔运算失败是最隐蔽的。CadQuery 做差集运算时如果两个实体刚好相切比如孔的边缘和板的边缘重合OCCT 内核可能算不出结果。我的经验是留 0.01 毫米的余量让孔稍微超出边界一点反而能稳定算出来。这个技巧文档里不会写是实打实试出来的。5.2 精度与文件体积的平衡技巧前面提过容差控制这里补充几个实操技巧。第一不同特征用不同容差。平面区域可以粗一点曲面和孔用细一点。CadQuery 支持对特定面单独设容差但 API 比较绕我一般整体设一个折中值。第二GLB 导出时开 Draco 压缩。Draco 是谷歌的网格压缩算法能把 GLB 体积压到原来的十分之一视觉几乎无损。trimesh 支持 Draco但需要额外装依赖。对于网页展示场景这个压缩是必须的。第三STL 导出用二进制格式。ASCII 格式的 STL 体积是二进制的五倍以上而且精度还低。CadQuery 默认导出二进制但如果你手动处理记得确认格式。5.3 批量生成时的性能优化如果你要批量生成几百个零件单线程跑会很慢。我的优化思路是把生成和执行分离。文本解析和代码生成是 IO 密集调 API可以并发代码执行是 CPU 密集用多进程池。两者用队列解耦整体吞吐能提升三到五倍。另外CadQuery 的模型对象不要跨进程传递它底层是 C 对象序列化会出问题。正确做法是在每个工作进程里独立构建模型只把最终的文件路径传回来。from multiprocessing import Pool def generate_one(spec): wp build_from_spec(spec) path foutput/{spec[id]}.step cq.exporters.export(wp, path) return path with Pool(processes4) as pool: results pool.map(generate_one, all_specs)注意多进程下每个进程都会加载一份 OCCT 库内存占用会翻倍。如果内存紧张把进程数控制在 CPU 核心数的一半左右。6. 格式转换与下游对接的实战经验6.1 STEP 转 STL 的那些坑很多人以为 STEP 转 STL 就是点一下导出实际上这里面的坑不少。最常见的问题是单位不一致。STEP 文件内部有单位定义但有些软件导出时不写单位或者默认用英寸。转 STL 时如果不做单位换算零件尺寸会差 25.4 倍。我的做法是在转换前先读 STEP 的包围盒和预期尺寸对比。如果差得离谱就是单位问题。CadQuery 读 STEP 时可以指定单位但更稳妥的是读进来后统一缩放到毫米。另一个坑是曲面精度丢失。STEP 里的圆柱面是精确的解析曲面转 STL 时离散成三角面片如果容差设太大圆柱会变成明显的棱柱。对于要 3D 打印的零件这个影响很大因为打印出来的圆孔会不圆。我的经验是圆柱特征多的零件线性偏差压到 0.02 毫米以下。6.2 GLB 在网页预览中的优化GLB 主要用于网页 3D 预览。我做过一个在线预览功能用户生成模型后直接在浏览器里转着看。这里有几个优化点。第一减面。CadQuery 生成的网格面数可能上万网页加载慢。用 trimesh 的simplify_quadric_decimation做减面减到原来的三成视觉几乎无差别。第二合并材质。GLB 里每个材质是一个 draw call材质多了渲染卡。把相同颜色的部件合并成一个材质能显著提升帧率。第三加环境光。纯几何的 GLB 在网页里看起来灰扑扑的加一个 HDR 环境贴图金属质感立刻就出来了。这个不是必须的但用户体验差别很大。6.3 与下游 CAD 软件的兼容性生成的 STEP 最终要导进各种 CAD 软件。我测过 SolidWorks、中望 CAD、FreeCAD 和 Fusion 360兼容性整体不错但有几个细节要注意。倒角和圆角的表示方式在不同内核间可能有差异。OCCT 生成的倒角在 SolidWorks 里有时会被识别成样条曲面而不是标准倒角导致后续编辑困难。如果下游要做大量编辑建议在生成时尽量用标准特征少用复杂的变半径倒角。装配体的处理也是个问题。text-to-cad 目前主要生成单个零件如果要做装配需要在中间表示里加装配约束。这块我还在摸索目前的方案是生成多个零件后用坐标变换把它们摆到正确位置再导出成一个 STEP。但这样导出的是死装配没有配合关系下游改尺寸会散架。7. 我踩过的那些坑和最后的几句实话做 text-to-cad 这大半年最大的体会是这东西的难点不在 AI在几何。大模型负责把话听懂但真正决定成败的是几何内核的稳定性、坐标系的一致性、容差的合理性。我见过太多人把精力全花在调提示词上结果生成的模型尺寸对不上白忙一场。另一个体会是别追求一步到位。我一开始想做一个什么都能生成的通用工具结果什么都不精。后来收缩到只做板类和轴类零件把这两类的成功率做到九成五以上反而有了实际价值。text-to-cad 现在的阶段窄场景做深比宽场景做浅有用得多。最后分享一个实用技巧给生成的模型加一个尺寸标注图。用 CadQuery 的导出功能把模型的工程图带尺寸标注的 2D 视图也导出来。这样用户拿到模型的同时能看到关键尺寸方便核对。这个功能实现起来不难但对用户的信任感提升很大因为工程上最怕的就是模型看着对尺寸是错的。如果你也在做类似的东西我的建议是先把手动建模的流程跑通再考虑用 AI 替代哪一步。很多时候一个参数化的模板库加一个简单的表单界面比硬上大模型更靠谱。AI 是加速器不是发动机底层的几何能力才是根本。