让 LLM 操作 CAD:四条技术路线的取舍

让 LLM 操作 CAD:四条技术路线的取舍

给 agent 接 CAD 能力这件事,我一开始把重心放错了。我以为难点在"让 LLM 听懂'画一个 300×200 的钢板'这种话",于是花了很多时间调 prompt、设计 JSON schema。两周跑下来发现,语义理解是整条链路上最不值一提的一环------真正的坑全在 CAD 那一侧:坐标为什么非得是 VARIANT、插件为什么必须放安装目录、三维建模为什么只能在主线程、几十万张 DWG 凭什么这么难读。这些脏活,跟 LLM 一点关系都没有。

所以这篇不写"选型结论表"那种东西,就把我按什么顺序试、在哪翻了车、最后怎么拍板的过程记下来。想让 agent 画图的人照着这个顺序走一遍,能少踩一半坑。

一、我原来的计划,和它是怎么被打脸的

动手之前我的设想特别简单:让 LLM 输出一个 JSON(长宽高、材料、倒角),再用一个 Python 库把 JSON 渲染成图纸文件,齐活。这个链路我两天就跑通了,用的是 ezdxf:

python 复制代码
import ezdxf
doc = ezdxf.new("R2010")
msp = doc.modelspace()
msp.add_lwpolyline([(0,0),(300,0),(300,200),(0,200)], close=True)
doc.saveas("plate.dxf")

"画个钢板"从一句自然语言变成一张 DXF,十分钟的事。

但跑了没多久我就意识到两个问题,而这两个问题决定了一切:

第一,生成文件 ≠ 操作软件。设计师真正的工作流是开着 CAD,图要出现在他眼前的窗口里,他还要能继续改。你丢给他一个 DXF 文件,他得手动打开、手动检查,等于把最该自动化的那一步又踢回给了他。

第二,存量图纸是 DWG,不是 DXF。DWG 是 Autodesk 的私有二进制格式,ezdxf 根本读不了。手头几十万张历史图纸,全是 DWG。

于是需求被拆成三块,三块对应的是完全不同的技术栈:

  1. 生成图纸文件 → ezdxf 能解决
  2. 控制正在运行的 CAD → 得用各软件的二次开发接口,一条软件一条路
  3. 读存量 DWG → 得先转格式,再解析

从这儿开始,"让 LLM 画图"才真正变成"让 LLM 操作 CAD",脏活全在后面两块。

二、先捡最软的柿子:ezdxf

ezdxf 放第一位试,不是因为它重要,是因为它便宜:纯 Python、不装任何 CAD、Linux 上能跑。事实证明它对得起"最稳兜底"这个定位------图元(线圆弧多段线样条椭圆)、标注(线性/对齐/半径/直径/角度/弧长)、文字、剖面线、块引用,全都能生成。

但它有两个我一开始没料到的边界:

  • 它生成的是静态文件。图纸一旦落盘,就和"交互"绝缘了。后面 CAXA 那条路卡住的时候,我就是靠"ezdxf 生成 DXF 让用户手动开"才没把 CAXA 整条线砍掉。
  • 它救了我一个意外的场:CAXA CAD 2027 能直接打开 ezdxf 生成的 DXF R2010 文件,无需任何转换。这个事实在后面做选型时成了关键论据------兜底方案在 CAXA 环境里是通的。

ezdxf 这块我后面(第四篇)会展开,包括怎么解析而不是生成。

三、控制正在运行的 CAD:AutoCAD 的 COM,和 ZWCAD 的白捡

要"控制正在运行的软件",AutoCAD 是绕不开的第一站,因为它有一条最成熟的接口:COM。

COM 一句话说清:Windows 上的一种跨进程对象调用机制,AutoCAD 启动后把自己暴露成一个对象,外部程序拿到这个对象,就能调它的方法去画线画圆。Python 这边用 pywin32 拿对象:

python 复制代码
import win32com.client
acad = win32com.client.Dispatch("AutoCAD.Application")
doc = acad.ActiveDocument
ms = doc.ModelSpace

看着就三行,但我在这栽了第一个跟头。

坐标传不进去。 我按直觉写 ms.AddCircle([50, 50, 0], 30),结果要么报 com_error,要么更阴险------不报错,但圆不在 (50,50),在别处。后者比报错还难查,因为你会以为自己的逻辑错了,其实错在类型。

排查过程是这样的:先怀疑坐标顺序(不是),再怀疑单位(不是),最后翻 pywin32 文档才明白------CAD 的 COM 接口里,点坐标的签名是 VARIANT 数组,不是 Python 的 list。VARIANT 是 COM 的动态类型,list 传过去会被当成一个普通的 Variant 值而不是坐标数组,CAD 拿到的数就乱了。

修法是包一层,显式声明"这是 double 数组":

python 复制代码
def V(p):
    return win32com.client.VARIANT(
        pythoncom.VT_ARRAY | pythoncom.VT_R8,   # 显式声明:double 数组
        [float(p[0]), float(p[1]), float(p[2])]
    )

之后所有坐标都走 V([x,y,z])。这个坑是 AutoCAD COM 自动化里 90% 的人第一个踩的,第二篇会从头讲透,包括更隐蔽的坑------AutoCAD COM 的圆弧角度是弧度不是度,写错了画出来的弧全不对。

转机出现在我意识到真正要支持的是 ZWCAD(国产环境,客户用的是它而不是 AutoCAD)。当时心里咯噔一下:不会要再学一套接口吧?

结果没有。ZWCAD 对 AutoCAD 的 COM 接口做了兼容,只改一个字符串:

python 复制代码
acad = win32com.client.Dispatch("ZWCAD.Application")

其余代码一行不动。这个发现直接省掉了"再摸一套国产 CAD 接口"的预算,是四条路线里 ROI 最高的一条。

四、三维建模没有 COM:ZW3D 逼我写起了 C++

ZW3D 是国产三维 CAD,麻烦在于:它没有 COM 接口。官方给的是 C API + 插件机制,意思是你要么用 C++ 写插件塞进它进程里,要么别玩。

这就把我从 Python 的舒适区拽到了 C++。我当时的思路是:写一个 C++ DLL 插件,插件加载时起一个 HTTP 服务,外面的程序(包括 LLM 的 MCP 工具)用 HTTP 请求来调它,插件收到请求后调用 ZW3D 的 API 画图。

听着顺,但这里藏着一道几乎所有 GUI 二次开发都会撞的墙:线程安全。

我一开始图省事,想直接在 HTTP 处理线程里调 ZW3D API。结果根本不行------ZW3D 的 API 必须在主线程调用,从别的线程调,要么直接崩,要么静默无效。这不是 ZW3D 独有的毛病,所有带 GUI 的桌面软件都这样(Android 里 UI 只能主线程改,同理)。HTTP 服务天然多线程,每个请求来一个线程,正好踩在雷上。

排法是把"调 API"这件事投递回主线程:HTTP 线程把任务塞进队列,用 ZwCommandPost 唤醒主线程处理,再用条件变量把结果传回来。这套"队列 + 投递 + 等待"的模式是 ZW3D 方案能不能成立的关键,也是我花时间最多的地方,第三篇完整展开。

这还只是逻辑层的坑,部署层的更气人:

  • DLL 放错目录。我照着直觉把编译好的 DLL 扔到用户目录 %appdata%\...\custom\apilibs,重启 ZW3D,命令就是没注册。查日志,Init 函数压根没被调用。折腾大半天才搞明白:ZW3D 只从安装目录的 apilibs 加载并调用 Init,放用户目录等于白放。
  • 中文乱码。面板上中文全变问号,最后定位到 ZW3D 中文版按 GBK(代码页 936)解析字符串,而我的源码是 UTF-8,编译时还加了 /utf-8,两边一打架就乱了。得把 .cpp.ui 全转成 GBK 存。

这些坑单独拿出来都不大,串在一起,ZW3D 成了四条路线里"架构最完整、门槛也最高"的那条。它真正值钱的地方在后面:一旦 HTTP 通了,就能在它之上做质量评估闭环------让 agent 画完图后自己检查"视图齐不齐、标注有没有冲突、内容出没出纸",不合格就重画,最多迭代 5 次。这套"画完自检"的能力,是 COM 那条路线给不了的。

五、CAXA:能画,但"遥控"卡住了,我还没排掉

CAXA 是最后试的,也是最折腾的。它走 CRX 插件路线(可以理解成国产版的 ObjectARX)。命令模式下没问题:注册个命令,画了 49 个图元、5 个标注、存 EXB,全通。

问题出在我要做"远程控制"时:把画图代码搬到 HTTP 回调里执行,appendAcDbEntity 明明返回成功,图形就是不显示。实体写进数据库了,但界面没刷新出来。我列了四个排查方向------文档锁定(lockDocument)、sendStringToExecute 投递命令、PostMessage 发消息、以及找 CAXA 有没有等价于 ZW3D ZwCommandPost 的机制------但两周内没排掉。

我在这里的态度是:不硬撑。CAXA 这条先标成"半通",兜底用 ezdxf 生成 DXF 让用户打开。等哪天真要远程控制 CAXA,再集中 1-2 周啃这个命令上下文问题。调研的价值之一,就是知道什么该现在做、什么该先放着。

六、读存量图纸:50 万张 DWG 怎么啃

生成和控制都摸了一遍,最后是"读"------几十万张历史 DWG。

第一反应是直接解析 DWG 二进制。这条路有个开源库叫 LibreDWG,但它 GPLv3,商用场景 license 是个雷,直接放弃。

最终方案是绕:ODA File Converter(免费)把 DWG 转成 DXF,再用 ezdxf 解析。ODA 是 Open Design Alliance 出的免费命令行工具,一条命令批量转:

arduino 复制代码
ODAFileConverter.exe "D:\dwg" "D:\dxf" ACAD2018 DXF 1

实测 5-10 文件/秒,50 万张约 14-28 小时。听着吓人,但这是纯一次性批量任务,扔台机器挂一晚上的事。

解析这步看着简单,其实有个"做对了和做错了天差地别"的点:别只数图元。数出"这张图有 200 条线 30 个圆"没用,真正有用的是 get_measurement() 拿到标注的真实数值(200mm、Ø120 这种),以及按坐标位置把标题栏、BOM、技术要求归位出来。这些才是能喂给检索和 LLM 的东西。第四篇会把这套流水线完整拆开。

七、把 LLM 放回它该在的位置

绕了一大圈,回到最开始的问题:LLM 到底站在哪?

答案是:指挥,不是执行。LLM 不直接调 COM、不写 C++,它干的是把"画个 300×200 的板"变成结构化参数,然后从工具列表里挑出"画板件"这个工具、传参。真正的画图动作,是底下的接口干的。

objectivec 复制代码
用户自然语言
    ↓
LLM(语义理解 → 结构化参数 → 工具选择)
    ↓
MCP 工具层(统一接口,LLM 靠工具的 docstring 理解每个工具能干嘛)
    ├── AutoCAD/ZWCAD  → COM 直连
    ├── ZW3D           → HTTP 插件
    ├── CAXA           → HTTP 插件(半通)
    └── ezdxf          → 直接生成 DXF
    ↓
CAD 出图 / 文件输出

这里要单独说一句 MCP 工具层为什么必要:LLM 不认识你的代码,它只认识工具的"名字 + 描述 + 参数"。所以封装工具时,docstring 写得清不清楚,直接决定 LLM 会不会用错工具------参数单位是毫米还是米、角度是度还是弧度,不写清楚它就瞎传。这个细节看着小,实际上决定了整条链路的上限。

好消息是这块有现成的:autocad-mcp-pro 已经封装了 154 个 AutoCAD 工具,multiCAD-mcp 一套代码通吃 AutoCAD/ZWCAD/GstarCAD/BricsCAD。想省事直接 pip install,想理解原理就自己写一遍(第二篇有完整例子)。

八、最后怎么选(不是结论表,是四句实话)

试完四条,我的判断是:

  • 只要"生成图纸文件",不碰软件 → ezdxf。一天上手,但要知道它只管文件。
  • 要控制正在跑的 AutoCAD/ZWCAD → COM + MCP。半天打通,ZWCAD 白捡,ROI 最高的一条。
  • 要远程控制 ZW3D 做三维 → C++ HTTP 插件。预算 1-2 周,能换来 COM 给不了的"画完自检"闭环。
  • 要远程控制 CAXA → 现阶段别硬啃,用 ezdxf 兜底,等命令上下文问题解决。

接下来这三篇

  • 第二篇:AutoCAD/ZWCAD 的 COM 自动化,从 VARIANT 这个坑讲起,全部 API 清单 + 怎么封装成 MCP 工具 + 批量转 50 万张 DWG
  • 第三篇:ZW3D 插件,从"Init 死活不被调用"排到"HTTP 通了能远程建模",含线程调度那套
  • 第四篇:图纸的正反两面------LLM 生成 DXF,以及 50 万张存量图怎么解析入库喂回 LLM
相关推荐
东方小月2 小时前
从零开发一个 Coding Agent(十):实现 CLI 参数解析与静态命令
前端·人工智能·开源
前端小白乘风2 小时前
github Copilot 接入deepseek-v4-pro
人工智能
qpsj2 小时前
ZW3D 插件开发实战:从搜索面板到 LLM 远程建模
llm
还不秃顶的计科生2 小时前
具身智能论文学习1:PaLM: Scaling Language Modeling with Pathways
人工智能·语言模型·palm
qq_454245032 小时前
Agent数据价值分类存储原则
大数据·人工智能·分类
怪奇云呼军2 小时前
从声音特征到 CRM 回流:闪电智能 Voice Agent 沟通策略自适应系统 v1 实战
android·人工智能·python·音视频·语音识别
Python私教2 小时前
AI 并行编码的 Worktree 生命周期:创建、隔离与安全回收
人工智能·git
ZhengEnCi2 小时前
什么是 SFT 监督微调:大模型从只会续写到会听话的关键一步
人工智能
桃西西呀3 小时前
你的 AI Agent 真的会做题吗?Harbor评测框架帮你测评
人工智能