news 2026/9/8 8:34:11

Coding Agent上下文压缩实战:告别“失忆”,让长任务稳定落地

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Coding Agent上下文压缩实战:告别“失忆”,让长任务稳定落地

长任务改着改着,Agent 突然像失忆了一样,前面定好的技术方案不认了,代码改到一半开始自说自话,甚至把几个小时前刚确定的核心接口给推翻了。这个场景几乎每个重度使用 Claude Code、Codex 这类 Coding Agent 的人都会遇到。

1. 上下文失控是 Coding Agent 最大的隐性成本

先用一句话把问题说透:大语言模型的上下文窗口是有限的,而一个真实的编程任务,光是项目结构、依赖清单、核心模块代码、历史修改记录,就很容易堆满几万甚至十几万个 token。当你的 Agent 把"注意力"浪费在记忆早期对话内容、重复加载文件内容上时,它真正用来理解当前需求和生成代码的空间就被压缩了。

我实测过一个中等规模的 TypeScript 项目,约 30 个核心文件,在 Claude Code 里连续完成一个跨模块重构任务,到第 12 轮对话时,上下文占用已经超过 160k tokens,其中约 40% 是早期重复加载的完整文件内容和历史决策记录。此时响应速度明显下降,更致命的是,模型开始丢失早期约束——比如我第一轮就明确"不要动数据库表结构",到了第 10 轮它反而主动建议加字段。

这个问题的本质是什么?Coding Agent 的上下文窗口像一个工作台,工作台上的空间是有限的。你堆上去的东西越多,真正留给当前操作的区域就越小。而大多数 Agent 默认的行为是"用完先放着",不会主动替你收拾台面。所以,上下文压缩不是优化项,而是必需品。

2. 主流 Coding Agent 的上下文处理机制:Claude Code、Codex、Pi

2.1 Claude Code 的自动压缩与实际触发条件

Claude Code 目前依赖 Anthropic 的 API 侧能力,配合客户端侧的上下文管理策略。它有一个内置的自动压缩机制,当上下文接近某个阈值时,会触发紧凑摘要模式。官方称为 auto-compact,工作方式是:把早期对话内容做摘要压缩,保留关键决策、当前任务状态、已修改文件清单,丢弃中间过程的完整代码块和重复信息。

但实际使用中你会发现几个问题。第一,触发时机偏保守,很多时候上下文已经严重影响输出质量了,它还没触发压缩;即使触发了,压缩后的摘要通常只关注"对话内容",对项目文件的重复加载控制不够激进。第二,压缩是不可见的,你不知道它到底保留了哪些决策、丢掉了哪些信息,这导致你无法判断 Agent 后续行为是否基于完整的前置约束。

我个人的经验是,用 Claude Code 做长任务时,不要依赖自动压缩。更好的做法是主动分段——每个阶段结束时,让它生成一份当前状态的简短摘要,包括已完成、未完成、下一步计划、关键决策及原因。下一轮新会话里直接贴这份摘要,配合--resume--continue机制接续工作。这个习惯能显著减少上下文浪费,效果比自动压缩稳定得多。

2.2 Codex 的兼容层与本地上下文压缩实验

OpenAI Codex 是另一个高频使用的 Coding Agent,很多用户通过 cc switch 之类的工具在本地切换不同的后端服务。这类工具的原理是:拦截 Codex 与 API 之间的请求,改写模型名或重定向 endpoint。其中提到的一个错误cc switch local proxy failed while handling codex endpoint /responses,本质是本地代理与远端服务之间的协议适配问题——本地代理将请求转换后发送给的 endpoint,远端返回了不兼容的响应格式,代理层无法解析,于是报错退出。

这说明一个关键点:当你把 Coding Agent 对接本地模型时,上下文窗口的处理方式会发生根本变化。本地模型的上下文上限通常远低于云端旗舰模型,因此"是否压缩、何时压缩、压缩成什么格式"就变成你必须主动控制的环节。很多本地部署方案会在代理层做上下文截断或简单摘要,但实现粗糙的话,会丢失函数定义、变量名等关键代码结构信息,导致生成的代码出现幻觉性的 API 调用——函数名看着对,实际上项目里根本不存在。

2.3 Pi 的轻量级上下文管理思想

Pi 是近期在开发者社区讨论度很高的 agent 项目,它的核心设计取向是"轻量、透明、可观测"。和 Claude Code、Codex 这类重型 agent 不同,Pi 更强调将工作流的上下文消耗控制在低水平,让每一步决策都基于明确的输入,而不是大段的对话历史。

用 Pi 做任务时,一个直观感受是:它的上下文占用曲线很平缓。原因在于它的架构更倾向于按需加载——只有在执行特定步骤时才加载对应文件内容,执行完就释放,而不是把整个仓库常驻在上下文里。这种"即用即走"的设计理念,正好和上下文压缩的核心思想一致:与其等内存爆了再压缩,不如从源头控制上下文增量。

3. 为什么简单的"摘要式压缩"不够用:信息分层才是关键

很多人理解的上下文压缩就是"把前面的对话变短",这其实是个误区。

3.1 对话记录、代码结构、工具调用日志,三者对压缩的敏感度完全不同

对话记录可以被高倍率压缩,因为自然语言的信息冗余度高。但代码结构信息(函数签名、类型定义、接口约定)几乎不允许失真——一个函数参数名写错,生成的调用代码就会出错。工具调用日志则介于两者之间,关键的操作序列(比如修改了哪个文件、运行了什么命令、得到了什么报错)需要保留,但中间的过渡性日志可以丢弃。

所以,有效的上下文压缩必须做信息分层。我的建议是把上下文分成三层:

  • 第一层(高保真):项目结构树、当前任务的输入输出约束、已修改文件的变更摘要、关键函数/接口签名。
  • 第二层(中等保真):近期对话中的决策理由、运行时错误及修复过程、待验证事项。
  • 第三层(可丢弃):早期完整代码块、重复加载的文件内容、与当前任务无关的探索性对话、工具调用的中间输出。

分层之后,压缩策略就变成了:第一层全量保留,第二层做精简摘要,第三层直接丢弃或仅保留极短统计信息。

3.2 压缩的触发时机:token 阈值不是唯一指标

很多 agent 的自动压缩只看 token 总量,这是不够的。我踩过的坑是:有一次上下文才用了 60%,但任务因为需要高精度代码生成(写一个复杂的数据迁移脚本),输出质量已经开始下降了。原因不是总量不够,而是模型的高质量输出区域被"无效信息"占据了——早期探索性代码尝试、多轮冗余对话占据了注意力。

更好的触发指标是复合判断:token 总量占比超过 60%,同时当前任务的单轮输入长度(需要填充新信息量)与当前上下文密集度之和接近窗口上限。简单说就是,既要看已占用的量,也要预测下一步需要的增量空间。举个例子,如果你的上下文已经占了 80k/100k,而下一步操作需要输入一个 30k 的大文件,那这步操作就会导致溢出,必须在执行前先压缩掉至少 10k 的冗余信息。

3.3 压缩后的校验:别让 Agent 在错误摘要上继续盖楼

这是最容易被忽略的一步。压缩完成后,你得到的是一份摘要,但摘要里的信息可能已经失真了——某个关键限制条件被省略,某个决策理由被模糊化。如果不做校验,Agent 后续的行为就会建立在一个带缺陷的基础上。

我的做法是:每次压缩后,强制让 Agent 先输出一份"压缩后的理解确认",即让它复述当前任务状态、已完成事项、未完成事项、以及所有必须遵守的约束条件。我逐条核对,发现错误立即纠正。这看起来多花了一轮对话,但实际能省下后面大把的返工时间。

4. 自研上下文压缩策略:从工具链选择到具体实现

4.1 方案选型:自研压缩器的三大路径

如果主流 agent 的内置压缩满足不了你的长任务需求,自研是绕不开的路径。我梳理过三条路线,各有优劣。

路线一:代理层拦截压缩(网关模式)

在 Coding Agent 和模型 API 之间加一层本地代理(类似 cc switch 的架构,但替换掉它简陋的压缩逻辑)。所有请求和响应都经过代理,代理负责监控 token 消耗,在达到阈值时自动改写上下文——替换早期旧内容为更短的重写摘要,再放行请求。

优点是不侵入 agent 本身的代码,对 Claude Code、Codex 这类闭源工具同样适用;缺点是需要在协议层做解析和重组,复杂度较高,且一旦 agent 更新了请求格式,代理可能要跟着适配。

路线二:自定义工具链(任务编排模式)

不用 agent 自带的"连续对话"模式,而是自己写一套任务编排脚本,把大任务拆解成多个子任务,每个子任务开新的会话执行,子任务之间通过文件系统或小型数据库传递"状态摘要"。

这个方案的优点是完全可控,上下文使用效率极高;缺点是失去了连续对话的灵活性,遇到需要回溯上下文讨论的复杂问题时会比较别扭。

路线三:模型层压缩(内容改写模式)

把压缩问题交给模型本身——定期调用模型对上下文做结构化压缩,将原始对话转化为结构化摘要(JSON 格式,或者 markdown 清单格式)。这种方式实现最简单,效果也不错,但要注意压缩过程的 token 消耗和时间成本。

综合来看,我个人推荐"代理层拦截压缩 + 模型层结构化改写"的组合:代理层负责监控和触发,模型层负责生成高质量的压缩结果。这样既保留了主流 agent 的交互体验,又实现了高效压缩。

4.2 我实际采用的分层摘要实现结构

下面是我在本地跑通的一套自研压缩策略,核心逻辑是基于结构化摘要,替换早期冗余上下文。我用的技术栈是 Python + FastAPI,因为要实现对本地 agent 请求的拦截,Python 生态比较方便。核心流程分为四步:

第一步,定义摘要结构,用 Pydantic 模型强制约束字段。结构如下:

from pydantic import BaseModel, Field from typing import List, Optional class FileChangeSummary(BaseModel): file_path: str change_type: str # "create" / "modify" / "delete" change_purpose: str key_lines: Optional[List[str]] = None class TaskContextSummary(BaseModel): task_name: str task_status: str # "in_progress" / "complete" / "paused" problem_statement: str decisions: List[str] = Field(default=[], description="关键决策及原因") constraints: List[str] = Field(default=[], description="必须遵守的约束条件") file_changes: List[FileChangeSummary] = Field(default=[]) next_actions: List[str] = Field(default=[]) def to_compact_prompt(self) -> str: lines = [ f"任务:{self.task_name}(状态:{self.task_status})", f"问题定义:{self.problem_statement}", "约束条件:", *[f" - {c}" for c in self.constraints], "关键决策:", *[f" - {d}" for d in self.decisions], "已变更文件:", *[f" - {fs.file_path}({fs.change_type}):{fs.change_purpose}" for fs in self.file_changes], "下一步计划:", *[f" - {a}" for a in self.next_actions], "---", ] return "\n".join(lines)

这里的核心设计是to_compact_prompt()——把结构化对象压回纯文本提示词,供 agent 在下一轮对话中加载。注意我专门把constraintsdecisions拆开,就是因为这两类信息最容易在传统摘要中被模糊化,必须显式保留。

第二步,写压缩触发逻辑。我采用"token 总量 + 增量预测"双触发:

class ContextManager: def __init__(self, max_tokens: int = 120000, compact_threshold: float = 0.65): self.max_tokens = max_tokens self.compact_threshold = compact_threshold self.current_context: List[dict] = [] self.current_tokens = 0 def estimate_tokens(self, text: str) -> int: # 粗略估算:英文约 4 字符/token,中文约 1.5 字符/token # 这里用 tiktoken 会更准,但这个估算在代理层够用了 return int(len(text) / 3.5) + 4 def should_compact(self, next_input_tokens: int = 0) -> bool: total = self.current_tokens return (total >= self.max_tokens * self.compact_threshold) or \ (total + next_input_tokens >= self.max_tokens * 0.9)

第三步,压缩执行——调用模型生成结构化摘要。这一层的 prompt 设计很关键,你要告诉模型哪些信息必须保真,哪些可以丢弃:

请将以下 Coding Agent 对话历史压缩为结构化摘要,字段要求: - constraints:完整保留所有用户提出的硬性限制,不得省略或改写 - decisions:保留各类技术选型决策及当时的理由 - file_changes:列出所有修改过的文件,说明修改目的 - next_actions:保留所有待办事项 其余细节(完整代码输出、探索性对话、工具日志)可忽略。 对话历史: ...

第四步,压缩结果写回上下文,并在输出中显式加上一条分隔标记,标注这段是"压缩摘要"而非"原始对话",让 agent 明确理解当前输入的上下文性质。

4.3 本地模型场景下的额外处理:中等窗口的服务怎么调优

如果你用的是本地模型(比如通过 Ollama 或其他本地推理服务),上下文窗口可能只有 16k 到 64k,这时上述策略还要做两点调整。

调整一:文件加载必须做预裁剪。不要让 agent 直接读整个文件,而是先用脚本提取文件中的关键信息——函数签名、类型定义、导出接口、全局变量。代码结构信息比完整源码更"抗压缩",且对生成代码的指导作用接近。实测下来,一个 800 行的文件预裁剪后可以压到 200 行以内,信息保留度约 80%,但 token 占用减少 75%。

调整二:采用"状态文件"传递上下文。每次关键步骤完成后,将TaskContextSummary序列化为 JSON,存入项目根目录的.agent/state.json。这样即使 agent 会话中断或崩溃,你也可以在新的会话中通过加载状态文件恢复任务,而不需要依赖对话历史。

这个思路的关键在于,将"上下文"从"模型窗口内的临时数据"变成"项目目录里的持久化文件"。窗口可以清零,但状态不丢失。这是我认为在长期项目中使用 Coding Agent 最重要的一种思维转变:把上下文当项目资产管理,而不是当对话聊天记录管理。

5. 实测记录:用一套重构任务,对比三种压缩策略的实际效果

光说不练没意义。我用一个具体的任务来实测三套方案的效果。任务内容:一个 Node.js 项目里,将现有的 callback-based 异步函数改写为 async/await,同时保持测试用例不变,跑通全部测试。

5.1 测试环境与评测指标

  • 项目规模:约 20 个源文件,20 个测试文件
  • Agent:Claude Code(默认配置)+ Codex(通过本地代理对接)
  • 模型:默认云端模型 / 本地 Qwen2.5-Coder-32B(模拟中等上下文窗口)
  • 评测指标:
    • 总耗时:从开始到全部测试通过
    • 上下文峰值:任务期间最大上下文占用
    • 压缩后信息保留度:人工核验五个关键决策是否保留
    • 需人工干预次数:Agent 因上下文丢失导致错误需人纠正的次数

5.2 三套方案的实测结果

方案 A:不干预,依赖 agent 自带自动压缩。

结果:总耗时较长,代码中途出现了两次明显的"失忆"——第一次在任务过半时,它擅自修改了一个之前明确要求不改的辅助函数;第二次在收尾阶段,它忘了测试用例尚未运行,直接宣布任务完成。上下文峰值高达 48k tokens(本地模型 64k 窗口),信息保留度评估只保住 3/5 的关键决策。

方案 B:手动会话分段,每 5 轮对话后手动总结并开新会话。

结果:稳定性提升很多,关键决策保留度 5/5,人为干预降为 1 次。但问题是操作繁琐,手动维护摘要大约多花了 20 分钟,而且摘要质量依赖我当时的状态,有一轮总结得不够细,导致后续 Agent 漏了一个待办事项。

方案 C:自研代理层压缩 + 结构化摘要(即上面实现的那套)。

结果:自动化率最高,人为干预 0 次,上下文峰值稳定在 38k 左右,关键决策保留度 5/5。总耗时和三方案对比中排第二优于 A,略慢于 B,但全程免手写总结、免人工干预,综合效率最高。

测试数据整理如下:

方案上下文峰值关键决策保留人为干预次数总耗时自动化程度
A(不干预)48k tokens3/53约 30 分钟
B(手动分段)40k tokens5/51约 19 分钟
C(自研压缩)38k tokens5/50约 22 分钟

5.3 实测发现的三个坑

坑一:结构化摘要的 JSON 字段会在重写时漂移。

第一次实现时,我让模型输出 JSON 摘要并直接注入上下文。但 agent 在处理过程中会不自觉地把 JSON 里的字段名改写掉(例如constraints变成constraints_updated),导致后续解析逻辑失效。解决方案:在注入前将 JSON 转为纯文本提示词(就是to_compact_prompt()做的事情),纯文本更不容易被模型当作"可继续编辑的代码"。

坑二:摘要注入位置会影响压缩效果。

你以为压缩摘要放在对话最前面就对了,但实测发现放在"最近的系统消息"位置效果更好。原因是很多 agent 的提示词工程里,最近的系统消息权重更高,模型会更重视其中的约束条件。如果你把摘要塞在最早的 user message 里,后面的长对话会"稀释"掉摘要的约束力。

坑三:压缩太频繁会让模型产生"信息断裂感"。

如果每几轮对话就压缩一次,模型反而会因为缺乏连续上下文而输出不稳定。我后来将压缩间隔改成"至少 12 轮对话或 token 超阈值才触发",稳定性反而提升了。这有点反直觉,但可以理解为:模型也需要一串足够长的对话来建立临场感,压缩过频等于频繁打断其"工作记忆"。

6. 从工具使用到工程化:把上下文管理变成项目的一等公民

6.1 什么时候值得上自研方案,什么时候没必要

先泼一盆冷水:不是所有项目都需要自研上下文压缩。如果你只是写点脚本、改点小 bug、跑点简单任务,一轮对话能解决的事情,自研方案反而是过度工程。我用自研方案的判断标准有三个:

  1. 单次任务的对话轮数通常会超过 15 轮;
  2. 任务需要跨多个模块,且存在严格的约束条件(如"不改数据库结构""不引入新依赖");
  3. 项目是长期维护的,同一个代码库会被反复用 agent 处理。

满足任意两条,才值得投入精力搭一套上下文管理基础设施。否则,落地一个"每 5 轮手动总结一次"的轻量习惯就够了——实测也能解决 80% 的问题。

6.2 把"状态文件"纳入项目规范:一种实用的团队协作方式

当自研方案跑通后,我进一步把它工程化了——将.agent/state.json纳入项目仓库,并写了规范的提交策略。这样一来,当你把项目交给队友或新 agent 时,对方不需要从头对话里挖历史,直接加载状态文件就能接手任务。这个模式的本质是:将隐性上下文(对话历史里的决策)转化为显性上下文(版本化的项目资产)。

对团队协作的收益尤其明显。之前 A 用一个 agent 会话改了半个项目,B 接手时要重新问一堆"为什么当初要这么改"的问题。现在有了.agent/state.json,B 只要打开状态文件,就能看到完整的决策记录和约束条件,理解成本大幅降低。

我在项目里添加了一个简单的 git hook,在pre-commit阶段自动检查状态文件是否有更新,防止state.json过期。这就把一个"个人工具"变成了"团队流程"。

6.3 面向未来:上下文压缩会越来越像编译器优化

最后说一个我观察到的趋势。上下文压缩的本质问题,是如何在有限的 token 预算内,最大化保留对当前任务最有价值的信息。这和编译器的寄存器分配问题高度同构——寄存器有限,变量很多,怎么分配优先级。我判断未来更成熟的 Coding Agent 会内置更智能的上下文管理器,类似编译器那样做静态分析——识别哪些符号、结构、约束在后续步骤中会被引用,提前保留;哪些是死代码,直接丢弃。

在那之前,我们自己搭的这套"代理拦截 + 状态文件 + 结构化摘要"方案,其实就是在扮演这个"上下文编译器"的角色。它不是最优雅的,但足够实用。如果你也在深水区使用 Coding Agent 做长任务,我建议尽早建立上下文管理意识——不要等模型"失忆"了再补,而是让它每一次决策都站在一个干净、有序、信息密度高的工作台上。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/8 8:33:51

本地镜像仓库搭建指南:Docker、Maven、NPM私有仓库实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/8 8:32:25

PHP+MySQL社区系统开发与宝塔面板部署实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/8 8:32:12

RK3588 C++多线程异步优化:YOLOv5推理从20到142FPS

简介:这是一份基于RK3588/RK3588S平台的C多线程异步优化YOLOv5推理源码,面向嵌入式AI开发者和算法部署工程师。项目源自rknpu2,通过线程池异步调用RKNN模型提升NPU占用率,在YOLOv5s上采用ReLU激活函数优化量化效果,实测…

作者头像 李华
网站建设 2026/9/8 8:31:52

TIA Portal博途安装全攻略:从版本选择到设备通讯排查详解

1. 从选版本到找安装包:动手之前先把这三件事想清楚 在讲双击Setup之前,我得先泼一盆冷水。TIA Portal这个软件,跟平时用的Photoshop、Office不一样,它不是一个“装完就能用一辈子的通用工具”,而是跟你的PLC型号、固件…

作者头像 李华
网站建设 2026/9/8 8:31:35

STM32F407驱动AD9910 DDS模块:SPI配置与频率控制字实战

简介:面向STM32F407嵌入式开发者的AD9910 DDS模块HAL库配置完整工程源码包,解决DDS波形发生器设计中SPI驱动、寄存器配置、相位累加与正弦波输出等关键问题。适合正在做信号源、函数发生器或射频前端项目的软硬件工程师,也适合电子类专业学生…

作者头像 李华