7.2 压缩与摘要:何时丢、丢什么


7.2 压缩与摘要:何时丢、丢什么

本节摘要:驱逐是零钱,压缩是大扫除。何时压缩:水位线两档——软水位(约 80%,示意)触发摘要式压缩,硬水位(约 95%,示意)时摘要已来不及,直接硬截断最旧输出保命;Claude Code 的 compaction 就是这类机制的公开实例(2.1 节,社区资料),且压缩前后留有 hook 挂载点(第 8.3 节)。丢什么保什么:保任务目标、未完成计划、红线约束、关键发现与文件路径;丢旧工具输出原文、失败尝试的过程细节、重复读取的文件内容。怎么丢:摘要不是"把对话缩短",而是用一次额外的模型调用把历史重构为结构化任务简报(已完成 / 未完成 / 关键发现 / 涉及文件 / 红线约束)——简报是给"下一个自己"的交接文档。最后诚实交代压缩的三个风险:有损、灾难性遗忘(丢掉的恰是"为什么不做 X")、二次压缩漂移,以及各自的缓解。

学习目标

阅读完本节,你应当能够:

  1. 设计软 / 硬两档水位线与各自的动作。
  2. 列出压缩的"保 / 丢"清单并解释红线约束为何必保。
  3. 写出结构化任务简报的模板与 compactor.py
  4. 说出压缩三风险与对应缓解。

一、何时压缩:水位线两档

触发(示意) 动作 理由
软水位 已用量 ≥ 80% 摘要式压缩:旧历史 → 任务简报,然后继续 此时还有余量承受一次压缩调用与它的不完美
硬水位 已用量 ≥ 95% 硬截断:最旧的大块输出直接砍到线索级 保命优先:先把本轮供料做完,摘要以后再补

水位线数值是示意起点,真实取值取决于任务类型与模型窗口;调法见第 9 章——用 trace 记录每次压缩发生时的步数与压缩后表现,评测集上比一版(9.2 节)。另外两处触发点值得知道:任务阶段切换时(如"探索结束、开始改代码")主动压缩,比水位被动触发更从容;子智能体归队时(Task 类工具返回)只让摘要回流,本身也是一种压缩(2.1 节 Claude Code 的 Task 工具即此思路,社区)。

二、丢什么、保什么

必保 为什么 可以丢 为什么
任务原文 失忆的根源 旧工具输出原文 盘上还有,可重读(引用化)
未完成计划 / TODO 下一步的根据 失败尝试的过程细节 结论(此路不通)够了
红线约束 丢了就会"转个圈又做 X" 重复读取的同一文件 一次引用化条目足够
关键发现(根因、位置) 重发现的成本极高 探索性对话原文 信息已沉淀进简报
关键文件路径清单 重找的成本高 中间版本的代码快照 最新版在盘上

⚠️ 灾难性遗忘的典型形态:任务说"不许动 legacy 目录,只加新模块"。20 步之后压缩,摘要只保留了"在加新模块",把红线丢了——模型在剩余步数里"热心地"重构了 legacy。缓解有二:红线条目进 pinned(7.1 节,驱逐与压缩都不碰);压缩后加一道校验——任务原文与 pinned 必须原样在场,否则压缩视为失败回滚。

三、怎么丢:简报不是缩写

错误的压缩观:把 30 轮对话"缩写"成一段话。正确的压缩观:给下一个自己写交接文档——视角从"保留对话"变成"保留工作状态":

[此前会话的摘要简报] 已完成: - 定位到崩溃根因:parser.py 第 47 行的空列表分支 - 修复已写入并通过 3 个相关用例 未完成: - 全量测试未跑(上次中断在第 4 步) - CHANGELOG 未更新 关键发现: - tests/ 下有两个用例依赖旧签名,改签名会连带它们 涉及文件:parser.py、tests/test_parser.py、CHANGELOG.md 红线约束:不改 public API;不动 legacy/ 目录

这样一份简报通常几百 token,替换掉的是几万 token 的历史——而且信息密度更高:对话里的犹豫、重复、试错全部消失,只剩状态。

四、compactor.py

# compactor.py —— 压缩与摘要(写法示意,以官方文档为准) WATERMARK_SOFT, WATERMARK_HARD = 0.80, 0.95 # 示意水位线 def need_compact(used: int, budget: int) -> str | None: """返回 None / 'soft' / 'hard'。""" r = used / budget return "hard" if r >= WATERMARK_HARD else "soft" if r >= WATERMARK_SOFT else None COMPACT_PROMPT = """你是会话压缩器。把给定对话历史压缩为任务简报,结构固定: 已完成:<逐条> 未完成:<逐条> 关键发现:<代码位置、根因、已排除的方向> 涉及文件:<路径清单> 红线约束:<任务中任何"不许做 / 必须做"的约定,逐字保留> 总长不超过 400 字。只输出简报。""" def compact(model_call, messages: list[dict], keep_last: int = 6) -> list[dict]: """软压缩:head 与最近 keep_last 轮原样保留,pinned 条目不参与压缩。""" head, old, recent = messages[:2], messages[2:-keep_last], messages[-keep_last:] if not old: return messages brief = model_call( [{"role": "system", "content": COMPACT_PROMPT}, {"role": "user", "content": str(old)[:20000]}], # 防单次超限 tools=None) pinned = [m for m in old if m.get("pinned")] # 红线等永不压缩 return (head + pinned + [{"role": "user", "content": f"[此前会话的摘要简报]\n{brief}"}] + recent)

五、压缩的三个风险

风险 形态 缓解
有损 摘要必然丢信息,丢的可能是关键的 简报结构化(逼摘要覆盖固定栏目);关键发现单列
灾难性遗忘 红线 / 否定性结论被"正能量化"掉 红线进 pinned + 压缩后校验任务原文在场
二次压缩漂移 简报再被压缩,摘要的摘要逐渐失真 简报不参与下一轮压缩素材,只与"上次简报 + 新历史"合并重生成

第三条展开一句:每次压缩都从原始任务 + 上一份简报 + 新增历史重新生成整份简报,而不是在旧简报上打补丁——代价是压缩调用略贵,换来的是简报不随次数累积漂移。

本节要点回顾

  1. 两档水位:80% 软压缩(摘要)、95% 硬截断(保命);阶段切换与子智能体归队是另两个天然触发点。
  2. 保丢清单:任务原文、计划、红线、关键发现、文件路径必保;旧输出原文、失败过程、重复读取可丢。
  3. 简报即交接文档:结构固定五栏目;红线条目要求"逐字保留"。
  4. 三风险三缓解:有损→结构化;遗忘→pinned+校验;漂移→每次从素材重新生成。

压缩解决"这个会话装得下",但任务结束、终端关掉,下个会话从零开始吗?下一节把窗口之外的世界也纳进来:分层记忆。


作者与出处
原作者: 灏天文库
整理: 灏天文库整理
本站整理收录,版权归原作者/开源协议所有;欢迎通过原文链接访问源仓库。
发布者: 作者: 灏天文库 转发
评论区 (0)
U