ARTICLE · 1141318
Lisp插件报错看不懂?3个方法让AI自己找出来
AI降低了编程的门槛,但lisp需要在解释器运行,AI无法直接自主调试,三种方法,让AI自己调试

把报错丢给 AI,它猜一个可能,改完再跑,还是错——这是代码小白写 LISP 插件常见的一幕。
来回十几轮,人先崩了。
问题不在 AI 不够聪明——你读不懂 LISP,AI 读得懂,却看不见现场。
用下面三种方法,AI 就能从"猜"变成"看"。
一、让 AI 插桩:生成代码时就把日志写进去

传统调试靠断点。网页版 AI 没有断点,所以退一步:插桩。
在关键位置插一行日志,看它到底走到哪。
关键前提是:日志要让 AI 能自己读到,而不是你复制粘贴给它。
写到命令行 (princ ...):网页版 AI 看不见,只能你复制粘贴;写到文件:AI 智能体能直接读文件,不用你复制——这就形成一个闭环了。
模板可直接抄:
;; 排错时 *dbg* 置 T,交付前改回 nil(setq *dbg* T *dbg-file* (strcat (getenv "TEMP") "/dbg.log")) ; 别写死 C:/temp,它常不存在(defun tm:log (x / f) (if *dbg* (progn (setq f (open *dbg-file* "a")) ; 打不开会返回 nil (if f (progn (write-line (vl-princ-to-string x) f) ; 先转字符串再写 (close f))))) ; 必须配对 close x) ; 值透传四个要点:
- 值透传
。 tm:log返回原值,能直接塞进表达式中间:(tm:log (sslength ss)),不破坏原逻辑。 - 必须判
f。 (open ...)打不开时返回nil,而(close nil)会直接报参数类型错误: streamp nil。插桩函数自己变成新的错误源——为了找错,结果自己成了错源,这就本末倒置了。 - 埋点位置
:分支入口(有没有走进去)、 ssget前后计数(有没有选到)、循环序号与关键坐标(有没有算错)。日志停在哪里,错就在它后面。 - 交付前复位
: *dbg*改回nil、删掉日志文件。交付版不该往用户硬盘上写日志。
二、vl-bt:一行拿到错误栈,你看不懂,AI 一眼看懂

vl-bt 是 AutoCAD 内建的反向跟踪函数(实测 (type vl-bt) 返回 SUBR),但没进公开文档,所以知道的人不多。
它的回溯会走 AutoCAD 的命令行。
把 LOGFILEMODE 设为 1,命令行内容就会同步落进 (getvar "LOGFILENAME") 指向的日志文件。AI 读这个文件就行。
平时没人看它,因为原始输出长这样(真实抓的):
反向跟踪:[0.77] (VL-BT)[2.68] (_call-err-hook #<SUBR @... TM:ERR> "参数类型错误: consp 5")[3.62] (sys-error "参数类型错误: consp 5")[4.57] (#<SUBR @... nil> "参数类型错误: consp 5"):ERROR-BREAK.49 nil[6.46] (CAR 5)[7.41] (TM:AUX 5)[8.36] (TM:MAIN)[9.32] (#<SUBR @... -rts_top->)[11.26] (_al-load-file "C:/.../batch_0f31a952.lsp" ".LSP")[12.20] (LOAD "C:/.../batch_0f31a952.lsp")人看着确实头大。但 AI 扫一眼就把用户函数挑出来了:CAR <- TM:AUX <- TM:MAIN。
错在 TM:AUX 里的 (car 5),由 TM:MAIN 调用。剩下 sys-error、_call-err-hook、#<SUBR …>、-rts_top->、_al-load-file 全是求值器的噪声帧。
要让它自动触发,套一个 *error* 处理器:
;; 只存一次,重复加载不会把自己的处理器存成"原处理器"(if (not *err-saved*) (setq *old-err* *error* *err-saved* T));; 必须是具名函数,lambda 形式会报函数错误(defun tm:err (msg) (vl-bt) ; 打印调用栈 (setq *error* *old-err*) ; 交还控制权 (princ (strcat "\nERR: " msg)))(setq *error* tm:err)(setvar "LOGFILEMODE" 1) ; 命令行内容同步落盘,路径见 (getvar "LOGFILENAME")三个实战细节:
日志是缓冲写出的:异常刚发生时里面还没有回溯,要轮询等几百毫秒再读。 抽帧时滤掉 VL-BT、sys-error、#<SUBR …>这类噪声帧,只留用户函数。- 只在
*error*里调它。平时空跑 (vl-bt),打出来的全是加载器管道(veval-str-body、_al-load-file、-lambda-),一个用户函数都看不到,还会顺手取消掉当前命令。
另外,ACAD 的帧只有函数名、没有行号。
读法还是那句话:让 AI 自己去读文件或命令行,而不是你贴给它。
用完记得还原 (setvar "LOGFILEMODE" 0),否则命令行日志会一直长下去。
三、智能体 + LISP 编程助手:把"猜"变成自驱循环

前面两个方法已经能让智能自己读到日志和错误栈,形成一定的自驱循环。
但智能体还判断不了结果对不对。「LISP 编程助手」把前两环整合进来,补上缺的那一环。
把 CAD 接成智能体可调用的工具后,lisp_eval 通道是同步返回值的——AI 发一段 LISP,结果直接回到对话里。
于是排错有了从便宜到贵的四级手段:
Lisp求值错误: 参数类型错误: consp 5;正常就是返回值 | ||
分段原文(9 字符)((tm:main)) | ||
[调试旁听] 栈 CAR <- TM:AUX <- TM:MAIN | ||
再配上其他工具,闭环就成立了:
- 看懂意图
: query_entities/entity_info/get_bounds读 CAD 图纸,别照口语瞎猜; - 听命令行
: read_commandline带tail=N,看命令行的提示语、看谁在等输入、看有没有被拒; - 核对结果
: verify_result改前拍快照、改后 diff,返回值与图纸双向一致才算跑通; - 看画面
: screenshot截图,自己"看"一眼。
配套的「LISP 编程助手」技能把这些动作固化成流程,还规定了双模式写法:同一套逻辑,交互与调试只有一个分支点。
(defun c:XXX () ;; 交互入口:只设模式变量 (setq *xx-mode* 'i) (tm:main) (princ));; 调试入口:智能体注入两行;; 零交互、参数全预设;; (setq *xx-mode* 't *xx-ent* nil;; *xx-csv* "C:/temp/out.csv");; (tm:main)这样 AI 可以无人值守地跑完「写码 → 执行 → 读返回值/读栈 → 定位 → 改 → 复跑」,跑通再交给你验收。
小结
代码小白写 LISP 插件,真正的门槛不是语法,是反馈回路:
网页版 AI:靠你人肉搬运日志,一轮一轮磨; 智能体:AI 自己读日志、读错误栈、修 bug; 智能体 + LISP 编程助手:AI 自己读文件、读返回值、读错误栈、回读图纸,自己收敛。

让 AI 有能力看见错误,它才不需要猜。
「LISP 编程助手」是基于 tmagent-cad 的 Skill,两者都已上架 SkillHub,如果这篇文章对你有用,请点个赞再走!也希望和各位大佬深度交流。