资讯动态

AutoLISP自动验收:无头CAD+AI编程助手的闭环实践

发布时间:2026/10/20 13:37:24 来源:尧图企业网站定制
干 CAD 二次开发这些年我有个越来越强烈的体会写 AutoLISP 最磨人的不是写是“验”。尤其是图纸批量处理类的工具用户一句“你帮我跑一下看看”你就得打开 CAD、逐条加载、找测试图形、执行命令、再肉眼核对结果。现在用 TmAgent 搭了个 Lisp 编程助手把流程改成了让 AI 写完 AutoLISP自动丢进无头 CAD 环境跑通再对输出结果做数值校验全部通过之后才交付给用户。这篇文章就把这套方案的思路、环境和踩坑全过程完整拆一遍适合正在做 CAD 二次开发、或者想给 AI 编程加一层自动验收机制的工程师参考。1. 为什么写 AutoLISP 这么容易翻车又为什么需要自动验收1.1 写代码只占工作量的一半AutoLISP 是一种很“老”但生命力极强的语言几乎所有主流 CAD 平台都保留了对它的支持。很多我接到的需求都很实在把图里所有闭合多段线的面积汇总出来、批量改图框属性、按图层归并对象、生成材料表。这类需求用 AutoLISP 写起来其实不难难的是“对当前这张图有效”。LISP 的括号嵌套、半角引号、反斜杠转义一个字符不对整个文件加载即失败。更麻烦的是运行时状态图纸单位、图层是否存在、文字样式、坐标系转换、对象句柄变化都会影响结果。早期我用 AI 写 LISP 也踩过不少坑模型给出的代码看着很合理但一加载就是error: malformed list或者运行结果和实际图面对不上。AI 负责了“写”但“验证”如果全靠人肉效率反而比手写还低。1.2 传统测试 AutoLISP 的方式有多痛苦常规测试流程大概是这样打开 CAD用appload加载脚本创建或打开一张测试图手动画几个样本在命令行执行自定义函数再回去看图形变化。遇到结果不对就得反复打断点——当然 LISP 没有 IDE 那种断点只能在关键位置加(princ)输出或者一段一段注释排查。这套流程有几个固有痛点。第一环境依赖重电脑必须装完整 CAD不能随时随地在服务器上批量跑第二肉眼核对结果主观性强面积差 0.01 可能看不出来但实际已经算错了第三没法自动化回归改了一版代码旧功能是否还能用完全不知道。对于要交付给用户反复使用的插件这种测试强度显然不够。1.3 TmAgent 的核心思路写、跑、验、交付形成闭环TmAgent 编程助手的核心思路只有一句话不让 AI 直接交代码而是让 AI 在闭环里“跑题”。我把它拆成四步分别对应写、跑、验、交付四个动作。写大模型根据需求生成 AutoLISP 代码以及对应的测试入口函数。跑把代码写入临时目录调用无头 CAD 环境自动加载执行不弹界面。验把运行结果和预先定义的期望值做比对区分离散值和连续值、绝对误差和相对误差。交付只有校验通过才生成交付物否则把失败信息反馈给模型进入下一轮修改最多循环若干次。这个闭环的好处在于把“AI 生成代码”从一次性赌博变成了可持续收敛的过程。第一次写不对没关系第二次、第三次能根据执行反馈纠正过来就行这是 AI Agent 落地到工程领域最实用的形态。2. 搭建 TmAgent 编程助手前的基础准备2.1 无头 CAD 环境的配置要让 LISP 自动跑先要有能跑 LISP 的无头环境。AutoCAD 安装目录里有一个accoreconsole.exe可以在不启动图形界面的情况下加载图纸、执行脚本、输出结果。我用的是相对新版的环境安装路径大致是C:\Program Files\Autodesk\AutoCAD 20xx\accoreconsole.exe。建议在环境变量或配置中心统一维护这个路径别写到脚本里写死因为不同机器版本不一样。无头模式的基本调用方式是accoreconsole.exe /i C:\tests\drawing.dwg /s C:\tests\run.scr其中/i指定要打开的 DWG 文件/s指定要执行的脚本文件。脚本文件本质是一个文本文件里面可以写 LISP 表达式和命令行里逐条输入的效果类似。我通常会让脚本干三件事加载待测 LSP、调用测试入口函数、通过princ把结果写到独立的文本文件。注意无头模式不会显示图形窗口所以凡是依赖用户交互选对象的代码都必须改成不交互的测试版本。这也是后文要求 AI 额外生成测试入口函数的原因。2.2 给 LISP 代码设计“测试入口”AI 直接生成的业务函数多半是c:xxx这样的交互命令会在执行时等待用户选择对象这在自动测试里完全跑不通。所以我在提示词里加了硬性要求每个交付的代码文件必须同时包含一个独立的测试入口函数比如defun tm-test-xxx ()或defun run-xxx-test ()。它不接受交互、不弹选择框而是直接在函数内部构造测试数据并返回可以被文本化的结果。测试入口函数的设计有几个讲究第一它不能污染生产代码所以通常放在同一个文件末尾交付时可以保留也可以裁掉我一般建议保留以后做回归测试还能继续用第二它要能把结果统一输出到指定文件而不能只在命令行打印因为无头模式下命令行输出不好抓第三它要在函数入口处处理加载依赖例如在文件头部调用(vl-load-com)避免执行到一半才报错。2.3 TmAgent 的调度流程设计TmAgent 本身不是一个复杂系统一个 Python 脚本就能把整个闭环串起来。我把它分成了五个阶段每个阶段之间通过配置文件和数据文件连接这样即使某个环节崩溃了也能从断点继续。阶段一是需求解析把用户描述转成结构化的功能点验收标准例如“面积误差小于 0.01 平方米、输出文件格式为每行一个句柄和面积”。阶段二是生成代码调用大模型 API把功能需求、测试环境、工程规范一起作为上下文。阶段三是自动执行把代码写入临时目录调用accoreconsole跑测试脚本。阶段四是校验结果读取输出文件按验收标准逐项比对。阶段五是反馈迭代如果失败就把错误日志、实际输出、期望结果整理成反馈文本再交给模型修改。我建议给整个流程设置一个最大迭代次数比如 5 次防止模型陷入反复修不好的死循环同时每次迭代的日志都完整保留方便事后定位问题。3. 实操案例让 AI 生成“批量面积标注”并自动验收3.1 需求定义与验收标准用一个我实际走通的例子来说明需要开发一个 AutoLISP 工具遍历当前模型空间中的所有闭合多段线把每条多段线的面积以平方米为单位标注在图面上同时把每个对象的句柄和面积值输出到外部文件。这个需求本身不复杂难的是验收。我给它定了三条硬性标准第一加载无误能在无头 CAD 中正常完成整个流程不出现中断第二测试图纸里有一条已知面积为 15.24 平方米的多段线程序输出的面积值和 15.24 的绝对误差不超过 0.01第三输出文件是纯文本每行格式为handle area例如1A2B 15.24。为了构造这个验收标准我提前用一张干净图纸画了三条闭合多段线并在 CAD 里用面积查询命令人工记录下它们的精确值。这三条值就当作基准后续所有 AI 生成版本都必须通过同一套基准比对。3.2 AI 生成的 LISP 代码解析第一轮模型给出了一个比较典型的版本核心逻辑如下(vl-load-com) (defun c:annotate-area (/ ss i ent obj area pt) (if (setq ss (ssget _X ((0 . LWPOLYLINE)))) (progn (setq i 0) (repeat (sslength ss) (setq ent (ssname ss i)) (setq obj (vlax-ename-vla-object ent)) (setq area (vla-get-area obj)) (setq pt (vlax-get obj Centroid)) (entmake (list (0 . TEXT) (cons 10 pt) (cons 40 2.0) (cons 1 (rtos area 2 2)))) (setq i (1 i))) ) ) (princ) ) (defun tm-test-annotate-area (/ f) (setq f (open C:/tests/result.txt w)) (if (setq ss (ssget _X ((0 . LWPOLYLINE)))) (progn (setq i 0) (repeat (sslength ss) (setq ent (ssname ss i)) (setq obj (vlax-ename-vla-object ent)) (setq area (vla-get-area obj)) (write-line (strcat (cdr (assoc 5 (entget ent))) (rtos area 2 2)) f) (setq i (1 i)) ) ) ) (close f) (princ) )这个版本的优点是vl-load-com放在了开头vla-get-area也能在 LWPOLYLINE 对象上正常返回。但它试图用vlax-get obj Centroid获取质心这在多段线对象上并不总是可用会在无头模式下直接报错。另一个细节是ssget _X会把图纸里所有的 LWPOLYLINE 都选进来如果测试图纸里还包含了其他干扰图形面积列表就不可控了。我没有手改代码而是把执行日志和报错信息直接反馈给模型请它修正。第二轮的代码在计算面积时不再依赖质心属性改为用vlax-curve-getStartPoint作为文字插入点同时额外过滤了闭合对象。它写的核心改动如下(setq obj (vlax-ename-vla-object ent)) (setq area (vla-get-area obj)) (setq start (vlax-curve-getStartPoint obj))这两处修改让代码顺利跑通了输出的面积值也和基准值精确匹配。3.3 自动跑通与结果确认过程TmAgent 调度器把测试脚本写成下面这种格式(load C:/tests/annotate.lsp C:/tests/loaderror.log) (tm-test-annotate-area)然后调用accoreconsole.exe /i C:/tests/base.dwg /s C:/tests/run.scr执行结束后程序去C:/tests/result.txt读取内容逐行拆分句柄和面积再与基准表比对。这个环节的核心在于比对逻辑不能只做字符串相等判断因为浮点数存在计算误差。我的实现是把面积转为数值类型然后用abs(actual - expected) tolerance判断容差值按需求设置为 0.01 平方米。完整跑通一次后TmAgent 还会做一次“逆向确认”就是重新加载生成的 LSP再跑一遍同一张测试图把两次输出做一致性比对。这能发现一些随机性问题比如某些版本由于内存清理不及时导致第二次运行结果漂移。3.4 交付物清单自动验收通过之后交付的不再只是一份.lsp文件而是一个包含四件套的文件包业务代码文件annotate.lsp即 AI 最终通过验收的版本测试入口函数包含在同一个 LSP 文件中便于回归测试报告report.md记录验收标准、基准值、实际输出值和比对结论遗留风险说明比如该版本使用ssget _X在超大图纸上可能较慢。这个交付物清单是我后来加上的因为只给用户一个 LSP 文件对方不会知道 AI 改过几轮、测试结果如何。有了报告之后用户至少能判断这个脚本到底“测过什么、没测过什么”。4. 常见问题与排查技巧实录4.1 脚本明明没报错测试结果却不对这是最容易让人困惑的情况。程序正常加载、正常退出、没有error:提示但结果文件里的内容完全是空的或者不是预期格式。我遇到最多的是两个原因一是业务函数里用了(princ)而不(close)文件导致内容虽然写进了缓存但没有落盘二是输出路径使用了中文或带空格目录无头模式的脚本解析对路径空格处理不够友好导致(open ... w)失败。排查方法很简单在脚本里先检查文件对象是否建立成功再判断write-line是否执行。我通常在测试入口函数最前面加一段验证(setq f (open output-file w)) (if (null f) (progn (princ \nERROR: cannot open output file) (exit)) (princ \nOK: output file opened) )4.2 AI 生成的代码在命令行能跑进无头就挂命令行指的是完整 CAD 图形界面无头模式是accoreconsole。两者虽然共享大部分 LISP 引擎但差异非常明显。很多跟显示相关的函数在无头环境下不可用比如vla-zoom、command ZOOM这类视口操作强制调用可能导致异常退出。还有一些对话框函数、文件选择函数在无人值守环境下会直接卡死。我后来把这类问题写进了给模型的提示词里要求 AI 在生成代码时避免任何 UI 交互所有文件路径都直接以字符串常量传入不要用getfiled。这个约束生效后无头环境报错率明显下降。4.3 结果比对的坑坐标、单位与精度AutoLISP 里面积和长度的数值单位紧密依赖当前图纸的单位设置。一张图如果是毫米单位1 平方米等于 1e6 平方毫米如果是英寸单位转换系数又不一样。模型生成的rtos area 2 2只是做了格式化并不会自动转换单位。如果验收基准用的是平方米而图纸里实际是毫米输出值就会差一百万倍。我在验收标准里固定了一张专用测试图该图的单位设置是米这样面积值天然就是平方米。如果确实需要交付给不同单位制度的图纸我建议在函数里显式获取单位设置再通过系数统一而不是让 AI 自己猜。精度方面也有坑。rtos是四舍五入但对比时应该用原始数值而不是字符串否则 15.245 可能被格式化成 15.25直接字符串比对必然失败。4.4 反馈给 AI 的信息怎么写才有效大模型修 bug 的效果很大程度上取决于你反馈了什么。最差的反馈是“程序错了请修复”模型只能瞎猜。最有效的反馈应该包含四部分错误现象、完整日志、期望结果、实际结果以及模型可以自由修改的范围。我设计了一个模板每次迭代都按这个模板组织信息场景AutoLISP 批量面积标注无头 CAD 环境执行。 错误现象运行结束后 result.txt 为空文件。 日志accoreconsole 输出无明显 error但脚本在打开输出文件时失败。 实际结果无输出。 期望结果每行一个句柄和一个面积值。 请修改 tm-test-annotate-area 函数不要改动 c:annotate-area 的业务逻辑。注意输出路径必须使用纯英文绝对路径。这样告诉模型“改哪里、不要碰哪里、什么算成功”修改效率比自然对话高出一大截。实测下来大部分语法问题和逻辑问题在 2 到 3 轮迭代内就能解决。5. 把自动验收流程从 Demo 扩展到日常交付的几点体会5.1 测试图纸要按失败样本持续沉淀最初我只有一张测试图用来验证面积标注这一个功能。后来遇到图层状态导致的 bug就加一张“图层缺失测试图”遇到单位制导致的 bug就加一张“英寸单位测试图”。每修掉一个典型错误就把对应的最小复现图纸保存下来放进测试资源目录。现在这套测试图纸已经积累了一小批每次 AI 生成新代码我都会让 TmAgent 把这些图纸全部跑一遍形成回归测试。之前为什么不敢让 AI 频繁改代码就是怕改一处坏另一处现在有了回归集这个问题基本解决了。5.2 给 AI 加“先解释后写码”的约束刚开始要求 AI 直接写代码写出来的东西经常有“看起来很专业但从头错到尾”的问题。后来我把提示词改成两个阶段第一阶段让模型分析需求并明确写出自己的实现方案包括函数间的关系和可能的边界条件第二阶段再去写代码。我发现这种方式能显著降低反复试错的次数因为很多逻辑错误在做方案阶段就已经暴露了。比如“如何获取多段线质心”这个问题如果模型先解释方案就会暴露它对对象模型不了解的地方早发现在早改正。5.3 交付前仍要保留人工抽查自动验收能替代掉大部分机械性验证但我会在交付前保留一次人工抽查把脚本加载进完整 CAD 图形界面目测执行两三步操作确认视觉效果和用户体验符合预期。无头模式不管文字是否压线、图层是否命名规范、颜色是否合适这些“人看一眼就知道不对”的问题自动化反而很难量化。人工抽查并用检查清单记录时间成本很低但能拦住九成以上的“能跑但难用”问题。5.4 可以继续扩展的能力这套自动化管线并不只适用于 AutoLISP。只要是能在命令行或无头模式执行的语言比如用于其他设计平台的脚本都能套同一个“写-跑-验-交付”框架。我目前正在把 TmAgent 的校验器模块抽象成通用组件让用户只需要给出一个“运行命令”和“解析规则”就能接入新的环境类型。如果你想把这套方法落地到自己项目里建议从最小闭环开始先只用一张测试图和一个功能命令跑通之后再加测试集、加回归、加人工抽查。不要把第一步就设计成庞大平台否则大概率停在半路上。最后再分享一个小技巧所有测试记录我都会保留原始日志包括失败日志和成功日志。现在 AI 编程助手每天跑好几轮日志就是最好的数据集。遇到模型反复修不好的问题时把成功案例的日志翻出来喂给它往往比重新描述需求更有效。这个习惯帮我省下了大量沟通成本也让整个交付流程越来越顺。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑