
一直觉得CAD工具最大的门槛不在功能而在“把脑子里的形状翻译成鼠标操作”。text-to-cad这个方向就是把翻译这件事交给大语言模型你描述一个零件它生成建模脚本再把脚本渲染成可编辑的三维模型。我最近用一套很小的组合把它跑通了整个过程远没有想象中那么玄。这篇文章从技术路线、核心细节、最小可运行Demo、常见坑到落地场景一次性讲透给想入坑的朋友一条能照抄的路径。1. 这层窗户纸捅破之后text-to-cad到底在解决什么问题1.1 两条技术路线先分清再说text-to-cad到现在为止其实有两条完全不同的技术路线我建议先搞清楚它们再选方向。第一条是“生成式直接出几何”。做法是把大量CAD模型转成体素、点云或者三角网格训练一个类似图像生成模型的3D生成器。用户给一句描述模型直接output一个Mesh文件。这条路视觉上很震撼一段话能冒出一个看起来像模像样的零件但它有几个硬伤输出的是离散网格不是参数化特征后期没法改约束换一个尺寸就得重新生成而且网格质量离工程制造差得很远。它在概念设计、灵感草稿阶段有价值但离“CAD”的严格定义还很远。第二条是“大模型生成建模代码”。核心思路是让大语言模型看自然语言描述输出一段参数化建模脚本最常见的是OpenSCAD脚本也可以用Python调用FreeCAD的API或者某些开源内核的Python接口再让脚本引擎渲染成模型。这条路保留了完整的特征历史树尺寸、草图和布尔操作都记录在代码里改一个参数就能重新生成这才是真正的CAD。我强烈建议普通开发者和设计师从第二条路线入手。它不要求你有训练扩散模型的算力也不需要囤积海量CAD数据集本质上是把“编程能力”和“CAD领域知识”以提示词的形式塞给现成的语言模型。门槛低、可控性强、可解释性好出了问题还能在代码层排查。1.2 为什么代码生成路线更接近“真正能用”用一个例子体会两条路线的差别。想做一个法兰盘外径60毫米内径25毫米厚度10毫米均布4个直径6毫米的安装孔。生成式方案会给你一个看起来像法兰的三角形网格但孔位是不是严格均布、内孔是不是真圆、倒角到底多大它一概不保证。这个文件放到CAM软件里刀具路径都没法稳定算出来因为几何拓扑可能是脏的。代码生成方案给的是几十行OpenSCAD代码里面清清楚楚写着cylinder(d60)、difference()、for(i[0:3]) rotate(...)。你只要扫一眼代码就知道逻辑对不对甚至可以反问模型“把孔的个数改成6个”它改一行循环条件就完成了。这个“可参数化修改”的能力才是工业生产里最值钱的资产。还有一个容易忽略的因素调试成本。Mesh生成方案出了问题你根本不知道是训练数据问题、采样噪声问题还是Prompt理解偏差脚本生成方案出了问题直接把错误信息丢回给模型让它自己改马上形成闭环。这一点在后面排查章节会详细讲。2. 核心细节解析与实操要点把大模型调教成CAD建模师2.1 输入侧自然语言怎么变成可计算的参数很多人以为text-to-cad就是“把中文句子发给大模型就行”第一次跑会发现自己想多了。自然语言是模糊的“一个大盖子”没说直径“一个支架”没说壁厚“圆润一点”在工程上等于没有。我实际使用中把输入侧拆成三层来解决第一层是“单位强制层”。我要求所有文本描述里必须显式声明单位默认是毫米。这一层看起来简单但能避免大量尺寸漂移问题。工业件描述里默认毫米3D打印场景也默认毫米可大模型训练数据里混杂了英寸、厘米、像素如果不强制它真敢给你生成一个直径60英尺的法兰。第二层是“参数抽取层”。文本进入模型之前我先有一个固定的解析模板要求模型先用结构化JSON列出它识别出的关键尺寸参数再开始写代码。比如用户说“一个方形底座长80宽50高15中心一个直径20的盲孔”模型应该先输出{ base_length: 80, base_width: 50, base_height: 15, hole_diameter: 20, units: mm }这一步非常关键。参数先行能防止模型在长代码中把尺寸写丢也方便你后续做自动化校验比如检查JSON里hole_diameter是否为正数、是否超过底座宽度的一半。第三层是“制造约束层”。这一步容易被新手忽略。我会在提示词里固定要求所有凸台与立壁之间必须加圆角圆角默认半径3毫米所有贯穿孔默认做成通孔避免沉孔和阶梯孔混淆如果描述里没提公差默认采用中等公差。这样做是因为纯文本描述的大多数零件都有工程合理性要求直接生成的模型往往“看起来像”实际没法用。2.2 输出侧代码校验与几何合法性检查模型生成代码之后你不能直接拿去渲染得先过一道校验关卡。我把校验分为三档。第一档是“文本级检查”。不碰几何引擎只查代码格式。花括号是否匹配、方括号是否对称、有没有不小心写出的中文标点、必需的建模函数是否齐全。用一段Python脚本就能做成本几乎为零。别小看这个检查大模型输出的代码里出现全角括号的情况我见得太多了OpenSCAD遇到这种情况直接报错而且报错信息非常难懂。第二档是“语法级检查”。把模型输出的代码保存成.scad文件用OpenSCAD的命令行参数做一次解析测试。OpenSCAD有--check-parameters这样的开关可以检查参数是否存在但它的语法错误信息不算友好所以我通常的做法是直接尝试渲染一个低分辨率的预览能出结果就说明语法基本过关。这一步要设置超时比如10秒内渲染不出来就直接杀掉进程防止模型生成死循环。第三档是“几何合理性检查”。渲染成功后还要检查体积是否为0、包围盒尺寸是否在用户预期范围内、有没有明显的负体积警告。最简单的办法就是解析OpenSCAD渲染日志看到ERROR和WARNING关键字就记录下来。这个检查能挡住很多隐患比如布尔运算把主体整个减没了结果渲染出一个空模型只在文本层面根本发现不了。2.3 模型选型本地小模型还是云端大模型选模型是绕不开的问题。我的建议分两种情况。如果你的场景是“研究、学习、个人3D打印”直接用商业大模型API最划算。现在各家大模型API都支持很长的上下文对OpenSCAD这种语法简单的脚本语言理解得相当好Few-shot能力也强。你给它两个法兰盘的例子它就能写出第三个。成本很低一个零件描述加生成的token量折合人民币几乎可以忽略。如果场景是“企业内部使用、涉及图纸保密、需要私有化部署”那就得用本地模型。我实测下来7B到14B参数量级别的量化模型可以把简单零件的生成做到基本可用但复杂零件会频繁出现尺寸漏写、布尔运算瞎写、函数名编造的问题。这时候不要硬上把生成的策略改成“本地小模型做参数抽取和JSON结构化云端大模型或者微调后的中等模型做代码生成”效果会稳很多。另外提醒一句不要追新追大。text-to-cad的任务特点决定了模型必须具备“代码生成能力”和“空间推理能力”双重素质有些对话能力很强的模型反而写不好OpenSCAD因为它们在代码类任务上缺乏针对性训练。先用一个稳定版本的模型把全链路跑通再横向切换别一上来就做模型评测的“端水大师”。3. 实操过程与核心环节实现搭一个能跑的text-to-cad最小系统3.1 环境准备与依赖安装这一步没有任何黑魔法三个组件就够了Python环境、OpenSCAD命令行工具、一个大语言模型的调用入口。OpenSCAD的安装很直接。在Debian/Ubuntu系上执行sudo apt install openscadmacOS用户执行brew install openscad装完后验证一下openscad --version看到版本号输出就说明环境通了。Python这边只需要标准库加一个HTTP客户端库我用requests就够了。不需要引入任何重型框架。关于大模型调用入口我不绑定任何特定服务商。你只需要一个“传入消息数组、返回文本”的接口哪怕是本地起一个兼容接口都行。我封装的时候统一走一个叫llm_complete的函数方便后面切换。3.2 提示词模板与LLM调用封装这是整个系统的灵魂。提示词模板我建议这样写你是一个参数化CAD建模助手。用户会用自然语言描述一个机械零件。 第一步提取关键尺寸参数输出一个JSON对象字段必须包含长度、宽度、高度、直径等数值单位为毫米。 第二步基于提取的参数输出一段OpenSCAD代码实现这个零件。 规则 1. 所有尺寸默认毫米不要加单位后缀。 2. 必须使用cube、cylinder、hull、linear_extrude、rotate、translate、difference、union等基础操作。 3. 不能使用任何import或外部依赖。 4. 必须在代码开头设置$fn80保证曲面平滑。 5. 如果零件有安装孔必须使用difference做减法。 6. 只输出JSON和代码不要任何解释性文字。对应的Python封装长这样import json import subprocess import requests from pathlib import Path def llm_complete(messages, max_tokens2000): # 这里按你使用的接口规范填写 # 例如兼容接口POST到本地/云端的chat/completions地址 resp requests.post( http://your-llm-endpoint/v1/chat/completions, json{ model: your-model-name, messages: messages, max_tokens: max_tokens, temperature: 0.2, }, timeout120, ) resp.raise_for_status() return resp.json()[choices][0][message][content] def text_to_cad(user_desc: str) - tuple[dict, str]: prompt PROMPT_TEMPLATE \n用户描述 user_desc raw llm_complete([{role: user, content: prompt}]) params, code parse_model_output(raw) return params, code注意这里temperature0.2非常关键。生成CAD代码不是写诗太高的随机性会带来海量低级错误。我实测在temperature0.2附近代码稳定性和可读性都最好。parse_model_output函数负责从模型的原始输出里拆出JSON和代码块def parse_model_output(raw: str): # 从文本中提取第一个json块和第一个openscad块 import re json_match re.search(rjson\n(.*?), raw, re.S) code_match re.search(r(?:openscad|scad)?\n(.*?), raw, re.S) params json.loads(json_match.group(1)) if json_match else {} code code_match.group(1).strip() if code_match else raw.strip() return params, code这个正则看起来简单但能挡掉一大批输出格式乱掉的意外情况。3.3 校验与渲染环节拿到代码之后先过校验函数def validate_code(code: str) - list[str]: errors [] if code.count({) ! code.count(}): errors.append(花括号数量不匹配) if import in code.lower(): errors.append(禁止使用import) for kw in [cube, cylinder]: if kw not in code: # 不是所有零件都必须有cube/cylinder视需求决定是否报警 pass return errors校验通过后写临时文件并调用OpenSCAD渲染STLdef render_stl(code: str, output_path: Path, timeout_sec30): tmp_scad output_path.with_suffix(.scad) tmp_scad.write_text(code, encodingutf-8) result subprocess.run( [ openscad, -o, str(output_path), str(tmp_scad), ], timeouttimeout_sec, capture_outputTrue, textTrue, ) if result.returncode ! 0: raise RuntimeError(f渲染失败{result.stderr[-500:]}) return output_path30秒超时是必须的防止模型生成类似for(i[0:99999999])的死循环。渲染日志里如果出现ERROR要捕获下来后续可以回喂给模型让它修。3.4 实测两个样例从文本到STL样例一我用开头说的法兰盘描述“生成一个法兰盘外径60毫米内径25毫米厚度10毫米均布4个直径6毫米的安装孔孔心所在圆直径45毫米”。模型生成的代码我整理后如下$fn 80; outer_d 60; inner_d 25; thickness 10; hole_d 6; hole_circle_d 45; difference() { cylinder(d outer_d, h thickness); cylinder(d inner_d, h thickness 1); for (i [0:3]) { rotate([0, 0, i * 90]) translate([hole_circle_d / 2, 0, -1]) cylinder(d hole_d, h thickness 2); } }这个结果直接可渲染STL文件没有问题。注意内孔圆柱高度写成thickness 1安装孔高度写成thickness 2目的是穿透整个主体避免布尔运算留下残面。这是普通用户写代码最容易漏掉的细节模型在良好提示词下会主动补上。样例二我测试了一个不对称结构“做一个L形支架竖直板高40毫米长60毫米厚度8毫米水平板长60毫米宽30毫米厚度8毫米两块板连接处外侧加半径4毫米的圆角。”模型给出的策略是用两次linear_extrude分别生成竖直板和水平板再用hull处理圆角过渡或者直接在交接处叠加一个圆柱体做圆角。实际渲染结果很接近预期这说明只要提示词里把“圆角”这个需求说得足够明确模型是能处理非对称组合体的。我实测下来文本描述的颗粒度直接决定输出质量。描述越接近“工程图语言”模型输出的代码越稳。你用“一个大一点的圆板中间掏个洞”这种口语它会给你一堆不可控的默认值你用“直径60毫米、厚度10毫米、中心孔径25毫米的圆环”它基本一次成型。4. 常见问题与排查技巧实录4.1 高频故障速查表我把这段时间遇到的典型问题整理成一个表基本覆盖了text-to-cad最常见的事故现场。现象可能原因处理办法模型输出了一堆解释没有代码提示词约束不够强硬在提示词末尾加“只输出代码块不要任何解释”并做正则提取兜底代码花括号不匹配大模型长代码生成时括号漂移校验层拦截并将错误信息回喂给模型重修渲染结果是空模型布尔运算把主体剪光了将difference临时改成union观察中间形态检查内外径大小关系尺寸比例离谱单位约定失效提示词强制输出JSON参数先人工核对参数再渲染圆柱面变成多边形缺少$fn或值太小代码开头强制$fn80以上渲染超时循环次数过多或者hull计算爆炸外层加timeout限制循环变量范围和步数STL能打开但工程软件报错模型存在自相交面或非流形边用网格修复工具过一遍或让模型改用纯参数化特征生成零件没有圆角倒角提示词未明确制造约束在模板里加入默认圆角规则要求所有棱边R3mm这里有两条经验值得一提。第一很多“渲染失败”其实是模型把OpenSCAD的保留关键字拼错了比如把difference写成differance把translate写成translation。这类错误人眼一扫就能发现却会让模型陷入长时间自我怀疑。我的做法是给模型回喂错误日志时明确告诉它“你的输出里可能存在拼写问题请检查所有OpenSCAD内置函数名”这一句提示比让模型瞎猜强得多。第二空模型问题非常隐蔽。我遇到过一次用户要求“外径60内径25”模型生成的代码里外层圆柱直径写成了d25内层也写成了d25两个重叠圆柱相减结果直接得到零体积。文本检查完全看不出问题必须靠几何体积检查才能发现。所以我强烈建议渲染完读一下STL体积如果体积接近0直接判定为逻辑错误。4.2 一个被低估的救场技巧让模型自己修自己的代码这是我觉得整个流程里性价比最高的一步。传统的校验只负责“报错”而text-to-cad可以把报错信息变成新Prompt让模型自我修复。我把这个循环叫“自愈回路”。实现方式非常简单。渲染失败后抓取OpenSCAD的stderr输出拼成一段新对话def repair_code(code: str, error_text: str) - str: messages [ {role: user, content: 请修复以下OpenSCAD代码的语法或几何错误只输出修复后的完整代码。}, {role: user, content: f代码\nopenscad\n{code}\n}, {role: user, content: f错误信息\n{error_text}}, ] raw llm_complete(messages) return parse_code_block(raw)这段代码用了一个非常核心的工程思想不要一次把任务做到位而是把错误信息作为新的上下文让模型在可见错误上做修正。测试下来第一轮修复的成功率在七成以上尤其是拼写错误、括号不匹配、布尔运算参数写反这类低级问题基本一轮就能解决。需要注意的是自愈循环最多跑三轮。超过三轮还在同样位置报错基本说明模型的空间理解有问题这时候应该简化用户描述或者拆成两个零件分别生成再手动合并不要无限循环。5. 场景价值和影响范围text-to-cad到底能改变什么5.1 快速原型与3D打印从一句话到实物对3D打印玩家来说text-to-cad的直接价值就是把“想一个零件”到“手里拿到零件”的时间从几小时压缩到几分钟。以前画一个简单的手机支架要用CAD软件画草图、拉伸出厚度、挖槽、倒圆角熟练操作也要20分钟现在用text-to-cad生成初始模型再进入传统CAD微调整个过程在10分钟以内就能到达切片软件。我实际打过一个传感器支架描述是“一个L形支架一边固定在墙面另一边托住直径40毫米的圆柱体两个端面各留一个直径5毫米的螺丝孔”。第一版模型生成的孔位方向反了我把问题描述给它让它调整旋转方向第二次就正确了。打印出来的实物不需要任何手工修改直接能用。这种体验在以前是不敢想象的。5.2 设计教育与参数化知识资产化在教育场景text-to-cad的价值不是替代教学而是降低起步门槛。学生不需要先记住七个工具按钮才能表达一个结构创意他们可以先用自己的话描述再看模型生成了什么然后对比“标准答案”和“自己的描述”之间的差异。这个过程能非常自然地培养空间想象能力。另一个容易被忽视的价值是参数化知识资产化。很多老工程师脑子里有大量“说不出来但画得出来”的经验比如“这种支座的高度不要超过直径的1.5倍壁厚至少3毫米否则铸件容易缩孔”。如果把这些经验写进提示词模板变成可复用的参数约束text-to-cad就能沉淀成企业内部的标准件生成库。以后任何人输入“按我们公司标准生成一个XX支座”模型自动带出全部约束和公差。这才是真正的长期价值。5.3 现阶段边界能做什么千万别指望什么必须冷静说一下边界。text-to-cad目前最擅长的是机械零件、规则几何体、以拉伸旋转和布尔运算为主的零件比如法兰、支架、底座、外壳、齿轮毛坯、传感器罩。这些零件的特点是特征清晰、尺寸明确、制造方式常规天然适合脚本化描述。它目前还撑不住的是复杂自由曲面、精密配合副、装配体约束关系、仿真分析模型。比如汽车覆盖件那种A级曲面或者需要严格GDT标注的精密模具纯靠文本生成完全不现实。这类工作必须回到传统CAD加专家经验。还有一个软肋是模型精度LLM生成的代码数值通常只有一到两位小数微米级别的配合公差它做不到需要后期在参数化软件里锁定关键尺寸。所以我的定位很明确text-to-cad是“高级草图生成器”和“设计意图翻译器”不是设计师和工程师的替代者。它负责把模糊的创意快速变成一个可编辑、可改参、可进入后续流程的起点真正出图、出制造方案、做结构校核还是靠人。最后分享两个我在实际项目里沉淀下来的小习惯。第一所有提示词模板里一定要有“参数先于代码”的结构模型一旦先输出了结构化JSON后面就算代码写乱你手里也还有一份可靠的参数清单兜底。第二永远保留一份“标准答案样例”放在提示词末尾Few-shot的稳定作用比任何花哨配置都强。text-to-cad这条路走得通但走得好不好完全取决于你有没有把这个翻译过程当成一个严肃的工程问题来对待。