为什么先记账,而不是先换模型

agent 跑长任务出问题时,绝大多数团队的第一反应是换一个更强的模型。但换模型解决不了「不知道它在哪一步走偏」这件事。你需要的是一份能在事后逐条复盘的台账:每一步它做了什么、基于什么判断、花了多少、结果能不能验证。有了这份台账,换不换模型、要不要加控制层,都会变成可以用数据回答的问题,而不是靠感觉。

这份台账不用做得很重。原则只有一条:能落进结构化字段的,就不要留成一段话;无法自动验证的东西,尽量少写进去。

每一步该记什么:六个字段

  1. 步号与时间:用来对齐预算消耗与外部事件(上游报错、网络超时、被抢占)
  2. 动作类型:读文件 / 写文件 / 调用哪个工具 / 请求人工确认。类型要收敛成有限枚举,否则没法统计
  3. 输入摘要:这一步看到了什么——文件名加哈希、检索命中的条数、上游返回的状态码,不要存整段正文
  4. 判断依据:为什么选这一步。来自控制层就写下候选列表与选择理由;模型自由发挥就如实标注「无显式依据」
  5. 成本:token 数、耗时、外部调用次数分开记,这三项的增长方式完全不同
  6. 产出与可验证性:产出了什么(文件路径加哈希、结构化结果),以及这一步的结果有没有自动判据、通过了没有
一个实用判据:如果一份台账里超过一半的步骤,「判断依据」写的是「无显式依据」,说明这个 agent 还没有真正的控制层——它只是在按惯性往下走。

落盘:一份只能追加的事件流就够了

不要一上来就上数据库。用按行追加的 JSONL,一行一条步骤记录,天然支持中途崩溃后续写,也方便按步号切片回放。需要频繁查询的成本与耗时可以另外汇总成一张小表,但事件流本身保持只能追加、不能修改——一旦允许改历史,事故复盘就没有可信依据了。

  • 字段固定,缺项写 null 而不是省略键,否则后续统计脚本会长出一堆分支
  • 写入必须是原子的:把这一行写完之后再执行下一步,否则崩溃时丢的恰好是最关键的那一步
  • 给每个任务一个 run id,把同一任务的所有步骤串起来,跨任务对比全靠它

用台账回答三个问题

台账不是写给人看着舒服的,它要能回答三个具体问题;回答不了,就说明字段还不够。

  1. 预算花在哪了:按动作类型汇总成本。如果「重做」和「重新读一遍已有产物」占比很高,是控制层的问题,不是模型的问题
  2. 失败长什么样:把失败步骤按动作类型分组。集中在一类就是设计问题,散在各类才是模型能力问题
  3. 哪一步能自动验证:把步骤分成「有自动判据」与「只能靠人看」两组,前者的比例决定了你能把这个 agent 放多远

把台账变成回归门禁

台账最大的价值是被固化下来:从失败案例里挑出最有代表性的若干条,连同当时的输入一起存成回归集;以后每次改提示词、换模型、动工具,先把这批重跑一遍。这样就不需要再争论「这次改动会不会变差」——它有一个可复现的答案。

回到开头:换模型提升的是「单步做对的概率」,台账与回归提升的是「你能发现它做错、并定位到哪一步」。后者是你能自己控制的部分,也应该先做。