1. 为什么“AI 写完 AutoLISP 直接交付”这件事一直不靠谱AutoLISP 这门语言在工程制图圈子里属于典型的“小众但刚需”。它跑在 CAD 环境内部,语法古老、调试手段原始,很多写法依赖宿主环境的运行时状态。你让一个通用大模型直接吐一段 AutoLISP 出来,它大概率能给你一段“看起来很像”的代码——括号配对没问题,函数名也眼熟,但一加载就报错,或者更糟:不报错,但画出来的东西是错的。我最早尝试用 AI 辅助写 AutoLISP 的时候,踩的就是这个坑。当时的需求很简单:批量给一批图元按图层改颜色,再统计数量输出到命令行。AI 给的代码逻辑上没毛病,ssget过滤、entmod修改、princ输出,结构完整。但实际跑起来,ssget的过滤条件里用了一个在当前 CAD 版本里已经废弃的组码,结果选择集永远是空的,程序“成功”跑完,什么都没改。这种“静默失败”比直接报错更可怕,因为你以为它干完了。这就是标题里那个核心痛点的来源:AI 生成代码的能力已经够用了,缺的是“自己跑通、自己确认结果正确”的闭环。TmAgent 这类智能体框架的价值,不在于它能写 AutoLISP,而在于它能驱动一个真实的 CAD 环境去执行、去验证、去根据反馈修正。换句话说,它把“写代码”和“验证代码”这两件事绑在了一起,而不是把验证的锅甩给用户。这篇文章我想聊的就是这套思路的完整落地:怎么让一个智能体真正把 AutoLISP 跑起来,怎么设计验证环节让它能判断“结果对不对”,以及在这个过程中有哪些只有实际做过才会知道的坑。适合已经会用 AutoLISP 做基础自动化、想进一步把 AI 引入工作流的工程师,也适合对智能体落地感兴趣、想找一个具体场景练手的开发者。2. TmAgent 驱动 AutoLISP 的执行闭环到底长什么样2.1 拆解“写完就跑通”需要的四个能力很多人对“AI 编程助手”的想象是:我描述需求,它给代码,我复制粘贴。这个模式在 AutoLISP 上基本行不通,因为 AutoLISP 没有独立的编译运行环境,它必须寄生在 CAD 进程里。所以一个能“自己跑通”的智能体,至少要具备四个能力,缺一不可。第一个是代码生成能力,这个现在的大模型都具备,但要注意 AutoLISP 的语料相对稀少,模型容易把其他 Lisp 方言的写法混进来,比如把 Common Lisp 的setf当成 AutoLISP 的赋值用,这是高频错误。第二个是环境注入能力。智能体得知道怎么把生成的代码送进 CAD。常见做法有两种:一种是通过 CAD 的命令行接口,把代码写成临时文件再用load加载;另一种是通过进程间通信,直接把表达式发给运行中的 CAD 实例。前者简单但每次都要落盘,后者响应快但对环境配置要求高。第三个是执行监控能力。代码送进去之后,智能体要能拿到执行结果——是正常返回、还是抛了异常、还是卡住了。AutoLISP 的错误处理比较弱,很多错误不会中断整个程序,而是返回nil继续往下跑,所以监控不能只看“有没有崩”,还要看关键变量的值。第四个是结果验证能力,这也是最容易被忽略的一环。验证分两个层次:语法层验证(代码能不能加载)和语义层验证(执行结果符不符合预期)。前者靠 CAD 的加载反馈就行,后者需要智能体自己设计断言,比如“修改后图层 A 的图元数量应该等于修改前的数量”“新生成的图元坐标应该落在指定范围内”。把这四个能力串起来,才构成一个完整的闭环。我见过不少方案只做了前两个,生成完注入进去就完事,结果就是用户还得自己盯着看对不对,那智能体的价值就打了对折。2.2 一个最小可用的执行链路设计具体到实现层面,我建议的最小链路是这样的:智能体先生成代码,同时生成一段“验证脚本”,两者一起注入 CAD。验证脚本负责在代码执行后检查关键状态,把结果写到一个约定的输出位置,智能体再读取这个输出判断成败。这里有个设计上的取舍值得说。为什么不把验证逻辑直接写在主代码里?因为主代码是要交付给用户用的,塞一堆验证逻辑进去会污染代码,而且用户环境里未必有验证需要的辅助函数。把验证脚本独立出来,主代码保持干净,验证只在智能体的工作流里跑,交付时只给主代码。这个分离思路很重要,后面讲交付环节还会用到。验证脚本的写法有讲究。AutoLISP 里做断言不像 Python 那么方便,没有现成的assert。我的做法是定义一个简单的检查函数,把“期望值”和“实际值”传进去,不相等就往一个全局列表里追加错误信息。代码执行完后,智能体读取这个列表,空的就是通过,非空就带着错误信息回去让模型修。;; 验证辅助函数示例 (setq *verify-errors* nil) (defun vcheck (label expected actual) (if (not (equal expected actual)) (setq *verify-errors* (cons (strcat label ": expected " (vl-princ-to-string expected) ", got " (vl-princ-to-string actual)) *verify-errors*))))这段代码本身很简单,但它是整个闭环的支点。没有它,智能体就只能靠“代码跑完没报错”来判断成功,而 AutoLISP 的静默失败会让这个判断完全失效。2.3 为什么验证脚本必须由智能体动态生成有人可能会想,验证脚本能不能写死一套通用的?答案是基本不行。因为验证的内容高度依赖具体任务。改颜色的任务要验证颜色值,画线的任务要验证端点坐标,统计的任务要验证计数。通用验证脚本只能检查“有没有报错”,检查不了“结果对不对”。所以验证脚本必须和主代码一起,由智能体根据需求动态生成。这对智能体的要求就高了一层:它不仅要理解“要做什么”,还要理解“做完之后怎么判断做对了”。这其实是一个很好的约束,因为一个连验证方法都想不出来的需求,往往本身就是模糊的,逼着智能体在生成阶段就把需求想清楚。我在实际用的时候发现,让智能体先写验证脚本、再写主代码,效果比反过来好。因为写验证的过程会倒逼它明确“输入是什么、输出是什么、中间状态怎么变”,主代码反而写得更准。这个顺序上的小技巧,是我试了好几轮才总结出来的。3. 让智能体判断“结果正确”的验证策略设计3.1 从“跑通”到“跑对”的鸿沟“跑通”和“跑对”之间的差距,在 AutoLISP 场景里被放得特别大。原因前面提过,AutoLISP 的错误处理机制太宽松。一个函数调用失败返回nil,如果调用方没检查返回值,程序会若无其事地继续执行下一行。等整个程序跑完,你看到的是“命令执行完毕”,但中间可能有一半的操作根本没生效。我遇到过一个典型案例:批量偏移多段线。代码逻辑是遍历选择集,对每个多段线调用vla-offset。问题在于,当偏移距离过大导致自交时,vla-offset会失败返回nil,但代码没检查,继续处理下一个。最后程序报告“处理完成”,实际上有一批多段线被跳过了,用户拿到结果还以为全处理了。要避免这种情况,验证策略必须覆盖到“每个操作单元”的层面,而不是只看整体有没有崩。具体来说,我通常会让智能体设计三层验证。第一层是前置条件验证,在操作前检查输入是否符合预期。比如选择集是否为空、目标图层是否存在、关键参数是否在合理范围内。这一层能在问题发生前就拦住一部分错误。第二层是操作结果验证,每个关键操作后检查返回值或状态变化。比如entmod之后重新读取实体属性确认修改生效,vla-offset之后确认返回了新对象而不是nil。第三层是后置状态验证,全部操作完成后做整体检查。比如图元总数是否符合预期、关键属性是否全部正确、有没有遗漏的对象。三层验证叠起来,才能把“静默失败”的空间压到最小。当然,验证越密,代码量越大,执行越慢,这里要根据任务的重要程度做权衡。对于一次性的小工具,可能只做第三层就够了;对于要反复用的批处理,三层都值得加上。3.2 用“预期状态快照”做对比验证有一种验证思路我觉得特别适合 AutoLISP,就是“预期状态快照对比”。做法是在操作前把关键状态记录下来,操作后重新记录,然后对比差异是否符合预期。举个例子,任务是“把所有红色图元改成蓝色”。操作前,智能体先统计红色图元的数量 N。操作后,检查红色图元数量是否为 0,蓝色图元数量是否增加了 N。如果红色还剩几个,说明有遗漏;如果蓝色增加的数量对不上,说明有误改。这个对比逻辑简单但非常有效,能抓住绝大多数逻辑错误。实现上,快照可以用关联列表存,键是图层名或颜色值,值是数量。对比的时候遍历预期键,逐个检查。这种结构在 AutoLISP 里用assoc和cdr就能操作,不需要复杂的数据结构。;; 统计指定颜色图元数量的快照函数 (defun snapshot-by-color (color / ss count) (setq count 0) (if (setq ss (ssget "X" (list (cons 62 color)))) (setq count (sslength ss))) count)用这个函数,操作前后各调一次,对比返回值就能判断修改是否完整。这种“用数据说话”的验证方式,比让智能体去“读代码判断逻辑对不对”可靠得多,因为代码逻辑可以自洽但结果错误,而数据对比是骗不了人的。3.3 验证失败后的自动修复循环怎么设验证失败之后怎么办,这是区分“玩具”和“工具”的关键。如果验证失败只是报个错让用户自己改,那智能体的价值还是有限。真正有用的是让它带着失败信息回去自动修复。修复循环的设计要注意几点。首先是重试次数要有上限,我一般设 3 次。AutoLISP 的很多错误是环境相关的,模型再聪明也修不了,比如 CAD 版本不支持某个函数,重试多少次都没用,不如早点放弃让用户介入。其次是失败信息要具体。不能只说“验证失败”,要把哪个检查项失败、期望值多少、实际值多少都带上。模型拿到具体信息才能定位问题。前面那个vcheck函数把 label、expected、actual 都记下来,就是为了这个。第三是修复要有策略。我观察到模型在修复时容易“过度修改”,把原本没问题的部分也改了,结果引入新 bug。所以我会在提示里明确要求“只修改与失败项相关的代码,其他部分保持不变”。这个约束能显著提高修复成功率。实际跑下来,大部分问题在第一次修复就能解决,少数需要两次。三次还修不好的,基本就是需求本身有问题或者环境不支持,这时候让智能体输出一份“失败分析报告”,说明它尝试了什么、卡在哪里,对用户也有参考价值。4. 交付前的最后一道关:把验证结果变成可读的交付说明4.1 交付物不只是代码智能体跑通验证之后,交付给用户的不应该只是一段代码。用户需要知道这段代码干了什么、怎么用、验证过哪些点、有什么限制。这些信息如果让用户自己去读代码总结,那智能体的价值又打了折扣。我的做法是让智能体在验证通过后,自动生成一份简短的交付说明,包含四块内容:功能描述、使用方法、验证结论、已知限制。功能描述用一两句话讲清楚代码做什么;使用方法说明怎么加载、怎么调用、需要什么前置条件;验证结论列出实际验证了哪些检查项、结果如何;已知限制说明在什么情况下可能不适用。这份说明不需要很长,但要具体。比如验证结论不能只写“验证通过”,要写“验证了图元数量守恒、颜色值正确、无遗漏对象”这样的具体项。用户看到这些,才能对代码的可靠性有个判断。4.2 把验证证据一并附上比交付说明更进一步的做法,是把验证过程中的关键证据也附上。比如操作前后的状态快照对比、关键检查项的通过记录。这些证据让用户能自己复核,而不是只能信任智能体的结论。我通常会让智能体把验证输出整理成一个简单的表格,列出每个检查项、期望值、实际值、结论。这个表格在验证脚本执行时就能生成,不需要额外工作。用户拿到这个表格,一眼就能看出代码在哪些方面被验证过、哪些方面没覆盖到。检查项期望值实际值结论红色图元数量00通过蓝色图元数量原红色数+原蓝色数匹配通过图元总数操作前总数匹配通过图层属性完整性无变化无变化通过这种表格的价值在于,它把“智能体说没问题”变成了“数据说没问题”。用户对智能体的信任,应该建立在这种可复核的证据上,而不是盲信。4.3 交付说明里必须写清楚“没验证什么”这一点我觉得特别重要,但很容易被忽略。验证再全面,也总有覆盖不到的地方。诚实地说明“没验证什么”,比假装什么都验证了更专业。比如一个批量修改图元的工具,验证可能覆盖了数量、颜色、图层这些属性,但没验证修改后图元的显示顺序、没验证在极端数据量下的性能、没验证在特定 CAD 版本下的兼容性。这些“没验证”的点,用户在实际使用时可能会碰到,提前说明能帮他们规避风险。让智能体生成这部分内容,其实也是在训练它对自己的输出保持诚实。一个总是宣称“完全正确”的智能体,反而让人不敢用。承认边界,才是可靠的表现。5. 实操中踩过的坑和几条硬经验5.1 括号配对只是最低要求,别被它骗了AutoLISP 最出名的就是括号多,所以很多人把“括号配对”当成代码正确的标志。但括号配对只是语法正确的最低要求,配对正确但逻辑错误的代码太常见了。我见过把if的 then 分支和 else 分支写反的,括号完全正确,结果完全相反。所以验证绝对不能停在“能加载”这一层。能加载只说明语法没问题,说明不了任何逻辑问题。真正的验证必须跑到语义层,检查执行结果。这一点我在前面反复强调,因为它是整个方案成立的前提。5.2 选择集操作是静默失败的重灾区如果要选一个 AutoLISP 里最容易出静默失败的地方,我投选择集操作一票。ssget的过滤条件写错,返回空集,后续遍历直接跳过,程序正常结束。ssadd、ssdel操作失败,也是返回nil不报错。这些地方如果不加检查,问题会一直藏着。我的经验是,凡是涉及选择集的地方,操作后都要检查一下集合是否为空、长度是否符合预期。这个检查成本很低,但能拦住大量问题。具体做法是在遍历前加一句判断,集合为空就记录一条警告,而不是默默跳过。5.3 别让智能体在验证脚本里“偷懒”智能体生成验证脚本时,有个倾向是写得过于宽松。比如检查数量时用“大于 0”而不是“等于预期值”,检查坐标时用“在某个大范围内”而不是“精确匹配”。这种宽松验证看起来通过了,实际上没起到把关作用。我的应对办法是在提示里明确要求“验证条件要尽可能严格,能用等号就不用范围,能精确匹配就不模糊匹配”。同时我会抽查几次生成的验证脚本,发现偷懒就调整提示。这个调优过程需要几轮,但一旦调好,后面就稳定了。5.4 执行超时和死循环的兜底AutoLISP 代码如果写了死循环,CAD 会直接卡死,智能体也跟着卡住。这种情况必须有兜底。我的做法是在执行环节加超时控制,超过设定时间就强制中断,把控制权收回来。超时时间设多少要看任务复杂度,简单的属性修改几秒就够,复杂的批量处理可能要几十秒。我一般设 30 秒作为默认值,特殊任务再调。中断之后,智能体要能判断是“任务太复杂”还是“代码有死循环”,前者可以提示用户拆分任务,后者要回去修代码。5.5 版本兼容性是个绕不开的坎AutoLISP 在不同 CAD 版本之间是有差异的,有些函数在新版本里废弃了,有些行为变了。智能体如果不知道目标版本,生成的代码可能在某些版本上跑不了。我的处理方式是在任务开始时就让用户指定目标版本,把这个信息作为约束传给智能体。如果用户不确定,就按较老的版本生成,因为老版本能跑的代码通常在新版本也能跑,反过来不一定。这个策略不完美,但能减少大部分兼容性问题。6. 这套方案适合什么场景,不适合什么场景6.1 最适合的是“规则明确、结果可量化”的任务这套方案在规则明确、结果可量化的任务上效果最好。比如批量修改属性、按条件筛选统计、标准化图层命名、生成规则图形。这些任务的共同点是“做对了什么样”很容易定义,验证脚本好写,智能体的闭环能跑得很顺。我实际用下来,这类任务的首次通过率能到七八成,剩下的一两次修复基本能解决。这个成功率已经能显著提升效率了,尤其是那些重复性的、逻辑不复杂但写起来烦的活儿。6.2 涉及复杂交互和主观判断的任务要谨慎反过来,涉及复杂用户交互或者主观判断的任务,这套方案就不太适用。比如需要用户在运行过程中做选择的工具,或者“美观”“合理”这种没法量化的标准,验证脚本很难写,智能体也没法判断做得好不好。这类任务不是不能做,而是验证环节要弱化,更多依赖用户自己判断。这时候智能体的价值就退回到“帮你写代码”,闭环的那部分用不上。认清这个边界,能避免在不合适的场景上浪费时间。6.3 一次性脚本和长期工具的投入产出比不同还有一点值得考虑的是投入产出比。如果只是写个一次性用的小脚本,花大力气设计验证可能不划算,简单跑一下看看结果就行。但如果是会反复使用的工具,或者要交给别人用的,那验证的投入就非常值得,因为一次验证能省下后面无数次的排查。我的建议是,对于要交付给他人的工具,验证环节一定要做扎实,因为别人不会像你一样了解代码的边界,出了问题排查成本很高。对于自己临时用的小脚本,可以适当简化,但基本的数量检查还是要有,防止静默失败。7. 我对这套工作流的一点个人体会用这套方案跑了一段时间之后,我最大的感受是:智能体的可靠性不取决于它写代码多聪明,而取决于它的验证多严格。一个写代码一般但验证扎实的智能体,比一个写代码漂亮但验证敷衍的智能体有用得多。因为前者你知道它交付的东西是经过检验的,后者你永远得自己再验一遍。另一个体会是,验证脚本的设计能力,其实是比代码生成能力更稀缺的。让模型写一段 AutoLISP 不难,让它设计一套能抓住问题的验证方案,需要它对任务本质有更深的理解。这也是为什么我在提示里会花不少篇幅引导它先想清楚“怎么算做对了”,再动手写代码。最后分享一个小技巧:如果你也在搭类似的流程,不妨先从最简单的任务开始,把验证闭环跑通,再逐步增加任务复杂度。我一开始就想做个能处理各种情况的通用工具,结果验证逻辑复杂到没法维护。后来退回到“一次只做一类任务”,反而跑得很稳。这个从简到繁的路径,比一上来就追求大而全要靠谱得多。
1. 为什么“AI 写完 AutoLISP 直接交付”这件事一直不靠谱AutoLISP 这门语言在工程制图圈子里属于典型的“小众但刚需”。它跑在 CAD 环境内部,语法古老、调试手段原始,很多写法依赖宿主环境的运行时状态。你让一个通用大模型直接吐一段 AutoLISP 出来,它大概率能给你一段“看起来很像”的代码——括号配对没问题,函数名也眼熟,但一加载就报错,或者更糟:不报错,但画出来的东西是错的。我最早尝试用 AI 辅助写 AutoLISP 的时候,踩的就是这个坑。当时的需求很简单:批量给一批图元按图层改颜色,再统计数量输出到命令行。AI 给的代码逻辑上没毛病,ssget过滤、entmod修改、princ输出,结构完整。但实际跑起来,ssget的过滤条件里用了一个在当前 CAD 版本里已经废弃的组码,结果选择集永远是空的,程序“成功”跑完,什么都没改。这种“静默失败”比直接报错更可怕,因为你以为它干完了。这就是标题里那个核心痛点的来源:AI 生成代码的能力已经够用了,缺的是“自己跑通、自己确认结果正确”的闭环。TmAgent 这类智能体框架的价值,不在于它能写 AutoLISP,而在于它能驱动一个真实的 CAD 环境去执行、去验证、去根据反馈修正。换句话说,它把“写代码”和“验证代码”这两件事绑在了一起,而不是把验证的锅甩给用户。这篇文章我想聊的就是这套思路的完整落地:怎么让一个智能体真正把 AutoLISP 跑起来,怎么设计验证环节让它能判断“结果对不对”,以及在这个过程中有哪些只有实际做过才会知道的坑。适合已经会用 AutoLISP 做基础自动化、想进一步把 AI 引入工作流的工程师,也适合对智能体落地感兴趣、想找一个具体场景练手的开发者。2. TmAgent 驱动 AutoLISP 的执行闭环到底长什么样2.1 拆解“写完就跑通”需要的四个能力很多人对“AI 编程助手”的想象是:我描述需求,它给代码,我复制粘贴。这个模式在 AutoLISP 上基本行不通,因为 AutoLISP 没有独立的编译运行环境,它必须寄生在 CAD 进程里。所以一个能“自己跑通”的智能体,至少要具备四个能力,缺一不可。第一个是代码生成能力,这个现在的大模型都具备,但要注意 AutoLISP 的语料相对稀少,模型容易把其他 Lisp 方言的写法混进来,比如把 Common Lisp 的setf当成 AutoLISP 的赋值用,这是高频错误。第二个是环境注入能力。智能体得知道怎么把生成的代码送进 CAD。常见做法有两种:一种是通过 CAD 的命令行接口,把代码写成临时文件再用load加载;另一种是通过进程间通信,直接把表达式发给运行中的 CAD 实例。前者简单但每次都要落盘,后者响应快但对环境配置要求高。第三个是执行监控能力。代码送进去之后,智能体要能拿到执行结果——是正常返回、还是抛了异常、还是卡住了。AutoLISP 的错误处理比较弱,很多错误不会中断整个程序,而是返回nil继续往下跑,所以监控不能只看“有没有崩”,还要看关键变量的值。第四个是结果验证能力,这也是最容易被忽略的一环。验证分两个层次:语法层验证(代码能不能加载)和语义层验证(执行结果符不符合预期)。前者靠 CAD 的加载反馈就行,后者需要智能体自己设计断言,比如“修改后图层 A 的图元数量应该等于修改前的数量”“新生成的图元坐标应该落在指定范围内”。把这四个能力串起来,才构成一个完整的闭环。我见过不少方案只做了前两个,生成完注入进去就完事,结果就是用户还得自己盯着看对不对,那智能体的价值就打了对折。2.2 一个最小可用的执行链路设计具体到实现层面,我建议的最小链路是这样的:智能体先生成代码,同时生成一段“验证脚本”,两者一起注入 CAD。验证脚本负责在代码执行后检查关键状态,把结果写到一个约定的输出位置,智能体再读取这个输出判断成败。这里有个设计上的取舍值得说。为什么不把验证逻辑直接写在主代码里?因为主代码是要交付给用户用的,塞一堆验证逻辑进去会污染代码,而且用户环境里未必有验证需要的辅助函数。把验证脚本独立出来,主代码保持干净,验证只在智能体的工作流里跑,交付时只给主代码。这个分离思路很重要,后面讲交付环节还会用到。验证脚本的写法有讲究。AutoLISP 里做断言不像 Python 那么方便,没有现成的assert。我的做法是定义一个简单的检查函数,把“期望值”和“实际值”传进去,不相等就往一个全局列表里追加错误信息。代码执行完后,智能体读取这个列表,空的就是通过,非空就带着错误信息回去让模型修。;; 验证辅助函数示例 (setq *verify-errors* nil) (defun vcheck (label expected actual) (if (not (equal expected actual)) (setq *verify-errors* (cons (strcat label ": expected " (vl-princ-to-string expected) ", got " (vl-princ-to-string actual)) *verify-errors*))))这段代码本身很简单,但它是整个闭环的支点。没有它,智能体就只能靠“代码跑完没报错”来判断成功,而 AutoLISP 的静默失败会让这个判断完全失效。2.3 为什么验证脚本必须由智能体动态生成有人可能会想,验证脚本能不能写死一套通用的?答案是基本不行。因为验证的内容高度依赖具体任务。改颜色的任务要验证颜色值,画线的任务要验证端点坐标,统计的任务要验证计数。通用验证脚本只能检查“有没有报错”,检查不了“结果对不对”。所以验证脚本必须和主代码一起,由智能体根据需求动态生成。这对智能体的要求就高了一层:它不仅要理解“要做什么”,还要理解“做完之后怎么判断做对了”。这其实是一个很好的约束,因为一个连验证方法都想不出来的需求,往往本身就是模糊的,逼着智能体在生成阶段就把需求想清楚。我在实际用的时候发现,让智能体先写验证脚本、再写主代码,效果比反过来好。因为写验证的过程会倒逼它明确“输入是什么、输出是什么、中间状态怎么变”,主代码反而写得更准。这个顺序上的小技巧,是我试了好几轮才总结出来的。3. 让智能体判断“结果正确”的验证策略设计3.1 从“跑通”到“跑对”的鸿沟“跑通”和“跑对”之间的差距,在 AutoLISP 场景里被放得特别大。原因前面提过,AutoLISP 的错误处理机制太宽松。一个函数调用失败返回nil,如果调用方没检查返回值,程序会若无其事地继续执行下一行。等整个程序跑完,你看到的是“命令执行完毕”,但中间可能有一半的操作根本没生效。我遇到过一个典型案例:批量偏移多段线。代码逻辑是遍历选择集,对每个多段线调用vla-offset。问题在于,当偏移距离过大导致自交时,vla-offset会失败返回nil,但代码没检查,继续处理下一个。最后程序报告“处理完成”,实际上有一批多段线被跳过了,用户拿到结果还以为全处理了。要避免这种情况,验证策略必须覆盖到“每个操作单元”的层面,而不是只看整体有没有崩。具体来说,我通常会让智能体设计三层验证。第一层是前置条件验证,在操作前检查输入是否符合预期。比如选择集是否为空、目标图层是否存在、关键参数是否在合理范围内。这一层能在问题发生前就拦住一部分错误。第二层是操作结果验证,每个关键操作后检查返回值或状态变化。比如entmod之后重新读取实体属性确认修改生效,vla-offset之后确认返回了新对象而不是nil。第三层是后置状态验证,全部操作完成后做整体检查。比如图元总数是否符合预期、关键属性是否全部正确、有没有遗漏的对象。三层验证叠起来,才能把“静默失败”的空间压到最小。当然,验证越密,代码量越大,执行越慢,这里要根据任务的重要程度做权衡。对于一次性的小工具,可能只做第三层就够了;对于要反复用的批处理,三层都值得加上。3.2 用“预期状态快照”做对比验证有一种验证思路我觉得特别适合 AutoLISP,就是“预期状态快照对比”。做法是在操作前把关键状态记录下来,操作后重新记录,然后对比差异是否符合预期。举个例子,任务是“把所有红色图元改成蓝色”。操作前,智能体先统计红色图元的数量 N。操作后,检查红色图元数量是否为 0,蓝色图元数量是否增加了 N。如果红色还剩几个,说明有遗漏;如果蓝色增加的数量对不上,说明有误改。这个对比逻辑简单但非常有效,能抓住绝大多数逻辑错误。实现上,快照可以用关联列表存,键是图层名或颜色值,值是数量。对比的时候遍历预期键,逐个检查。这种结构在 AutoLISP 里用assoc和cdr就能操作,不需要复杂的数据结构。;; 统计指定颜色图元数量的快照函数 (defun snapshot-by-color (color / ss count) (setq count 0) (if (setq ss (ssget "X" (list (cons 62 color)))) (setq count (sslength ss))) count)用这个函数,操作前后各调一次,对比返回值就能判断修改是否完整。这种“用数据说话”的验证方式,比让智能体去“读代码判断逻辑对不对”可靠得多,因为代码逻辑可以自洽但结果错误,而数据对比是骗不了人的。3.3 验证失败后的自动修复循环怎么设验证失败之后怎么办,这是区分“玩具”和“工具”的关键。如果验证失败只是报个错让用户自己改,那智能体的价值还是有限。真正有用的是让它带着失败信息回去自动修复。修复循环的设计要注意几点。首先是重试次数要有上限,我一般设 3 次。AutoLISP 的很多错误是环境相关的,模型再聪明也修不了,比如 CAD 版本不支持某个函数,重试多少次都没用,不如早点放弃让用户介入。其次是失败信息要具体。不能只说“验证失败”,要把哪个检查项失败、期望值多少、实际值多少都带上。模型拿到具体信息才能定位问题。前面那个vcheck函数把 label、expected、actual 都记下来,就是为了这个。第三是修复要有策略。我观察到模型在修复时容易“过度修改”,把原本没问题的部分也改了,结果引入新 bug。所以我会在提示里明确要求“只修改与失败项相关的代码,其他部分保持不变”。这个约束能显著提高修复成功率。实际跑下来,大部分问题在第一次修复就能解决,少数需要两次。三次还修不好的,基本就是需求本身有问题或者环境不支持,这时候让智能体输出一份“失败分析报告”,说明它尝试了什么、卡在哪里,对用户也有参考价值。4. 交付前的最后一道关:把验证结果变成可读的交付说明4.1 交付物不只是代码智能体跑通验证之后,交付给用户的不应该只是一段代码。用户需要知道这段代码干了什么、怎么用、验证过哪些点、有什么限制。这些信息如果让用户自己去读代码总结,那智能体的价值又打了折扣。我的做法是让智能体在验证通过后,自动生成一份简短的交付说明,包含四块内容:功能描述、使用方法、验证结论、已知限制。功能描述用一两句话讲清楚代码做什么;使用方法说明怎么加载、怎么调用、需要什么前置条件;验证结论列出实际验证了哪些检查项、结果如何;已知限制说明在什么情况下可能不适用。这份说明不需要很长,但要具体。比如验证结论不能只写“验证通过”,要写“验证了图元数量守恒、颜色值正确、无遗漏对象”这样的具体项。用户看到这些,才能对代码的可靠性有个判断。4.2 把验证证据一并附上比交付说明更进一步的做法,是把验证过程中的关键证据也附上。比如操作前后的状态快照对比、关键检查项的通过记录。这些证据让用户能自己复核,而不是只能信任智能体的结论。我通常会让智能体把验证输出整理成一个简单的表格,列出每个检查项、期望值、实际值、结论。这个表格在验证脚本执行时就能生成,不需要额外工作。用户拿到这个表格,一眼就能看出代码在哪些方面被验证过、哪些方面没覆盖到。检查项期望值实际值结论红色图元数量00通过蓝色图元数量原红色数+原蓝色数匹配通过图元总数操作前总数匹配通过图层属性完整性无变化无变化通过这种表格的价值在于,它把“智能体说没问题”变成了“数据说没问题”。用户对智能体的信任,应该建立在这种可复核的证据上,而不是盲信。4.3 交付说明里必须写清楚“没验证什么”这一点我觉得特别重要,但很容易被忽略。验证再全面,也总有覆盖不到的地方。诚实地说明“没验证什么”,比假装什么都验证了更专业。比如一个批量修改图元的工具,验证可能覆盖了数量、颜色、图层这些属性,但没验证修改后图元的显示顺序、没验证在极端数据量下的性能、没验证在特定 CAD 版本下的兼容性。这些“没验证”的点,用户在实际使用时可能会碰到,提前说明能帮他们规避风险。让智能体生成这部分内容,其实也是在训练它对自己的输出保持诚实。一个总是宣称“完全正确”的智能体,反而让人不敢用。承认边界,才是可靠的表现。5. 实操中踩过的坑和几条硬经验5.1 括号配对只是最低要求,别被它骗了AutoLISP 最出名的就是括号多,所以很多人把“括号配对”当成代码正确的标志。但括号配对只是语法正确的最低要求,配对正确但逻辑错误的代码太常见了。我见过把if的 then 分支和 else 分支写反的,括号完全正确,结果完全相反。所以验证绝对不能停在“能加载”这一层。能加载只说明语法没问题,说明不了任何逻辑问题。真正的验证必须跑到语义层,检查执行结果。这一点我在前面反复强调,因为它是整个方案成立的前提。5.2 选择集操作是静默失败的重灾区如果要选一个 AutoLISP 里最容易出静默失败的地方,我投选择集操作一票。ssget的过滤条件写错,返回空集,后续遍历直接跳过,程序正常结束。ssadd、ssdel操作失败,也是返回nil不报错。这些地方如果不加检查,问题会一直藏着。我的经验是,凡是涉及选择集的地方,操作后都要检查一下集合是否为空、长度是否符合预期。这个检查成本很低,但能拦住大量问题。具体做法是在遍历前加一句判断,集合为空就记录一条警告,而不是默默跳过。5.3 别让智能体在验证脚本里“偷懒”智能体生成验证脚本时,有个倾向是写得过于宽松。比如检查数量时用“大于 0”而不是“等于预期值”,检查坐标时用“在某个大范围内”而不是“精确匹配”。这种宽松验证看起来通过了,实际上没起到把关作用。我的应对办法是在提示里明确要求“验证条件要尽可能严格,能用等号就不用范围,能精确匹配就不模糊匹配”。同时我会抽查几次生成的验证脚本,发现偷懒就调整提示。这个调优过程需要几轮,但一旦调好,后面就稳定了。5.4 执行超时和死循环的兜底AutoLISP 代码如果写了死循环,CAD 会直接卡死,智能体也跟着卡住。这种情况必须有兜底。我的做法是在执行环节加超时控制,超过设定时间就强制中断,把控制权收回来。超时时间设多少要看任务复杂度,简单的属性修改几秒就够,复杂的批量处理可能要几十秒。我一般设 30 秒作为默认值,特殊任务再调。中断之后,智能体要能判断是“任务太复杂”还是“代码有死循环”,前者可以提示用户拆分任务,后者要回去修代码。5.5 版本兼容性是个绕不开的坎AutoLISP 在不同 CAD 版本之间是有差异的,有些函数在新版本里废弃了,有些行为变了。智能体如果不知道目标版本,生成的代码可能在某些版本上跑不了。我的处理方式是在任务开始时就让用户指定目标版本,把这个信息作为约束传给智能体。如果用户不确定,就按较老的版本生成,因为老版本能跑的代码通常在新版本也能跑,反过来不一定。这个策略不完美,但能减少大部分兼容性问题。6. 这套方案适合什么场景,不适合什么场景6.1 最适合的是“规则明确、结果可量化”的任务这套方案在规则明确、结果可量化的任务上效果最好。比如批量修改属性、按条件筛选统计、标准化图层命名、生成规则图形。这些任务的共同点是“做对了什么样”很容易定义,验证脚本好写,智能体的闭环能跑得很顺。我实际用下来,这类任务的首次通过率能到七八成,剩下的一两次修复基本能解决。这个成功率已经能显著提升效率了,尤其是那些重复性的、逻辑不复杂但写起来烦的活儿。6.2 涉及复杂交互和主观判断的任务要谨慎反过来,涉及复杂用户交互或者主观判断的任务,这套方案就不太适用。比如需要用户在运行过程中做选择的工具,或者“美观”“合理”这种没法量化的标准,验证脚本很难写,智能体也没法判断做得好不好。这类任务不是不能做,而是验证环节要弱化,更多依赖用户自己判断。这时候智能体的价值就退回到“帮你写代码”,闭环的那部分用不上。认清这个边界,能避免在不合适的场景上浪费时间。6.3 一次性脚本和长期工具的投入产出比不同还有一点值得考虑的是投入产出比。如果只是写个一次性用的小脚本,花大力气设计验证可能不划算,简单跑一下看看结果就行。但如果是会反复使用的工具,或者要交给别人用的,那验证的投入就非常值得,因为一次验证能省下后面无数次的排查。我的建议是,对于要交付给他人的工具,验证环节一定要做扎实,因为别人不会像你一样了解代码的边界,出了问题排查成本很高。对于自己临时用的小脚本,可以适当简化,但基本的数量检查还是要有,防止静默失败。7. 我对这套工作流的一点个人体会用这套方案跑了一段时间之后,我最大的感受是:智能体的可靠性不取决于它写代码多聪明,而取决于它的验证多严格。一个写代码一般但验证扎实的智能体,比一个写代码漂亮但验证敷衍的智能体有用得多。因为前者你知道它交付的东西是经过检验的,后者你永远得自己再验一遍。另一个体会是,验证脚本的设计能力,其实是比代码生成能力更稀缺的。让模型写一段 AutoLISP 不难,让它设计一套能抓住问题的验证方案,需要它对任务本质有更深的理解。这也是为什么我在提示里会花不少篇幅引导它先想清楚“怎么算做对了”,再动手写代码。最后分享一个小技巧:如果你也在搭类似的流程,不妨先从最简单的任务开始,把验证闭环跑通,再逐步增加任务复杂度。我一开始就想做个能处理各种情况的通用工具,结果验证逻辑复杂到没法维护。后来退回到“一次只做一类任务”,反而跑得很稳。这个从简到繁的路径,比一上来就追求大而全要靠谱得多。