Coding Agent 为什么压缩上下文后还能继续工作?上下文模块设计拆解

发布时间:2026/7/18 3:33:10
Coding Agent 为什么压缩上下文后还能继续工作?上下文模块设计拆解 你好我是程序员无隅这是我的个人主页无隅的主页“技术永无止境希望我的内容能帮到你”让 Coding Agent 修改一个稍微复杂的项目时它通常要经历很多轮工具调用读取文件、搜索符号、修改代码、运行测试再根据报错继续调整。任务刚开始时Agent 往往反应很快。随着对话变长工具结果和历史消息不断累积输入 Token 越来越多最终可能逼近模型的上下文窗口。一个直观做法是把旧消息删掉或者生成一段摘要。但真正实现时会遇到更棘手的问题刚读过的文件还能不能记住已经激活的 Skill 会不会丢失用户最早给出的禁止项是否还有效工具调用和工具结果会不会被拆开压缩失败后会不会反复重试拖垮整个 Agent 循环所以生产级上下文管理解决的并不只是 Token 太多。上下文模块的本质是在有限窗口中完成信息分级、工作状态重建和失败兜底让 Agent 在压缩之后仍然知道目标、约束、当前进度和下一步。这篇文章从一个真实 Coding Agent 的上下文模块出发拆解它如何通过工具结果预算、全对话压缩、恢复附件和会话持久化让长任务可以继续运行。一、上下文不是聊天记录而是 Agent 的工作内存上下文窗口可以理解成模型生成下一次响应时能够参考的工作内存。它不只包含用户和助手的文字消息。系统提示词、工具定义、文件内容、命令输出、工具调用结果以及模型即将生成的输出都会占用窗口空间。Anthropic 的上下文窗口文档也明确指出工具结果、图片、文档和工具定义都计入上下文。这意味着 Coding Agent 的上下文增长速度通常比普通聊天更快。用户可能只输入了一句“帮我修复这个问题”但 Agent 为了完成任务读取了多个文件、执行了测试并拿到大量日志。真正占据上下文的往往不是这句用户需求而是执行过程中产生的工作材料。上下文过长会带来三个直接问题请求可能超过模型的硬窗口限制。每轮都要携带更多历史信息延迟和成本随之增加。重要约束被大量旧工具结果淹没模型的检索与判断会变得困难。所以上下文管理不能只在“快超限”时临时救火。它需要从每轮 Agent 循环开始就决定什么内容保留原文、什么内容外移、什么内容摘要以及压缩后怎样恢复。二、上下文模块在 Agent 主循环中的位置上下文管理不是一个独立运行的定时任务它嵌在每轮模型请求之前和之后。一次完整链路可以概括为解析当前模型的上下文窗口大小。注入环境信息、系统提醒和当前任务状态。检查原始对话是否接近窗口上限必要时执行 Auto-Compact。在发送模型请求前对工具结果应用预算策略生成本轮专用的 API 对话副本。模型响应后记录真实 Token 用量作为下一轮估算的锚点。工具执行后记录后续压缩可能需要恢复的文件和 Skill 状态。如果发生压缩将结构化压缩边界交给会话层持久化。这里有一个很重要的职责划分原始对话和真正发送给模型的对话副本不是同一个对象。工具结果预算不会直接修改原始历史而是基于原始对话构造一份api_conv。这样既能为当前请求减小输入也能保留完整历史供摘要、恢复和会话持久化使用。Auto-Compact 则不同。它处理的是原始对话把早期消息替换为摘要再拼回近期原文真正改变后续对话的基础状态。一个负责“本轮怎么发”一个负责“以后还保留什么”两层不能混在一起。三、压缩之前先把 Token 预算算明白上下文管理的所有阈值判断都依赖两个基础数据模型窗口有多大以及当前用了多少 Token。如果这两个数字不可靠压缩可能触发太早浪费可用空间也可能触发太晚在摘要请求发出前就已经撞上窗口限制。1. 上下文窗口使用四层回退不同模型、不同服务商甚至不同部署方式窗口大小都可能不同。因此这个上下文模块没有把窗口写死而是按优先级解析用户在配置中显式指定的窗口。运行时从服务商模型接口获取并缓存的窗口。内置的“模型名称到窗口大小”映射。无法识别时使用保守默认值。这条回退链的设计原则是显式配置永远优先自动发现尽量准确内置映射负责离线兜底默认值保证系统仍能运行。2. Token 估算使用“真实锚点 增量估算”只用字符数估算整段历史误差会随着对话增长不断累积。更稳妥的方式是每次模型响应后把 API 返回的真实用量记录为锚点。下一轮检查时只对锚点之后新增加的消息做字符估算。可以把当前 Token 近似理解为当前用量 上次 API 返回的真实用量 上次响应后新增消息的估算值如果系统刚启动或者对话刚经历压缩、旧锚点已经失效再退化为估算整段历史。下一次 API 响应到来后重新建立真实锚点。这样估算误差只集中在最新增量而不会污染整个会话。四、第一层防线给工具结果设置预算Coding Agent 最容易制造大上下文的地方是工具结果。一次文件读取可能返回几万字符一次测试可能输出大量日志一轮并发工具调用还可能把多个中等结果聚合到同一条消息中。这个上下文模块使用三步预算处理。1. 单个结果过大原文落盘只保留预览当单个工具结果超过设定上限时完整内容写入会话目录上下文中只保留前部预览、原始大小和文件位置。这不是直接删除信息而是把信息从昂贵的模型工作内存搬到便宜的外部存储。后续真的需要细节时Agent 可以重新读取文件。当前实现使用约 5 万字符作为单条结果阈值并保留约 2000 字符预览。这个数字是工程配置不是所有 Agent 都必须照抄的标准。2. 多个结果聚合过大优先外移最大的结果单条结果没有超限不代表整条消息安全。如果一轮产生多个工具结果它们的总量超过聚合预算系统会按大小排序优先把最大的结果外移直到消息重新回到预算以内。这种策略的好处是修改次数少每外移一个大结果都能回收更多空间同时保留更多小结果原文。3. 陈旧工具结果只保留短预览工具结果的价值会随任务推进而变化。Agent 早期读过的文件可能已经被修改早期测试日志也可能被新的测试结果替代。当前实现会保留近期若干轮的完整结果对更早且较长的结果进行裁剪。这一步体现了上下文管理的核心判断信息价值不只由大小决定也由新鲜度决定。五、为什么替换决策必须冻结工具结果预算还有一个容易被忽略的问题同一段旧历史会在每轮请求前被重新处理。如果系统每次都重新决定某个工具结果是否外移或者每次生成的预览文本存在细微差异那么发送给模型的历史前缀就会不断变化。这不仅会让行为难以复现还会影响 Prompt Cache。Anthropic 的 Prompt Cache 使用提示词前缀进行匹配。官方文档说明缓存覆盖tools → system → messages的有序前缀缓存点之前的内容发生变化会产生不同的前缀哈希使原缓存无法复用。因此上下文模块会为每个工具调用结果记录两类状态这个结果是否已经评估过。如果决定替换固定使用哪一段预览文本。一旦结果被判断为“保留原文”后续不再重新评估一旦被判断为“替换”后续始终使用完全相同的预览。替换记录还会追加写入会话目录。恢复会话时系统根据当前消息中的工具调用 ID 重建状态只恢复仍然存在的决策避免旧记录污染新会话。决策冻结表面上是在减少重复计算本质上是在稳定请求前缀和跨轮行为。六、第二层防线摘要旧前缀保留近期原文第一层能够控制工具结果但无法阻止普通消息持续增长。当当前用量接近窗口上限时系统会触发 Auto-Compact。它没有把整个历史粗暴压成一段摘要而是把对话切成两部分较早的前缀交给模型生成结构化摘要。最近一段消息保持原样直接拼回压缩后的历史。近期原文负责保存刚刚发生的代码修改、报错和工具交互摘要负责保存更早的任务目标、关键决策和完成状态。1. 阈值要给摘要本身留空间压缩不是免费的。生成摘要同样需要一次模型调用也需要输出空间。因此自动压缩阈值不能等于完整窗口而应该从窗口中扣除摘要输出预留和安全余量压缩阈值 上下文窗口 − 摘要输出预留 − 安全余量当前实现为自动压缩保留了更大的安全余量手动压缩则使用较小余量。原因是手动操作发生得更可控自动检查还要防止两轮之间突然出现大工具结果。2. 尾部保留不能拆断工具调用近期原文从历史尾部向前选择同时考虑 Token 数、消息数量和硬上限。但切分点不能随便落在消息之间。如果保留区从一条tool_result开始却把前面的tool_use放进摘要区模型会看到一个找不到来源的工具结果。所以系统会把边界向前对齐确保工具调用和对应结果成对保留。3. 回收空间太少时宁可不压缩如果可摘要的前缀很短生成摘要消耗的 Token 可能和回收空间差不多。当前实现会检查前缀规模。收益不足时直接跳过压缩避免为了“执行过压缩”而做一次没有实际价值的模型调用。七、压缩后不失忆重建 Agent 的工作现场摘要最大的风险不是语句变短而是工作状态被抽象掉。例如摘要可能记住“正在修改文件读取逻辑”却没有保留刚读过的具体代码可能记住“使用了某个 Skill”却丢掉 Skill 规定的执行步骤也可能把用户的原始禁止项改写成含义接近但不够精确的表达。所以压缩后的新历史不是只有摘要。它由四部分组成1. 结构化摘要摘要保存任务目标、关键技术点、涉及文件、错误与修复、当前工作和后续待办。摘要模型只负责整理历史不允许调用工具也不能继续执行原任务。这样能够避免压缩过程意外改变业务状态。2. 近期原文最近的消息不经过摘要直接拼回新历史。Agent 因此还能看到刚刚执行的工具调用、最近一次测试结果和当前推理链附近的上下文而不是完全依赖有损摘要。3. 恢复附件Agent 在运行过程中会记录最近读取的文件和已经激活的 Skill。压缩发生时这些快照按预算重新附加到摘要消息中。恢复附件还会列出当前可用工具的名称和简短描述提醒模型压缩后仍然具备哪些操作能力。文件快照不是永久真相。恢复信息会明确提示如果需要当前字节内容应该重新读取文件不能根据旧快照猜测。4. 关键用户约束原文系统会从早期用户消息中挑选任务目标、禁止项、格式要求、路径、命令和验收标记并尽量保留原文。这相当于为摘要增加一条确定性防线即使摘要模型遗漏了约束关键要求仍然可以通过附件回到新上下文。经过重建后新的对话结构是摘要消息 关键约束 文件与 Skill 恢复附件 近期原文这就是 Agent 压缩后还能继续工作的关键。它不是单纯“记住过去”而是恢复继续执行任务所需的最小工作现场。八、持久化、重试和熔断如何兜底上下文压缩依赖模型调用因此它本身也可能失败。如果摘要请求仍然过长系统会按对话轮次裁掉一部分最早内容再重新尝试如果连续多次失败则记录失败次数并打开熔断器停止自动压缩提示用户改用手动处理。当前实现把连续三次失败作为熔断条件。一次成功压缩后失败计数会被清零。这个机制防止 Agent 进入危险循环上下文超限。自动压缩失败。下一轮再次自动压缩。不断消耗请求却始终无法继续任务。除了运行时熔断压缩结果还会以结构化边界交给会话层。边界包含摘要和原样保留的尾部消息恢复会话时可以直接重建压缩后的状态而不必重新执行一次摘要。工具结果的替换决策也会独立持久化。这样恢复后的会话仍能保持与压缩前一致的预览文本和预算行为。压缩负责减少上下文持久化负责跨会话恢复熔断负责在压缩失败时保护主循环。九、这套设计仍然有哪些边界上下文管理本质上是在信息完整性、Token 成本和执行连续性之间做取舍不可能完全无损。1. 摘要仍然是有损压缩即使摘要 Prompt 规定了固定结构模型仍可能遗漏细节。恢复附件只能保护部分高价值状态不能保证还原完整对话。因此系统保留完整会话记录路径需要精确代码、错误原文或用户原话时应重新读取而不是相信摘要中的近似表达。2. 文件快照可能已经过时压缩附件中的文件内容代表上次读取时的状态。如果文件后来被工具或用户修改快照就不是当前版本。它适合帮助模型恢复思路不适合替代再次读取。3. 固定阈值需要结合模型和任务调整单条结果上限、聚合预算、摘要预留和尾部保留窗口都是工程参数。文件分析型任务、命令执行型任务和多 Agent 协作产生的上下文结构并不相同。更成熟的实现可以结合真实运行指标按模型窗口、工具类型和任务阶段动态调整。4. Prompt Cache 不能替代上下文压缩缓存能够降低重复前缀的处理成本和延迟但缓存中的 Token 仍然占用上下文窗口。Anthropic 官方文档也明确说明Prompt Cache 改变的是这些 Token 的处理方式和价格不会让它们从窗口中消失。所以决策冻结和上下文压缩解决的是两个问题前者保持前缀稳定后者真正回收窗口空间。十、总结Coding Agent 的上下文模块不是一个简单的messages数组也不是快超限时调用一次摘要函数。一套能够支撑长任务的上下文设计需要同时解决以下问题通过窗口回退链和真实用量锚点判断什么时候需要压缩。通过单条预算、聚合预算和陈旧裁剪控制工具结果增长。通过替换决策冻结保持跨轮行为和请求前缀稳定。通过“摘要旧前缀 保留近期原文”回收空间又保留当前细节。通过关键约束、文件快照、Skill 和工具列表重建压缩后的工作现场。通过压缩边界持久化、重试和熔断保证失败不会拖垮 Agent 主循环。所以上下文管理真正要回答的不是“删掉多少 Token”而是哪些信息必须留在模型眼前哪些信息可以移到外部需要压缩时如何保住任务状态压缩失败时又怎样安全退场。当这些边界被设计清楚Coding Agent 才能从一次性的代码问答工具变成能够持续执行长任务的工程系统。参考资料Anthropic《Context windows》https://platform.claude.com/docs/en/build-with-claude/context-windowsAnthropic《Prompt caching》https://platform.claude.com/docs/en/build-with-claude/prompt-caching