ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Agent上下文管理实战:Context Editing、Compaction与Memory Tool

2026/10/5 4:42:13 拓冰建站 浏览量
Agent上下文管理实战:Context Editing、Compaction与Memory Tool 1. 从第20轮失忆说起Agent 上下文管理的真实痛点如果你正在做 Agent 开发大概率遇到过这个场景前几轮对话还挺聪明到第十几轮、二十轮之后它开始答非所问忘了用户最开始说的约束条件甚至把之前确认过的结论推翻重来。很多人第一反应是模型不行换个更大的模型结果发现该忘还是忘。问题不在模型本身而在于你喂给它的**上下文Context**已经变成了一锅粥。大模型的上下文窗口是有限的哪怕标称 1M token 的窗口塞进去的东西越多注意力被稀释得越厉害关键信息被淹没在大量冗余历史里模型自然就失忆了。这里要先厘清一个概念上的分水岭管理历史和管理上下文是两回事。管理历史把每一轮对话原封不动地存下来按时间顺序拼进 prompt。这是大多数初学者的做法本质上是日志式思维。管理上下文把历史当作原材料经过筛选、压缩、结构化、分层之后只把当前这一步真正需要的信息放进窗口。这是工程式思维。标题里说的高手管理的是上下文指的就是后者。这篇文章我会把 Agent 上下文管理的几个核心机制——Context Editing上下文编辑、Compaction压缩、Memory Tool记忆工具——从原理到落地拆开讲结合我在实际项目里踩过的坑给出一套可以直接抄作业的方案。适合正在做 Agent 开发、被上下文超长折磨过的同学也适合刚入门想搞清楚上下文到底怎么管的新手。先说结论上下文管理不是某一个 API 调用能解决的它是一套分层策略。你得先想清楚哪些信息该留在窗口里、哪些该压缩、哪些该外置到记忆里然后才是选工具、写代码。2. 上下文窗口到底是怎么被撑爆的2.1 一个真实的 token 消耗账本很多人对 token 消耗没有概念觉得不就是几段文字吗。我拿一个典型的 Agent 会话给你算笔账。假设你做一个客服类 Agent系统提示词System Prompt里包含角色设定、工具说明、输出格式约束大概 1500 token。每轮用户输入平均 100 tokenAgent 回复平均 300 token。如果 Agent 每轮还要调用工具工具返回结果平均 500 token。那么第 N 轮结束时累积的上下文大致是轮次累积 token粗略说明第 1 轮1500 100 300 500 2400还很清爽第 5 轮2400 4 × 900 6000开始有压力第 10 轮2400 9 × 900 10500注意力开始分散第 20 轮2400 19 × 900 19500关键信息基本被淹没第 50 轮2400 49 × 900 46500接近很多模型的窗口上限这还只是保守估计。如果工具返回的是长文档、网页内容、代码文件单轮就能吃掉几千甚至上万 token。我见过一个做代码分析的 Agent读一个中等规模的仓库文件单次工具返回就 3 万 token三轮下来窗口就满了。注意token 数不是线性影响效果的。窗口用到 50% 和用到 90%模型的表现可能差出一个档次。不是没超就行而是越满越糊。2.2 为什么塞满反而变笨这里涉及一个常被忽略的机制注意力稀释。Transformer 的注意力机制本质上是给上下文里每个 token 分配权重。当上下文很短时关键信息比如用户的核心诉求能拿到很高的权重。但当上下文里塞了几万 token 的闲聊、重复的工具返回、已经过时的中间结论时这些噪声会分走注意力关键信息的相对权重被压低。打个比方你在一个安静的房间里跟人说话对方听得清清楚楚。但如果房间里同时有 50 个人在聊天你说同样的话对方就得费劲去分辨。模型也是一样它不是记不住而是分不清哪个重要。更麻烦的是**中间遗忘Lost in the Middle**现象。研究发现模型对上下文开头和结尾的信息记得比较牢中间部分容易被忽略。而按时间顺序拼接的历史最重要的信息比如用户最初的约束往往就在开头中间全是过程性内容结尾是最近的对话——结果就是开头和结尾还行中间一塌糊涂。2.3 常见的三种错误应对我见过太多团队在这上面走弯路总结下来有三种典型错误第一种无脑截断。窗口快满了就把最早的历史删掉。这会导致用户最开始说的约束条件丢失Agent 开始违反最初的需求。比如用户一开始说所有金额用人民币聊到第 30 轮截断后Agent 又开始用美元了。第二种无脑摘要。把历史全部丢给模型做摘要然后只保留摘要。问题是摘要会丢细节而且摘要本身也可能失真。更坑的是摘要做多了会摘要的摘要信息层层衰减最后剩下一堆正确的废话。第三种换大窗口模型。以为 1M 窗口就能解决一切。实际上窗口越大注意力稀释越严重而且成本飙升。1M 窗口的模型跑一次费用可能是普通模型的几十倍延迟也高得离谱。这三种做法的共同问题是没有区分信息的生命周期。有些信息是永久有效的用户身份、核心约束有些是阶段性的当前任务的中间结果有些是一次性的某个工具的原始返回。把它们一视同仁地处理必然出问题。3. Context Editing不是删历史是重新编排3.1 Context Editing 的核心思路Context Editing 这个词听起来玄乎其实核心就一句话在把上下文送进模型之前动态地重新编排它。注意关键词是动态和重新编排不是简单的增删。它包含几个动作筛选从完整历史里挑出当前这一步真正相关的部分。重排把最重要的信息放在开头或结尾避开中间遗忘区。替换把冗长的原始内容替换成结构化摘要或引用。注入把外置记忆里相关的片段拉进当前上下文。这跟传统的历史管理最大的区别是历史是只读的存档上下文是每轮重新生成的视图。同一份历史在不同轮次可以生成完全不同的上下文。3.2 一个可落地的分层结构我在项目里用的是一套四层结构你可以直接参考层级内容生命周期处理方式固定层系统提示词、角色设定、工具定义永久每轮原样注入放在最前约束层用户核心诉求、硬性约束、已确认结论会话级结构化存储每轮注入工作层当前任务的中间结果、最近几轮对话任务级保留最近 N 轮超出则压缩归档层更早的历史、工具原始返回永久存档外置存储按需检索注入固定层和约束层是永远在场的工作层是滚动窗口归档层是按需调取。这样设计的好处是无论会话多长模型每轮看到的上下文都是结构清晰、重点突出的而不是一坨越来越大的历史。3.3 约束层怎么提取和维护约束层是这套结构里最容易被忽略、但价值最高的部分。它的作用是把用户在整个会话里说过的硬性要求抽出来单独维护每轮都注入。具体怎么做我的做法是首次提取会话开始时用一次轻量模型调用从用户首轮输入里抽取约束存成结构化 JSON。增量更新每轮对话后判断用户是否新增或修改了约束如果有就更新。冲突检测如果新约束和旧约束冲突主动向用户确认而不是默默覆盖。举个例子用户说帮我写个爬虫只要标题和链接不要正文输出成 CSV。约束层就是{ task: 写爬虫, fields: [标题, 链接], exclude: [正文], output_format: CSV }之后无论聊到第几轮这段 JSON 都会出现在上下文里。模型就不会因为中间聊了别的内容突然又开始抓正文了。提示约束层不要用自然语言存用结构化格式JSON/YAML。自然语言容易被模型重新解读结构化数据更稳定。3.4 重排策略把关键信息放在黄金位置前面提到中间遗忘现象所以重排很重要。我的经验是开头放系统提示词、约束层。这是模型注意力最集中的区域之一。结尾放当前用户输入、最近一轮的工具返回。这是另一个高注意力区。中间放工作层的中间结果。这部分即使被部分忽略影响也相对小。如果某轮任务特别依赖某个中间结果我会把它从中间提到结尾紧挨着用户输入。这个动作看起来小但实测对准确率提升明显。4. Compaction压缩不是摘要是有损但可控的降维4.1 Compaction 和普通摘要的区别很多人把 Compaction 等同于让模型总结一下历史这是误解。普通摘要是无差别压缩Compaction 是有策略的有损压缩。区别在哪普通摘要会把所有内容揉成一段话细节全丢。Compaction 会保留结构只压缩冗余部分。比如工具返回了一个 5000 token 的 JSONCompaction 不是把它总结成工具返回了一些数据而是提取出关键字段丢掉重复的元数据压成 200 token 的结构化结果。我常用的 Compaction 策略有三种结构化提取从冗长的工具返回里抽出关键字段丢掉包装层。对话折叠把多轮确认-回复折叠成一条结论。引用替换把长内容替换成一个引用 ID需要时再展开。4.2 触发时机什么时候该压缩压缩不是越早越好也不是越晚越好。触发太早信息还没用上就被压没了触发太晚窗口已经爆了。我的做法是设双阈值软阈值比如窗口的 60%开始对归档层做后台压缩不影响当前对话。硬阈值比如窗口的 85%强制压缩工作层把最老的部分折叠掉。这样设计的好处是压缩动作是渐进的不会在某一轮突然大改上下文导致模型行为突变。4.3 压缩的保真度控制压缩最大的风险是丢关键信息。我的经验是给压缩加一个保真度检查压缩完成后用一次轻量调用判断压缩后的内容是否还能支撑当前任务。如果判断为信息不足就回退到更保守的压缩策略或者从归档层补拉原始内容。这个检查看起来多花了一次调用但比起因为压缩丢信息导致任务失败重来成本低得多。4.4 一个压缩前后的对比实例假设工具返回了这样一段简化版{ status: success, request_id: abc-123-def-456, timestamp: 2024-01-15T10:30:00Z, data: { user: {id: 1001, name: 张三, email: zhangsanexample.com, created_at: 2023-05-01}, orders: [ {order_id: O001, amount: 299, status: paid, items: 3}, {order_id: O002, amount: 158, status: pending, items: 1} ] }, meta: {page: 1, total: 2, elapsed_ms: 45} }压缩后{ user: 张三(1001), orders: [ {id: O001, amt: 299, st: paid}, {id: O002, amt: 158, st: pending} ] }从约 400 token 压到约 60 token关键信息用户、订单、金额、状态全保留丢掉的是 request_id、timestamp、email、meta 这些当前任务用不上的字段。这就是有损但可控。5. Memory Tool把记不住变成查得到5.1 记忆工具解决的是什么问题Context Editing 和 Compaction 解决的是窗口内的问题但有些信息天然就不该常驻窗口——比如用户三个月前的偏好、上一个会话的结论、知识库里的领域知识。这些信息应该外置存储按需检索。这就是 Memory Tool 的定位。Memory Tool 的本质是给 Agent 配一个外部大脑它包含两个动作写入Write把值得长期保留的信息存进去。检索Retrieve在当前任务需要时把相关片段拉进上下文。5.2 什么信息该写进记忆不是所有信息都值得存。我的判断标准是三条跨会话有效这个信息在下一次会话里还有用吗如果只在当前会话有用放工作层就行。稳定不变用户偏好、身份信息、长期约束这些相对稳定。频繁变化的信息不适合长期存。检索可命中存进去的信息要能被语义检索到。如果存的时候没有好的描述检索时也找不到。按这个标准适合存记忆的有用户画像、历史结论、领域知识、常用配置。不适合的有临时中间结果、一次性工具返回、当前会话的闲聊。5.3 检索注入的时机和粒度记忆检索最容易犯的错是每次都全量拉。这等于把外置记忆又变成了窗口负担。我的做法是按需检索 粒度控制按需只在当前任务确实需要外部信息时才检索不是每轮都查。粒度检索返回的是片段不是整条记忆。比如用户偏好有 20 条只返回和当前任务相关的 3 条。具体实现上我会在每轮开始前做一次轻量的意图判断决定这轮要不要查记忆、查什么。这个判断可以用规则关键词匹配也可以用模型轻量分类看你的成本预算。5.4 记忆的更新和遗忘记忆不是只写不删的。我见过一些项目记忆库越堆越大检索越来越慢命中率越来越低。所以记忆需要更新和遗忘机制更新同一类信息有新版本时覆盖旧的而不是追加。比如用户换了偏好旧偏好要标记失效。遗忘长期未被检索命中的记忆降低权重或归档。可以用最后命中时间做淘汰依据。冲突处理检索到互相矛盾的记忆时以时间最新的为准并在上下文里标注冲突。注意记忆的写入最好也走一次价值判断不是什么都说记住这个。我见过 Agent 把用户的每句话都存进记忆结果检索出来的全是噪声。6. 三者怎么配合一套完整的上下文流水线6.1 每轮对话的完整流程把 Context Editing、Compaction、Memory Tool 串起来一轮对话的上下文处理流程大致是接收用户输入。意图判断这轮要不要查记忆要不要更新约束记忆检索如果需要从 Memory Tool 拉相关片段。约束更新如果用户新增/修改了约束更新约束层。上下文组装固定层 约束层 工作层 检索到的记忆 当前输入。阈值检查如果组装后超过软阈值触发 Compaction。调用模型。结果处理把值得长期保留的信息写入 Memory Tool。工作层滚动把本轮加入工作层超出窗口的部分移入归档层。这套流程看起来步骤多但大部分是轻量操作真正花 token 的只有第 7 步。实测下来整体延迟增加在可接受范围内。6.2 不同场景的策略侧重不是所有场景都需要全套。我按场景给个侧重建议场景侧重机制原因短会话客服Context Editing会话短压缩和记忆收益低长会话助手Compaction 约束层历史长需要持续压缩跨会话个人助理Memory Tool核心价值在跨会话记忆代码分析 AgentCompaction 归档检索工具返回巨大必须压缩多轮任务编排全套约束多、历史长、需跨任务记忆6.3 成本与效果的平衡上下文管理本身也有成本。每次压缩、每次记忆检索都要花 token 和时间。所以要做收益判断如果会话预计很短比如 5 轮内结束别上全套简单截断就行。如果任务对准确性要求极高比如金融、医疗压缩要保守宁可多花 token。如果任务对延迟敏感比如实时对话压缩和检索要异步做别阻塞主流程。我的经验是先上约束层和基础 Compaction这两个投入产出比最高。Memory Tool 等到确实有跨会话需求时再加别一上来就搞复杂。7. 踩过的坑和实测有效的技巧7.1 坑一压缩把否定约束压没了这是我最惨的一次。用户说不要用红色不要用圆角压缩时模型觉得不要是冗余压成了配色和圆角待定。结果 Agent 后面用了红色圆角用户直接炸了。教训否定性约束不要、禁止、避免在压缩时必须原样保留不能改写。我后来在压缩 prompt 里明确加了规则所有否定性表述必须逐字保留。7.2 坑二记忆检索命中了过时信息用户半年前说我偏好简洁风格三个月前改成了我喜欢详细解释。记忆库里两条都在检索时命中了旧的那条Agent 又开始简洁了。教训记忆必须有时间戳和失效机制。同类信息新版本写入时旧版本要标记失效或降权。检索时优先返回最新的。7.3 坑三约束层和实际对话冲突约束层说输出 JSON但用户中途说这次用表格给我看。如果约束层不更新Agent 会继续输出 JSON用户觉得它不听人话。教训约束层要支持临时覆盖。可以给约束加一个作用域字段区分会话级和本轮级。本轮级的约束优先级更高用完即弃。7.4 实测有效的三个技巧技巧一给上下文加锚点。在每层内容前加一个简短标签比如[约束]、[最近对话]、[记忆]。模型对结构化标签的识别能力比纯文本强实测能减少张冠李戴。技巧二压缩后做一次自检。让模型回答压缩后的上下文是否还包含完成任务所需的所有约束如果回答否就回退。这个自检成本很低但能拦住大部分压缩事故。技巧三工作层保留最近 N 轮 关键轮。不是简单保留最近 N 轮而是最近 N 轮加上被标记为关键的历史轮次比如用户确认需求的轮次。这样既控制了长度又不丢关键节点。7.5 关于工具选型的一点看法市面上做上下文管理的工具和框架不少我的建议是别一上来就上重框架。先把约束层和基础 Compaction 用几十行代码实现出来跑通了再考虑引入专门的记忆工具。原因很简单上下文管理的核心是策略不是工具。策略想清楚了用什么工具都能实现策略没想清楚上再好的工具也是白搭。我见过团队花两周接入某个记忆框架结果发现核心问题约束没维护好根本没解决。如果你确实需要现成的记忆工具选型时重点看三点检索是否支持语义、是否支持时间衰减、是否支持结构化字段过滤。这三点决定了记忆能不能真正用起来。8. 写在最后的一点个人体会做 Agent 这几年我最大的感受是上下文管理是 Agent 工程里最不性感、但最决定成败的部分。模型能力大家都能买到工具调用大家都能接真正拉开差距的是你怎么组织喂给模型的那几千几万 token。标题说高手管理的是上下文我理解这句话的深层含义是高手把上下文当成一个需要持续经营的资源而不是一个被动堆积的容器。每一轮都在问自己这一步模型真正需要知道什么哪些可以压缩哪些该外置哪些必须原样保留这套思路不依赖特定模型也不依赖特定框架。你换成任何模型、任何框架这套分层策略都成立。这大概就是它值得花时间的原因。如果你现在正被第 20 轮失忆折磨我的建议是从约束层开始改。先把用户的核心诉求结构化地维护起来每轮注入你会发现很多失忆问题其实不是记忆问题而是重点没有被持续强调。这一步做完再考虑压缩和记忆工具路会顺很多。