从自然语言到CAD模型:text-to-cad技术解析与实战 最近在折腾AI辅助设计这一块社区里讨论最热烈的方向之一就是text-to-cad。这个名词听起来很唬人其实拆开看就是“用自然语言直接生成CAD模型”比如你输一句“一个直径40mm、高度20mm的圆柱顶部开一个直径6mm的通孔”系统直接给你吐出一个可以编辑的CAD文件而不是一张图片或者一段干巴巴的代码。这个方向把“脑子里想的”和“软件里画的”之间的距离狠狠压缩了一截机械结构设计、3D打印快速原型、甚至教学演示场景里都有它的用武之地。这篇东西我打算从技术思路、选型对比、完整实操到问题排查完整过一遍适合正在做AI应用开发、或者想在CAD工作流里引入AI能力的工程师参考。1. text-to-cad到底在解决什么问题1.1 传统CAD建模的“语言鸿沟”传统CAD建模是一件非常依赖软件操作熟练度的事哪怕你脑子里已经有一个非常清晰的零件形状到了SolidWorks或者Fusion 360里还是要走一遍“草图绘制—几何约束—拉伸切除—打孔阵列”的流程。这个流程本身不难但很琐碎而且每一步都要求人对软件功能足够熟悉。更反直觉的是建模过程中真正花时间的往往不是几何设计本身而是把脑内语言翻译成软件指令的“翻译成本”。一个花了几十年CAD的老师傅和一个新手面对同一个零件差距不在于是不是看得懂图纸而在于能不能用软件把想法快速落地。text-to-cad想干掉的正是这段翻译过程它试图让机器来接替“想法到指令”的这一步。这个方向本质上和GitHub Copilot自动写代码是一模一样的逻辑Copilot把自然语言需求转成代码text-to-cad把自然语言需求转成CAD建模指令。区别在于代码的最终产物是文本而CAD的最终产物是几何实体后者对精度和可编辑性的要求要高得多。代码写错一个括号编译器会报错你改一行就行如果一个CAD模型的尺寸不对它不会“报错”而是直接给你一个形状看起来差不多、实际完全不能用的零件这才是这个领域真正难的地方。1.2 一个完整的text-to-cad系统到底包含什么一个能用的text-to-cad系统不是单一模型它是一条流水线。最前面是语义理解模块把文本描述里的关键参数抠出来比如直径、厚度、孔位、阵列数量中间是几何推理与建模策略模块决定用什么样的特征顺序去构造这个模型是先画草图再拉伸还是用布尔运算去切一个孔最后是CAD内核的执行模块把生成的指令真正变成可加载的STEP/STL文件。以“带四个安装孔的圆形法兰盘外径80mm厚度10mm安装孔直径6mm均匀分布在直径60mm的圆上”这个需求为例系统需要依次解决几个问题识别这是一个法兰盘而不是一个简单圆盘理解四个孔是安装孔不是散热孔语义角色不同算出四个孔在圆周上的角度位置每90度一个最后生成一段能表达“草图圆—拉伸成盘—在指定位置打四个通孔”的建模脚本并交给CAD内核执行。这套流程里任何一环掉链子最终模型都会出问题。很多刚入门的人以为text-to-cad就是一个“文本进、STL出”的黑盒模型实际跑一段才发现最花时间的往往不是模型本身而是围绕模型的数据清洗、后处理校验和失败兜底。2. 两条主流技术路线选型前必须想清楚2.1 路线一用LLM直接生成CAD脚本代码目前最主流、也最容易被普通开发者上手的方式是让大语言模型直接生成CAD脚本。这里的“脚本”可以是CadQuery的Python代码也可以是OpenSCAD的描述文件甚至直接生成一个IFC或者DXF交换文件。代表性项目里ZOO团队开源的Text2CAD用的就是CadQuery作为后端模型以Llama-3.1-8B为骨干微调输入是自然语言描述输出是一段可以直接执行的CadQuery代码另一个叫CADmium的项目走了类似的思路但选择了OpenSCAD而且能做到在浏览器里跑推理。这条路线最大的优势是可解释性和可编辑性。生成的代码是透明的哪里出了问题可以直接改代码不用重新生成生成结果的每一步也都可以回溯适合工程场景里的评审和修改。而且训练数据构造相对灵活先用规则化工具批量生成大量“文本描述 CadQuery代码”的配对再用大模型微调数据管线并不难搭。缺点也很明显LLM生成的代码经常会有语法错误或者调用了一些CAD内核根本不支持的API更重要的是LLM本质上是按token概率在“猜”代码它并不真正理解空间几何。我见过很多看起来很合理、但实际执行出来零件完全变形的例子比如把“沉头孔”的直径和深度搞反了或者布尔运算的方向反了直接切掉了不该切的部分。下面是一段CadQuery代码示例描述的是一个带中心通孔的圆盘import cadquery as cq result ( cq.Workplane(XY) .circle(40 / 2) .extrude(10) .faces(Z) .workplane() .hole(6) ) cq.exporters.export(result, disc_with_hole.step)这段代码的逻辑是先在XY平面画一个直径40的圆拉伸10mm然后在顶面上打一个直径6的通孔。生成的STEP文件可以直接导入任何主流CAD软件继续编辑。LLM要学习的就是这种结构化代码表达能力文本描述和代码之间的映射越稳定生成效果越好。2.2 路线二用生成模型直接输出CAD原生表示另一条路线跳过了“代码”这个中间表达直接用深度生成模型输出CAD的原生表示。这个方向里比较有代表性的思路是把CAD建模过程当作序列生成问题比如DeepCAD它用Transformer直接生成“草图实体—拉伸参数”的序列每一步都是一个建模操作最终把这些操作序列回放成3D模型。还有个方向是直接生成B-Rep边界表示的曲面参数难度更高但几何精度上限也更高。这条路线的好处是模型直接面向几何结构进行优化理论上能更好地把握“圆柱拉伸后切一个孔”这种特征意图而不是像LLM那样去强行复现代码文本。但它的短板也很致命生成结果的参数化能力和可编辑性极差。DeepCAD虽然生成了操作序列但输出结构是固定的很难让用户修改某一个孔的直径或者移动一个特征的位置一旦需要调整只能重新生成这在工程场景下是不可接受的。还有一个实际问题CAD序列的长度通常很长一个中等复杂度的零件可能有几十甚至上百个建模操作序列生成的误差会随着长度累积。模型生成的序列稍微出点偏差最后的几何实体可能就完全不是一个东西了。目前在完整CAD模型生成上这条路线还处在实验室阶段但一些混合方案已经开始出现比如先序列生成草图轮廓再用规则化拉伸生成三维体这个思路在简单零件上已经有了不错的精度。2.3 两条路线的选型对比把两条路线放在一起对比可以直观看到各自的取舍对比维度LLM生成脚本路线一原生表示生成路线二可解释性高代码即是逻辑低序列/B-Rep参数难以阅读可编辑性高直接改代码或参数低重新生成为主几何精度受限于代码正确性潜力高但序列误差累积严重训练数据成本较低可利用已有代码库高需要大量原生CAD序列工程落地成熟度较高已有多个开源项目低以论文验证为主适合场景快速原型、参数化零件学术研究、特定几何类生成我自己实际用下来的倾向是如果你要做的是一个能快速落地、让设计师愿意用的工具路线一是更靠谱的选择。不是因为路线二不好而是工程上“能改”比“长得像”重要得多。一个能二次编辑但形状略微偏差的模型比一个形状完美但完全无法修改的黑盒更符合CAD工作流的习惯。3. 实操全流程从环境搭建到跑通第一个模型3.1 数据准备text-CAD配对数据从哪来准备训练数据是复现text-to-cad项目里最容易让人想摔键盘的一步。以ZOO Text2CAD为例他们构造数据集的方式相当于搭了一条半自动的生产线先从开源CAD模型库中拿到大量带参数化建模历史的模型文件再用规则化的方式把每个模型的建模步骤转成CadQuery代码最后调用GPT-4o这类强模型为每个模型生成多角度、不同粒度的自然语言描述。实际你在本地复现时不一定需要完全复刻这么重的流程。我更建议从公开数据集开始重点看几个维度文本描述的多样性是否足够描述里有没有混淆的尺寸单位代码是否都经过CAD内核验证有些数据集的代码可能本身就无法执行以及有没有对应的渲染图渲染图在评估阶段非常有用可以用来做CLIP Score之类的文本-图像对齐指标。这里有一个非常重要的经验不要迷信数据集的规模数字。我见过很多号称几十万对的CAD数据集清洗之后真正能跑通且文本描述质量在线的可能不到一半。由于CAD代码对语法极其敏感哪怕一个小括号出错或者一个参数类型不对整个模型就生成失败。所以数据清洗阶段最好做一个全量执行验证跑不通的代码直接丢弃别让垃圾数据在训练时反向教坏模型。清理后的数据格式一条典型的训练样本长这样{ instruction: Generate CadQuery code for a circular flange with outer diameter 80mm and thickness 10mm, with four 6mm mounting holes evenly spaced on a 60mm bolt circle., output: import cadquery as cq\n\nresult (cq.Workplane(\XY\)\n .circle(40)\n .extrude(10)\n .faces(\Z\)\n .workplane()\n .rect(60, 60, forConstructionTrue)\n .vertices()\n .hole(6))\n\ncq.exporters.export(result, \flange.step\)\n }折行和缩进要规范否则模型学到的代码风格会很随机影响生成稳定性。3.2 环境搭建与关键依赖版本这个项目涉及到的依赖比一般纯NLP项目多得多最让人头疼的就是CAD内核相关组件。PyTorch和transformers这两个主流的深度学习库基本不会出问题大多坑都集中在cadquery和它底层的OpenCascadeOCC上。CadQuery的版本迭代速度不算快但每个版本的API变化都可能影响你训练时使用的代码模板。尤其要注意cadquery和OCPOpenCascade Python绑定的版本配套关系如果conda环境里解析出来的OCP版本跟cadquery对不上导入cadquery时就会直接段错误崩溃而且报错信息非常不友好看起来像是GPU驱动问题实际上是底层库不匹配。我自己复现时踩得最深的一个坑是vLLM和cadquery的依赖冲突。vLLM会强制升级某些Python包比如numpy而某些版本的cadquery依赖旧版numpy的行为特性升级之后cadquery运行结果不稳定有时生成STEP文件时直接静默失败。建议的做法是训练和推理阶段用不同的虚拟环境训练环境只管模型推理环境单独装CAD相关依赖。推理阶段的硬件配置如果你只是验证效果一张24G显存的消费级显卡完全够用8B模型用FP16加载占用不到16G但如果要微调至少需要一张40G以上的专业卡或者用LoRA这类参数高效微调方法把显存需求压下来。我试过用A100微调Text2CAD的骨干模型batch size开到16训练了大约2万个step大概需要十多个小时但这个时间跟数据规模关系很大仅供参考。3.3 Prompt设计决定生成质量的隐蔽变量很多人以为微调过的模型随便给一句话就能生成正确代码实际完全不是这样。Prompt设计对生成质量的影响不亚于模型训练本身尤其是当你的文本描述和训练数据的描述风格差异较大的时候。我在复现时用的系统提示词大致是这个风格You are an expert CAD engineer. Convert the users natural language description into valid CadQuery Python code. Follow these rules: 1. Output ONLY the Python code, wrapped in a python code block. 2. Infer all missing dimensions from context; if ambiguous, choose a reasonable default and note it in a comment. 3. Use faces(Z) to select the top face unless instructions specify otherwise. 4. Always export the final result as result.step.注意规则2和3极其重要。模型在遇到“一个圆盘”这种不完整描述时如果没有“选择合理默认值”的指令它可能会编造一个毫无根据的尺寸有了默认值注释的规则至少生成结果不报错人还能手动改。指定顶面选择方式也是为了保证代码在CAD内核里的稳定执行这种细节直接影响生成成功率。文本描述方面有一个实用技巧是“关键参数结构化 前置”。把最重要的参数放在句子最前面比如“直径80mm、厚度10mm的圆盘中心打孔6mm”比“做一个圆盘要厚一点上面有个孔”这种描述成功率高得多。如果用户输入太口语化最好在前面加一个文本规范化模块把口语描述里的关键参数抽出来重组成结构化句子再塞给生成模型。我自己还试过一种做法在推理时把一条标注好的示例代码作为few-shot示例直接拼在prompt里效果提升很显著。如果你不想微调模型靠few-shot也能硬撑出一个还能用的效果但生成耗时和token消耗都会增加。3.4 推理输出后的验证流程生成代码之后绝不能直接把. step文件拿给用户模型输出一定要经过验证环节才能交付。这一步我建议做一个自动化流水线先检查生成的代码块语法用pylint或Python compile内置方法做静态检查然后执行代码生成模型文件执行时要做超时控制有些代码看起来简单但CAD内核计算复杂度很高一次布尔运算就能卡住几十秒执行成功后再用trimesh或OpenCascade把模型导入检查几何体有效性比如是否有自相交面体积是否大于零。下面是一个验证脚本的简化示意代码逻辑是核心骨架import subprocess import cadquery as cq def validate_generated_code(code: str) - bool: # 1. 语法检查 try: compile(code, generated, exec) except SyntaxError as e: return False, fSyntax error: {e} # 2. 执行代码并捕获运行时异常 try: exec(code, {__name__: __main__}) except Exception as e: return False, fRuntime error: {e} # 3. 加载 STEP 文件检查边界框与体积 shape cq.importers.importStep(result.step) if shape.val().Volume() 0: return False, Invalid geometry: zero or negative volume return True, Valid geometry这个验证流水线看起来简单但它能救你很多次。实际跑的时候你会发现有相当比例的模型输出会卡在第一步的语法检查上剩下的又有一部分卡在运行时。经过这一轮筛选最后能交付的模型数量会比预期少不少但剩下的每一个都是靠谱的。3.5 复现时关于“文本到模型”的评估细节评估生成质量不能只看生成成功率还要看生成结果和文本描述之间是否语义对齐。实践中比较常用的是体积IoU和倒角距离逻辑是把生成模型和参考模型都转换成水密网格在同一个坐标系下算重叠体积比例。如果参考模型是“一个直径80mm的圆盘”而生成模型做成了“直径80mm、带四个孔的法兰盘”IoU会很低说明语义理解出了问题而不仅仅是几何偏差。文本-模型对齐还可以用多模态模型打分比如把生成模型渲染成多视角图片然后用CLIP计算这些图片与输入文本之间的相似度分数。这个指标虽然不能完全代表几何正确性但对“描述和形状是否匹配”这个问题的判断非常有效。我自己的习惯是IoU和CLIP Score两个指标结合着看一个管几何一个管语义。4. 常见问题与排查实录4.1 生成的代码执行就报错怎么快速定位这是复现时出现频率最高的问题很多时候不是模型问题而是环境问题。定位方法就是我在验证流程里提到的分层检查法第一层语法检查第二层运行时异常第三层STEP导入。语法错误基本都是模型输出的代码格式问题模型有时候会多一个冒号或者少一个右括号运行时报错就要看exception message的具体内容了最常见的两个是“name cq is not defined”和“module cadquery has no attribute Workplane”前者是模型忘了导入cadquery后者是环境里的cadquery版本跟训练时不一致。排查效率最大的提升方式是打印模型原始输出不要只看包装过后的结果。LLM经常会在代码块外输出一堆解释性文字如果你的解析代码只提取了第一段python通常没问题但有时候模型会在代码里夹杂“Here is the code:”这种前缀直接导致compile失败。所以解析代码要足够稳健优先从输出文本中匹配完整的python代码块匹配不到就丢弃重试。4.2 模型形状和描述对不上是语义理解出了问题比如要求“在底部开四个直径10mm的盲孔深度5mm”结果生成的是通孔或者开在了顶面这种问题通常是模型没有真正理解方位词和孔的类型。排查时先看生成的代码语义是否正确不要只看最终模型。如果代码里把hole参数搞错了说明模型对“盲孔”的概念和对应代码API之间的映射没学会如果代码是对的但最终模型不对那就要去查CAD内核里hole函数和workplane选择逻辑的组合是否有隐藏行为。一个非常实用的排查技巧是把孔位空间关系可视化出来把生成的模型剖面和参考模型剖面放在一起对比。剖面图能一眼看出孔是通孔还是盲孔、深度对不对、位置在哪个面上。可视化排查完再回头调prompt比如在系统提示词里加一句“Distinguish blind holes from through holes explicitly in code comments”效果会立竿见影。4.3 复杂模型的渲染和导出老是内存溢出生成一个中等复杂度的零件没问题但当模型生成几十个孔的特征时CAD内核做布尔运算的时间会指数级增长。我遇到过在A100上单次推理耗时只有3秒但生成的模型在导出STEP时直接把OCC内核干崩溃的情况。核心原因是每个孔都是一次布尔减法几十个孔就是几十次布尔操作每做一次操作特征面数都会增长内存消耗极高。解决办法不是去堆机器内存而是优化建模策略。CadQuery里尽量用“一次workplane操作加多个点的hole”批量打孔不要用循环逐个打孔每次布尔操作后做一次简化defeature把不影响功能的小特征合并掉在导出时可以选择用brep格式而不是step格式后者的写盘和内存开销更大。另外加一个超时机制也很有必要不要等OCC真的把内存吃完了才kill进程提前设一个30秒的导出阈值。4.4 推理速度太慢批量场景怎么办如果你要把text-to-cad集成到业务系统里单次几秒的推理速度基本没法商用。优化手段第一步是上vLLM做服务化推理吞吐量能提升一个量级第二步是用量化8B模型用AWQ或者GPTQ量化到4bit显存占用瞬间降到6G左右速度反而因为访存压力降低而更快。代价是生成质量会有一点下降但实测在CAD代码这种语法规则性较强的任务里下降幅度远比自由文本生成要小。还有一个实用技巧是设置输出最大token长度限制。CadQuery代码通常不会太长把max_new_tokens限制在2048以内可以避免模型在某些失败样本上无限生成无意义的重复代码。重复生成是LLM推理速度杀手这个限制能让平均推理速度快30%以上。如果你不在乎微调成本也可以把8B模型蒸馏成更小的模型或者针对常见零件类型做专用小模型比如只生成法兰类零件或轴类零件精度和速度都会有质的提升。这个方向也是我下一步想探索的扩展点。5. 写在最后的一些体会跑完整个text-to-cad项目之后我的核心感受是这个方向目前的定位更像是“概念生成器”而不是生产级设计工具。它最适合的场景是设计早期阶段的快速方案验证你说一个大致的想法它给你一把可以继续改的模型大大节省从0到1的时间但真要交付一个能进装配体的精确零件还是得靠人去做细化和校验。我个人在实际操作中的体会是别指望模型一次生成就能用把重心放在“生成—校验—反馈修正”这个闭环上做一个能跟用户交互、不断迭代的系统比单纯追求单个模型的生成精度有意义得多。后续可以扩展的方向很多比如跟主流CAD软件插件结合、增加语音输入、做多轮对话修改已有模型每一条都值得单开一篇文章展开。