这周 agent 事故类新闻连着来:Meta 研究员的 agent 把她邮件删了,Reuters 报道一批 OpenAI agent 把德国网站劫持了,还有人扒出了 agent 之间互发消息的内部留言板。三件事看着是安全事故,其实是同一个工程问题:你手里的 agent 日志,出事时根本讲不清它为什么这么干。我的判断很直接——只记输入输出等于没记,复盘要的是模型当时的上下文,不是最后的输出。
两起事故一个剧本:只有结果,没有过程
Meta 那条细节很少,“意外删除邮件”(报道)一句话带过。但真正该回答的问题是:上下文里哪段内容让 agent 认为删除是对的?id 参数怎么选出来的?有没有确认环节?以多数 agent 项目的日志现状,这些很可能没答案。常规记录只有 user prompt、assistant reply、tool call 的名字和状态码。哪个环节看了哪封邮件、哪句话触发了删除,全线缺席。
德国站被劫持(Reuters)也一样。公开报道能看到“发生了什么”,看不到 agent 一步步的决策。不是怀疑厂商没做内部记录,而是工程里常见的“结果日志”在这个尺度下毫无复盘价值。怪模型之前,得先有一条“它到底看到了什么”的时间线。
四样东西存齐,复盘才不用猜
被坑几轮之后,我筛出必须落盘的四样。
第一是模型真实收到的消息数组,完整拼好的那份——工具结果返回后、送进推理之前的样子。框架常在我们写的 prompt 外再套一层,实际发出去的跟以为发出去的经常不是一个东西。只存自己写的那段,等于丢了最关键的那半截。
第二是工具调用的原始参数,wrapper 改写之前的版本。框架日志里存的往往是加工后的 JSON,真出问题,拿日志跟接口文档对不上,只能靠猜。
第三是工具返回的原文。只存“成功 / 失败”等于没存。哪怕截前几百字符加个截断标记也行——工具返回一万行,模型只用了其中三段就做决定。不记这个边界,agent 的行为看起来就是无缘无故地愚蠢。
还有 token 数和耗时。不是考核用,是定位用:哪一步 token 突然暴涨,多半是上下文被灌进了什么;哪一步耗时异常,多半是工具在空转重试。
全量记录的代价,是每行都可能被别人读到
到这里肯定有人反对:全量记录,敏感数据不都落盘了?对,而且这周就有案例——被公开扒出的 OpenAI agent 内部留言板(collusion.wiki)。不讨论泄露细节,原理很简单:任何写进日志的内容,都进入了备份、索引、同步的传播面,而日志恰恰是最容易被忽视防护的那类数据。
所以脱敏必须在写入前做。读取时脱敏是本末倒置——日志已经落盘了,挡住的只是“查询”这一个出口,挡不住爬取和备份的副本。更往前一步:尽量别让敏感字段进上下文。日志是上下文的影子,源头没有脏数据,影子就不会脏。字段级替换规则得维护,邮箱、内部路径换 marker,这是成本,但属于保险费,不是浪费。
一个 JSONL 文件就够的最小方案
不引数据库,不上 tracing 平台。给 LLM 调用和工具调用各包一层 handler,每次进出 append 一行:agent 名、时间戳、步数、模型、完整消息数组、工具名和原始参数、返回内容前 N 字符、token 数、耗时毫秒。
一行就是一次调用,复盘时 jq 过滤、grep 关键字都行。坑有三个:截断跟模型上下文对齐,你截到哪,模型的视野就到哪;写入时别顺手“清洗”参数,格式化归格式化,现场要能复原;脱敏规则放写入前,白名单化,别等事后补。
合格线就一句:出事的第二天,你能拿着日志给同事讲清楚——哪一步看了什么、凭什么做决定、从哪一步开始跑偏。连这个都做不到的日志,不是日志,是自我安慰。我们花了这么大力气让 agent 自主决策,却连它做过什么都留不下来,说不过去。