资讯动态

Coding Agent上下文压缩实战:Claude Code、Codex与Pi策略对比及自研方案

发布时间:2026/9/10 4:22:11 来源:尧图企业网站定制
干这行最崩溃的瞬间不是模型能力不行而是它“明明刚才还懂突然就不懂了”。我印象最深的一次Claude Code 改一个跨十个文件的后端服务改到第四个小时它开始把 A 模块的接口名写到 B 模块的调用里我指出来它道歉然后继续犯。我看了下 token 计数上下文已经快 150k脑子里只剩一个念头这不是模型蠢是它真的“记不住前面说了什么”了。这个问题就是 Coding Agent 的上下文压缩问题。这篇文章把三个主流 agent 的上下文策略拉通看一遍Claude Code、OpenAI Codex以及最近社区讨论很多的 Pi。然后我会重点讲我自己从“被迫用官方压缩”到“自己写压缩策略”的过程包括滚动摘要文件、小模型摘要器、按需检索注入这三套方案。适合两类人看一是每天跟长会话搏斗的开发者想知道什么时候该 /compact、什么时候该开新会话二是想自己写 agent 工具的人我尽量把里面的坑都标出来。1. 为什么长会话一定会爆炸上下文预算的底层逻辑1.1 模型不是硬盘是“工作台”先说一个核心概念大模型的上下文窗口不要把它理解为图书馆而要理解成一张工作台。你说“把 200k token 都给它”它确实能读但真正重点关注的只是开头、结尾和最近操作的部分中间部分很容易被稀释。业界把这个现象叫“lost in the middle”就是在长文本里中间信息丢失率远高于开头和结尾。对 coding agent 来说这事尤其致命因为它不像写文章那样线性输出而是反复在“看代码 → 改代码 → 跑测试 → 看报错”之间循环。为什么 coding agent 特别容易把上下文吃满因为一次工具调用的代价比你想象的大。一个rg functionName返回 30 个匹配可能就是 800 token读一个 200 行的文件2000 token 没了再跑一次编译、报错输出 50 行又是 1000 token 进去。你看着自己没做多少事十轮交互下来光工具输出就能吃掉 2 万 token。再加上模型输出的每一次解释、每一个代码块长会话冲到 100k 以上是常态。所以压缩这件事的本质不是“把东西删掉”而是把“过程”换成“结论”。同样是“改登录接口”过程可能消耗 3000 token包括你尝试 5 种写法、前 4 种失败、最后一次成功但结论只有一句话“登录接口改用 JWT校验逻辑在auth.ts的verifyToken里”。上下文压缩干的就是这个提炼动作。1.2 三个工具的定位重过程、重任务、重记忆Claude Code、Codex、Pi 虽然都叫 coding agent但它们对上下文问题的态度完全不一样。Claude Code 是我用下来最“重过程连续”的工具。它默认在一个会话里持续干活官方做了 auto-compact上下文快爆的时候自动把历史对话压缩成一份摘要然后继续。它的哲学是尽量让你不用开新会话一个 session 从头做到尾。这对大型重构很舒服但也意味着压缩是“被迫发生”的你干预不了太多细节。Codex 是 OpenAI 的 CLI agent策略明显更偏“任务切成小块”。你可以用codex --continue续接会话但它更鼓励你把一个复杂目标拆成多个独立 step每个 step 单独跑。它的上下文里会优先塞代码库相关片段而不是完整对话历史。换句话说它默认“历史没那么重要当前任务和代码状态才重要”。Pi 是最近社区里讨论度很高的新 agent我体感它最聪明的地方是“不硬记”。它把很多上下文固化成 memory 文件和 skill 技能文件需要的时候按需读取而不是一股脑全塞进对话。你让 Pi 做一个编码任务它不会像某些 agent 一样先把整个项目读一遍而是先看索引、再按需抓取。这个思路对长周期项目特别有价值我们后面自研策略的部分很多灵感就是从它这儿来的。1.3 压缩的三种粒度token 级、会话级、知识库级把工具差异抽象一下上下文压缩实际发生在三个粒度上。第一是 token 级压缩就是模型在生成前把输入的某些部分做摘要替代比如 Claude Code 的 auto-compact 会把前面的用户消息和助手消息折叠成一条 summary。这个粒度对用户是不可见的但底层最复杂因为要保证摘要能引导模型继续干活。第二是会话级压缩工具不压缩对话本身而是鼓励你“换新会话”。开新会话等于把旧上下文清零但通过记忆文件把最关键的信息带过去。Codex 的 AGENTS.md 就是这个思路Claude Code 的 CLAUDE.md 也是。这个方案最稳定模型永远面对干净的上下文代价是“带过去哪些信息”需要人工或脚本把关。第三是知识库级压缩把跨会话的长期记忆从对话历史里独立出来变成项目里的文档、检索索引、skill 文件。Pi 的 memory skills 机制就是典型。这个粒度最接近“人的学习方式”——你不是每次写代码都重新读一遍整个项目的全部 git log你只会去看相关的模块和之前的决策记录。这三个粒度不是互斥的一个成熟的 agent 策略往往是三者组合。下面我逐个拆解主流工具的具体实现然后再谈自研方案。2. 压缩什么、保留什么三个工具的细节拆解2.1 Claude Code 的 auto-compact好用的自动挡但也有盲区Claude Code 的自动压缩逻辑简单说就是当上下文接近窗口上限时它会自己触发一次 compact把之前的对话转成一份结构化摘要。你会在界面上看到一段提示说之前的对话被压缩了但注意压缩后原来的消息不一定还能完整回溯系统提示和 CLAUDE.md 是保留的但普通对话内容会被折叠。这时候就出现了一个关键点CLAUDE.md 是“永不压缩”的显式记忆。你把项目的架构约定、模块说明、TODO 写进去它在每次请求里都固定占预算但它不会被 compact 掉。所以我的经验是重要的事情一定尽早让 agent 写进 CLAUDE.md 或项目的其他 markdown 文件而不是指望它在对话里记住。我在实战里踩过一个坑有一次做数据库迁移agent 之前已经确认“旧表users的数据要迁到新表accounts并且accounts.email加了唯一索引”这个决策非常重要。但因为没写进文件auto-compact 之后agent 继续跑的时候居然又用users表去查数据导致后面报了一堆错。后来我养成了习惯涉及数据模型、接口定义、重要约束的结论必须让 agent 在执行后 30 秒内同步到一个PROGRESS.md文件里压缩就再也影响不到它了。另外Claude Code 有/compact手动压缩命令。我建议在自动压缩之前主动触发一次因为自动触发时往往是模型已经开始犯迷糊的时刻而手动压缩你可以选一个“任务里程碑”之后进行这时候压缩出来的摘要质量更高。触发完之后我会立刻发一条消息让它“基于 PROGRESS.md 复述当前完成状态并列出下一步的三条候选动作”。这一步是为了验证压缩摘要是否正常工作宁可多花一点 token也别带着失忆的 agent 继续写代码。2.2 Codex 的会话复用与 AGENTS.md把历史让位给代码状态Codex 的策略跟 Claude Code 很不一样。它默认的语境构建不全是“聊天记录堆叠”而是更偏“项目感知”。你用codex跑一个新任务它会自动找仓库里的 AGENTS.md、README、代码结构等信息按需组装上下文。也就是说它不太依赖你之前跟它聊了什么而更依赖当前代码库的状态。这个设计的优势在于你开新会话的成本很低因为前一个会话的“记忆”已经被代码本身和 AGENTS.md 接住了。但问题也在这如果 AGENTS.md 不维护或者代码状态混乱Codex 每次开新会话都像失忆。我见过不少人抱怨 Codex 不如 Claude Code 聪明其实很多时候是因为没有用好 AGENTS.md它每次都在“重新认识项目”。Codex 的长任务我更推荐“小步快跑”模式。把一个大需求拆成 5 个小需求每个小需求单独跑一个 codex 会话跑完就把改动 commit 掉。这样每个会话的上下文都控制得很小压根不需要压缩。它的--continue续接会话我也用过但只适配那种“上一个会话因为网络中断或临时有事打断”的场景日常开发不要依赖续接因为续着续着上下文又膨胀了。还有一个关于 token 的教训给 Codex 的任务描述不要写“请仔细分析所有可能的边界情况”这种开放式指令它会真的把整个代码库翻一遍然后给你一份很长的分析报告上下文瞬间爆掉。正确写法是“只处理 xxx忽略其他无关文件。输出只给最终代码和测试命令”。这相当于在做“输入侧的上下文控制”。2.3 Pi 的记忆文件和技能面向长期项目的上下文管理Pi 的设计理念往大了说是把“上下文”从“对话历史”重构为“持久化知识”。它的 memory 文件负责记录项目状态、偏好和决策skill 文件则把一套可复用的操作流程固化下来。你在对话里不需要反复解释“我们项目用 pnpm 不是 npm”、“错误日志看 stderr 不是 stdout”这些直接写在 memory 里Pi 按需读取。我试用 Pi 做一个小工具的时候最深的感受是它不会像某些 agent 一样开局就把整个项目读一遍。它先看索引结构再看需要的关键文件。这种“按需加载”看着保守实际在长会话里非常稳上下文始终处于低位模型注意力也集中。不过 Pi 的代价是把比较多的工作前置了。你要先花时间写 memory、写 skill如果项目只改一两个文件这个前置成本反而比直接开 Claude Code 高。所以我现在对 Pi 的定位是“长期项目管家”而不是“一次性任务执行器”。它适合那种你每周都要往同一个代码库里加功能的场景跑完一次就把经验沉淀进 skill下一次它直接调用越用越顺手。2.4 三个工具的核心差异对照工具压缩方式记忆载体最适合场景最大风险Claude Codeauto-compact 手动 /compactCLAUDE.md、PROGRESS.md大型重构、跨多文件的连续改动压缩后丢失关键决策细节Codex开新会话 任务拆分AGENTS.md、git 状态需求清晰、可拆分的独立任务强制维护 AGENTS.md 成本高Pimemory skill 按需读取memory 文件、skill 文件长期维护、重复性较高的项目前期记忆构建成本高3. 我自己搭的上下文压缩管线从摘要注入到检索3.1 先想清楚目标函数什么信息值得跨会话保留自己写压缩策略第一步不是写代码而是想清楚“我要保留的到底是什么”。我把跨会话信息分成四类。第一类项目目标和当前任务。就是“我们在做一个订单系统当前在实现退款流程”。这类信息一旦丢了agent 就会开始做“我觉得还应该做一个会员充值”跑偏是小事浪费上下文是大事。第二类已完成和进行中的模块。记录“订单服务已重构、支付模块改了一半、优惠券模块还没动”。这个是好几个工具都做得不太好的地方它们往往能记住“刚才聊过支付模块”但说不清支付模块到底改到哪一步。第三类接口和数据结构变更。数据库 schema 改了哪个字段、API 从POST /order改成POST /orders这种信息如果在压缩时丢了后续代码基本必错。我见过太多次 agent 压缩完重新读代码用旧字段名访问新结构。第四类待办和已知踩坑。“下一步要清理废弃的utils目录”、“错误日志里这个E401是测试环境才会报不用管”。这类信息是压缩最容易丢的因为它藏在一堆原始报错输出里摘要模型往往觉得“报错不重要”就给滤掉了。不保留的东西也很明确工具执行的原始输出、来回试错的提示、模型的客套话和解释。这些信息只在当轮有用跨会话保存纯属浪费。3.2 策略 A滚动摘要文件最简单的“人肉压缩”这个策略的核心就一句话项目里放一个PROGRESS.md每次任务到里程碑时让 agent 更新它。这相当于把压缩动作从“被迫突然做”变成“持续小额做”。我用的模板大概是这样的# 项目状态 ## 当前目标 - 实现订单超时自动关闭 ## 已完成 - 订单表增加 status 和 expire_at 字段 - 创建定时任务扫描超时订单 ## 进行中 - 支付回调接口联调中卡在异步签名验证 ## 待办 - 清理 OrderRepository 中废弃的 findByUserId - 补订单关闭后的通知 ## 踩坑记录 - 测试环境数据库时区少 8 小时比较时间不要用本地时间 - 退款接口需要幂等键否则重复回调会扣两次让 agent 更新它我会在任务描述末尾加这么一段话“每完成一个子任务用不超过 20 行更新项目根目录的 PROGRESS.md只记录结论不记录过程。禁止删除原有历史段落。”你也可以把这句放到 CLAUDE.md 或 AGENTS.md 里让它永远生效。这个方案为什么有效因为摘要变成了“持续的小额提交”而不是一次性的“大爆炸压缩”。大摘要容易丢细节小摘要的损失率低得多。而且这个文件是人类可读的你随时可以打开看一眼确认 agent 记录的结论是对的。省下的 token 也是实打实的每次开新会话只需要把 PROGRESS.md 的内容作为初始上下文而不用去回忆之前聊了什么。3.3 策略 B小模型摘要器把压缩做成自动化流水线光靠手写 PROGRESS.md人多多少少会懒。如果你的任务真的很长、很频繁可以考虑写一个小模型摘要器。思路是在会话边界把完整的 transcript 喂给一个小模型让它生成结构化摘要然后把摘要作为下一个会话的初始上下文。小模型选便宜的就行压缩不需要最强的推理能力关键是“能把历史去重、归纳成要点”。我自己的一个简化脚本长这样python 逻辑大概可以用 LangChain 拼也可以直接用命令行组织 prompt 然后调 APIimport json import subprocess # 假设你把历史对话导出成了 transcript.json,格式为 [{role, content}, ...] with open(transcript.json, r, encodingutf-8) as f: messages json.load(f) conversation_text \n.join( f{m[role]}: {m[content]} for m in messages ) prompt f你是工程师的代码审查助手。请把下面的对话压缩为 1. 当前任务目标1 句 2. 已完成改动列表最多 10 条 3. 进行中事项列表最多 5 条 4. 关键文件与接口变更列表 5. 待办与踩坑列表 要求只写结论不写中间尝试过程不要超过 800 字。 对话如下 {conversation_text} # 这里换成你自己的小模型调用方式 result subprocess.run( [llm, -m, gpt-4o-mini, prompt], capture_outputTrue, textTrue, checkTrue ) summary result.stdout.strip() with open(summary.md, w, encodingutf-8) as f: f.write(summary)这个方案的关键点在于 prompt 里的句式“只写结论不写中间尝试过程”“不要超过 800 字”。如果你不给这个限制小模型会把整个对话再复述一遍压缩了个寂寞。用下来有个明显的坑压缩器要给主模型“行动指令”而不是单纯“历史回顾”。我一开始让摘要器输出“用户问了 xxx助手回答了 xxx”下一轮 agent 拿到这个摘要基本还是懵的。后来改成输出“接下来应该继续做 xxx”模型就很容易接上状态。原因也好理解压缩摘要本质上是对未来行动的约束不是对过去对谈的存档。3.4 策略 C按需检索注入不做全量压缩滚动摘要和小模型压缩本质上还是“把历史对话压缩后塞回上下文”。但更彻底的方案是干脆不塞历史只把与当前任务相关的片段检索出来注入到新的 prompt 里。这个思路来自 Pi 的启发上下文应该按需加载而不是全量进来。最简单的实现方式不需要向量库一个项目小的时候rg就够用了。比如当前要改支付回调你只需要把PROGRESS.md里关于支付的部分、支付模块的入口文件、最近的 git diff 拿过来拼成一个精简的上下文喂给新的 agent 会话就行。我给你一个非常轻的命令行组合# 找支付相关文件 rg -l payment|refund|callback src/ | head -20 # 找最近一次关于支付的决策记录 rg -n 支付|退款|幂等 PROGRESS.md # 看当前 git 变更里跟支付相关的 git diff --stat -- src/*payment* src/*refund*把这些输出整理到一个context.md里再让 agent 基于它干活。整个上下文体量可能只有 5k token相比拖着 100k 历史跑胜出太多。如果你项目真的很大几千个文件那我建议再往上走一步给历史决策记录和代码注释做 embedding存到本地向量库里每次任务开始时用任务描述做一次相似度检索把 Top 10 相关片段注入 prompt。这个方案工程化程度高但我必须泼一盆冷水别为了炫技写一条 RAG 管线。除非你是天天长时间跑 agent、项目大到几百个文件、并且对 token 成本敏感否则rg PROGRESS.md的效果足够好而且可调试性强得多。等你真的到瓶颈了再上向量库也不迟。4. 翻车现场压缩后失忆的典型症状与排查4.1 症状一改了 A 忘了 B接口名前后不一致这个最常见也最隐蔽。agent 在压缩前确认了“把getUserById重命名为fetchUserById”压缩后继续跑代码里偶尔冒出一个getUserById。它自己看代码时看到的是旧名字于是“新代码里用旧名字”的 bug 就诞生了。这种问题不是 agent 笨而是摘要丢失了“变量重命名”这类低层级但高影响的决策。排查思路是先检查 PROGRESS.md 或 CLAUDE.md 里有没有记录重命名如果没有就是压缩摘要丢了。对策是养成把“所有符号变更”写成一条条短句的习惯而不是等压缩发生。4.2 症状二反复问已经决定过的问题比如你之前已经跟 agent 确认过“数据库用 PostgreSQL不用 MySQL”压缩后它又问你“要不要迁移到 MySQL 来规避某个问题”。这种失忆是最容易发现的因为没有上下文连续性。原因通常是摘要模型把“你做的决定”和“你随口提到的选项”混在一起了。对策是在摘要模板里单独列一项“已确认决策不可推翻”并且在对话里每做一次重大决策就说一句“请把这条写入 PROGRESS.md”。相信我多花这几秒钟能省下后面一小时。4.3 症状三摘要文件被模型改坏了自研压缩策略有个独特的翻车点让模型更新 PROGRESS.md它可能会为了“整洁”而删掉重要的老记录。有一次我让 agent 压缩 PROGRESS.md它把我记录的一条“测试环境生产环境字段名不一致”的坑删了理由是“与当前目标无关”。结果下一次会话踩了同样的坑。对策很简单PROGRESS.md 只追加不删改历史段落保留在旧版本里或者压缩 PROGRESS.md 时必须是“提取浓缩”而不是“删除”。给模型的指令里明确写“你只能合并重复内容不能删除包含具体字段名、表名、命令的记录”。4.4 常见问题速查表症状可能原因对策压缩后接口名不一致摘要丢失符号变更记录符号变更写入 CLAUDE.md/PROGRESS.md重复询问同一问题摘要把决策和选项混在一起专门维护“已确认决策”列表代码风格退化摘要丢失项目风格约定把风格约定写进 CLAUDE.md摘要文件被删改agent 为了整洁随意删记录PROGRESS.md 只追加压缩指令明确“不删除含具体信息的记录”压缩后第一次输出就跑偏没有做状态交接检查压缩后第一条消息让 agent 复述当前状态并对照文件校准工具状态丢了比如测试服务没启动摘要不记录运行环境运行环境信息写进 PROGRESS.md 或启动脚本4.5 一个值得养成的检查习惯压缩之后别急着继续干活。先花两分钟做“状态交接检查”让 agent 把当前项目状态、正在处理的任务、下一步计划各用三句话说清楚然后你拿它说的话跟 PROGRESS.md 对照不一致的地方立刻改。我这里有句话一直贴在自己的优化指令里“每当你从压缩摘要恢复或进入新会话时先用cat PROGRESS.md并复述当前任务状态不要直接开始写代码。”这句话看起来浪费 token但实测下来能把压缩后的无效迭代减少一半以上。因为大部分压缩失忆其实不会立刻爆炸而是在你毫无防备的时候用错误假设写出一段看似正确的代码。5. 实战选择什么场景用什么策略5.1 我自己的策略选择表给你一个可以直接抄作业的参考。假设今天是周五你面前有五种任务我建议怎么配上下文策略任务类型建议工具上下文策略一次改 1~2 个文件的 bug 修复Codex开新会话不需要压缩写清 AGENTS.md 中相关模块即可跨 5 个以上文件的功能开发Claude Code用 auto-compact同时维护 PROGRESS.md持续一周以上的大型重构Claude Code Pi 配合Claude Code 负责执行Pi 维护长期记忆每次会话结束更新 memory重复性较高的模块开发Pi把流程固化成 skill靠记忆文件跨会话复用自研 agent 项目自研压缩策略PROGRESS.md 小模型摘要最终按需检索5.2 几个关键判断原则第一个原则不要迷信自动压缩。auto-compact 确实是好东西但它只是兜底方案不应该是你唯一的记忆手段。真正可靠的是把“结论”沉淀到文件里让每次新会话都能读到。第二个原则压缩的代价不是免费的。它消耗 token、消耗时间、还可能丢信息。如果你的任务一个小时能做完压根就不需要压缩直接跑完一个会话就行。只有在会话即将超过窗口、或者你要跨天继续任务时才值得做压缩。第三个原则自研要克制。上下文压缩是个容易让人上头的方向你会想做向量检索、做自动分层摘要、做工具调用历史回放……但现实是 80% 的团队和开发者用“PROGRESS.md 手动 /compact”就能解决 90% 的问题。我自己也是在踩了很多坑之后才明白压缩策略的核心目标是“让 agent 更稳定”而不是“把工具链搭得更大”。你写的那条检索管线如果三个月没人调优大概率会在某次依赖升级后碎掉。最后说一个小技巧每次会话结束时不管是 Claude Code 还是 Codex都让 agent 跑一次“收尾三件事”——更新 PROGRESS.md、提交代码、清理临时文件。这三件事做完你随时可以开新会话继续彻底甩开上下文爆炸的焦虑。这个习惯比任何自动压缩都可靠。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价