分类目录归档:架构和远方

Agent 核心策略:Manus、Gemini CLI 和 Claude Code 的上下文压缩策略和细节

做 AI Agent 开发的都需要考虑上下文爆炸的问题,不仅仅是成本问题,还有性能问题。

许多团队选择了压缩策略。但过度激进的压缩不可避免地导致信息丢失。

这背后的根本矛盾在于:Agent 需要基于完整的历史状态来决策下一步行动,但我们无法预知当前的某个观察细节是否会在未来的某个关键时刻变得至关重要。

前面一篇文章讲了整个上下文的管理策略,今天着重聊一下上下文管理的压缩策略和细节。

下面根据 Manus、Gemini CLI 和 Claude Code 这三个项目的源码来聊一下上下文的压缩。

三个产品,有三个不同的选择,或者说设计哲学。

Manus 的选择是:永不丢失。 他们认为,从逻辑角度看,任何不可逆的压缩都带有风险。所以他们选择了一条看似”笨拙”但实际上很聪明的路:把文件系统当作”终极上下文”。

Claude Code 的选择是:极限压榨。 92% 的压缩触发阈值,这个数字相当激进。他们想要榨干上下文窗口的每一个 token,直到最后一刻才开始压缩。

Gemini CLI 的选择是:稳健保守。 70% 就开始压缩,宁可频繁一点,也要保证系统的稳定性。

这三种选择没有对错,只是适用场景不同。接下来我们逐个分析。

1. Manus

Manus 的压缩最让我印象深刻的是其在可恢复性上的策略。

Manus 团队认为:任何不可逆的压缩都带有风险。你永远不知道现在丢掉的信息是不是未来解决问题的关键。

他们选择了不做真正的删除,而是将其外部化存储。

传统做法是把所有观察结果都塞进上下文里。比如让 Agent 读一个网页,它会把整个 HTML 内容都存下来,可能就是上万个 token。Manus 不这么干。

当 Agent 访问网页时,Manus 只在上下文中保留 URL 和简短的描述,完整的网页内容并不保存。需要重新查看网页内容时,通过保留的 URL 重新获取即可。这就像是你在笔记本上记录”参见第 23 页的图表”,而不是把整个图表重新画一遍。

文档处理也是同样的逻辑。一个 100 页的 PDF 文档,Manus 不会把全部内容放进上下文,而是只记录文档路径、页数、最后访问的位置等元信息。当 Agent 需要查看具体内容时,再通过文件路径读取相应的页面。

Manus 把文件系统视为”终极上下文”——一个容量几乎无限、天然持久化、Agent 可以直接操作的外部记忆系统。

文件系统的层级结构天然适合组织信息。Agent 可以创建不同的目录来分类存储不同类型的信息:项目背景放一个文件夹,技术细节放另一个文件夹,错误日志单独存放。需要时按图索骥,而不是在一个巨大的上下文中大海捞针。

这不是简单的存储。Manus 训练模型学会主动使用文件系统来管理自己的「记忆」。当发现重要信息时,Agent 会主动将其写入特定的文件中,而不是试图把所有东西都记在上下文里。就像一个经验丰富的研究员,知道什么该记在脑子里,什么该写在笔记本上,什么该归档保存。

在 Manus 团队对外的文章开头,指出了为什么必须要有压缩策略:

第一,观察结果可能非常庞大。与网页或 PDF 等非结构化数据交互时,一次观察就可能产生数万个 token。如果不压缩,可能一两次操作就把上下文占满了。

第二,模型性能会下降。这是个很多人忽视的问题。即使模型声称支持 200k 的上下文窗口,但实际使用中,超过一定长度后,模型的注意力机制效率会显著下降,响应质量也会变差。这就像人的工作记忆一样,信息太多反而会降低处理效率。

第三,成本考虑。长输入意味着高成本,即使使用了前缀缓存等优化技术,成本依然可观。特别是在需要大量交互的场景下,成本会快速累积。

在具体实现过程中,可恢复压缩有几个关键点:

保留最小必要信息。对于每个外部资源,只保留能够重新获取它的最小信息集。网页保留 URL,文档保留路径,API 响应保留请求参数。这些信息占用的空间极小,但足以在需要时恢复完整内容。

智能的重新加载时机。不是每次提到某个资源就重新加载,而是根据上下文判断是否真的需要详细内容。如果只是确认文件存在,就不需要读取内容;如果要分析具体细节,才触发加载。

缓存机制。虽然内容不在上下文中,但 Manus 会在本地维护一个缓存。最近访问过的资源会暂时保留,避免频繁的重复加载。这个缓存是独立于上下文的,不占用宝贵的 token 额度。

2. Claude Code

Claude Code 的策略完全是另一个极端——他们要把上下文用到极致。

2.1 92% 的阈值

这个数字有一些讲究。留 8% 的缓冲区既保证了压缩过程有足够的时间完成,又避免了频繁触发压缩带来的性能开销。更重要的是,这个缓冲区给了系统一个「反悔」的机会——如果压缩质量不达标,还有空间执行降级策略。

const COMPRESSION_CONFIG = {
  threshold0.92,           // 92%阈值触发
  triggerVariable"h11",    // h11 = 0.92
  compressionModel"J7()",  // 专用压缩模型
  preserveStructuretrue    // 保持8段结构
};

2.2 八段式结构化摘要

Claude Code 的压缩不是简单的截断或摘要,而是八段式结构。这个结构我们可以学习一下:

const COMPRESSION_SECTIONS = [
  "1. Primary Request and Intent",    // 主要请求和意图
  "2. Key Technical Concepts",        // 关键技术概念
  "3. Files and Code Sections",       // 文件和代码段
  "4. Errors and fixes",              // 错误和修复
  "5. Problem Solving",               // 问题解决
  "6. All user messages",             // 所有用户消息
  "7. Pending Tasks",                 // 待处理任务
  "8. Current Work"                   // 当前工作
];

每一段都有明确的目的和优先级。「主要请求和意图」确保 Agent 永远不会忘记用户最初想要什么;「关键技术概念」保留重要的技术决策和约束条件;「错误和修复」避免重复踩坑;「所有用户消息」则保证用户的原始表达不会丢失。

这种结构的好处是,即使经过多次压缩,Agent 仍然能保持工作的连贯性。关键信息都在,只是细节被逐步抽象化了。

2.3 专用压缩模型 J7

Claude Code 使用了一个专门的压缩模型 J7 来处理上下文压缩。这不是主模型,而是一个专门优化过的模型,它的任务就是理解长对话并生成高质量的结构化摘要。

async function contextCompression(currentContext{
  // 检查压缩条件
  if (currentContext.tokenRatio < h11) {
    return currentContext;  // 无需压缩
  }
  
  // 调用专用压缩模型
  const compressionPrompt = await AU2.generatePrompt(currentContext);
  const compressedSummary = await J7(compressionPrompt);
  
  // 构建新的上下文
  const newContext = {
    summary: compressedSummary,
    recentMessages: currentContext.recent(5),  // 保留最近5条
    currentTask: currentContext.activeTask
  };
  
  return newContext;
}

AU2 负责生成压缩提示词,它会分析当前上下文,提取关键信息,然后构造一个结构化的提示词给 J7。J7 处理后返回符合八段式结构的压缩摘要。

2.4 上下文生命周期管理

Claude Code 把上下文当作有生命周期的实体来管理。这个设计理念很先进——上下文不是静态的数据,而是动态演化的有机体。

class ContextManager {
  constructor() {
    this.compressionThreshold = 0.92;  // h11 = 0.92
    this.compressionModel = "J7";      // 专用模型
  }
  
  async manageContext(currentContext, newInput) {
    // 1. 上下文更新
    const updatedContext = this.appendToContext(currentContext, newInput);
    
    // 2. 令牌使用量检查
    const tokenUsage = await this.calculateTokenUsage(updatedContext);
    
    // 3. 压缩触发判断
    if (tokenUsage.ratio >= this.compressionThreshold) {
      // 4. 八段式压缩执行
      const compressionPrompt = await AU2.generateCompressionPrompt(updatedContext);
      const compressedSummary = await this.compressionModel.generate(compressionPrompt);
      
      // 5. 新上下文构建
      return this.buildCompressedContext(compressedSummary, updatedContext);
    }
    
    return updatedContext;
  }
}

整个流程是自动化的。每次有新的输入,系统都会评估是否需要压缩。压缩不是一次性的动作,而是持续的过程。随着对话的进行,早期的详细内容会逐渐被抽象化,但关键信息始终保留。

2.5 优雅降级机制

当压缩失败时,系统不会死板地报错或者强行应用低质量的压缩结果,而是有一整套 Plan B、Plan C。这种”永不放弃”的设计理念,让系统在各种极端情况下都能稳定运行。

降级策略包括:

  • 自适应重压缩:如果首次压缩质量不佳,会调整参数重试
  • 混合模式保留:压缩旧内容,但完整保留最近的交互
  • 保守截断:最坏情况下,至少保证系统能继续运行

2.6 压缩后的信息恢复

虽然 Claude Code 的压缩是有损的,但它通过巧妙的设计最小化了信息损失的影响。压缩后的八段式摘要不是简单的文本,而是结构化的信息,包含了足够的上下文让 Agent 能够理解之前发生了什么,需要做什么。

特别值得一提的是第 6 段”All user messages”。即使其他内容被压缩了,用户的所有消息都会以某种形式保留。这确保了用户的意图和需求不会在压缩过程中丢失。

2.7 实践指南

Claude Code 在实践中还有一些最佳实践:

  • 定期使用 /compact 命令压缩长对话:用户可以主动触发压缩,不必等到自动触发
  • 在上下文警告出现时及时处理:系统会在接近阈值时发出警告,用户应该及时响应
  • 通过 Claude.md 文件保存重要信息:将关键信息外部化,减少上下文消耗

3. Gemini CLI

Gemini CLI 选择了一条中庸之道,或者说是实用之道。Gemini CLI 项目开源了,这部分的说明会多一些。

3.1 70/30?

Gemini CLI 选择了 70% 作为压缩触发点,30% 作为保留比例。这个比例我们也可以参考学习一下:

为什么是 70% 而不是 92%

  • 更早介入,避免紧急压缩导致的卡顿
  • 给压缩过程留出充足的缓冲空间
  • 适合轻量级应用场景,不追求极限性能

30% 保留的合理性:

  • 刚好覆盖最近 5-10 轮对话
  • 足够维持上下文连续性
  • 不会让用户感觉”突然失忆”

共背后的逻辑是:宁可频繁一点地压缩,也要保证每次压缩都是从容的、高质量的。

3.2 精选历史提取

Gemini CLI 有个独特的概念叫精选历史”。不是所有的历史都值得保留,系统会智能地筛选有效内容:

function extractCuratedHistory(comprehensiveHistory: Content[]): Content[] {
  if (comprehensiveHistory === undefined || comprehensiveHistory.length === 0) {
    return [];
  }
  const curatedHistory: Content[] = [];
  const length = comprehensiveHistory.length;
  let i = 0;
  while (i < length) {
    // 用户轮次直接保留
    if (comprehensiveHistory[i].role === 'user') {
      curatedHistory.push(comprehensiveHistory[i]);
      i++;
    } else {
      // 处理模型轮次
      const modelOutput: Content[] = [];
      let isValid = true;
      // 收集连续的模型轮次
      while (i < length && comprehensiveHistory[i].role === 'model') {
        modelOutput.push(comprehensiveHistory[i]);
        // 检查内容有效性
        if (isValid && !isValidContent(comprehensiveHistory[i])) {
          isValid = false;
        }
        i++;
      }
      // 只有当所有模型轮次都有效时才保留
      if (isValid) {
        curatedHistory.push(...modelOutput);
      }
    }
  }
  return curatedHistory;
}

这个策略的巧妙之处在于:

  • 用户输入全部保留:所有用户输入都被视为重要信息,无条件保留
  • 模型轮次有条件保留:连续的模型轮次被视为一个整体进行评估
  • 全有或全无的处理:要么全部保留,要么全部丢弃,避免了复杂的部分保留逻辑

3.3 内容有效性判断

什么样的内容会被认为是无效的?Gemini CLI 有明确的标准:

function isValidContent(content: Content): boolean {
  // 检查 parts 数组是否存在且非空
  if (content.parts === undefined || content.parts.length === 0) {
    return false;
  }
  for (const part of content.parts) {
    // 检查 part 是否为空
    if (part === undefined || Object.keys(part).length === 0) {
      return false;
    }
    // 检查非思考类型的 part 是否有空文本
    if (!part.thought && part.text !== undefined && part.text === '') {
      return false;
    }
  }
  return true;
}

无效内容包括:空响应、错误输出、中断的流式响应等。这种预过滤机制确保进入压缩流程的都是高质量的内容。

3.4 五段式结构化摘要

相比 Claude Code 的八段式,Gemini CLI 的五段式更简洁,但涵盖了所有关键信息:

1. overall_goal - 用户的主要目标
2. key_knowledge - 重要技术知识和决策
3. file_system_state - 文件系统当前状态
4. recent_actions - 最近执行的重要操作
5. current_plan - 当前执行计划

压缩时,系统会生成 XML 格式的结构化摘要。这种格式的好处是结构清晰,LLM 容易理解和生成,同时也便于后续的解析和处理。

3.5 基于 Token 的智能压缩

Gemini CLI 的压缩不是简单的定时触发,而是基于精确的 token 计算:

async tryCompressChat(
  prompt_id: string,
  force: boolean = false,
): Promise<ChatCompressionInfo | null> {
  const curatedHistory = this.getChat().getHistory(true);

  // 空历史不压缩
  if (curatedHistory.length === 0) {
    return null;
  }

  const model = this.config.getModel();

  // 计算当前历史的 token 数量
  const { totalTokens: originalTokenCount } =
    await this.getContentGenerator().countTokens({
      model,
      contents: curatedHistory,
    });

  // 获取压缩阈值配置
  const contextPercentageThreshold =
    this.config.getChatCompression()?.contextPercentageThreshold;

  // 如果未强制压缩且 token 数量低于阈值,则不压缩
  if (!force) {
    const threshold =
      contextPercentageThreshold ?? COMPRESSION_TOKEN_THRESHOLD; // 默认 0.7
    if (originalTokenCount < threshold * tokenLimit(model)) {
      return null;
    }
  }

  // 计算压缩点,保留最后 30% 的历史
  let compressBeforeIndex = findIndexAfterFraction(
    curatedHistory,
    1 - COMPRESSION_PRESERVE_THRESHOLD, // COMPRESSION_PRESERVE_THRESHOLD = 0.3
  );

  // 确保压缩点在用户轮次开始处
  while (
    compressBeforeIndex < curatedHistory.length &&
    (curatedHistory[compressBeforeIndex]?.role === 'model' ||
      isFunctionResponse(curatedHistory[compressBeforeIndex]))
  ) {
    compressBeforeIndex++;
  }

  // 分割历史为需要压缩和需要保留的部分
  const historyToCompress = curatedHistory.slice(0, compressBeforeIndex);
  const historyToKeep = curatedHistory.slice(compressBeforeIndex);

  // 使用 LLM 生成历史摘要
  this.getChat().setHistory(historyToCompress);
  const { text: summary } = await this.getChat().sendMessage(
    {
      message: {
        text: 'First, reason in your scratchpad. Then, generate the <state_snapshot>.',
      },
      config: {
        systemInstruction: { text: getCompressionPrompt() },
      },
    },
    prompt_id,
  );

  // 创建新的聊天历史,包含摘要和保留的部分
  this.chat = await this.startChat([
    {
      role: 'user',
      parts: [{ text: summary }],
    },
    {
      role: 'model',
      parts: [{ text: 'Got it. Thanks for the additional context!' }],
    },
    ...historyToKeep,
  ]);
}

这个实现有几个细节值得注意:

  • 支持强制压缩:通过 force 参数,用户可以主动触发压缩
  • 智能分割点选择:确保压缩点在用户轮次开始,避免打断对话逻辑
  • 两阶段压缩:先生成摘要,再重建对话历史

3.6 多层压缩机制

Gemini CLI 的压缩是分层进行的,每一层都有特定的目标:

第一层:内容过滤:过滤掉无效内容、thought 类型的部分,确保进入下一层的都是有价值的信息。

第二层:内容整合:合并相邻的同类内容,比如连续的纯文本 Part 会被合并成一个,减少结构冗余。

第三层:智能摘要:当 token 使用量超过阈值时,触发 LLM 生成结构化摘要。

第四层:保护机制:确保关键信息不被压缩丢失,比如用户的最新指令、正在进行的任务等。

3.7 模型适配的 Token 限制

不同的模型有不同的 token 限制,Gemini CLI 对此有精细的适配:

export function tokenLimit(model: Model): TokenCount {
  switch (model) {
    case 'gemini-1.5-pro':
      return 2_097_152;
    case 'gemini-1.5-flash':
    case 'gemini-2.5-pro':
    case 'gemini-2.5-flash':
    case 'gemini-2.0-flash':
      return 1_048_576;
    case 'gemini-2.0-flash-preview-image-generation':
      return 32_000;
    default:
      return DEFAULT_TOKEN_LIMIT; // 1_048_576
  }
}

系统会根据使用的模型自动调整压缩策略。对于支持超长上下文的模型(如 gemini-1.5-pro 的 200 万 token),可以更宽松;对于受限的模型,会更积极地压缩。

3.8 历史记录的精细处理

recordHistory 方法负责记录和处理历史,实施了多个优化策略:

  1. 避免重复:不会重复添加相同的用户输入
  2. 过滤思考过程:thought 类型的 Part 会被过滤掉,不进入最终历史
  3. 合并优化:相邻的模型轮次会被合并,相邻的纯文本也会合并
  4. 占位符策略:如果模型没有有效输出,会添加空的占位符保持结构完整

3.9 压缩的用户体验设计

Gemini CLI 特别注重压缩对用户体验的影响:

  • 无感压缩:70% 的阈值确保压缩发生在用户察觉之前
  • 连续性保持:保留 30% 的最新历史,确保当前话题的连贯性
  • 透明反馈:压缩前后的 token 数量变化会被记录和报告

4. 写在最后

研究完这三个项目的源码,我最大的感受是:压缩策略的选择,本质上是对「什么是重要的」这个问题的回答。 Manus 说「所有信息都可能重要,所以我不删除,只是暂时收起来」;Claude Code 说「结构化的摘要比原始细节更重要」;Gemini CLI 说「用户体验比技术指标更重要」。三种回答,三种哲学。

这让我想起一句话:在 AI 时代,真正稀缺的不是信息,而是注意力。 上下文压缩就是在教 AI 如何分配注意力——什么该记住,什么可以忘记,什么需要随时能找回来。

这是人类智慧的核心能力之一。我们每天都在做类似的决策:重要的事情记在心里,次要的写在本子上,琐碎的存在手机里。Manus、Claude Code 和 Gemini CLI 只是用不同的方式在教 AI 做同样的事。

没有完美的压缩策略,只有最适合你场景的策略。 选择哪种策略不重要,重要的是理解它们背后的设计智慧,然后根据自己的需求做出明智的选择。

以上。

从 Claude Code到 Gemini CLI,AI Agent 的上下文管理策略

对于一个与大型语言模型(LLM)打过交道的开发者来说,上下文管理都是一个绕不开的核心问题。它不仅决定了 AI 的智能程度,也直接关系到系统的性能和成本。

上周研究了各家 Agent 系统的实现,各家的上下文管理策略都不相同。最简单最傻的策略是一个不断累加对话历史,这种策略很快就会遇到 Token 限制和 API 的成本问题。

如果你是一个技术负责人,或者正在开发 AI Agent 相关的产品,需要在性能和成本之间找到平衡点,这篇文章应该对你有一些帮助。

今天所聊的内容是基于对 Claude Code、Manus、Gemini CLI,OpenManus 等多个项目的分析,以及自己在实践中的一些思考。

为什么要做上下文管理?

最新的 LLM 现在提供 128K Token 或更多的上下文窗口。听起来还挺多的,但在真实世界的 Agent 场景中,这通常远远不够。

尤其是当 Agent 与网页或PDF等非结构化数据交互时,Token 数需求会爆炸。

并且,随着 Token 数的增加,模型性能会在超过一定长度后明显下降,这就像让一个人同时记住一本书的所有细节,理论上可能,实际上很难做好。

就算我们的大模型有更多的窗口上下文支持,成本也是一个需要考虑的问题,就算有前缀缓存这样的优化,但传输和预填充每个 Token 都是要付费的。

为了解决这些问题,许多团队选择了压缩策略。但过度激进的压缩不可避免地导致信息丢失。

这个问题的本质在于 Agent 必须根据所有先前状态预测下一个动作——而我们无法可靠地预测哪个观察结果可能在十步之后变得至关重要。从逻辑角度看,任何不可逆的压缩都带有风险。

接下来我们看一看各项目的上下文管理策略,看看从中能否给到各位看官一些启发。

OpenManus 的上下文管理策略

OpenManus 采用了一个相对简单直接的上下文管理方案,主要特点是:

  1. 轻量级消息列表机制
  • 使用固定长度(默认100条)的消息列表作为内存存储
  • 采用简单的 FIFO(先进先出)策略,超出限制时截断最早的消息
  • 没有智能的上下文压缩或摘要机制
  1. Token 限制处理
  • 实施硬性 token 检查,超限直接抛出异常终止
  • 缺乏优雅的降级策略或自适应窗口裁剪
  • 在长对话或工具密集场景中容易触碰上限

虽然上下文管理比较简单,但是 OpenManus 为不同使用场景提供了定制化的上下文处理,如浏览器场景会动态注入浏览器状态,截图保存等

总的来说,这是一个原型实现,并不适合作为生产级环境使用,如果要上到生产环境需要自行做精细化的处理和架构。

Manus 的上下文管理策略

Manus 没有开源,但是其官方有发一篇文章出来。

Manus 采用了一种创新的方法:将文件系统作为终极上下文存储,而不是依赖传统的内存中上下文管理。

文件系统作为存储有如下的核心特性:

  • 无限容量:文件系统大小不受限制
  • 天然持久化:数据自动保存,不会丢失
  • 直接操作:智能体可以主动读写文件
  • 结构化记忆:不仅是存储,更是结构化的外部记忆系统

相对于传统的将完整的观察结果保存在上下文中,容易超限,Manus 实现了可恢复的信息压缩

  • 观察结果指向外部文件(Document X, File Y)
  • 上下文中只保留引用,不保存完整内容
  • 需要时可以从文件系统恢复完整信息

具体实现:

  • 网页内容可从上下文移除,只保留 URL
  • 文档内容可省略,只保留文件路径
  • 实现上下文压缩的同时不会永久丢失信息

Manus 团队认为,如果状态空间模型能够掌握基于文件的记忆管理:

  • 将长期状态外部化而非保存在上下文中
  • SSM 的速度和效率优势可能开启新型智能体
  • 基于 SSM 的智能体可能成为神经图灵机的真正继任者

与 OpenManus 的简单消息列表管理,Manus 的方案更加成熟:

  • OpenManus:固定长度消息列表,硬性截断,缺乏智能管理
  • Manus:文件系统作为无限外部记忆,可恢复压缩,主动记忆管理

Claude Code 的上下文管理

Claude Code 没有开源代码,但是国外有大神反编译其源码(虽然大神自己说:这并非真正意义上的反编译或逆向工程尝试,而更像是对 Claude 团队杰出工作的致敬。)

地址:southbridge-research.notion.site/claude-code…

通过反编译内容的分析,可以大概了解一些其策略和比较巧妙的点:

TodoWrite 工具

Claude Code 引入 TodoWrite 工具,支持模型主动维护自己的 To-Do 列表,替代传统的多 Agent 分工策略。

其优势:

  • 专注:Prompt 中反复提醒模型参考 ToDo,始终聚焦目标。
  • 灵活:「交错思考」机制使得 ToDo 可动态增删。
  • 透明:用户可实时查看计划与进度,提高信任度。

Token 统计的反向遍历

Toke 统计从后往前查找这个细节相当精妙。大部分系统都是傻乎乎地从头遍历,但 Claude Code 意识到了一个关键事实:Token 使用情况的统计信息总是出现在最新的 assistant 回复里。这种”知道去哪找”的优化思路,把原本可能的 O(n) 操作优化到了 O(k),在高频调用场景下,这种优化带来的性能提升是指数级的。

92% 阈值

留 8% 的缓冲区既保证了压缩过程有足够的时间完成,又避免了频繁触发压缩带来的性能开销。更重要的是,这个缓冲区给了系统一个”反悔”的机会——如果压缩质量不达标,还有空间执行降级策略。

8 段式结构化摘要

Claude Code 的 8 段式结构特别值得借鉴:

markdown

体验AI代码助手
代码解读
复制代码

1. Primary Request and Intent - 主要请求和意图

2. Key Technical Concepts - 关键技术概念

3. Files and Code Sections - 文件和代码片段

4. Errors and Fixes - 错误和修复

5. Problem Solving - 问题解决过程

6. All User Messages - 所有用户消息

7. Pending Tasks - 待处理任务

8. Current Work - 当前工作状态

优雅降级

当压缩失败时,系统不会死板地报错或者强行应用低质量的压缩结果,而是有一整套 Plan B、Plan C。从自适应重压缩,到混合模式保留,再到最后的保守截断——每一步都在努力保护用户体验。这种”永不放弃”的设计理念,让系统在各种极端情况下都能稳定运行。

向量化搜索

长期记忆层引入向量搜索,实际上是在为 AI 构建一个”联想记忆”系统。当用户提出新问题时,系统不仅能看到当前对话,还能”回忆”起过去处理过的类似问题。这种跨会话的知识迁移能力,让 Claude Code 从一个简单的对话工具进化成了一个真正的智能编程助手。

Gemini-cli 的上下文管理

Gemini-cli 的上下文管理走了一条和 Claude Code 相似但更加轻量的路线。它的核心理念很简单:文件系统就是天然的数据库

三层混合存储架构

与 Claude Code 类似,Gemini-cli 也采用了分层设计,但实现更加简洁:

第一层:纯内存工作区

  • 存储当前会话的聊天历史、工具调用状态、循环检测状态
  • 零延迟访问,不涉及任何 I/O 操作
  • 会话结束即清空,不留痕迹

第二层:智能压缩层

  • 触发阈值:70%(比 Claude Code 的 92% 更保守)
  • 保留策略:最新 30% 的对话历史
  • 压缩产物:5 段式结构化摘要

第三层:文件系统持久化

  • 全局记忆:~/.gemini/GEMINI.md
  • 项目记忆:向上递归查找直到项目根目录
  • 子目录上下文:向下扫描并尊重忽略规则

70/30

Gemini-cli 选择了 70% 作为压缩触发点,30% 作为保留比例。这个比例设计很有讲究:

为什么是 70% 而不是 92%?

  • 更早介入,避免紧急压缩导致的卡顿
  • 给压缩过程留出充足的缓冲空间
  • 适合轻量级应用场景,不追求极限性能

30% 保留的合理性

  • 刚好覆盖最近 5-10 轮对话
  • 足够维持上下文连续性
  • 不会让用户感觉”突然失忆”

5 段式压缩:够用就好

相比 Claude Code 的 8 段式结构,Gemini-cli 的压缩更简洁:

markdown

体验AI代码助手
代码解读
复制代码

1. overall_goal - 用户的主要目标

2. key_knowledge - 重要技术知识和决策

3. file_system_state - 文件系统当前状态

4. recent_actions - 最近执行的重要操作

5. current_plan - 当前执行计划

忽略规则

Gemini-cli 的 .geminiignore 机制是个亮点:

独立但兼容

  • 可以单独在非 git 仓库中生效
  • .gitignore 并行工作,互不干扰
  • 每个工具都有独立的忽略开关

明确的约束

  • 修改 .geminiignore 需要重启会话才生效
  • 这不是 bug,而是 feature——避免运行时状态混乱

Gemini-cli 的设计哲学可以总结为:不求最优,但求够用

它没有追求理论上的完美压缩比,也没有搞复杂的向量检索,而是用最简单的方案解决了 80% 的问题。这种务实的态度在工程实践中往往更受欢迎——系统简单意味着 bug 少,维护容易,用户上手快。

特别是”文件系统就是数据库”这个理念,虽然听起来有点”土”,但在实际使用中却异常可靠。你不需要担心数据库挂了、连接断了、事务死锁了…文件就在那里,看得见摸得着,出了问题 cat 一下就知道怎么回事。

这种设计思路值得很多过度工程化的项目学习:有时候,简单就是最好的复杂。

小结

上下文是智能的边界,压缩是性能的艺术。

在与大型语言模型打交道的过程中,上下文管理已成为决定智能上限与系统稳健性的关键。虽然现代 LLM 提供了百万级 Token 的窗口,但在实际 Agent 场景中,这远远不够,尤其当涉及非结构化数据(如网页、PDF)时,Token 使用会迅速膨胀。即使有前缀缓存等机制,成本与性能的双重压力仍然存在。因此,上下文压缩成了必选项——但压缩得太激进,又会导致信息丢失,损害 Agent 的决策能力。

聪明的系统不是记住所有,而是记住该记住的。

应对上下文限制的最佳方式不是简单保留或截断历史,而是构建一个具备“记忆力”的智能系统。Claude Code 以三层记忆架构为核心(短期、高速;中期、结构化压缩;长期、跨会话向量化搜索),同时引入 TodoWrite 工具,让模型自我管理计划任务。这使得 Agent 能专注目标、灵活调整、透明运行,形成类人思维般的任务记忆系统。关键机制如 Token 反向遍历、92% 阈值缓冲、8段式摘要结构与优雅降级策略,共同打造了一个稳健又高效的上下文生态。

工程的智慧在于‘够用’,而非‘极致’。

对比 Gemini-cli、OpenManus 与 Manus 的上下文策略,可以看出不同系统在工程实现上的取舍哲学。Gemini-cli 采用实用主义的轻量分层设计,70/30 压缩策略既简单又高效,让用户可控又无需担心性能瓶颈;Manus 则大胆将文件系统作为智能体的“外部大脑”,通过引用而非存储规避 Token 限制;而 OpenManus 则为最小可运行原型提供了基础模板。这些方案展现出一个共识:上下文不一定要复杂,关键在于是否服务于目标。

以上。

Multi-Agent 系统的主从架构

最近一年,Multi-Agent 系统持续发热发烫,融资不断,火了起来。

从 AutoGPT 到 MetaGPT,从 CrewAI 到 LangGraph,各种多代理框架层出不穷。国内现象级的 Manus,以及当天由 MetaGPT 的同学开源的 OpenManus。OpenAI 的 Swarm、微软的 AutoGen,还有 Anthropic 的 Claude Code,每个大厂都在探索自己的 Multi-Agent 方案。GitHub 上相关项目的 Star 数动辄上万,社区讨论热度持续攀升。

从这股热潮,我们可以看出 AI 应用的一个重要趋势:从单一模型调用走向多智能体协作。就像软件开发从单体应用演进到微服务架构,AI 系统也在探索如何通过多个专门化的 Agent 协同工作,完成更复杂的任务。

当我们仔细观察这些 Multi-Agent 系统的架构时,会发现一个规律:

MetaGPT 里有个产品经理角色,负责协调其他工程师角色;AutoGen 支持 Manager-Worker 模式;Claude Code 更是明确采用了主循环引擎加子任务代理的设计。在 OpenAI Swarm 中,也能看到 Orchestrator Agent 的影子。

这些系统都采用了某种形式的「主从」架构——有一个 Agent 负责全局协调,其他 Agent 提供专门化支持。

为什么?巧合吗?

今天我们就聊聊 Multi-Agent 系统的主从架构。从大模型的底层原理开始。

1 从大模型的原理说起

1.1 大模型的「注意力」机制

要理解为什么需要主从架构,得先理解大模型是怎么「思考」的。

大模型的核心是 Transformer 架构,而 Transformer 的灵魂是注意力机制(Attention)。简单来说,模型在生成每个 token 时,会「注意」到输入中的所有相关信息(上下文窗口内),然后综合这些信息做出决策。

这里有个关键点:大模型的每次决策都是基于它能「看到”」的全部上下文

这就像你在做一道数学题。如果题目是”小明有 5 个苹果,给了小红 2 个”,你要回答”小明还剩几个”,你必须同时看到”5 个”和”给了 2 个”这两个信息。如果你只看到其中一部分,就无法得出正确答案。

大模型也是如此。它的智能来源于对上下文的完整理解(其智能还来源于预训练时学到的知识,模式识别、知识迁移等能力)。一旦上下文缺失或者矛盾,模型的输出质量就会急剧下降。

1.2 多个模型协作的挑战

当多个大模型(Agent)需要协作时,如何保证它们都拥有必要的上下文?

假设我们有三个 Agent 并行进行开发部署工作:

  • Agent A:负责前端开发
  • Agent B:负责后端开发
  • Agent C:负责部署运维

理想情况下,它们应该像一个经验丰富的全栈工程师一样,时刻知道其他部分的设计决策。但实际上,每个 Agent 都是独立的大模型实例,它们各自维护着自己的上下文。

这就产生了第一个问题:上下文分裂

Agent A 决定使用 React,Agent B 决定用 Python Flask,这本来没问题。但当 Agent A 后续生成代码时假设后端返回 GraphQL,而 Agent B 实际提供的是 REST API,最终代码就无法正常工作。

并且大模型有个特性叫「自回归生成」——每个新的输出都依赖于之前的所有输出。这意味着一旦某个 Agent 做出了错误假设,这个错误会在后续生成中不断放大。

2 主从架构的设计哲学

2.1 为什么主从架构有效

主从架构的核心思想很简单:一个指挥,多个执行。一个主 Agent 掌控全局,其他从 Agent 提供子领域专业化的支持

这个设计直接解决了上下文分裂的问题。主 Agent 始终维护着完整的任务上下文,它知道:

  • 整体目标是什么
  • 已经做了哪些决策
  • 各个部分如何配合
  • 当前的优先级是什么

从 Agent 则像是主 Agent 的「外脑」——当主 Agent 需要专门知识时,会调用相应的从 Agent,但最终的决策和执行都由主 Agent 完成。

在我们做具体实现的时候,每个不同的从 Agent 都有自己的角色和系统 Prompt。

2.2 Claude Code 的实践印证

Claude Code 的设计诠释了主从的理念。根据 Github 上逆向工程的分析,它的架构中:

nO 主循环引擎(主 Agent)负责:

  • 维护完整的代码上下文
  • 协调所有子任务
  • 做最终决策
  • 生成实际代码

I2A 子任务代理(从 Agent)负责:

  • 回答特定问题
  • 提供专业建议
  • 探索可能的解决方案

Claude Code 刻意避免了子 Agent 的并行修改。当需要调查多个方向时,主 Agent 会有限范围内并行地咨询不同的子 Agent,确保每个决策都基于最新的完整上下文。

这种设计看起来「低效」,但实际上避免了大量的错误和重做,总体效率反而更高。

2.3 生物学的启发

并且,主从架构在生物界也是有比较多例子的。

人脑就是一个典型的例子。前额叶皮质充当「主 Agent」,负责高级决策和规划。而各个专门脑区(视觉皮层、听觉皮层、运动皮层等)就像「从 Agent」,处理特定类型的信息。

所有的感官输入最终都会汇聚到前额叶皮质,由它整合信息并做出决策。即使是反射动作,大脑也会在事后「知晓」并可能调整后续行为。

这种中心化的架构经过了数亿年的进化验证。

3 主从架构的技术实现

3.1 上下文管理

实现主从架构,最核心的是上下文管理。主 Agent 需要:

1. 维护完整但精简的上下文

并不是所有信息都同等重要。主 Agent 需要智能地压缩和总结历史信息。Claude Code 使用了一个策略:

当 token 使用量达到阈值的 92% 时,触发压缩机制。关键决策被保留,而从 Agent 的中间探索过程被压缩或丢弃。这样既保持了决策的连贯性,又避免了上下文爆炸。

2. 构建结构化的决策记录

不要只是简单地拼接所有的对话历史。需要结构化地记录:

  • 任务目标和约束
  • 已做出的关键决策
  • 各决策之间的依赖关系
  • 待解决的问题队列

3. 动态调整上下文窗口

根据任务的复杂度和当前阶段,动态调整传递给从 Agent 的上下文量。初期探索阶段可以更开放,后期执行阶段需要更精确。

3.2 Agent 的设计原则

Agent 不是越智能越好,而是要专注可控

1. 明确的能力边界

每个从 Agent 应该有清晰定义的能力范围。比如:

  • 代码审查 Agent:只负责发现潜在问题
  • 重构 Agent:只负责改进代码结构
  • 测试 Agent:只负责生成测试用例

2. 标准化的输入输出

从 Agent 的接口要标准化,这样主 Agent 可以用统一的方式调用它们。输出格式也要规范,便于主 Agent 解析和整合。

3. 无状态设计

从 Agent 最好是无状态的,每次调用都是独立的。这样可以避免状态管理的复杂性,也便于并行化(当任务确实独立时)。

3.3 协调机制的关键点

主 Agent 的协调能力决定了整个系统的表现:

1. 任务分解策略

并不是所有任务都要分解。主 Agent 需要学会判断:

  • 简单任务直接处理
  • 复杂任务分解但保持上下文
  • 探索性任务可以并行但结果需要串行整合

2. 冲突检测与解决

即使在主从架构下,从 Agent 的建议也可能相互矛盾。主 Agent 需要:

  • 检测潜在的冲突
  • 评估不同方案的优劣
  • 做出最终决策并保持一致性

3. 优雅降级

当从 Agent 失败或不可用时,主 Agent 应该能够:

  • 尝试从其它从 Agent 获取
  • 降级到自己处理
  • 调整任务策略

4 主从架构的优势与局限

4.1 主从架构的核心优势

1. 全局一致性保证主 Agent 作为唯一的决策中心,天然保证了架构决策的一致性。不只是技术栈的选择(比如统一使用 REST 还是 GraphQL),更重要的是接口约定、错误处理策略、命名规范等细节都能保持统一。这种一致性在复杂项目中价值巨大。

2. 清晰的决策链路每个决策都有明确的来源和依据。你可以在主 Agent 的对话历史中追踪每个架构决定是如何做出的,为什么选择某个方案。这种可追溯性在调试问题或向他人解释系统设计时非常有价值。

3. 优雅的错误处理主 Agent 掌握全局状态,当某个子任务失败时,它可以准确判断影响范围并制定恢复策略。比如,如果数据库设计出错,主 Agent 知道哪些 API 设计需要相应调整。而在去中心化系统中,这种级联影响很难追踪和修复。

4. 上下文利用最大化看似串行的决策流程,实际上优化了整体效率:

  • 避免了重复劳动(多个 Agent 不会各自生成相似的代码)
  • 减少了协调开销(不需要 Agent 间的大量通信)
  • 上下文复用充分(主 Agent 的决策历史可以直接传递给从 Agent)

在 Claude Code 的实践中,这种设计让系统能在有限的 token 预算内完成相当复杂的编程任务。

4.2 主从架构的局限性

1. 主 Agent 成为性能瓶颈所有决策都要经过主 Agent,当需要并行处理多个复杂子任务时,主 Agent 的串行决策会限制整体效率。就像一个项目经理同时管理太多团队,协调成本会急剧上升。

2. 对主 Agent 能力的高度依赖系统的智能上限取决于主 Agent 的能力。如果主 Agent 对某个领域理解不深,即使有专业的从 Agent,整体表现也会受限。这就像一个不懂技术的经理,很难充分发挥技术团队的潜力。

3. 缺乏真正的协作智能主从架构本质上是”分解-执行-组合”的模式,缺少 Agent 之间的平等协商和创造性互动。在需要头脑风暴或多视角探索的任务中,这种层级结构可能限制了解决方案的多样性。

4. 任务分解的粒度难题主 Agent 需要准确判断任务分解的粒度。分得太细,协调成本高;分得太粗,从 Agent 可能无法胜任。而且随着任务复杂度增加,找到合适的分解方式越来越难。

4.3 适用场景分析

主从架构特别适合:

1. 工程化任务

  • 代码生成
  • 系统设计
  • 文档编写

这些任务需要高度的一致性和结构化。

2. 有明确目标的任务

  • 问题诊断
  • 数据分析
  • 流程自动化

目标明确时,中心化协调更高效。

3. 需要可控性的场景

  • 金融交易
  • 医疗诊断
  • 法律咨询

这些领域不能接受不可预测的行为。

不太适合:

1. 创意生成

  • 头脑风暴
  • 艺术创作
  • 探索性研究

2. 大规模并行处理

  • 日志分析
  • 图像批处理
  • 分布式爬虫

3. 对等协作

  • 多人游戏 AI
  • 群体仿真
  • 去中心化系统

5 小结

随着大模型能力的提升,主从架构也在演进:

  • 更长的上下文窗口:GPT-4 已经支持 128K 的上下文,Claude 3 甚至到了 200K。这意味着主 Agent 可以维护更完整的历史,减少信息损失。
  • 更好的指令跟随:新一代模型在指令跟随上有显著提升,从 Agent 可以更准确地理解和执行主 Agent 的指令。
  • 原生的工具调用:模型开始原生支持函数调用,这让主从 Agent 之间的接口更加标准化和可靠。

如果你要实现一个主从架构的 Multi-Agent 系统,以下是一些建议:

1. 设计清晰的 Agent 角色:不要让从 Agent 职责过于宽泛。每个从 Agent 应该像 Unix 工具一样——做一件事,并做好。

2. 实现鲁棒的错误处理

从 Agent 失败是常态,不是异常。主 Agent 需要:

  • 超时机制
  • 重试策略
  • 降级方案
  • 错误隔离

3. 优化上下文传递:控制上下文的边界,并不是所有上下文都需要传递给给到 Agent。根据任务类型,精心设计上下文的内容和格式。

4. 监控和可观测性:记录所有的决策点和 Agent 交互,后面调试和优化用得上。

Multi-Agent 的主从架构,本质上是在解决一个古老的问题:如何组织多个智能体高效地完成复杂任务

从生物进化到人类社会,从计算机架构到分布式系统,我们一次次地发现:在需要一致性和可控性的场景下,某种形式的中心化协调是必要的。

大模型的出现并没有改变这个规律。相反,由于大模型对上下文的强依赖,主从架构变得更加重要。

随着大模型能力的提升和 Agent 技术的成熟,我们会看到更多创新的架构出现。但无论如何演进,那些基本的原则——上下文一致性、决策可控性、错误可恢复性——都是我们在实践中需要谨慎考虑的。

以上。