也许Agents需要个上下文管理器
今天在群里在群里跟网友吹比,吹着吹着突然发现一个问题。现在AI技术日新月异,我们似乎一直没有关注过上下文这一块的问题。
众所周知,LLM是一个你给他一段文本,它可以根据这段文本给出指定内容的模型。如果你想到一段话就直接丢给LLM,他有且只能给你丢给他的文本内容进行分析和回答。为了能让LLM看到之前的内容,我们不得不在我们本次要给他的文本内容之前,累加上之前的“聊天记录”。这些之前的“聊天记录”就是AI的上下文。
在实际使用诸如Claude Code、Codex等各类AI Agent工具的时候,每次运行,随着项目和全局提示词的整理、MCP检索的内容和各类Tools的调用的不断进行,上下文里被塞满了越来越多的文本内容。
时间一长,上下文里堆满了各种各样的信息,有的信息可能是冗余的,有的信息可能是无用的,这可能会让Agent推理的时候难以关注到对当前任务而言更有用的信息。
所以,我们是不是有必要整一个“上下文管理器”呢?
上下文腐烂
实际上,之所以出现上下文越堆越多的问题,根本原因在于我们之前聊过的所有内容、AI调用和MCP在内的工具的所有信息,全部都堆在了一起。
针对上下文长度不断增加,Agents性能显著下降的问题,其实AI圈里有一个专门的名词,叫做上下文腐烂(Context Rot)。
模型的上下文长度
刚才我们也提到了,裸的LLM是一个只能对当前单次请求内容的分析模型,如果想让他知道之前的内容,那就应该在当前请求内容前面叠加当前会话的历史聊天记录。当前会话里所有的聊天内容的长度最大上限就是模型最大能够处理的上下文长度(Context Window Length)。
现如今的各个模型厂商都在积极推出超长上下文的模型。比如今年初推出的GPT 5.4的上下文长度能到1M,还有之前的Claude Opus 4.6也早就支持了1M的上下文,Deepseek V4据说也要跟进1M上下文~~(但是为啥一直不出啊,你倒是出啊Deepseek!)~~。
这么多模型厂商在争相跟进超长上下文长度,想必模型变得越来越好用了吧?
根据我的身边统计学调查到的结果,似乎并不怎么好用。
就我个人使用实际使用体验而言,我在GPT 5.4开了1M上下文以后,我发现GPT的注意力是显著下降了。模型经常会注意不到目前我让他干的活,并且经常会把注意力放在一些奇奇怪怪的地方,比如之前已经修过的bug、之前有过但是现在已经没有的功能点等等。
也就是说,模型能处理这么多,不代表模型能处理好这么多。无脑叠加上下文长度是不可取的,我们很有必要去优化一下我们实际上给模型的具体内容。
上下文越多干扰越多
按道理来说的话,我们给AI更多的内容,他不应该能从里面分析到更多有意义的内容才对嘛,那理论上内容越多效果越好。
实际情况我感觉恰恰是相反的,如果我们给AI的内容不断增多的话,我们实际上给LLM的干扰项也就越来越多了,有很多内容对于AI而言似乎是一种“噪音”,让AI难以区分真正的目标信息。
这次在群里吹水的时候,我看到群友推的这篇名为“Context Rot: How Increasing Input Tokens Impacts LLM Performance”的论文里也提到了这个事情。这篇论文大概是在做RAG相关的内容。这篇研究做了一个实验,发现所有模型在完整上下文的性能腰显著低于聚焦上下文,GPT模型在这种情况下,遇到模棱两可的问题甚至变得异常的自信,产生幻觉。
上下文的归纳也很重要
同样是这篇论文,里面还做了一个有意思的实验。他们分别丢给了各种LLM上下文原文和随机打乱的上下文,结果居然发现随机打乱的上下文表现效果反而会更好。因此他们发现上下文里局部的连贯性会干扰AI的长距离注意力。
所以,上下文的内容顺序其实也会影响LLM的表现。
不负责的猜测一下为什么。当文本具有强逻辑结构的时候,模型可能倾向于在局部产生注意力。在连续文本里,模型也有可能过度依赖顺序推理。
比如同样是一句话,如果我们「先A,再B,最后C」这样组织内容,模型可能会倾向于去思考A句是如何导致B句的,B句又是如何支持C句的。这种前后因果追踪的现象也有可能让模型陷入对局部逻辑链的深入处理。
但是这种“钻牛角尖”的现象可能会让模型陷入对局部逻辑链的深入处理,也许会导致模型降低全局的敏感度。
所以长文档切分为更小的、相对独立的片段,可能反而帮助模型在超长的上下文里定位相关的信息。
我觉得别说AI了,我自己如果要是学一个东西,我也经常习惯把一个东西拆成一块一块的,我看到完整的一大堆东西我就烦,我一块一块看就更容易接受一点。
说不定对于AI也是这样的道理。
多Agents下更需要合格的上下文管理器
多Agents
从去年开始,多Agents变得越来越流行了。
实际上我们在之前的时候也“人工模拟”干过类似多Agents的活。
在之前的时候,git worktree变得火了起来。如果不是AI圈子里流行这个指令,我可能很难在日常用git的时候想到还有这么个东西。这个指令的具体作用是可以开多个workspace目录,每个workspace对应一个git分支。
如果要是在一个项目里有多个部分同时要让AI开发的话,我们可以给每个部分都开一个branch,然后利用git worktree指令开多个workspace,让每个AI Agent都在各自的workspace里干活,等各个Agent干完活了,我们再给他们merge一下。
我们一次性开了多个终端窗口对应了多个Agents来干活,这样不仅效率提升了,也防止了AI之间相互打架的问题。
现在慢慢在各个Agent工具里都内置了subagent功能。Claude Code、Codex、Opencode等各种工具现在都推出了Subagents。
之前我们干活都是只开一个Session,对应一个Agent来帮我们干活,现在一个Agent可以下发多个Tasks,根据任务的需要开多个Subagents并行帮我们干活。
去年的时候Opencode里一个叫oh-my-opencode的项目火了,他里面写了一堆他自定义的agent,并且每个agent可以对应一个AI模型,实现一个项目由多个特长各不相同的不同角色的AI模型并行干活的目的。
多Agents的上下文问题
实际上,多Agents的上下文管理基本上是稀烂的。
多Agents的原理就是让主Agents生成一个描述当前要干啥任务的提示词,然后启动一个subagent把提示词塞给他,这个新开的subagent把活干完以后又会把自己干的活总结一下再丢回给主agent。
也就是说,多Agents的Agent协作模式下,每个Agents都在写一种类似需求文档和交工报告之类的玩意儿,彼此之间传递。
然而实际上,大多数的需求内容都是在主Agent的上下文里的。
实际上仔细思考一下主Agent的上下文的作用,不难发现主Agent的上下文其实是整个项目的公共记忆库。因为子Agent的任务描述其实就来自于主Agent的上下文,而子Agent汇总自己工作的文本说到底也会流入主Agent的上下文里。
如果子Agent能有一种合理的机制直接从项目的公共记忆库里读取到任务列表,读取自己需要的记忆片段,然后再把结果嵌入到记忆库里,那不就能更理所应当的解决问题了吗?
上下文管理器应该长啥样?
说到这里我就在想,既然上下文管理是非常重要的,那这个上下文管理器应该长什么样呢?
记忆片段
都说到这里了,想必「上下文应该切成若干个记忆分段」这么一件事应该是无可争议的了。
每个记忆、用户的要求、Prompts、MCP的数据、工具的调用等等,应该被切为一个一个的片段。
而实际上这个片段并不难切分,因为仔细看过AI Agents的context history以后不难发现,主流的AI Agents工具里每个诸如此类的元素都是一个独立的json数据,通常就存储在一个jsonc文件里。
每个记忆片段都有自己的类型和时间。我觉得还需要考虑给记忆片段增加一定的标签,用来标注这个记忆片段是做什么工作用的,并且能够根据标签来检索对应的记忆片段,实现「根据具体的需求激活使用对应的记忆片段」的目的。
加载器
既然Agents的上下文窗口是有限的,加载器应该是非常有必要的。
我们需要一个加载器来决定具体目前要装载什么、丢弃什么、压缩什么记忆片段。
我觉得这方面的话可以参考一下操作系统的内存调度机制。
在操作系统里,得益于虚拟内存机制,内存可以灵活掉配物理内存的存储空间,装载目前程序需要的片段,卸载目前暂时不需要的片段。
针对Agent的上下文加载机制的话,操作系统的调配机制可能是十分基础的,我们可能需要根据目前的任务来发挥一些LLM的分析能力,来分析什么片段需要,什么片段不需要。
评估器
我觉得我们很有必要评估一下目前记忆库里的记忆片段的质量,来维护包括刚才所说的记忆片段的标签在内的记忆片段的元信息,来衡量一下这个记忆片段对应的信息是否过期,是否可信,是否符合当前的某些策略。
评估基于片段的质量我觉得是很有必要的,因为正如刚才我们分享的那篇研究所说,上下文的质量无疑是会影响到Agents的工作质量的。
目前的现状和阻碍
如果有一个合适的上下文管理器的话,可能我们确实效果会变得更好一些。
那这个东西听上去很好,实际上目前的做法是如何的呢?会有什么阻碍呢?
上下文压缩
实际上对于Agents,如果我们一直持续不断的给他的上下文窗口里增加内容,AI Agent会在上下文快满的时候启动上下文压缩(Context Compression)任务,来压缩当前的上下文。
上下文压缩会做一个压缩任务,来把当前的上下文进行精炼,把一些无关的内容给去掉,保留对目前要求而言最相关的内容。
但是上下文压缩这个东西对于之前的单Agent而言或许是有帮助的,但是如果要是铺开多Agents的话,这个东西似乎就变得具有破坏性了。并且这个东西似乎是没有解决本质问题的。
上下文压缩技术本质上解决的是容量的问题。LLM的上下文窗口长度客观来看就那么长,超过了就用不了了,所以需要上下文压缩来进行把控。
实际上要解决的问题在于LLM的注意力问题。我们实际上要做的事情是在目前的这个任务下,把最合适的上下文记忆片段丢给LLM进行处理,来规避LLM一下子看到的东西五花八门乱花渐欲迷LLM眼的问题。
因此,上下文压缩只能作为一个全局的把控机制,起不到我们所说的这个目的。
同时,压缩是一个一次性的处理步骤,忽视了多Agents应用场景下跨会话的持久化和状态管理问题。
对于多轮Agents协作,A Agent的上下文如何传递给B Agent,这并不是上下文压缩关心的问题,甚至对这个过程是破坏性的。
工具调用产生的中间结果,上下文压缩也很难保证下次会话的无关信息不被污染。
压缩没办法保证多Agents共享的安全问题,没人来去衡量每个Agents是否正确工作以及教出来合适的数据了。我们都知道,开个新的Session总归是没有之前那个Session好聊的,说不定新的Agent是在拉屎。
更关键的是,这个过程人类没法介入,人类没法去管理记忆,本来记忆就难管理,压缩以后更难管理了。
模型的计费方式
如果要是真用过了各个厂商出的API或者Coding Plan的话,会发现他们的模型计费机制里有Cache Read和Cache Write费用。
具体而言,模型厂商侧维护了一套模型缓存,如果我们的内容击中了缓存,模型厂商可以少收一点点费用。
那问题就在于「怎样才算集中缓存」上了,答案是很简单粗暴的「前缀匹配」。
也就是每次请求的时候,Agent工具都需要携带上下文和当前用户说的内容过去,如果要是模型那边发现上下文的前缀部分跟缓存是一模一样的(也就是一段内容前面的内容跟之前的一模一样),那就认为击中缓存了,按缓存击中收费,反之就是没击中。
那问题来了,如果之前的上下文是「先A再B最后C的」,我这次的上下文我给他改成「先C再A再B」了,内容本质也一样呀,怎么收费?
答案是按没击中收费,因为前缀不一样了,没击中缓存。
所以模型厂商的这个计价方式,导致如果我作为一个某个模型厂商的终端用户,想在上下文上玩花活,那我的钱包……
我不好说(x
总而言之
我觉得就目前的现状而言,我们正站在Agent架构从单线程脚本向并行工作进化的节骨眼上。
这里所说的所谓“上下文管理器”的本质,是要建立一套让记忆、工具调用、中间状态成为可追溯、可持久化、更易于管理的管理机制,也就是希望对上下文进行改造,让它具有结构话,利于多Agents协作和人类维护。
虽然当前模型厂商的计费机制确实给上下文重排和结构化管理的工作泼了冷水,但是我觉得AI技术是日新月异的,目前的上下文管理机制是非常抽象不合理的,总有一天会有人开窍,提出一种类似或者超越我今天瞎bb的内容的一种上下文管理机制,让Agent具有长期记忆和协作的基建。