text-to-cad,翻译成大白话就是“把一句文字描述直接变成可编辑的CAD模型”。我最近完整走了一遍这条技术路线,从方案选型、模型调用到模型后处理,中间踩的坑比预想的多得多。这篇文章不是理论综述,是我实际跑通一条 text-to-cad 最小流水线之后的复盘,适合正在做三维建模工具、想给产品接入自然语言建模能力的团队参考,也适合想搞懂这个方向到底靠不靠谱的个人开发者。
先说结论:text-to-cad 绝不是噱头,但它也不是“输入一句话就给你一个完美零件”的魔法。它的本质是把自然语言转换成某种中间表示,再由 CAD 内核去生成实体模型。中间表示选得好不好,直接决定你拿到的是“能改的模型”还是“只配看一眼的废网格”。
1. text-to-cad到底在解决什么问题
1.1 传统CAD建模的痛点在哪里
传统参数化 CAD 的使用门槛高,这是公认的。你让一个机械专业学生画一个带法兰盘的轴套,他得先掌握草图绘制、约束添加、特征树管理、布尔运算这一整套操作逻辑。哪怕是最简单的“直径60、四个均布孔”的圆盘,也要经历新建零件、选基准面、画草图、标约束、拉伸切除、阵列特征这一串步骤。这不只是软件操作熟练度的问题,更是把“工程意图”翻译成“建模操作序列”的成本问题。
熟练的工程师当然不觉得这有什么,但这句话本身就说明了问题:CAD 建模一直被一小部分熟练用户垄断。一台设备上的所有零件,设计阶段都要靠这些熟练工把二维图纸、口头描述、技术协议转换成三维模型。这个转换过程慢,而且容易失真。我见过不少因为沟通偏差导致的返工,设计师说“留点隙”,加工那边理解成“尽量紧配合”,最后装配过盈,整批件报废。
text-to-cad 想解决的,就是“意图到实体”这一公里的自动化。它让使用者用自然语言描述需求,系统负责把需求解析成参数化建模动作。注意,这里说的是“参数化建模动作”,不是简单地生成一个三角形网格。网格模型好看但不可编辑,生产制造根本没法用。传统轨迹在这里的优势是:模型自带特征树,尺寸可改、参数可调,这才是 CAD 领域的硬通货。
1.2 text-to-cad的核心价值与适用人群
这项技术降低的不是“学会操作软件”的门槛,而是“表达设计想法的成本”。一线装配工可以对着手机说“师傅,这个支架弯角改小10度,加两个腰型孔”,系统直接改给他看;创客和硬件创业者在做个外壳时,不必为了一个开孔位置翻两个小时的建模教程。
适用人群其实比想象中广:机械设计初学者用它做概念验证;产品经理用它快速出外观示意;标准化工程师可以用它批量生成相似规格的零件;甚至非技术背景的采购,也能用它向供应商传达设计意图。当然,目前它还不能完全替代高级参数化建模,毕竟复杂的曲面、严格的公差配合、多体运动仿真,还是得靠人肉建。
我自己的体会是,text-to-cad 最有价值的场景不是“从零生成复杂模型”,而是“从零生成常见零件 + 对已有模型做自然语言驱动的二次修改”。这两个场景覆盖了日常机械设计里七八成的工作量,而且刚好都在 LLM 的能力边界内。
2. 主流技术路线拆解:别急着写代码
项目启动前,最忌讳的就是直接开干。text-to-cad 不是单纯调一个模型 API 就完事,选错中间表示,后面全盘被动。当前跑得通的技术路线基本是三条,我挨个分析一遍。
2.1 路线A:LLM直接生成参数化脚本
这条路线是目前最成熟、最容易被工程团队接受的。做法是让大语言模型根据自然语言描述,生成 CadQuery 或 OpenSCAD 的程序代码,然后本地执行这段代码,由CAD内核解析并生成实体模型。CadQuery 用 Python 写参数化建模逻辑,OpenSCAD 用类似 C 的脚本语言描述 CSG 几何体,底层都有成熟的几何内核兜底。
这条路线的优势非常明显:模型输出的是文本形式的可编辑脚本,用户拿到的是完整参数化模型,尺寸、特征、约束全部保留。出一版 STEP、STL 很容易,改一个孔直径也只需要改 Python 变量的值。缺点同样明显:LLM 生成复杂几何的连续表达能力有限,让它写一个涡旋压缩机壳体、一个曲面渐消车灯,基本会输出一团逻辑混乱的废代码。
但工程上绝大多数零件是“规则几何的组合体”,法兰、支架、轴套、盖板、箱体框架,这些恰恰是脚本化建模的主场。我实测下来,只要提示词里给足约束和示例,经过一两次报错反馈,LLM 生成的 CadQuery 代码可用率能到七成以上,这在自动化建模范畴里已经是很惊人的数字了。
2.2 路线B:生成模型直接输出B-rep或网格
另一条路线是走扩散模型或Transformer,让模型“看”文本描述,直接生成 B-rep(边界表示)或者体素/点云/网格,再转换成 CAD 格式。学术圈管这叫 text-to-3D 或 text-to-shape,近两年进展很快,开源的 Text2CAD 类工作也陆续发布。
这条路线的优势是有机会处理自由曲面、有机形状、仿生结构,这是脚本化路线做不到的。但它有个致命伤:生成结果不可参数化。你拿到的是一个“形状对但尺寸没法保证”的实体或网格,没有特征树,没有参数表,工程上几乎没法用。B-rep 生成虽然比网格更接近 CAD 原生数据,但目前的可靠性、拓扑有效性都还不稳定,经常出现破面、缺面、自相交。
我个人判断,这条路线更适合用在概念设计、创意激发、仿真预研这种对精度要求不高的阶段。真要投入生产,至少还得配套一个“网格转参数化实体”的后处理环节,而这一步目前自动化水平很低,往往还是手动逆向抄数。
2.3 路线C:混合路线——检索+参数化拟合
最后是工程上很讨巧的混合路线:先建一个常见零件的参数化模型库(比如法兰、轴承座、齿轮坯这类高频件),用户输入文字后,先经过检索或者用 LLM 做语义匹配,从库里找到最相近的模板,再用 LLM 抽取关键参数填进模板生成模型。
这个方案稳定性和准确性最高,因为模板是人工验证过的参数化逻辑,LLM 只需要负责“理解需求 + 填写参数”,不需要从零写几何逻辑。缺点也很明确:库的覆盖范围决定了系统的能力上限,遇到库里没有的异形件,直接抓瞎。实际上很多商业产品(包括一些头部PLM系统)用的就是这个思路,先让用户选模板,再做参数驱动,自然语言只是在现有模板集合上做的映射。
三条路线不冲突,可以按场景混着用:模板库兜底、脚本生成打主力、生成模型做创意补充。我自己搭的最小流水线就是“模板+脚本”为主,后面实操部分详说。
3. 实操演示:一条能跑通的 text-to-cad 最小流水线
这一节我给出完整可复现的实操路径。我自己在 Linux 环境下测试通过,Windows 基本同理,只是个别依赖安装方式略有差异。
3.1 环境准备:这些库是真避不开
先说系统前提:Python 3.10 以上,建议用虚拟环境隔离,避免 CadQuery 的 OCP 绑定污染你其它项目的依赖版本。核心库是 CadQuery 和它的几何后端 OCP(OpenCascade Python 绑定),这是全流程里“从脚本到实体”的关键,没有它你得到的只是一堆 Python 字符串。
python -m venv text2cad_env source text2cad_env/bin/activate pip install cadquery pip install ipython-genutils # 部分版本需要此依赖才能正常导入CadQuery 装完以后一定要跑一个最小矩形拉伸测试,确认 OCP 内核绑定正常。我一次性就能导入成功是运气好,踩过最离谱的坑是 conda 环境里装了旧版 pybind11,直接导致 OCP 崩溃,后来重装 cadquery 到2.4.0以上版本才解决。
模型服务我走的是 HTTP API 接入通用大模型,选择标准只有一条:Python 代码生成能力强。目前主流的通用模型都能胜任 CadQuery 脚本生成,区别主要体现在对 CadQuery 特定 API 的记忆准确度上。如果你接入的模型对 CadQuery 不熟,强烈建议在系统提示词里给它塞一段官方 API 示例,这对输出质量的影响是决定性的。
# 验证CadQuery环境是否正常 import cadquery as cq result = cq.Workplane("XY").box(10, 10, 10) print(result.val().isValid()) # 输出 True 表示OK3.2 提示词工程:把需求转换成LLM能理解的约束
这一步是整个 pipeline 里最重要的。LLM 不读图,它只懂文字,所以你必须把几何需求“投影”到语义空间里。我用的提示词模板经历过差不多二十版迭代,最终留下了一套比较可靠的结构,核心是:给角色、给规则、给示例、给输出格式要求。
你是一名机械零件参数化建模专家。 根据用户的文字描述,用 Python CadQuery 2.4 编写建模脚本生成可编辑实体。 要求: 1. 使用 Workplane 语义,优先采用 "XY" 基准面开始拉伸; 2. 所有关键尺寸必须用变量定义,并以 <USER_PARAM> 形式标注允许用户后续修改; 3. 模型必须是一体化实体,禁止只输出线框或面; 4. 只输出 Python 代码块,不要解释,不要输出其它文字; 5. 若用户描述不清晰,按最常见工程惯例解释,并输出假设信息。 用户需求:{USER_INPUT}把这段模板放在系统提示词里,效果远好于直接丢一句“帮我画个法兰”。原因在于它提前锁定了 API 风格、输出结构和参数化要求,不这么做的话,模型很容易输出一段语义上像样、但 CadQuery 根本跑不起来的伪代码。
我这个模板已经经过多轮打磨,最初的版本我犯过一个低级错误——要求“使用 FreeCAD API”,那对模型的要求太高了,后来换回 CadQuery 才打开局面。
3.3 从生成代码到实体模型:脚本解析与安全执行
拿到 LLM 返回的代码块之后,不能直接裸奔执行。原因有两层:一是大模型生成的代码可能包含危险的系统调用(概率很低但存在);二是 CadQuery 脚本一旦几何逻辑错误,执行时大概率直接抛异常,需要拦截并反馈。
我采用的做法是:先把代码块从返回文本里剥离出来(正则匹配```python和```之间的内容),然后在一个受限命名空间里执行,只注入cadquery模块,禁用os、subprocess、sys等危险模块。实测这样做安全性和成功率都能兼顾。
import re import json import cadquery as cq def extract_python_code(text: str) -> str: """从LLM回复中提取第一个Python代码块""" pattern = r"```(?:python|Python)?\s*(.*?)\s*```" matches = re.findall(pattern, text, re.DOTALL) if not matches: return "" return matches[0] def run_cadquery_script(code: str): """ 在受限命名空间执行CadQuery脚本,返回模型或错误信息 """ allowed_globals = { "cq": cq, "CadQuery": cq, "Workplane": cq.Workplane, } result_holder = {} try: # 将最终变量名统一约定为 result,以便统一获取 exec(code, allowed_globals, result_holder) model = result_holder.get("result") if model is None: return {"success": False, "error": "脚本未定义 result 对象"} if not isinstance(model, cq.Workplane) and not hasattr(model, "val"): return {"success": False, "error": "result 不是有效的CadQuery对象"} # 关键校验:是否为有效实体,这一步拦掉了大量废输出 shape = model.val() if hasattr(model, "val") else model if not shape.isValid(): return {"success": False, "error": "实体无效,存在破面或非流形结构"} return {"success": True, "model": model, "shape": shape} except Exception as e: return {"success": False, "error": f"{type(e).__name__}: {str(e)}"} def export_stp(model, filepath="output.step"): """将模型导出为标准交换文件(STEP)""" cq.exporters.export(model, filepath)执行完之后,先验证isValid(),再把模型导出为 STEP 或 STL。这一步能挡掉大概三四成早期失败案例。导出 STEP 是最推荐的做法,因为 STEP 文件保留了几何边界表示,后续进任何主流的 CAM、CAE 软件都不会出问题。
我建议你在实际项目中,把“执行+校验+导出”包装成一个内部 API,前端的自然语言查询、参数修改都走这个接口,而不是每次直接在 Jupyter Notebook 里手动跑。
4. 问题排查与避坑实录
跑了这么长时间的 text-to-cad,失败和翻车常见得很,我把最典型的几类问题汇总一下,希望能帮你节省排查时间。
4.1 我踩过的五个典型坑
第一个坑是坐标系理解偏差。LLM 描述“四个均布孔”时,经常会默认把孔放在一个错误的直径位置上。比如用户说“孔心圆直径50”,模型可能生成polar(radius=50)的四个点,但 CadQuery 的polar接收的是半径参数,50 会被当成半径,结果孔全部跑到直径100的位置,直接翻倍。这类几何语义错误,单靠肉眼检查代码很难发现,所以我在校验阶段会额外检查孔位置是否超出了模型边界半径。
第二个坑是单位混淆。LLM 被训练的资料里有大量英制数据,一个螺丝的直径,它可能给出1.5 inches的潜台词,但用户在中文场景里说的是毫米。我遇到过用户说“直径15的轴”,模型生成的代码里写的是radius = 7.5(这其实对),但换个描述“轴径1/2英寸”,它就真的给了一个半径等于1/2的实体,最终模型尺子一量,寸径变毫米径,差了几十倍。解决方案是在提示词里明确声明:所有尺寸以毫米为单位,除非用户特别说明。
第三个坑是模型对 CadQuery API 的记忆不准。它可能写出cq.Workplane().box(10,20,30).faces(">Z").hole(5)这种在逻辑上没问题的代码,但faces(">Z")的语义模型不一定每次都选对位置。更常见的是写出根本不存在的类方法,比如cylinder(r=5, h=10),而 CadQuery 根本没有这种参数风格,语法错误倒是小问题,最怕的是 API 存在的但行为与模型预期完全不同,这种错误极其隐蔽。
第四个坑是布尔操作失败。建模里经常要做“基体上打孔”或“切割”,CadQuery 的布尔运算依赖几何内核,如果参与布尔的两个实体面没闭合、有微小缝隙,内核会直接抛异常,有时候甚至异常都不抛,但生成结果是无效实体。我建议对每次布尔运算后的结果都做一次isValid()检查,并且尽量让打孔这类操作使用 CadQuery 原生的hole()而不是手写 cut+cylinder。
第五个坑是生成代码结果被“幻觉约束”污染。LLM 自动补充的 R 角、倒角、拔模斜度,表面上好看,但对制造来说可能是灾难。比如一个铸铝支架,模型自动加了半径1.5的圆角,实际机加工那边根本不需要;又比如孔洞阵列被加了等比约束,改一个孔的位置,其它孔跟着动。为了应对这个问题,我在提示词里专门加了一行“不允许添加用户未提出的任何额外特征”,实测能显著降低这种污染。
4.2 常见问题速查表
我把高频问题整理成一张表,方便你快速定位。注意这张表是拿真实跑批统计出来的,不是凭空假设。
| 现象 | 根因 | 处理方案 |
|---|---|---|
| 生成的脚本语法错误频繁 | 模型对CadQuery API版本不熟 | 系统提示词里附上CadQuery 2.4版本的官方示例代码 |
| 模型能跑通但尺寸明显偏大/偏小 | 单位混淆或半径/直径混淆 | 提示词强制标注毫米,并在执行后检查包围盒尺寸 |
| 孔、圆角等特征位置不对 | 坐标系推断错误 | 增加“必须从XY基准面开始”的硬性约束,必要时附参考图 |
| 实体无效或布尔运算失败 | 面未闭合、非流形边缘 | 每个特征后加 is_valid() 断言,失败则回退到简化几何 |
| 生成了正确模型但不按用户参数走 | 参数被硬编码进脚本 | 提示词要求所有尺寸用变量定义,并在结尾返回参数表 |
| 代码执行报模块缺失 | 环境依赖不完整 | 锁定 cadquery 2.4 以上版本,验证 OCP 导入正常 |
| 输出是多个实体而非一个实体 | 模型用多个 separate cube/cylinder 拼装 | 强制使用 union() 合并,校验最终只有一个 Solid |
其实最值得强调的并不是某个具体 bug,而是一定要把报错信息回灌给模型。一次生成失败的代码,把它交给模型说一句“这段代码报错了:{报错信息},请修正后重新输出”,成功率会大幅提升。这比什么高级提示词都管用,本质上是利用了 LLM 的纠错能力构建了一个“生成-执行-反馈-再生成”的闭环。我后来在流水线里自动加了这个循环,最多重试三轮,整体成功率从初始的55%左右拉到了80%以上。
4.3 一些参数上、流程设计上的经验心得
做这套流程,除了解决 bug,我也积累了几条常规文档里不会写的心得,这里一并分享下。
心得一是“能少让模型算的就别让它算”。LLM 做逻辑推理很强,但做精确数学计算很弱。比如“孔心圆直径是螺栓直径的3倍”,不要让模型自己算 3×8=24,而是让它在脚本里定义hole_circle_d = lubricated_bolt_d * 3,把计算逻辑交给 Python,而不是让 LLM 把数值硬编码出来,这能杜绝一大部分算术幻觉。
心得二是把“模板代码”做扎实。我给项目搭了一套基础 CadQuery 模板,里面写好了文件头、单位声明、实体合并逻辑、导出函数。LLM 生成的时候只需要填充build_model()函数体里面的特征代码,不用管框架,这样模型的任务更纯粹,错误率也明显下降。
心得三是必做“输入侧结构化”。用户给的自然语言需求五花八门,“大一点的支架”“孔距离差不多就行”这种模糊描述,模型只能猜。我自己做了一个前端小表单,让用户先选零件类型、填关键参数,然后再把结构化结果拼接成自然语言发给模型。这一步看着土,但效果惊人,把成功率又往上拉了接近10个百分点。
5. 从单体零件到真正可用,还有哪些事可以做
跑通“文字到单个零件”的流水线只是第一步。一个认认真真想用 text-to-cad 提效的团队,后面还有三件事值得投入。
5.1 从单零件到装配体与约束关系
单个零件好生成,装配体是另一个量级。“把电机座固定在底板上,两者用四个M6螺栓连接”这句话,牵扯到坐标系对齐、配合面、螺栓孔阵列对齐的问题。LLM 单独生成两个零件没问题,但要它保证两个零件上的螺栓孔位置完全一致,就很容易翻车,因为模型生成第二件时往往记不住第一件的精确孔位。我目前的应对策略是:先生成基准零件,再从基准零件提取关键连接参数,拼进第二个零件的提示词里,让第二个零件主动对齐第一个。这就是工程上常说的“自顶向下设计”的自然语言版。
5.2 接仿真与制造环节
模型生成的 STEP 文件,接下来要进仿真和 CAM。我在后处理里直接连了三个出口:一是 STEP 导入 Ansys 做静力分析;二是导出 DXF 给激光切割路径用;三是连 OpenFOAM 做管路流道仿真。text-to-cad 的价值不在于孤零零生成一个三维模型,而在于它能无缝接入已有的数字制造链条,让“描述-建模-分析-制造”的通路大幅缩短。
5.3 可以继续做的研究方向
我目前手头在试的有三个方向:一是把公差信息写进文本描述里,让生成模型直接带 PMI(产品制造信息);二是基于历史模型做参数回归,用户说“类似三通阀,但入口口径更大”,系统不只是重新生成,而是修改已有模型;三是把自然语言建模能力和 PLM/PDM 系统集成,设计变更记录自动生成文本说明。
说实话,text-to-cad 能走多远,很大程度上取决于我们能不能把模型输出稳定地约束在“工程习惯”里。技术再强,生成的模型不符合制图规范、不符合机加工艺,就永远只是玩具。我在做这个项目时最深的体会是:真正卡住落地效果的往往不是模型能力,而是工程化兜底的细节——模板规范、参数约束、校验逻辑、反馈闭环。把这些细节夯实,text-to-cad 就能从“偶尔好用”变成“每天都在用”。