技术 · 熟练
上下文塞满之前,Agent 该记住什么?
不是压得更狠,是把细节换成指针
长跑 Agent 记不住事,多半不是窗口太小,而是你把「要记的事」和「要查的事」都塞进了窗口。这一页给的是另一种存法:给每个对象发一个永不变的编号,窗口里只留「编号 + 一行摘要」,细节全部倒出去变成指针,要用时按编号取回来。它解决的不是「塞不下」,是「塞进去的会糊」 —— 摘要越写越短,短到最后你无法判断当时到底发生了什么。判据只有一条:这一次回答需要的那条细节,是在窗口里,还是能按编号取回来 —— 能取回,才算真的记住了。
方法 · 一图看懂
1
先分三类对象 — 进来的事件(Trigger)· 一串相关的事(Thread)· 一次执行会话(Worker),各发一种前缀的编号
2
窗口只留摘要与编号 — 一行写清「发生了什么」,其余整段倒到能按编号取的地方
3
按编号三次跳转取回 — 从线程找到执行会话,从会话找到那次事件,再取它的原始负载
4
先立四条接口契约 — 编号永不变 · 按编号查要精确且轻 · 查不到要报错 · 翻页游标不能丢
原理 · 为什么有效
为什么这一套比「再压一次摘要」管用:模型的窗口是缓存,不是档案柜。摘要做的是「把细节换成更短的细节」,每压一次就丢一层;编号做的是「把细节换成地址」,细节本身没动,只是挪到了窗口外。这就是 Inline 与 Spill 的分工 —— 大概率马上要用的(编号、一行摘要、相邻几条消息)留在窗口里,其余全部 Spill 出去,只留指针。本文引的实测很说明问题:把「整段线程塞进上下文」这一步去掉、改成传编号之后,token 用量反而上升了 37% —— 因为它要额外发起取回请求。所以它管的从来不是省钱,是「在有限窗口里保住可追溯」。
第二个关键点是:它和向量检索不是同一件事,也不能互相替代。向量检索找的是「相似」—— 你问「上次那个支付重试的改动」,它按语义相似度捞回一堆看着相关的段落,可能捞错;编号找的是「相连」—— 线程指向执行会话、会话指向那次触发,是一条确定的路径,走三步就到,不会捞错。凡是「你手上有编号」的场景,用编号;凡是「你只有一个模糊的说法、连编号都不知道」的场景,才用检索。顺序是先编号后检索:先用编号把范围收到一条线上,再在这条线上做语义查找,而不是一上来就在全库里捞。
适用场景
适合:适合:已经在跑长会话 Agent 的人(自动化任务、CI 失败排查、工单流转、多轮代码改动),以及被「摘要越写越糊、翻不回原始记录」卡住的人。也适合做审计:每一次回答都能指回具体的触发事件。
不适合:一次性问答、上下文本来就只有几轮的任务 —— 给它们加编号只是多一层壳;也不适合主键本身不稳定的数据源(编号会变、会复用、查不到时返回空对象而不是报错的系统)—— 那种情况先把数据侧的契约修好,再谈这套。
自测 · 5 问
我能说出项目里「事件 / 线程 / 执行」三类对象各自长什么样、编号带什么前缀吗?
上一次 Agent 找不到上下文,是「根本没存」,还是「存了但不知道编号」?
我的按编号查询,查不到时返回的是明确报错,还是一个空对象?
窗口里现在留的是「摘要加编号」,还是整段原始日志?
我分得清哪些该走向量检索(找相似)、哪些该走编号(找相连)吗?
怎么上手 · 验证步骤
第一步只做一件事:给最近 20 条事件补编号。挑你最近踩过的一次坑(一次失败的任务、一张工单、一次构建失败),把它的原始负载完整存到一个能按编号取的地方,然后在窗口里只写一行摘要加这个编号。第二步把这 20 条按「属于哪一次执行、哪一串事」串起来 —— 线程与执行会话也各发编号。第三步写一个最小的取回动作:给它编号,返回原始负载;查不到就明确报错。第四步才是接进 Agent 的循环:每次回答末尾标出它引用了哪几个编号。
自检一个动作就够:随口报出上周某次失败的事件编号,并在三次跳转内取回那次的完整日志。报不出来、或者要靠翻聊天记录才找得到,说明这套还停在纸面 —— 编号发了,但没有成为取回的唯一入口。