上下文压缩
上下文压缩在对话或 Agent 任务接近窗口上限时,用更短的表示替换旧内容,让任务能继续执行。压缩一定会丢信息,本文说明几种常见方案、触发时机、必须保留的信息,以及如何检查压缩是否破坏了任务。
1. 面试核心
先记住这几条,再读正文- 常见方案有截断、滑动窗口、摘要、工具结果清理、外部存储加检索。它们在信息保留、成本和实现复杂度上不同,长任务通常组合使用。卡 · 压缩方案
- 压缩时必须保留的是任务目标、用户约束、已做出的决定、已修改的产物位置、未解决的错误和下一步;可以丢弃的是已被后续结果取代的中间输出和冗长的原始工具结果。卡 · 保留什么
- 触发方式有按 Token 阈值自动触发、按轮次触发和用户手动触发。阈值要给本轮输入和输出留出空间。卡 · 触发时机与合并
- 工具结果是长任务中增长最快的部分,优先清理已使用过的旧结果,保留调用记录和结论。卡 · 压缩方案
- 压缩后的事实以实际状态为准:恢复任务时重新检查文件、测试和外部系统,不把摘要中的“已完成”当成验收结果。
2. 压缩方案
| 方案 | 做法 | 优点 | 缺点 |
|---|---|---|---|
| 截断 | 删除最早的消息 | 简单、无额外调用 | 早期目标和约束会丢失 |
| 滑动窗口 | 只保留最近 N 轮 | 成本可控,近期信息完整 | 窗口外信息全部丢失 |
| 摘要 | 用模型把旧历史总结成一段文字 | 保留要点,长度可控 | 多一次模型调用;摘要可能遗漏或改写细节 |
| 窗口 + 摘要 | 旧内容摘要,最近 N 轮保留原文 | 兼顾远期要点和近期细节 | 需要设计摘要格式和合并方式 |
| 工具结果清理 | 把旧工具结果替换为占位或简短结论 | 收益大,不影响对话主线 | 之后需要时要重新调用工具 |
| 外部存储 + 检索 | 历史写入数据库或文件,按需检索 | 理论上不丢原文 | 依赖检索质量,增加延迟 |
摘要失败的典型表现是“细节被平均化”:用户最初说“不要改公共接口”,摘要写成“用户希望改动尽量小”,后续步骤就可能修改接口。因此摘要提示要指定必须逐字保留的内容类型。
3. 保留什么
压缩前先把信息分类:
- 必须保留:任务目标和验收条件;用户明确提出的约束和偏好;已经做出的关键决定及原因;已修改或生成的文件、数据位置;未解决的错误信息和失败尝试;当前进度和下一步。
- 可以摘要:探索过程中读过的代码和文档,只保留结论和位置;讨论过但被否定的方案,保留“已否定及原因”。
- 可以丢弃:已经被后续结果覆盖的中间输出、重复的工具结果、寒暄。
结构化摘要比自由文本更不容易遗漏:
## 目标
修复 /api/orders 分页在最后一页返回重复数据的问题。
## 约束
- 不修改响应字段(前端已上线)。
## 已完成
- 定位到 OrderRepository.page():offset 计算使用了 page 而非 page-1。
- 已修改 src/order/repo.ts:41,单测 order.repo.test.ts 通过。
## 未解决
- 集成测试 orders.e2e 超时,原因未确认。
## 下一步
- 本地启动数据库后重跑 e2e。
另一种做法是把关键信息写在上下文之外的进度文件或仓库指令中,压缩时不依赖模型记住它们。
4. 触发时机与合并
- 按 Token 阈值:当前上下文达到窗口的某个比例时触发,阈值要为下一轮的输入、工具结果和输出预留空间。
- 按轮次:每 N 轮把最早的一段压缩成摘要,长度增长更平稳。
- 按任务边界:一个子任务完成时压缩它的过程,只留结论,适合计划明确的长任务。
- 手动触发:用户在切换话题前要求压缩,并可以说明需要保留的重点。
多次压缩后会产生多份摘要。可以每次把旧摘要和新内容一起重写成一份(长度稳定,但多次重写会累积失真),也可以分段保留多份摘要(不重复改写早期内容,但总长度会继续增长)。常见折中是近期分段保留,远期合并,并始终保留原始的目标和约束段。
5. 常见问题
5.1 怎样验证压缩没有破坏任务
建立回归用例:在长对话中途插入约束,触发压缩后检查模型是否仍遵守。对 Agent 任务,比较开启和关闭压缩时的成功率,并抽查失败轨迹中压缩前后的信息差异。
5.2 摘要可以“还原”成原文吗
不能从摘要中还原丢掉的信息。需要还原时,压缩前把原始历史存入外部存储,摘要中保留引用,必要时按引用检索原文。
6 · 面试题卡片
6.1 对话超过上下文窗口时,有哪些处理方案?滑动窗口和动态摘要各有什么优缺点?
出现公司:虾皮、字节跳动、阿里淘天、百度、去哪儿旅行、OPPO
考察重点
- 截断、滑动窗口、摘要、工具结果清理和外部检索的取舍。
- 组合方案的设计。
相关内容:压缩方案。
参考回答
方案有:截断最早消息、滑动窗口保留最近 N 轮、用模型把旧历史生成摘要、清理旧工具结果、把历史写入外部存储再按需检索。
滑动窗口实现简单、没有额外调用,近期内容完整,但窗口外的目标和约束会全部丢失。动态摘要能保留远期要点,代价是多一次模型调用,并且摘要可能遗漏或改写细节。常用组合是:系统规则和任务目标固定保留,旧轮次压缩成结构化摘要,最近几轮保留原文,旧工具结果只留结论;原始历史存入外部存储以便检索。
6.2 压缩或摘要一定会丢失信息,如何让信息丢失最小化?
出现公司:字节跳动、拼多多、安软、腾讯
考察重点
- 必须保留、可摘要、可丢弃信息的划分。
- 结构化摘要、外部存储和压缩效果验证。
相关内容:保留什么、怎样验证压缩没有破坏任务。
参考回答
先分类:目标、验收条件、用户约束、关键决定、产物位置、未解决的错误和下一步必须保留;探索过程只留结论和位置;被后续结果覆盖的中间输出可以丢弃。
摘要用固定字段的结构化格式,要求约束和错误信息原文保留。关键信息同时写在上下文之外,例如进度文件、仓库指令或状态存储,压缩时不依赖模型记住它们。原始历史存档,摘要中留引用以便检索。最后用回归用例验证:在长对话中插入约束,压缩后检查是否仍被遵守,并比较开启压缩前后的任务成功率。
6.3 常见的压缩触发机制有哪些?旧摘要和新摘要应该合并还是分别保留?
出现公司:快手、美团、百度
考察重点
- 阈值、轮次、任务边界和手动触发。
- 多次压缩的失真累积与长度增长。
相关内容:触发时机与合并。
参考回答
触发方式有:上下文 Token 达到窗口某个比例时自动触发,并为下一轮输入和输出预留空间;每 N 轮压缩最早的一段;一个子任务完成时压缩它的过程;用户手动触发并指定保留重点。编码 Agent 一般在接近上限时自动压缩,也支持手动命令。
合并重写成一份摘要,长度稳定,但每次重写都可能改变早期内容,失真会累积;分段保留不改写旧摘要,信息更稳定,但总长度会继续增长。常见做法是近期分段、远期合并,并让最初的目标和约束作为固定段落不参与重写。
6.4 Tool Result 如何淘汰或压缩?
出现公司:字节跳动、4399
考察重点
- 工具结果在长任务中的增长方式。
- 结果截断、落盘、清理与保留调用记录。
相关内容:压缩方案、按需加载与渐进式披露。
参考回答
在返回时就控制大小:大结果截断或分页,完整内容写入文件,上下文只放摘要、行数和路径。日志类结果先在程序侧过滤,例如只保留错误级别和相关请求 ID。
在历史中,已经被模型使用过的旧结果替换为简短结论或占位符,保留工具名、参数和调用 ID,使调用记录完整;之后需要原文可以重新调用工具或读取落盘文件。最近几步的结果和包含未解决错误的结果保留原文。