Agent_上下文压缩提示词

对于当前ClaudeCode与Gemini的压缩提示词的解析

Agent 上下文压缩设计笔记

参考文章:上下文压缩指令:ClaudeCode与Gemini的压缩提示词解析

上下文压缩解决什么问题

Agent 的上下文窗口不是无限的。随着多轮对话、工具调用、文件读取、报错日志和代码 diff 不断累积,模型会逐渐接近上下文上限。上下文压缩的目标不是简单地“变短”,而是在尽量少损失任务连续性的前提下,把历史对话整理成下一轮 Agent 可以继续工作的状态。

可以把上下文压缩理解为一次“工作交接”:

  • 保留用户真正想做什么
  • 保留项目约束、技术栈和关键决策
  • 保留已经读过、改过、创建过的文件状态
  • 保留报错、修复方案和仍未解决的问题
  • 丢弃重复、过时、冗长的工具输出
  • 让新的上下文窗口可以接着做,而不是重新探索

一个好的压缩系统应该回答三个问题:

  • 什么时候压缩:由 token 阈值、消息长度、工具输出规模等调度策略决定
  • 压缩什么:决定保留用户消息、系统约束、工具结果、文件状态还是计划
  • 如何压缩:使用 LLM 摘要、规则裁剪、检索重建,或组合方案

经典方案一:LLM 摘要压缩

Claude Code 和 Gemini CLI 都采用了一个重要思路:当上下文过长时,把历史消息交给一个模型,让模型输出结构化摘要。这个摘要会成为新上下文窗口中的核心记忆。

这类方案的优点是语义保留能力强,能够把分散在历史中的目标、约束、错误和计划重新组织起来。缺点是压缩结果依赖模型判断,如果提示词设计不好,可能丢失文件路径、代码片段、用户偏好或未完成任务。

Claude Code 风格:详细结构化摘要

Claude Code 的压缩提示词偏“完整交接文档”。它强调按时间顺序分析历史,并关注用户请求、技术细节、文件变更、错误修复和下一步。

适合保留的字段可以设计为:

字段作用
主要请求和意图保留用户最初目标和后续意图变化
关键技术概念记录技术栈、框架、架构模式、依赖
文件和代码部分记录读过、改过、创建过的文件,以及关键代码片段
错误和修复避免压缩后重复踩坑
问题解决区分已经解决的问题和仍在排查的问题
用户消息保留用户原始反馈,减少意图被摘要扭曲
待处理任务让 Agent 知道还有哪些明确任务没做
当前工作记录压缩发生前正在做什么,停在哪里
可选下一步只保留与当前任务直接相关的后续动作

这个方案的核心不是“总结得漂亮”,而是“让下一个上下文窗口能继续干活”。尤其是 coding agent 场景,文件路径、函数名、测试命令、失败日志和用户纠正非常关键。

可以抽象成下面的压缩模板:

请将历史对话压缩为一份可继续执行任务的工作交接摘要。

必须保留:
1. 用户的主要目标和明确请求
2. 项目技术栈、架构约束和关键决策
3. 已读取、修改、创建、删除的文件及其原因
4. 关键代码片段、函数签名、配置项
5. 已遇到的错误、报错信息、修复方式
6. 用户的重要反馈和偏好
7. 已完成事项、待处理事项、当前停顿位置
8. 下一步建议,但只能包含与当前任务直接相关的动作

必须删除:
1. 重复解释
2. 过时的工具输出
3. 对后续没有帮助的中间尝试
4. 无关寒暄

Gemini CLI 风格:状态快照

Gemini CLI 的压缩提示词更像是生成一个精简的 state_snapshot。它保留的字段更少,但密度更高。

典型字段包括:

字段作用
overall_goal用一句话描述用户的高层目标
key_knowledge记录必须记住的事实、约束、约定
file_system_state记录文件系统层面的创建、读取、修改、删除
recent_actions记录最近关键动作和结果
current_plan记录当前计划,以及哪些步骤已完成

这个方案适合做“运行状态快照”,尤其适合 Agent 在任务中断后恢复执行。它比 Claude Code 风格更短,但对细节保留的要求更严格。

可以抽象成:

<state_snapshot>
  <overall_goal>用户当前想完成的高层目标</overall_goal>
  <key_knowledge>关键事实、约束、偏好、技术决策</key_knowledge>
  <file_system_state>文件读取、修改、创建、删除状态</file_system_state>
  <recent_actions>最近执行过的重要动作及结果</recent_actions>
  <current_plan>当前计划、已完成步骤、未完成步骤</current_plan>
</state_snapshot>

经典方案二:工具消息裁剪

在真实 Agent 系统里,最占上下文的往往不是用户消息,也不是助手回复,而是工具调用结果。例如读取文件、搜索代码、运行测试、查看日志,都会产生大量文本。

因此,工具消息裁剪是非常实用的压缩策略:

  • 保留系统消息
  • 保留普通用户消息和助手消息
  • 删除过时的工具调用和工具结果
  • 只保留最近 N 轮工具调用
  • 对关键工具结果先摘要,再删除原始长输出

一个简单策略是:识别所有工具调用轮次,只保留最后 N 轮工具调用,其余工具输入和输出全部移除。

伪代码如下:

type MessageRole = 'system' | 'user' | 'assistant' | 'tool';

interface Message {
  role: MessageRole;
  content: string;
  tool_calls?: unknown[];
  tool_call_id?: string;
}

interface CompressionOptions {
  enabled: boolean;
  keepLastToolRounds: number;
}

function compressToolMessages(
  messages: Message[],
  options: CompressionOptions
): Message[] {
  if (!options.enabled) return messages;

  const toolRounds = identifyToolRounds(messages);
  const roundsToKeep = toolRounds.slice(-options.keepLastToolRounds);
  const keepIndexes = new Set(roundsToKeep.flatMap(round => round.indexes));

  return messages.filter((message, index) => {
    if (message.role === 'system') return true;
    if (keepIndexes.has(index)) return true;

    const isToolRelated =
      message.role === 'tool' ||
      (message.role === 'assistant' && Boolean(message.tool_calls));

    return !isToolRelated;
  });
}

这个方案的关键判断是:工具输出是不是还能帮助后续决策。如果已经被模型吸收成结论,或者只是中间探索结果,就可以删;如果是最新测试结果、关键报错、重要文件内容,则应该保留或先摘要。

经典方案三:中间移除、最旧移除与混合策略

除了让 LLM 总结,也可以用规则算法直接裁剪消息。这种方案更可控、成本更低,但语义理解能力弱一些。

常见三种裁剪方式:

策略做法适用场景
中间移除保留开头和结尾,删除中间消息开头有系统约束、结尾有当前任务
最旧移除从最早消息开始删除,保留最近消息长对话、近期上下文最重要
混合策略根据对话特征动态选择不同模型、不同任务混合使用

中间移除策略

中间移除适合这种结构:

开头:系统提示词、项目规则、用户目标
中间:大量工具调用、搜索过程、尝试过程
结尾:当前问题、最近代码、最新错误

它的优势是保留“任务框架”和“当前现场”。缺点是中间可能包含关键决策,如果没有先做摘要,容易丢失重要信息。

最旧移除策略

最旧移除更像传统滑动窗口。它默认最近消息最重要,适合长对话持续推进的场景。

它的优势是简单直接,能保持当前任务连续性。缺点是可能丢掉早期用户约束、架构决策或项目目标。

混合策略

混合策略可以根据以下特征选择:

  • 当前 token 数与目标 token 数的压缩比例
  • 消息总数
  • 最近几条消息占总 token 的比例
  • 是否包含长消息
  • 是否包含系统消息
  • 是否包含大量工具消息
  • 当前使用的模型和上下文窗口大小

一个可落地的选择规则:

条件推荐策略原因
轻度压缩且对话较短中间移除开头和结尾通常最重要
重度压缩且对话很长最旧移除最新上下文优先级更高
最近消息 token 占比很高中间移除需要保护最近现场
有系统消息或工具消息中间移除保留开头规则和结尾状态
不确定同时试两种,按评分选择用数据而不是拍脑袋

可以用一个简单评分函数评估裁剪结果:

效率分数 = token 减少率 * 0.6 + 消息保留率 * 0.4

如果系统更重视“压到目标 token 以下”,就提高 token 减少率权重;如果系统更重视“少丢上下文”,就提高消息保留率权重。

推荐的组合式压缩架构

单一压缩方式往往不够稳。更适合 Agent 的做法是组合:

原始历史消息
统计 token 和消息结构
判断是否达到压缩阈值
先裁剪过时工具消息
对关键历史做 LLM 结构化摘要
生成 state snapshot / handoff summary
重建新上下文窗口

推荐保留四层上下文:

层级内容存放方式
稳定规则层系统提示词、项目规则、安全约束常驻 prompt 或规则文件
工作记忆层当前目标、计划、待办、用户偏好结构化摘要
证据层最新工具结果、关键错误、关键代码片段最近 N 轮工具消息或摘要
外部知识层文档、代码库、历史记录RAG / 文件检索

压缩后新上下文可以这样组织:

系统提示词
项目规则
压缩说明开篇语
结构化摘要
最近几轮完整对话
最近关键工具结果
当前用户请求

其中“最近几轮完整对话”很重要。摘要可以保留大局,但最新几轮的原始表达通常包含微妙意图、语气、纠正和边界条件。

压缩提示词设计要点

设计压缩 prompt 时,重点不是让模型自由发挥,而是给它一个稳定的交接格式。

建议包含:

  • 明确角色:你是上下文压缩器,不是任务执行者
  • 明确目标:生成下一轮 Agent 可以继续工作的状态
  • 明确保留项:目标、约束、文件、代码、错误、计划、用户反馈
  • 明确删除项:重复内容、无关工具输出、寒暄、中间噪声
  • 明确输出格式:Markdown、XML、JSON 或自定义标签
  • 明确禁止行为:不要编造文件状态,不要添加未发生的决策,不要开始执行下一步

一个实用压缩 prompt:

你是 Agent 的上下文压缩器。

请把历史对话压缩成一份中文工作交接摘要。这个摘要将成为新上下文窗口继续执行任务的主要依据。

必须保留:
- 用户的主要目标、明确请求和重要反馈
- 技术栈、项目约束、架构决策、工具偏好
- 已读取、修改、创建、删除的文件路径
- 关键代码片段、函数名、配置项、命令
- 已遇到的错误、失败测试、修复过程
- 已完成任务、未完成任务、当前停顿位置
- 下一步建议,但只能包含与当前任务直接相关的动作

必须删除:
- 重复解释
- 无关寒暄
- 已无价值的工具输出
- 没有影响最终决策的中间尝试

不要编造历史中没有出现的信息。
不要执行任务,只输出压缩摘要。

工程落地建议

触发时机

可以在这些情况下触发压缩:

  • 当前 token 超过模型上下文窗口的 70% 到 85%
  • 单次工具输出超过阈值
  • 工具调用轮次超过阈值
  • 任务阶段完成,需要生成阶段性 handoff
  • 用户主动输入 /compact 或类似命令

压缩顺序

推荐顺序:

  1. 先清理明显无价值的工具输出
  2. 再保留最近 N 轮完整对话
  3. 对旧消息生成结构化摘要
  4. 将摘要、规则、最近消息重新组装为新上下文
  5. 记录压缩统计,如压缩前后 token、删除消息数、保留工具轮次

风险控制

上下文压缩最常见的失败不是“压缩率不够”,而是“关键事实丢失”。尤其要防止:

  • 丢失用户明确限制
  • 丢失文件路径
  • 丢失最新报错
  • 丢失已经尝试过但失败的方案
  • 把推测写成事实
  • 把已完成任务和待办任务混在一起

因此,压缩结果最好保留“状态标签”:

[已完成] 修复登录页表单校验
[失败尝试] 直接修改 schema 会破坏旧接口
[待确认] 是否保留旧版导出格式
[下一步] 运行 pnpm test 验证 auth 模块

我的总结

上下文压缩本质上是 Agent 的“记忆管理”和“工作交接系统”。Claude Code 风格更适合保留完整开发上下文,Gemini CLI 风格更适合生成高密度状态快照,工具消息裁剪则是最直接有效的 token 降噪方案。

如果要实现一个稳定的 Agent 压缩模块,我会优先选择这套组合:

最近对话完整保留
+ 过时工具消息裁剪
+ LLM 结构化摘要
+ 文件状态快照
+ 当前计划和待办列表
+ 压缩统计和可观测日志

最终目标不是让上下文最短,而是让 Agent 在压缩之后仍然知道:用户要什么、项目是什么、我做过什么、哪里失败过、现在停在哪里、下一步该怎么走。