text-to-cad 实战:用大模型和 CadQuery 将自然语言生成 STEP/STL/GLB 三维模型

发布时间:2026/10/8 13:14:46
text-to-cad 实战:用大模型和 CadQuery 将自然语言生成 STEP/STL/GLB 三维模型
1. 从一段文字到三维实体text-to-cad 到底在解决什么问题第一次听到 text-to-cad 这个词很多人会下意识觉得它离自己很远像是实验室里的概念演示。但如果你真的动手做过参数化建模就会明白它瞄准的痛点非常具体从脑子里有个形状到软件里有个可编辑的实体之间横着一条又长又枯燥的手工链路。传统流程里你得先想清楚尺寸再打开 CAD 软件一个草图一个草图地画一个约束一个约束地加最后拉伸、旋转、倒角才能得到一个能导出 STEP 的模型。这个过程对于简单零件可能只要十分钟但对于需要反复调整尺寸的方案改一个参数就要重画半张草图时间全耗在重复劳动上。text-to-cad 想做的事情就是把这层手工操作压缩成一句话。你输入一个外径 60mm、内径 30mm、厚度 10mm 的圆环边缘倒角 2mm系统直接给你输出一个三维模型文件。这个文件可以是STEP可以是STL也可以是GLB。这三个格式分别对应了工程制造、3D 打印和可视化展示三条下游链路覆盖了从设计师到工程师再到展示人员的完整需求。我之所以对这个方向感兴趣是因为在实际工作中接触过太多改尺寸改到崩溃的场景。比如做非标设备的结构件客户今天说要加长 20mm明天说孔径从 8mm 改成 10mm每次改动都要重新走一遍建模流程。如果有一个工具能把自然语言直接翻译成参数化模型哪怕只覆盖标准件和简单几何体也能省下大量机械劳动。这就是 text-to-cad 的核心价值它不是要取代 CAD 工程师而是把工程师从重复建模中解放出来让他们把精力放在真正需要判断力的地方。这篇文章适合三类人看。第一类是对 AI 辅助设计好奇、想动手试一下的工程师第二类是需要批量生成标准件模型、但又不想学复杂脚本的从业者第三类是纯粹想了解文字怎么变成三维模型这个技术链路的技术爱好者。我会从实际可跑通的方案讲起把每一步的选择理由、踩过的坑、以及实测效果都摊开说清楚。2. 文字变模型的技术链路LLM 负责理解几何内核负责落地2.1 为什么不能指望大模型直接吐出三维坐标很多人第一反应是既然大模型能写代码那让它直接输出一个 STL 文件的顶点坐标不就行了我一开始也这么想过实测下来完全不可行。原因很简单大模型擅长的是语言和逻辑不是精确的数值计算。一个稍微复杂一点的模型STL 文件动辄几万个三角面片每个面片三个顶点、每个顶点三个浮点数让模型逐字生成这些数字既慢又容易出错而且生成出来的东西根本没有参数化可言改一个尺寸等于全部重来。正确的思路是让大模型做它擅长的事——理解意图、提取参数、生成结构化描述然后把真正的几何计算交给专业的几何内核。这个分工是整个 text-to-cad 链路的核心设计原则。大模型负责把一个边长 50mm 的立方体上面挖一个直径 20mm 的通孔翻译成类似{shape: box_with_hole, length: 50, hole_diameter: 20}这样的结构化数据几何内核负责根据这些参数生成精确的 B-rep 实体或网格。2.2 几何内核的选择OpenCASCADE 与网格方案的取舍几何内核这块目前开源方案里最成熟的是OpenCASCADE简称 OCCT。它支持 B-rep 表示能生成真正的实体模型导出的 STEP 文件可以被主流 CAD 软件正常读取和编辑。这是它最大的优势STEP 是工程界的通用语言你生成的模型能直接进 SolidWorks、中望 CAD、FreeCAD 继续加工而不是一个只能看不能改的网格壳子。另一条路线是纯网格方案直接生成 STL 或 GLB。这条路实现起来简单很多不需要理解 B-rep 的拓扑结构只要把三角面片拼出来就行。但代价是模型没有参数化信息圆孔会变成多边形近似尺寸精度受网格密度影响。如果你的下游是 3D 打印或者网页展示网格方案够用但如果要进工程链路还是得走 OCCT。我实测下来的组合是用 Python 的 CadQuery 库作为几何层它底层封装了 OCCT同时提供了非常友好的链式 API。CadQuery 的好处是它本身就是为参数化建模设计的写出来的代码可读性极强而且天然支持导出 STEP、STL 两种格式。GLB 的话需要额外走一步转换后面会讲。2.3 从自然语言到 CadQuery 代码的映射逻辑大模型在这里的角色是翻译官。你给它一段自然语言描述它输出一段 CadQuery 的 Python 代码。为什么选 CadQuery 而不是直接输出 OCCT 的 C 调用因为 CadQuery 的 API 更接近人类的建模思维大模型训练数据里也有大量 CadQuery 示例生成质量更稳定。一个典型的映射关系是这样的自然语言片段提取的参数生成的 CadQuery 操作边长 50mm 的立方体length50cq.Workplane(XY).box(50, 50, 50)直径 20mm 的通孔diameter20.faces(Z).workplane().hole(20)边缘倒角 2mmradius2.edges().chamfer(2)绕 Z 轴旋转 45 度angle45.rotate((0,0,0), (0,0,1), 45)这张表看起来简单但实际落地时会遇到很多模糊表述。比如一个圆柱——直径多少高度多少如果用户没说你是追问还是给默认值我的做法是给一套合理的默认值同时在输出里明确标注哪些参数是推断的让用户知道哪里可能需要调整。这比反复追问体验好得多。3. 搭一套能跑的文字转 CAD 环境依赖、版本与第一个模型3.1 环境准备中最容易翻车的依赖问题CadQuery 的安装是第一个坑。它依赖 OCCT 的 Python 绑定在不同操作系统上的安装方式差异很大。我建议直接用 conda 或者 mamba 来装pip 装 CadQuery 在部分平台上会遇到编译问题。conda create -n text2cad python3.10 conda activate text2cad conda install -c conda-forge cadquery装完之后验证一下import cadquery as cq result cq.Workplane(XY).box(10, 10, 10) print(result.val().Volume())如果输出接近 1000说明环境没问题。如果报错找不到 OCCT 相关的动态库大概率是 conda 环境没激活对或者系统里存在多个 Python 版本冲突。提示不要混用 pip 和 conda 安装 CadQuery 及其依赖OCCT 的二进制绑定对版本非常敏感混装极易出现符号找不到的问题。3.2 大模型接口的接入方式与提示词设计几何层搞定之后接大模型。这里可以用任何提供 API 的大模型服务核心是设计好提示词。我的提示词模板大致是这样的SYSTEM_PROMPT 你是一个 CAD 建模助手。用户会用自然语言描述一个三维模型 你需要输出一段 CadQuery Python 代码来生成这个模型。 要求 1. 只输出代码不要输出解释 2. 代码最后必须把结果赋值给名为 result 的变量 3. 所有尺寸单位默认为毫米 4. 如果用户没有指定某个尺寸使用合理的默认值并在注释中标注 5. 只使用 cadquery 库不要引入其他依赖 这个提示词的关键在于约束输出格式。如果不强制要求赋值给result模型可能用各种变量名后续提取结果时就要做兼容处理很麻烦。强制只输出代码也很重要否则模型会在代码前后加一堆解释文字解析时还得做字符串清洗。3.3 第一个完整可跑的例子从一句话到 STEP 文件把几何层和模型层串起来一个最小可跑的例子长这样import cadquery as cq def text_to_cadquery(user_input, llm_client): response llm_client.chat(SYSTEM_PROMPT, user_input) code extract_code(response) local_vars {} exec(code, {cq: cq}, local_vars) return local_vars[result] def export_step(model, filepath): cq.exporters.export(model, filepath, exportTypeSTEP) model text_to_cadquery(一个外径60内径30厚度10的圆环边缘倒角2mm, client) export_step(model, ring.step)这里用exec执行模型生成的代码安全性上确实有风险生产环境一定要做沙箱隔离或者代码白名单校验。但在个人实验阶段这个方案能让你在十分钟内跑通整条链路先验证可行性再考虑加固。实测下来对于圆环、方块、圆柱、带孔板这类基础几何体一次生成的成功率大概在八成以上。失败的情况主要集中在两类一是模型对 CadQuery API 记错了比如把hole()的参数搞混二是自然语言描述本身有歧义比如一个带凹槽的圆柱没说凹槽在哪一端。4. 三种输出格式的实战差异STEP、STL、GLB 各自怎么用4.1 STEP工程链路的通行证STEP 是 text-to-cad 最有价值的输出格式因为它是唯一能保留完整 B-rep 拓扑信息的格式。什么意思就是你导出的 STEP 文件拿到 SolidWorks 里打开它还是一个实体你能继续在上面打孔、倒角、做装配而不是一堆散落的曲面。导出 STEP 在 CadQuery 里就一行cq.exporters.export(model, output.step, exportTypeSTEP)但这里有个细节值得注意STEP 的精度设置。CadQuery 默认的导出精度对大多数场景够用但如果你做的是精密零件公差要求在微米级就需要调整tolerance参数。我一般会在导出前检查一下模型的包围盒尺寸确认单位是毫米而不是米因为单位搞错是新手最常见的翻车点。4.2 STL3D 打印的默认选择但精度要自己控STL 是网格格式导出时最关键的是弦高公差linear deflection和角度公差angular deflection。这两个参数决定了曲面被离散成多少三角面片。公差越小面片越多文件越大但曲面越光滑。cq.exporters.export( model, output.stl, exportTypeSTL, tolerance0.01, angularTolerance0.1 )tolerance0.01意味着曲面上的点与真实曲面的偏差不超过 0.01mm。对于 3D 打印来说这个精度已经远超大多数打印机的分辨率了。如果你只是做个概念验证tolerance0.1就够了文件能小一个数量级。注意STL 导出后圆孔会变成多边形。如果孔径很小而公差设得太大孔可能直接变成三角形甚至消失。做小孔零件时务必把公差调小。4.3 GLB网页展示和快速预览的最优解GLB 是 glTF 的二进制版本优势是文件小、加载快、浏览器原生支持。如果你想把生成的模型直接嵌到网页里给客户看GLB 是最合适的。但 CadQuery 本身不直接支持导出 GLB需要走一步转换。我的做法是先把模型导出为 STL然后用trimesh库读进来再导出 GLBimport trimesh mesh trimesh.load(output.stl) mesh.export(output.glb)这一步转换会丢失 B-rep 信息但对于展示用途来说无所谓。trimesh还能顺便帮你做网格简化如果 STL 面片太多导致 GLB 文件过大可以在这一步降采样。三种格式的对比总结如下格式保留参数化可编辑性典型用途文件大小STEP是强工程制造、CAD 二次编辑中等STL否弱3D 打印、网格分析较大GLB否弱网页展示、AR/VR较小5. 实测中暴露的问题与我的处理方案5.1 模型生成的代码执行失败从报错到修复的完整链路跑通第一个例子之后我批量测试了二十多个描述发现大约有 15% 到 20% 的生成代码会执行失败。失败原因排下来第一位是API 参数用错比如box()只传了两个参数、hole()用在了没有工作平面的对象上。第二位是逻辑顺序错误比如先倒角再打孔导致倒角面被后续操作破坏。处理这类问题的思路不是让模型一次生成完美代码而是加一层自动修复循环。具体做法是执行失败时把报错信息和原始代码一起回传给模型让它修正后重新生成。实测下来第二轮修复能解决大部分问题第三轮之后还失败的基本就是描述本身有歧义需要人工介入。def generate_with_retry(user_input, max_retries3): code llm_generate(user_input) for i in range(max_retries): try: return execute_code(code) except Exception as e: code llm_fix(code, str(e)) raise RuntimeError(多次修复后仍无法生成有效模型)这个重试机制看起来简单但效果非常明显。我的测试集里单轮成功率从 80% 提升到了 95% 以上。5.2 尺寸单位混乱毫米和米的隐形陷阱这是最隐蔽的坑。CadQuery 内部默认单位是毫米但大模型在生成代码时有时候会自作主张把用户说的0.5 米直接写成box(0.5, 0.5, 0.5)结果生成一个 0.5 毫米的方块肉眼几乎看不见。更麻烦的是这种错误不会报错模型能正常生成只是尺寸完全不对。我的解决方案是在提示词里强制要求所有尺寸先转换为毫米并且在代码执行后加一道校验检查模型的包围盒尺寸是否在合理范围内。如果用户说的是一个手掌大小的盒子而生成的模型包围盒只有 0.1mm那就触发警告。def validate_scale(model, expected_range(1, 1000)): bbox model.val().BoundingBox() max_dim max(bbox.xlen, bbox.ylen, bbox.zlen) if not (expected_range[0] max_dim expected_range[1]): print(f警告模型最大尺寸为 {max_dim}mm请确认单位是否正确)5.3 复杂模型的描述歧义什么时候该追问什么时候该给默认值简单模型好办复杂模型就麻烦了。用户说一个带法兰的管子法兰多大几个螺栓孔孔怎么分布这些信息如果全部追问交互体验极差如果全部给默认值生成的结果可能完全不是用户想要的。我的策略是分层处理核心尺寸管径、管长如果缺失就追问因为这些参数错了整个模型就废了次要特征法兰厚度、螺栓孔数量给行业常见默认值同时在输出里明确标注以下参数为默认值可调整。这样既保证了交互流畅又给了用户修正的入口。实测下来这种分层策略在标准件场景下特别好用。比如法兰、轴承座、支架这类有行业惯例的零件默认值往往就能满足需求用户只需要微调几个关键尺寸。6. 把 text-to-cad 用起来几个真实场景的落地思路6.1 批量生成标准件库一次描述批量出图这是我觉得最有价值的应用场景。做非标设计的人都知道标准件虽然叫标准但每次用的时候还是得重新建模因为规格不同。如果用 text-to-cad你可以写一个循环把规格表里的参数逐行喂进去批量生成 STEP 文件。specs [ {name: ring_60_30, od: 60, id: 30, thickness: 10}, {name: ring_80_40, od: 80, id: 40, thickness: 12}, {name: ring_100_50, od: 100, id: 50, thickness: 15}, ] for spec in specs: desc f外径{spec[od]}内径{spec[id]}厚度{spec[thickness]}的圆环 model text_to_cadquery(desc, client) export_step(model, f{spec[name]}.step)这个思路可以扩展到螺栓、垫片、型材截面等各种标准件。一次配置批量产出比手工建模快一个数量级。6.2 与现有 CAD 工作流的衔接STEP 导入后的注意事项生成的 STEP 文件导入到主流 CAD 软件时有几个细节要注意。第一导入后检查单位虽然 STEP 标准里带了单位信息但不同软件的解析行为不一致偶尔会出现 1000 倍缩放的问题。第二检查实体是否有效极少数情况下生成的 B-rep 会有微小缝隙导致软件识别为曲面而非实体这时候需要在 CAD 里做一次缝合操作。我一般会在导入后先做一个简单的测量确认关键尺寸和预期一致再进行后续操作。这个习惯帮我避免了好几次因为单位问题导致的返工。6.3 当前方案的边界哪些描述它做不了说了这么多好处也得说清楚边界。目前的 text-to-cad 方案对于自由曲面、复杂扫掠、参数化阵列这类高级建模操作生成质量还很不稳定。比如一个沿螺旋线扫掠的变截面管道这种描述即使对熟练的 CAD 工程师来说也需要仔细规划建模步骤指望大模型一次生成正确代码成功率很低。我的建议是把 text-to-cad 定位为标准件和简单几何体的快速生成器而不是万能建模助手。在这个定位下它的效率提升非常明显超出这个范围还是老老实实手工建模或者写脚本更靠谱。另外代码执行的安全性也值得重视。exec执行模型生成的代码理论上存在被注入恶意代码的风险。个人实验无所谓但如果要做成对外服务一定要加沙箱限制可调用的库和系统资源。7. 我在这套流程里踩过的几个坑第一个坑是过早追求功能完整。一开始我想让系统支持所有 CAD 操作结果提示词越写越长模型反而更容易出错。后来砍掉了一大半功能只保留基础几何体和布尔运算成功率立刻上来了。这让我意识到text-to-cad 的价值在于覆盖高频简单场景而不是追求大而全。第二个坑是忽略了几何有效性检查。有段时间我发现生成的 STEP 文件在某些软件里打不开排查了很久才发现是模型存在自相交或者零厚度面。后来在导出前加了一步model.val().isValid()检查问题就少多了。第三个坑是对模型输出的稳定性期望过高。同一个描述不同时间调用大模型生成的代码可能不一样有时候能跑通有时候报错。这不是 bug而是大模型本身的特性。接受这一点之后我把重试机制做成了标配心态也平和了很多。如果你也想动手试一下我的建议是从最简单的几何体开始先把整条链路跑通再逐步增加复杂度。不要一上来就挑战复杂模型那样很容易在环境配置和调试上耗尽耐心。跑通第一个圆环、第一个带孔方块之后你对整个流程的理解会完全不一样。