<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>潘锦的空间 &#187; Gemini CLI</title>
	<atom:link href="https://www.phppan.com/tag/gemini-cli/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.phppan.com</link>
	<description>SaaS SaaS架构 团队管理 技术管理 技术架构 PHP 内核 扩展 项目管理</description>
	<lastBuildDate>Sat, 25 Apr 2026 00:56:17 +0000</lastBuildDate>
	<language>zh-CN</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=3.9.40</generator>
	<item>
		<title>Agent 核心策略：Manus、Gemini CLI 和 Claude Code 的上下文压缩策略和细节</title>
		<link>https://www.phppan.com/2025/09/agent-core-strategy-context-compression-strategy-and-details-for-manus-gemini-cli-and-claude-code/</link>
		<comments>https://www.phppan.com/2025/09/agent-core-strategy-context-compression-strategy-and-details-for-manus-gemini-cli-and-claude-code/#comments</comments>
		<pubDate>Sat, 06 Sep 2025 14:31:40 +0000</pubDate>
		<dc:creator><![CDATA[admin]]></dc:creator>
				<category><![CDATA[架构和远方]]></category>
		<category><![CDATA[AIAgent]]></category>
		<category><![CDATA[Claude Code]]></category>
		<category><![CDATA[Gemini CLI]]></category>
		<category><![CDATA[Manus]]></category>

		<guid isPermaLink="false">https://www.phppan.com/?p=2415</guid>
		<description><![CDATA[做 AI Agent 开发的都需要考虑上下文爆炸的问题，不仅仅是成本问题，还有性能问题。 许多团队选择了压缩策 [&#8230;]]]></description>
				<content:encoded><![CDATA[<section id="nice" style="color: #000000;" data-tool="mdnice编辑器" data-website="https://www.mdnice.com">
<p data-tool="mdnice编辑器">做 AI Agent 开发的都需要考虑上下文爆炸的问题，不仅仅是成本问题，还有性能问题。</p>
<p data-tool="mdnice编辑器">许多团队选择了压缩策略。但过度激进的压缩不可避免地导致信息丢失。</p>
<p data-tool="mdnice编辑器">这背后的根本矛盾在于：<strong style="color: #0e88eb;">Agent 需要基于完整的历史状态来决策下一步行动，但我们无法预知当前的某个观察细节是否会在未来的某个关键时刻变得至关重要。</strong></p>
<p data-tool="mdnice编辑器">前面一篇文章讲了整个上下文的管理策略，今天着重聊一下上下文管理的压缩策略和细节。</p>
<p data-tool="mdnice编辑器">下面根据 Manus、Gemini CLI 和 Claude Code 这三个项目的源码来聊一下上下文的压缩。</p>
<p data-tool="mdnice编辑器">三个产品，有三个不同的选择，或者说设计哲学。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">Manus 的选择是：永不丢失。</strong> 他们认为，从逻辑角度看，任何不可逆的压缩都带有风险。所以他们选择了一条看似&#8221;笨拙&#8221;但实际上很聪明的路：把文件系统当作&#8221;终极上下文&#8221;。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">Claude Code 的选择是：极限压榨。</strong> 92% 的压缩触发阈值，这个数字相当激进。他们想要榨干上下文窗口的每一个 token，直到最后一刻才开始压缩。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">Gemini CLI 的选择是：稳健保守。</strong> 70% 就开始压缩，宁可频繁一点，也要保证系统的稳定性。</p>
<p data-tool="mdnice编辑器">这三种选择没有对错，只是适用场景不同。接下来我们逐个分析。</p>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">1. Manus</span></h1>
<p data-tool="mdnice编辑器">Manus 的压缩最让我印象深刻的是其在可恢复性上的策略。</p>
<p data-tool="mdnice编辑器">Manus 团队认为：任何不可逆的压缩都带有风险。你永远不知道现在丢掉的信息是不是未来解决问题的关键。</p>
<p data-tool="mdnice编辑器">他们选择了不做真正的删除，而是将其外部化存储。</p>
<p data-tool="mdnice编辑器">传统做法是把所有观察结果都塞进上下文里。比如让 Agent 读一个网页，它会把整个 HTML 内容都存下来，可能就是上万个 token。Manus 不这么干。</p>
<p data-tool="mdnice编辑器">当 Agent 访问网页时，Manus 只在上下文中保留 URL 和简短的描述，完整的网页内容并不保存。需要重新查看网页内容时，通过保留的 URL 重新获取即可。这就像是你在笔记本上记录&#8221;参见第 23 页的图表&#8221;，而不是把整个图表重新画一遍。</p>
<p data-tool="mdnice编辑器">文档处理也是同样的逻辑。一个 100 页的 PDF 文档，Manus 不会把全部内容放进上下文，而是只记录文档路径、页数、最后访问的位置等元信息。当 Agent 需要查看具体内容时，再通过文件路径读取相应的页面。</p>
<p data-tool="mdnice编辑器">Manus 把文件系统视为&#8221;终极上下文&#8221;——一个容量几乎无限、天然持久化、Agent 可以直接操作的外部记忆系统。</p>
<p data-tool="mdnice编辑器">文件系统的层级结构天然适合组织信息。Agent 可以创建不同的目录来分类存储不同类型的信息：项目背景放一个文件夹，技术细节放另一个文件夹，错误日志单独存放。需要时按图索骥，而不是在一个巨大的上下文中大海捞针。</p>
<p data-tool="mdnice编辑器">这不是简单的存储。Manus 训练模型学会主动使用文件系统来管理自己的「记忆」。当发现重要信息时，Agent 会主动将其写入特定的文件中，而不是试图把所有东西都记在上下文里。就像一个经验丰富的研究员，知道什么该记在脑子里，什么该写在笔记本上，什么该归档保存。</p>
<p data-tool="mdnice编辑器">在 Manus 团队对外的文章开头，指出了为什么必须要有压缩策略：</p>
<p data-tool="mdnice编辑器">第一，<strong style="color: #0e88eb;">观察结果可能非常庞大</strong>。与网页或 PDF 等非结构化数据交互时，一次观察就可能产生数万个 token。如果不压缩，可能一两次操作就把上下文占满了。</p>
<p data-tool="mdnice编辑器">第二，<strong style="color: #0e88eb;">模型性能会下降</strong>。这是个很多人忽视的问题。即使模型声称支持 200k 的上下文窗口，但实际使用中，超过一定长度后，模型的注意力机制效率会显著下降，响应质量也会变差。这就像人的工作记忆一样，信息太多反而会降低处理效率。</p>
<p data-tool="mdnice编辑器">第三，<strong style="color: #0e88eb;">成本考虑</strong>。长输入意味着高成本，即使使用了前缀缓存等优化技术，成本依然可观。特别是在需要大量交互的场景下，成本会快速累积。</p>
<p data-tool="mdnice编辑器">在具体实现过程中，可恢复压缩有几个关键点：</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">保留最小必要信息</strong>。对于每个外部资源，只保留能够重新获取它的最小信息集。网页保留 URL，文档保留路径，API 响应保留请求参数。这些信息占用的空间极小，但足以在需要时恢复完整内容。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">智能的重新加载时机</strong>。不是每次提到某个资源就重新加载，而是根据上下文判断是否真的需要详细内容。如果只是确认文件存在，就不需要读取内容；如果要分析具体细节，才触发加载。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">缓存机制</strong>。虽然内容不在上下文中，但 Manus 会在本地维护一个缓存。最近访问过的资源会暂时保留，避免频繁的重复加载。这个缓存是独立于上下文的，不占用宝贵的 token 额度。</p>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">2. Claude Code</span></h1>
<p data-tool="mdnice编辑器">Claude Code 的策略完全是另一个极端——他们要把上下文用到极致。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">2.1 92% 的阈值</span></h2>
<p data-tool="mdnice编辑器">这个数字有一些讲究。留 8% 的缓冲区既保证了压缩过程有足够的时间完成，又避免了频繁触发压缩带来的性能开销。更重要的是，这个缓冲区给了系统一个「反悔」的机会——如果压缩质量不达标，还有空间执行降级策略。</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-keyword" style="color: #c678dd;">const</span> COMPRESSION_CONFIG = {
  <span class="hljs-attr" style="color: #d19a66;">threshold</span>: <span class="hljs-number" style="color: #d19a66;">0.92</span>,           <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 92%阈值触发</span>
  <span class="hljs-attr" style="color: #d19a66;">triggerVariable</span>: <span class="hljs-string" style="color: #98c379;">"h11"</span>,    <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// h11 = 0.92</span>
  <span class="hljs-attr" style="color: #d19a66;">compressionModel</span>: <span class="hljs-string" style="color: #98c379;">"J7()"</span>,  <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 专用压缩模型</span>
  <span class="hljs-attr" style="color: #d19a66;">preserveStructure</span>: <span class="hljs-literal" style="color: #56b6c2;">true</span>    <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 保持8段结构</span>
};
</code></pre>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">2.2 八段式结构化摘要</span></h2>
<p data-tool="mdnice编辑器">Claude Code 的压缩不是简单的截断或摘要，而是八段式结构。这个结构我们可以学习一下：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-keyword" style="color: #c678dd;">const</span> COMPRESSION_SECTIONS = [
  <span class="hljs-string" style="color: #98c379;">"1. Primary Request and Intent"</span>,    <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 主要请求和意图</span>
  <span class="hljs-string" style="color: #98c379;">"2. Key Technical Concepts"</span>,        <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 关键技术概念</span>
  <span class="hljs-string" style="color: #98c379;">"3. Files and Code Sections"</span>,       <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 文件和代码段</span>
  <span class="hljs-string" style="color: #98c379;">"4. Errors and fixes"</span>,              <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 错误和修复</span>
  <span class="hljs-string" style="color: #98c379;">"5. Problem Solving"</span>,               <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 问题解决</span>
  <span class="hljs-string" style="color: #98c379;">"6. All user messages"</span>,             <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 所有用户消息</span>
  <span class="hljs-string" style="color: #98c379;">"7. Pending Tasks"</span>,                 <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 待处理任务</span>
  <span class="hljs-string" style="color: #98c379;">"8. Current Work"</span>                   <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 当前工作</span>
];
</code></pre>
<p data-tool="mdnice编辑器">每一段都有明确的目的和优先级。「主要请求和意图」确保 Agent 永远不会忘记用户最初想要什么；「关键技术概念」保留重要的技术决策和约束条件；「错误和修复」避免重复踩坑；「所有用户消息」则保证用户的原始表达不会丢失。</p>
<p data-tool="mdnice编辑器">这种结构的好处是，即使经过多次压缩，Agent 仍然能保持工作的连贯性。关键信息都在，只是细节被逐步抽象化了。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">2.3 专用压缩模型 J7</span></h2>
<p data-tool="mdnice编辑器">Claude Code 使用了一个专门的压缩模型 J7 来处理上下文压缩。这不是主模型，而是一个专门优化过的模型，它的任务就是理解长对话并生成高质量的结构化摘要。</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-keyword" style="color: #c678dd;">async</span> <span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">function</span> <span class="hljs-title" style="color: #61aeee;">contextCompression</span>(<span class="hljs-params">currentContext</span>) </span>{
  <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 检查压缩条件</span>
  <span class="hljs-keyword" style="color: #c678dd;">if</span> (currentContext.tokenRatio &lt; h11) {
    <span class="hljs-keyword" style="color: #c678dd;">return</span> currentContext;  <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 无需压缩</span>
  }
  
  <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 调用专用压缩模型</span>
  <span class="hljs-keyword" style="color: #c678dd;">const</span> compressionPrompt = <span class="hljs-keyword" style="color: #c678dd;">await</span> AU2.generatePrompt(currentContext);
  <span class="hljs-keyword" style="color: #c678dd;">const</span> compressedSummary = <span class="hljs-keyword" style="color: #c678dd;">await</span> J7(compressionPrompt);
  
  <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 构建新的上下文</span>
  <span class="hljs-keyword" style="color: #c678dd;">const</span> newContext = {
    <span class="hljs-attr" style="color: #d19a66;">summary</span>: compressedSummary,
    <span class="hljs-attr" style="color: #d19a66;">recentMessages</span>: currentContext.recent(<span class="hljs-number" style="color: #d19a66;">5</span>),  <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 保留最近5条</span>
    <span class="hljs-attr" style="color: #d19a66;">currentTask</span>: currentContext.activeTask
  };
  
  <span class="hljs-keyword" style="color: #c678dd;">return</span> newContext;
}
</code></pre>
<p data-tool="mdnice编辑器">AU2 负责生成压缩提示词，它会分析当前上下文，提取关键信息，然后构造一个结构化的提示词给 J7。J7 处理后返回符合八段式结构的压缩摘要。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">2.4 上下文生命周期管理</span></h2>
<p data-tool="mdnice编辑器">Claude Code 把上下文当作有生命周期的实体来管理。这个设计理念很先进——上下文不是静态的数据，而是动态演化的有机体。</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-class"><span class="hljs-keyword" style="color: #c678dd;">class</span> <span class="hljs-title" style="color: #e6c07b;">ContextManager</span> </span>{
  <span class="hljs-keyword" style="color: #c678dd;">constructor</span>() {
    <span class="hljs-keyword" style="color: #c678dd;">this</span>.compressionThreshold = <span class="hljs-number" style="color: #d19a66;">0.92</span>;  <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// h11 = 0.92</span>
    <span class="hljs-keyword" style="color: #c678dd;">this</span>.compressionModel = <span class="hljs-string" style="color: #98c379;">"J7"</span>;      <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 专用模型</span>
  }
  
  <span class="hljs-keyword" style="color: #c678dd;">async</span> manageContext(currentContext, newInput) {
    <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 1. 上下文更新</span>
    <span class="hljs-keyword" style="color: #c678dd;">const</span> updatedContext = <span class="hljs-keyword" style="color: #c678dd;">this</span>.appendToContext(currentContext, newInput);
    
    <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 2. 令牌使用量检查</span>
    <span class="hljs-keyword" style="color: #c678dd;">const</span> tokenUsage = <span class="hljs-keyword" style="color: #c678dd;">await</span> <span class="hljs-keyword" style="color: #c678dd;">this</span>.calculateTokenUsage(updatedContext);
    
    <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 3. 压缩触发判断</span>
    <span class="hljs-keyword" style="color: #c678dd;">if</span> (tokenUsage.ratio &gt;= <span class="hljs-keyword" style="color: #c678dd;">this</span>.compressionThreshold) {
      <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 4. 八段式压缩执行</span>
      <span class="hljs-keyword" style="color: #c678dd;">const</span> compressionPrompt = <span class="hljs-keyword" style="color: #c678dd;">await</span> AU2.generateCompressionPrompt(updatedContext);
      <span class="hljs-keyword" style="color: #c678dd;">const</span> compressedSummary = <span class="hljs-keyword" style="color: #c678dd;">await</span> <span class="hljs-keyword" style="color: #c678dd;">this</span>.compressionModel.generate(compressionPrompt);
      
      <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 5. 新上下文构建</span>
      <span class="hljs-keyword" style="color: #c678dd;">return</span> <span class="hljs-keyword" style="color: #c678dd;">this</span>.buildCompressedContext(compressedSummary, updatedContext);
    }
    
    <span class="hljs-keyword" style="color: #c678dd;">return</span> updatedContext;
  }
}
</code></pre>
<p data-tool="mdnice编辑器">整个流程是自动化的。每次有新的输入，系统都会评估是否需要压缩。压缩不是一次性的动作，而是持续的过程。随着对话的进行，早期的详细内容会逐渐被抽象化，但关键信息始终保留。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">2.5 优雅降级机制</span></h2>
<p data-tool="mdnice编辑器">当压缩失败时，系统不会死板地报错或者强行应用低质量的压缩结果，而是有一整套 Plan B、Plan C。这种&#8221;永不放弃&#8221;的设计理念，让系统在各种极端情况下都能稳定运行。</p>
<p data-tool="mdnice编辑器">降级策略包括：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">自适应重压缩</strong>：如果首次压缩质量不佳，会调整参数重试</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">混合模式保留</strong>：压缩旧内容，但完整保留最近的交互</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">保守截断</strong>：最坏情况下，至少保证系统能继续运行</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">2.6 压缩后的信息恢复</span></h2>
<p data-tool="mdnice编辑器">虽然 Claude Code 的压缩是有损的，但它通过巧妙的设计最小化了信息损失的影响。压缩后的八段式摘要不是简单的文本，而是结构化的信息，包含了足够的上下文让 Agent 能够理解之前发生了什么，需要做什么。</p>
<p data-tool="mdnice编辑器">特别值得一提的是第 6 段&#8221;All user messages&#8221;。即使其他内容被压缩了，用户的所有消息都会以某种形式保留。这确保了用户的意图和需求不会在压缩过程中丢失。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">2.7 实践指南</span></h2>
<p data-tool="mdnice编辑器">Claude Code 在实践中还有一些最佳实践：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">定期使用 <code style="color: #0e8aeb;">/compact</code> 命令压缩长对话</strong>：用户可以主动触发压缩，不必等到自动触发</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">在上下文警告出现时及时处理</strong>：系统会在接近阈值时发出警告，用户应该及时响应</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">通过 <code style="color: #0e8aeb;">Claude.md</code> 文件保存重要信息</strong>：将关键信息外部化，减少上下文消耗</section>
</li>
</ul>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">3. Gemini CLI</span></h1>
<p data-tool="mdnice编辑器">Gemini CLI 选择了一条中庸之道，或者说是实用之道。Gemini CLI 项目开源了，这部分的说明会多一些。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">3.1 70/30？</span></h2>
<p data-tool="mdnice编辑器">Gemini CLI 选择了 70% 作为压缩触发点，30% 作为保留比例。这个比例我们也可以参考学习一下：</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">为什么是 70% 而不是 92%</strong></p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">更早介入，避免紧急压缩导致的卡顿</section>
</li>
<li>
<section style="color: #010101;">给压缩过程留出充足的缓冲空间</section>
</li>
<li>
<section style="color: #010101;">适合轻量级应用场景，不追求极限性能</section>
</li>
</ul>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">30% 保留的合理性：</strong></p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;">刚好覆盖最近 5-10 轮对话</section>
</li>
<li>
<section style="color: #010101;">足够维持上下文连续性</section>
</li>
<li>
<section style="color: #010101;">不会让用户感觉&#8221;突然失忆&#8221;</section>
</li>
</ul>
<p data-tool="mdnice编辑器">共背后的逻辑是：宁可频繁一点地压缩，也要保证每次压缩都是从容的、高质量的。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">3.2 精选历史提取</span></h2>
<p data-tool="mdnice编辑器">Gemini CLI 有个独特的概念叫精选历史&#8221;。不是所有的历史都值得保留，系统会智能地筛选有效内容：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">function</span> <span class="hljs-title" style="color: #61aeee;">extractCuratedHistory</span>(<span class="hljs-params">comprehensiveHistory: Content[]</span>): <span class="hljs-title" style="color: #61aeee;">Content</span>[] </span>{
  <span class="hljs-keyword" style="color: #c678dd;">if</span> (comprehensiveHistory === <span class="hljs-literal" style="color: #56b6c2;">undefined</span> || comprehensiveHistory.length === <span class="hljs-number" style="color: #d19a66;">0</span>) {
    <span class="hljs-keyword" style="color: #c678dd;">return</span> [];
  }
  <span class="hljs-keyword" style="color: #c678dd;">const</span> curatedHistory: Content[] = [];
  <span class="hljs-keyword" style="color: #c678dd;">const</span> length = comprehensiveHistory.length;
  <span class="hljs-keyword" style="color: #c678dd;">let</span> i = <span class="hljs-number" style="color: #d19a66;">0</span>;
  <span class="hljs-keyword" style="color: #c678dd;">while</span> (i &lt; length) {
    <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 用户轮次直接保留</span>
    <span class="hljs-keyword" style="color: #c678dd;">if</span> (comprehensiveHistory[i].role === <span class="hljs-string" style="color: #98c379;">'user'</span>) {
      curatedHistory.push(comprehensiveHistory[i]);
      i++;
    } <span class="hljs-keyword" style="color: #c678dd;">else</span> {
      <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 处理模型轮次</span>
      <span class="hljs-keyword" style="color: #c678dd;">const</span> modelOutput: Content[] = [];
      <span class="hljs-keyword" style="color: #c678dd;">let</span> isValid = <span class="hljs-literal" style="color: #56b6c2;">true</span>;
      <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 收集连续的模型轮次</span>
      <span class="hljs-keyword" style="color: #c678dd;">while</span> (i &lt; length &amp;&amp; comprehensiveHistory[i].role === <span class="hljs-string" style="color: #98c379;">'model'</span>) {
        modelOutput.push(comprehensiveHistory[i]);
        <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 检查内容有效性</span>
        <span class="hljs-keyword" style="color: #c678dd;">if</span> (isValid &amp;&amp; !isValidContent(comprehensiveHistory[i])) {
          isValid = <span class="hljs-literal" style="color: #56b6c2;">false</span>;
        }
        i++;
      }
      <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 只有当所有模型轮次都有效时才保留</span>
      <span class="hljs-keyword" style="color: #c678dd;">if</span> (isValid) {
        curatedHistory.push(...modelOutput);
      }
    }
  }
  <span class="hljs-keyword" style="color: #c678dd;">return</span> curatedHistory;
}
</code></pre>
<p data-tool="mdnice编辑器">这个策略的巧妙之处在于：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">用户输入全部保留</strong>：所有用户输入都被视为重要信息，无条件保留</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">模型轮次有条件保留</strong>：连续的模型轮次被视为一个整体进行评估</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">全有或全无的处理</strong>：要么全部保留，要么全部丢弃，避免了复杂的部分保留逻辑</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">3.3 内容有效性判断</span></h2>
<p data-tool="mdnice编辑器">什么样的内容会被认为是无效的？Gemini CLI 有明确的标准：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">function</span> <span class="hljs-title" style="color: #61aeee;">isValidContent</span>(<span class="hljs-params">content: Content</span>): <span class="hljs-title" style="color: #61aeee;">boolean</span> </span>{
  <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 检查 parts 数组是否存在且非空</span>
  <span class="hljs-keyword" style="color: #c678dd;">if</span> (content.parts === <span class="hljs-literal" style="color: #56b6c2;">undefined</span> || content.parts.length === <span class="hljs-number" style="color: #d19a66;">0</span>) {
    <span class="hljs-keyword" style="color: #c678dd;">return</span> <span class="hljs-literal" style="color: #56b6c2;">false</span>;
  }
  <span class="hljs-keyword" style="color: #c678dd;">for</span> (<span class="hljs-keyword" style="color: #c678dd;">const</span> part of content.parts) {
    <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 检查 part 是否为空</span>
    <span class="hljs-keyword" style="color: #c678dd;">if</span> (part === <span class="hljs-literal" style="color: #56b6c2;">undefined</span> || <span class="hljs-built_in" style="color: #e6c07b;">Object</span>.keys(part).length === <span class="hljs-number" style="color: #d19a66;">0</span>) {
      <span class="hljs-keyword" style="color: #c678dd;">return</span> <span class="hljs-literal" style="color: #56b6c2;">false</span>;
    }
    <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 检查非思考类型的 part 是否有空文本</span>
    <span class="hljs-keyword" style="color: #c678dd;">if</span> (!part.thought &amp;&amp; part.text !== <span class="hljs-literal" style="color: #56b6c2;">undefined</span> &amp;&amp; part.text === <span class="hljs-string" style="color: #98c379;">''</span>) {
      <span class="hljs-keyword" style="color: #c678dd;">return</span> <span class="hljs-literal" style="color: #56b6c2;">false</span>;
    }
  }
  <span class="hljs-keyword" style="color: #c678dd;">return</span> <span class="hljs-literal" style="color: #56b6c2;">true</span>;
}
</code></pre>
<p data-tool="mdnice编辑器">无效内容包括：空响应、错误输出、中断的流式响应等。这种预过滤机制确保进入压缩流程的都是高质量的内容。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">3.4 五段式结构化摘要</span></h2>
<p data-tool="mdnice编辑器">相比 Claude Code 的八段式，Gemini CLI 的五段式更简洁，但涵盖了所有关键信息：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;">1. overall_goal - 用户的主要目标
2. key_knowledge - 重要技术知识和决策
3. file_system_state - 文件系统当前状态
4. recent_actions - 最近执行的重要操作
5. current_plan - 当前执行计划
</code></pre>
<p data-tool="mdnice编辑器">压缩时，系统会生成 XML 格式的结构化摘要。这种格式的好处是结构清晰，LLM 容易理解和生成，同时也便于后续的解析和处理。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">3.5 基于 Token 的智能压缩</span></h2>
<p data-tool="mdnice编辑器">Gemini CLI 的压缩不是简单的定时触发，而是基于精确的 token 计算：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-keyword" style="color: #c678dd;">async</span> tryCompressChat(
  prompt_id: <span class="hljs-built_in" style="color: #e6c07b;">string</span>,
  force: <span class="hljs-built_in" style="color: #e6c07b;">boolean</span> = <span class="hljs-literal" style="color: #56b6c2;">false</span>,
): <span class="hljs-built_in" style="color: #e6c07b;">Promise</span>&lt;ChatCompressionInfo | <span class="hljs-literal" style="color: #56b6c2;">null</span>&gt; {
  <span class="hljs-keyword" style="color: #c678dd;">const</span> curatedHistory = <span class="hljs-keyword" style="color: #c678dd;">this</span>.getChat().getHistory(<span class="hljs-literal" style="color: #56b6c2;">true</span>);

  <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 空历史不压缩</span>
  <span class="hljs-keyword" style="color: #c678dd;">if</span> (curatedHistory.length === <span class="hljs-number" style="color: #d19a66;">0</span>) {
    <span class="hljs-keyword" style="color: #c678dd;">return</span> <span class="hljs-literal" style="color: #56b6c2;">null</span>;
  }

  <span class="hljs-keyword" style="color: #c678dd;">const</span> model = <span class="hljs-keyword" style="color: #c678dd;">this</span>.config.getModel();

  <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 计算当前历史的 token 数量</span>
  <span class="hljs-keyword" style="color: #c678dd;">const</span> { totalTokens: originalTokenCount } =
    <span class="hljs-keyword" style="color: #c678dd;">await</span> <span class="hljs-keyword" style="color: #c678dd;">this</span>.getContentGenerator().countTokens({
      model,
      contents: curatedHistory,
    });

  <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 获取压缩阈值配置</span>
  <span class="hljs-keyword" style="color: #c678dd;">const</span> contextPercentageThreshold =
    <span class="hljs-keyword" style="color: #c678dd;">this</span>.config.getChatCompression()?.contextPercentageThreshold;

  <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 如果未强制压缩且 token 数量低于阈值，则不压缩</span>
  <span class="hljs-keyword" style="color: #c678dd;">if</span> (!force) {
    <span class="hljs-keyword" style="color: #c678dd;">const</span> threshold =
      contextPercentageThreshold ?? COMPRESSION_TOKEN_THRESHOLD; <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 默认 0.7</span>
    <span class="hljs-keyword" style="color: #c678dd;">if</span> (originalTokenCount &lt; threshold * tokenLimit(model)) {
      <span class="hljs-keyword" style="color: #c678dd;">return</span> <span class="hljs-literal" style="color: #56b6c2;">null</span>;
    }
  }

  <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 计算压缩点，保留最后 30% 的历史</span>
  <span class="hljs-keyword" style="color: #c678dd;">let</span> compressBeforeIndex = findIndexAfterFraction(
    curatedHistory,
    <span class="hljs-number" style="color: #d19a66;">1</span> - COMPRESSION_PRESERVE_THRESHOLD, <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// COMPRESSION_PRESERVE_THRESHOLD = 0.3</span>
  );

  <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 确保压缩点在用户轮次开始处</span>
  <span class="hljs-keyword" style="color: #c678dd;">while</span> (
    compressBeforeIndex &lt; curatedHistory.length &amp;&amp;
    (curatedHistory[compressBeforeIndex]?.role === <span class="hljs-string" style="color: #98c379;">'model'</span> ||
      isFunctionResponse(curatedHistory[compressBeforeIndex]))
  ) {
    compressBeforeIndex++;
  }

  <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 分割历史为需要压缩和需要保留的部分</span>
  <span class="hljs-keyword" style="color: #c678dd;">const</span> historyToCompress = curatedHistory.slice(<span class="hljs-number" style="color: #d19a66;">0</span>, compressBeforeIndex);
  <span class="hljs-keyword" style="color: #c678dd;">const</span> historyToKeep = curatedHistory.slice(compressBeforeIndex);

  <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 使用 LLM 生成历史摘要</span>
  <span class="hljs-keyword" style="color: #c678dd;">this</span>.getChat().setHistory(historyToCompress);
  <span class="hljs-keyword" style="color: #c678dd;">const</span> { text: summary } = <span class="hljs-keyword" style="color: #c678dd;">await</span> <span class="hljs-keyword" style="color: #c678dd;">this</span>.getChat().sendMessage(
    {
      message: {
        text: <span class="hljs-string" style="color: #98c379;">'First, reason in your scratchpad. Then, generate the &lt;state_snapshot&gt;.'</span>,
      },
      config: {
        systemInstruction: { text: getCompressionPrompt() },
      },
    },
    prompt_id,
  );

  <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 创建新的聊天历史，包含摘要和保留的部分</span>
  <span class="hljs-keyword" style="color: #c678dd;">this</span>.chat = <span class="hljs-keyword" style="color: #c678dd;">await</span> <span class="hljs-keyword" style="color: #c678dd;">this</span>.startChat([
    {
      role: <span class="hljs-string" style="color: #98c379;">'user'</span>,
      parts: [{ text: summary }],
    },
    {
      role: <span class="hljs-string" style="color: #98c379;">'model'</span>,
      parts: [{ text: <span class="hljs-string" style="color: #98c379;">'Got it. Thanks for the additional context!'</span> }],
    },
    ...historyToKeep,
  ]);
}
</code></pre>
<p data-tool="mdnice编辑器">这个实现有几个细节值得注意：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">支持强制压缩</strong>：通过 force 参数，用户可以主动触发压缩</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">智能分割点选择</strong>：确保压缩点在用户轮次开始，避免打断对话逻辑</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">两阶段压缩</strong>：先生成摘要，再重建对话历史</section>
</li>
</ul>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">3.6 多层压缩机制</span></h2>
<p data-tool="mdnice编辑器">Gemini CLI 的压缩是分层进行的，每一层都有特定的目标：</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">第一层：内容过滤</strong>：过滤掉无效内容、thought 类型的部分，确保进入下一层的都是有价值的信息。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">第二层：内容整合</strong>：合并相邻的同类内容，比如连续的纯文本 Part 会被合并成一个，减少结构冗余。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">第三层：智能摘要</strong>：当 token 使用量超过阈值时，触发 LLM 生成结构化摘要。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">第四层：保护机制</strong>：确保关键信息不被压缩丢失，比如用户的最新指令、正在进行的任务等。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">3.7 模型适配的 Token 限制</span></h2>
<p data-tool="mdnice编辑器">不同的模型有不同的 token 限制，Gemini CLI 对此有精细的适配：</p>
<pre class="custom" data-tool="mdnice编辑器"><code class="hljs" style="color: #abb2bf;"><span class="hljs-keyword" style="color: #c678dd;">export</span> <span class="hljs-function"><span class="hljs-keyword" style="color: #c678dd;">function</span> <span class="hljs-title" style="color: #61aeee;">tokenLimit</span>(<span class="hljs-params">model: Model</span>): <span class="hljs-title" style="color: #61aeee;">TokenCount</span> </span>{
  <span class="hljs-keyword" style="color: #c678dd;">switch</span> (model) {
    <span class="hljs-keyword" style="color: #c678dd;">case</span> <span class="hljs-string" style="color: #98c379;">'gemini-1.5-pro'</span>:
      <span class="hljs-keyword" style="color: #c678dd;">return</span> <span class="hljs-number" style="color: #d19a66;">2</span>_097_152;
    <span class="hljs-keyword" style="color: #c678dd;">case</span> <span class="hljs-string" style="color: #98c379;">'gemini-1.5-flash'</span>:
    <span class="hljs-keyword" style="color: #c678dd;">case</span> <span class="hljs-string" style="color: #98c379;">'gemini-2.5-pro'</span>:
    <span class="hljs-keyword" style="color: #c678dd;">case</span> <span class="hljs-string" style="color: #98c379;">'gemini-2.5-flash'</span>:
    <span class="hljs-keyword" style="color: #c678dd;">case</span> <span class="hljs-string" style="color: #98c379;">'gemini-2.0-flash'</span>:
      <span class="hljs-keyword" style="color: #c678dd;">return</span> <span class="hljs-number" style="color: #d19a66;">1</span>_048_576;
    <span class="hljs-keyword" style="color: #c678dd;">case</span> <span class="hljs-string" style="color: #98c379;">'gemini-2.0-flash-preview-image-generation'</span>:
      <span class="hljs-keyword" style="color: #c678dd;">return</span> <span class="hljs-number" style="color: #d19a66;">32</span>_000;
    <span class="hljs-keyword" style="color: #c678dd;">default</span>:
      <span class="hljs-keyword" style="color: #c678dd;">return</span> DEFAULT_TOKEN_LIMIT; <span class="hljs-comment" style="font-style: italic; color: #5c6370;">// 1_048_576</span>
  }
}
</code></pre>
<p data-tool="mdnice编辑器">系统会根据使用的模型自动调整压缩策略。对于支持超长上下文的模型（如 gemini-1.5-pro 的 200 万 token），可以更宽松；对于受限的模型，会更积极地压缩。</p>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">3.8 历史记录的精细处理</span></h2>
<p data-tool="mdnice编辑器"><code style="color: #0e8aeb;">recordHistory</code> 方法负责记录和处理历史，实施了多个优化策略：</p>
<ol data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">避免重复</strong>：不会重复添加相同的用户输入</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">过滤思考过程</strong>：thought 类型的 Part 会被过滤掉，不进入最终历史</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">合并优化</strong>：相邻的模型轮次会被合并，相邻的纯文本也会合并</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">占位符策略</strong>：如果模型没有有效输出，会添加空的占位符保持结构完整</section>
</li>
</ol>
<h2 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">3.9 压缩的用户体验设计</span></h2>
<p data-tool="mdnice编辑器">Gemini CLI 特别注重压缩对用户体验的影响：</p>
<ul data-tool="mdnice编辑器">
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">无感压缩</strong>：70% 的阈值确保压缩发生在用户察觉之前</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">连续性保持</strong>：保留 30% 的最新历史，确保当前话题的连贯性</section>
</li>
<li>
<section style="color: #010101;"><strong style="color: #0e88eb;">透明反馈</strong>：压缩前后的 token 数量变化会被记录和报告</section>
</li>
</ul>
<h1 data-tool="mdnice编辑器"><span class="content" style="color: #0e8aeb;">4. 写在最后</span></h1>
<p data-tool="mdnice编辑器">研究完这三个项目的源码，我最大的感受是：<strong style="color: #0e88eb;">压缩策略的选择，本质上是对「什么是重要的」这个问题的回答。</strong> Manus 说「所有信息都可能重要，所以我不删除，只是暂时收起来」；Claude Code 说「结构化的摘要比原始细节更重要」；Gemini CLI 说「用户体验比技术指标更重要」。三种回答，三种哲学。</p>
<p data-tool="mdnice编辑器">这让我想起一句话：<strong style="color: #0e88eb;">在 AI 时代，真正稀缺的不是信息，而是注意力。</strong> 上下文压缩就是在教 AI 如何分配注意力——什么该记住，什么可以忘记，什么需要随时能找回来。</p>
<p data-tool="mdnice编辑器">这是人类智慧的核心能力之一。我们每天都在做类似的决策：重要的事情记在心里，次要的写在本子上，琐碎的存在手机里。Manus、Claude Code 和 Gemini CLI 只是用不同的方式在教 AI 做同样的事。</p>
<p data-tool="mdnice编辑器"><strong style="color: #0e88eb;">没有完美的压缩策略，只有最适合你场景的策略。</strong> 选择哪种策略不重要，重要的是理解它们背后的设计智慧，然后根据自己的需求做出明智的选择。</p>
<p data-tool="mdnice编辑器">以上。</p>
</section>
]]></content:encoded>
			<wfw:commentRss>https://www.phppan.com/2025/09/agent-core-strategy-context-compression-strategy-and-details-for-manus-gemini-cli-and-claude-code/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
