ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

第二次实验:把 Agent 的失败变成最小可复现案例

第二次实验:把 Agent 的失败变成最小可复现案例 把 Agent 的失败变成最小可复现案例我踩了两个坑导语上一篇我讲了怎么让 Agent 的执行可复现。但能复现只是第一步——一条 7 步的失败轨迹摆在你面前你还是不知道哪一步是根因。这篇记录我怎么把它自动缩减成 2 步的以及过程中踩的两个大坑。一、问题7 步轨迹哪一步是根因假设你用 Agent 做一个任务它跑失败了。你打开日志7 步操作摆在眼前step 1 run_shell ls ← 先看看目录里有什么 step 3 write_file scratch.txt ← 写了个笔记 step 5 read_file data.txt ← 读了输入数据 step 7 write_file count.py ← 写了个统计脚本 step 9 run_shell python count.py ← 跑脚本 step 11 write_file notes.md ← 写了个说明文档 step 13 read_file out.json ← 读结果最终结果是错的——词频统计没转小写The和the被算成了两个词。问题来了到底哪一步是根因是 step 7 写脚本写错了还是 step 9 跑的时候出了什么问题还是前面某一步埋了雷你总不能一步步删了试吧7 步还好70 步怎么办二、第一反应套用经典算法这个问题听起来很眼熟——编译器社区早就解决过类似的怎么把一个 10 万行的崩溃用例缩成 10 行答案是ddmin 算法Delta Debugging。思路很简单把输入切成两半删掉一半看看还崩不崩还崩 → 接受删除继续切不崩 → 换另一半删重复直到不能再删经典 ddmin 在 C 程序的 core dump 上效果很好能把几万行代码缩到几十行。我当时想先把轨迹当成输入然后跑 ddmin 。三、第一个坑1 步的最小用例我信心满满地跑了第一版算法R0然后看到了这个结果1 步我当时还挺得意缩减率 86%不错啊。然后我点开最小用例内容等等。这一步是读 out.json。它怎么可能复现词频统计没转小写这个 bug我回去看 R0 的失败签名原始失败签名(‘WRONG_OUTPUT’, ‘969a12dd4967’)R0 给出的签名完全不是一回事原始 bug 是输出内容错了R0 给出的是输出文件根本不存在。为什么会这样我把步骤一步步删下去删掉 step 1ls→ 还失败吗→ 失败了 → 接受删掉 step 3写笔记→ 还失败吗→ 失败了 → 接受删掉 step 5读数据→ 还失败吗→ 失败了 → 接受删掉 step 7写脚本 count.py→ 还失败吗→ 失败了因为 step 9 跑python count.py时报can’t open file→接受问题就在这删掉写脚本那步后跑脚本报错了。R0 的判定条件只是还失败吗——它不知道这是同一个失败还是不同的失败。这就像你说我家灯不亮我把灯泡拧了然后说看现在更黑了确实是灯的问题。我把原始问题弄丢了。四、解法一失败签名必须完全一致想通这个坑之后我加了一层判定不能只问还失败吗必须问是不是同一个失败。我定义了四级失败签名签名含义PASS结果正确WRONG_OUTPUT(哈希)产物在但内容错了TOOL_ERROR(错误末行)工具报错了NO_OUTPUT产物根本不存在现在再删 step 7删掉后签名变成TOOL_ERROR: cant open file...原始签名是WRONG_OUTPUT: 969a12dd4967不一样 → 拒绝这版R1跑出来的结果对了就是 step 7写脚本 step 9跑脚本这两步。根因就是脚本写错了。但等等……R1 跑了14 次测试执行才找到这个 2 步用例。这也太多了吧五、第二个坑加了依赖闭包反而更差了我想加速。既然我知道 step 9 依赖 step 7跑脚本需要先写脚本那我就加一个依赖闭包——保留 step 9 的时候自动把 step 7 也拉进来。这样就不用试只删 step 7这个组合了。我把这版叫 R2信心满满地跑了。结果R2 比 R1 还差R1 找到 2 步用例R2 找到了 5 步我人傻了。加了优化怎么还变慢了排查为什么闭包反而删不掉东西我去看依赖图发现了问题我把控制依赖也算进强依赖了什么是控制依赖就是 Agent 的消息链——step 3 的输出喂给了 LLMLLM 基于这个输出决定下一步做什么。所以每一步都接着上一步。如果把这个也算强依赖那每一步都依赖上一步你什么都删不掉这就像你说我要保留做蛋糕的最后一步吃蛋糕那我必须保留前面所有步骤——打鸡蛋、和面、烤、装饰——因为每一步都接着上一步。但你真正需要的只是烤箱和蛋糕材料不是装饰的时候放了什么音乐。六、修正强依赖和弱依赖必须分开我把依赖分成了两类类型例子参与闭包吗强依赖资源依赖step 9 跑脚本需要 step 7 先写好脚本✅ 参与弱依赖控制依赖step 3 的输出喂给了 LLM影响下一步决策❌ 不参与为什么要分因为控制依赖是上下文不是前置条件。你删掉 step 3写笔记step 7写脚本照样能跑——只是 LLM 少看了一段笔记而已不影响结果。修正之后再跑 R23 次R1 跑了 14 次R2 只跑了 3 次就找到了同样的 2 步用例。七、最终结果三种策略的对比策略最小用例缩减率复现同一失败执行次数R0 宽松判定1 步86%❌ 假失败2R1 严格签名2 步71%✅ 是14R2 严格签名 依赖闭包2 步71%✅ 是37 步轨迹 → 2 步最小用例只跑了 3 次测试。这 2 步就是根因step 7 write_file count.py ← 这里写错了没转小写 step 9 run_shell python count.py ← 跑错了的脚本八、两个坑总结坑 1假失败你以为删掉一些步骤后还失败 删对了。实际上删掉后失败了但失败原因完全不同——这不是最小用例这是另一个 bug。解法失败签名必须完全一致不能只问还失败吗。坑 2控制依赖算成强依赖你以为每一步都接着上一步所以都算依赖。实际上控制依赖是上下文不是前置条件。把它算强依赖你什么都删不掉。解法强依赖资源/数据和弱依赖控制必须分开只有强依赖参与闭包。九、延伸规模化验证我还在 12 条轨迹上跑了基准测试R0 的有效率0/12——12 次全部给出假失败R1/R2 的有效率12/12——全部正确R2 比 R1 平均快1.67 倍3.8 次 → 2.2 次结论依赖闭包在完全不损失结果质量的前提下稳定降低了缩减的代价。十、下一篇这篇解决了怎么定位根因。但还有个问题为什么 Agent 会不可复现真的只是 LLM 采样随机吗下一篇我会写一个控制变量实验——把 Agent 的非确定性拆成四个来源一个个单独开关敬请期待吧如果这篇对你有帮助点个赞让更多人看到。有问题欢迎评论区交流。标签AI AgentDelta Debugging调试LLM程序分析
返回列表