上下文工程:从 Manus 实践看 AI 智能体的成本与性能优化

Manus 与 LangChain 团队进行了一场深度对话,话题聚焦在一个被长期忽视的技术命题:上下文工程

为什么说被忽视?因为大多数智能体开发者把精力放在提示词调优、模型选型、工具设计上,却很少系统性地思考「上下文本身」该如何管理。而 Manus 的实践表明,这恰恰是生产环境中最影响成本和性能的因素。


一、核心问题:上下文腐烂

1.1 什么是上下文腐烂?

智能体与普通聊天机器人的关键区别在于:它需要频繁调用工具,而每次工具调用的结果都会被追加到上下文中。

想象一个典型的智能体任务:用户要求「分析这个 CSV 文件并生成报告」。智能体可能需要:

  1. 读取 CSV 文件(工具调用 1,返回 5000 tokens 的文件内容)
  2. 分析数据结构(工具调用 2,返回 1000 tokens 的分析结果)
  3. 执行 Python 代码进行统计(工具调用 3-10,每次返回数百 tokens)
  4. 搜索相关背景知识(工具调用 11-15,每次返回 2000 tokens)
  5. 生成图表(工具调用 16-20)
  6. 撰写报告(工具调用 21-25)

每一次工具调用的完整输入和输出都会被追加到上下文中。到第 25 次调用时,上下文可能已经膨胀到 50k-100k tokens。

Manus 披露的数据:

  • 平均每个任务涉及 50+ 次工具调用
  • 任务长度大约每 7 个月翻一番
  • 复杂任务可能涉及 上千次工具调用

1.2 上下文腐烂的三重后果

第一,成本急剧增加

以 Claude 3 Sonnet 为例,输入价格为 $3/百万 tokens。一个 50 次工具调用的任务,假设每轮平均上下文为 30k tokens:

ini 复制代码
总输入成本 = 50 × 30,000 × $3/1,000,000 = $4.5

如果任务需要重试或涉及多轮迭代,成本会快速累积到数十美元。

第二,延迟累积

处理更长的上下文需要更多时间。首 token 延迟与上下文长度正相关:

  • 10k tokens: ~500ms
  • 50k tokens: ~2s
  • 100k tokens: ~5s

对于需要实时交互的场景,这种延迟是不可接受的。

第三,性能下降

研究表明,模型性能会随着上下文长度而衰减。大多数模型在远低于上下文上限时就开始出现问题:

  • 重复生成:模型开始重复之前说过的内容
  • 推理变慢:复杂推理能力下降
  • 遗忘早期内容:中间迷失现象
  • 指令遵循能力下降:更容易偏离用户的原始意图

这种现象被称为「上下文腐烂」,通常在 200k tokens 左右开始显现。


二、最重要的单一指标:KV 缓存命中率

2.1 为什么是 KV 缓存?

Manus 团队的判断:

「如果只能选择一个指标,键值缓存命中率是生产阶段智能体最重要的单一指标。」

这个判断基于一个关键事实:智能体的输入输出比例约为 100:1

普通聊天场景,用户输入 100 个字,模型输出 500 个字,输入输出比例约 1:5。但智能体场景完全不同------每一轮对话都要把所有历史(系统提示词 + 工具定义 + 之前所有的对话和工具调用结果)传给模型,而模型输出可能只是一个工具调用请求。

makefile 复制代码
典型智能体的一轮对话:
输入:50,000 tokens(历史上下文)
输出:500 tokens(一个工具调用)
比例:100:1

这意味着,优化输入成本的效果是优化输出成本的 100 倍

2.2 什么是提示词缓存?

提示词缓存是模型提供商提供的优化机制:

如果连续两次 API 调用的提示词前缀相同,第二次调用可以复用第一次的计算结果。

大模型的推理过程分为两个阶段:

  1. 预填充阶段:处理输入的所有 tokens,生成 KV 缓存
  2. 解码阶段:逐个生成输出 tokens

预填充阶段的计算量与输入长度成正比,是主要的延迟来源。如果输入的前缀与之前的请求相同,就可以直接复用之前计算好的 KV 缓存,跳过预填充阶段的大部分计算。

2.3 缓存命中的收益

以各厂商的定价为例:

厂商 原价($/百万 tokens) 缓存命中价 折扣率
Anthropic (Claude) $3.00 $0.30 90%
DeepSeek $0.14 $0.014 90%
OpenAI $2.50 $1.25 50%

缓存命中意味着

  • 成本降低 50-90%
  • 延迟大幅减少(跳过预填充计算)
  • 吞吐量提升(相同算力可处理更多请求)

对于一个日均百万次调用的智能体服务,缓存命中率从 50% 提升到 90%,每月可节省数十万美元。

2.4 前缀匹配的工作原理

缓存基于字节级前缀匹配,这是理解所有优化策略的基础:

ini 复制代码
第一次调用:
[System Prompt][Tool Definitions][User Message 1][Tool Output 1]
                                                  ↑
                                           缓存写入到这里

第二次调用:
[System Prompt][Tool Definitions][User Message 1][Tool Output 1][User Message 2]
└──────────────────────── 完全相同的前缀 ────────────────────────┘
                                 ↓
                           可以复用缓存!

第三次调用(前缀被破坏):
[System Prompt v2][Tool Definitions][User Message 1][Tool Output 1][User Message 2]
        ↑
  这里变了,后面的缓存全部失效!

关键约束

  • 缓存基于字节级前缀匹配,哪怕差一个空格也会失效
  • 任何位置的修改都会破坏从该位置开始的缓存
  • 前面的内容改变,后面的缓存全部失效

这就解释了为什么「前缀稳定性」如此重要------系统提示词和工具定义位于上下文的最前面,一旦变化,整个缓存链条都会断裂。


三、五个维度的平衡艺术

Manus 将上下文工程分为五个相互关联的维度:

维度 范畴 核心含义
卸载 长期持久化 把信息从上下文搬到外部存储,跨任务复用
缩减 会话级上下文 过滤 + 压缩 + 摘要,减少当前会话的上下文体积
检索 信息恢复 从外部状态(文件系统、数据库)恢复被卸载或缩减的信息
隔离 架构设计 子智能体独立上下文,过程噪音不污染主上下文
缓存 推理优化 优化 KV 缓存命中率,降低推理成本

3.1 五维度的相互作用

scss 复制代码
                    ┌─────────────┐
                    │   缓存层    │
                    │ (推理优化)  │
                    └──────┬──────┘
                           │ 影响
                           ▼
┌─────────────┐    ┌─────────────┐    ┌─────────────┐
│   卸载      │◄──►│   缩减      │◄──►│   隔离      │
│ (长期存储)  │    │ (会话管理)  │    │ (架构设计)  │
└──────┬──────┘    └──────┬──────┘    └─────────────┘
       │                  │
       └────────┬─────────┘
                ▼
         ┌─────────────┐
         │   检索      │
         │ (信息恢复)  │
         └─────────────┘

这五个维度并非相互独立,而是存在复杂的相互作用:

「卸载和检索使得缩减更为高效,而稳定的检索则使隔离变得安全。但隔离又会减慢上下文的传递速度,降低缩减的频率。然而,更多的隔离和缩减又会影响缓存效率和输出质量。所以,归根结底,上下文工程是一门艺术与科学,它要求在多个可能相互冲突的目标之间找到完美的平衡。这真的很难。」

下面逐一展开。


四、上下文卸载:长期持久化

卸载面向长期,目的是把信息从上下文搬到外部存储,实现跨任务复用。

4.1 为什么需要卸载?

上下文窗口是「短期记忆」,有容量限制,且会话结束后消失。卸载的核心思想是:

「信息被外部化了,没有真正丢失。只要信息能从外部状态重建,就应该从上下文中剔除。」

卸载解决两类问题:

  1. 长期知识的持久化:用户偏好、项目背景、过往发现
  2. 工具定义的外部化:减少工具占用的上下文空间

4.2 长期记忆系统

智能体可以主动将重要信息存储到外部,以便在未来的任务中复用。

适合卸载的内容

  • 用户偏好:代码风格、语言习惯、输出格式偏好
  • 项目背景:技术栈、架构设计、团队规范
  • 任务历史:过往任务的关键发现、解决方案
  • 领域知识:专业术语、业务规则

记忆的存储形式

json 复制代码
{
  "user_id": "user_123",
  "memories": [
    {
      "content": "用户偏好使用 TypeScript 而非 JavaScript",
      "source": "对话记录",
      "created_at": "2024-12-20",
      "importance": "high"
    },
    {
      "content": "项目使用 Next.js 14 + Tailwind CSS",
      "source": "代码分析",
      "created_at": "2024-12-18",
      "importance": "high"
    }
  ]
}

记忆的检索时机

  • 新对话开始时,检索用户相关的长期记忆
  • 遇到特定领域问题时,检索领域知识
  • 任务规划时,检索过往类似任务的经验

4.3 技能系统:工具定义的卸载

随着系统变得越来越复杂,工具本身也会占用大量上下文:

「上下文中工具过多会导致混乱,我们称之为'上下文混淆',模型可能会调用错误的工具,甚至是不存在的工具。所以我们必须找到一种方法,也能卸载工具。」

问题分析

假设一个智能体有 50 个工具,每个工具定义平均 500 tokens:

ini 复制代码
工具定义总占用 = 50 × 500 = 25,000 tokens

这 25k tokens 在每一轮对话都要传递给模型,即使大多数工具在当前任务中根本用不到。

动态检索增强的问题

一个常见的解决方案是对工具描述进行动态检索增强------根据当前任务按需加载工具。但这也会带来问题:

  1. 破坏缓存:工具定义位于上下文的前端,每次变动都会导致 KV 缓存重置
  2. 历史混乱:模型过去对那些已被移除的工具的调用记录仍然存在于上下文中,这可能会误导模型

Manus 的解决方案:分层式行为空间

Manus 试验了一种三层设计:

层级 模型可见性 示例 上下文占用
第一层:函数调用 可见 read_file, shell_exec, web_search ~5,000 tokens
第二层:沙盒工具 不可见 grep, ffmpeg, awk, curl 0
第三层:软件包与 API 不可见 Pandas, Numpy, requests, 外部 API 0

工作原理

  • 模型只看到约 10 个核心「元工具」(如 shell_execpython_execute
  • 通过这些元工具,模型可以调用第二、三层的任意工具
  • 第二、三层的工具定义不进入上下文,而是存储在文件系统中

示例

css 复制代码
用户请求:「分析这个视频的音频质量」

传统方式(工具都在上下文中):
[ffmpeg_tool] [audio_analyzer_tool] [spectral_tool] ... 
→ 50 个工具定义,25k tokens

Manus 方式(分层行为空间):
[shell_exec_tool] [python_execute_tool]
→ 模型通过 shell_exec 调用 ffmpeg
→ 2 个工具定义,1k tokens

效果

  • 最大化缓存命中率:模型可见的工具定义极度稳定(约 10 个核心工具)
  • 零额外占用:第二、三层的海量工具不占用上下文
  • 无限扩展:熟悉 Linux/Python 就拥有无限工具箱

4.4 Anthropic 的解决方案:技能系统

Anthropic 在 Claude for Enterprise 中采用了另一种思路------技能系统(Skill System)。

核心思想

  • 把复杂工具的使用方法封装为「技能文档」
  • 技能文档存储在文件系统中,不进入上下文
  • 模型需要使用某个技能时,按需加载对应的技能文档

技能文档的结构

markdown 复制代码
# 技能名称:数据可视化

## 适用场景
- 需要生成图表、数据可视化
- 支持的图表类型:折线图、柱状图、饼图、散点图

## 使用方法
1. 准备数据:确保数据为 CSV 或 JSON 格式
2. 调用 python_execute,使用 matplotlib 或 plotly
3. 保存图表到 /workspace/charts/

## 示例代码
​```python
import matplotlib.pyplot as plt
import pandas as pd

df = pd.read_csv('data.csv')
plt.figure(figsize=(10, 6))
plt.bar(df['category'], df['value'])
plt.savefig('/workspace/charts/output.png')
​```

## 注意事项
- 中文需要设置字体:plt.rcParams['font.sans-serif'] = ['SimHei']
- 大数据集建议使用 plotly,支持交互式图表

与 Manus 方案的对比

维度 Manus 分层行为空间 Anthropic 技能系统
抽象层次 底层工具(shell、python) 高层能力(数据可视化、文档处理)
学习曲线 需要模型熟悉 Linux/Python 技能文档自包含,降低认知负担
可维护性 依赖模型的通用能力 技能文档可独立更新、版本管理
缓存友好性 工具定义极度稳定 技能按需加载,可能影响缓存

实际应用中的选择

  • Manus 方案适合技术能力强的场景,模型自由度高
  • 技能系统适合标准化流程,可控性强,便于团队协作

4.5 Scratchpad 模式:主动外部化

除了系统自动的卸载,智能体还可以主动将关键发现写入文件(如 /workspace/notes/),这是独立于系统压缩/摘要的第一道防线。

特性 主动外部化 系统压缩
触发者 智能体自己(提示词引导) 系统自动(> 阈值 tokens)
存储内容 提炼后的关键发现(50-200 tokens) 原始工具输出(5000+ tokens)
恢复成本 低(读取简短笔记) 高(读取原始大文件)
存储位置 /workspace/notes/(用户可见) .context/(系统目录)

价值:在长对话中,主动外部化可避免恢复时重读大量原始数据。


五、上下文缩减:会话级管理

缩减面向短期,目的是管理当前会话的上下文体积。包含三种策略:过滤、压缩、摘要。

5.1 过滤:入口拦截

当工具返回超大结果时,系统自动拦截并转移到文件系统,同时生成智能预览传给模型。

触发条件

yaml 复制代码
单个工具结果 > 5000 tokens → 触发过滤

任务感知的混合过滤策略

关键设计是根据内容类型选择不同的过滤策略:

内容类型 过滤器 策略详情 需要模型
JSON 结构化过滤 提取键名、数组长度、嵌套结构
XML 结构化过滤 提取标签层级、属性摘要
代码 结构化过滤 提取函数/类签名、导入语句
HTML 语义过滤 模型生成任务相关摘要
Markdown 语义过滤 模型生成任务相关摘要
纯文本 语义过滤 模型生成任务相关摘要

为什么要区分?

结构化内容有明确的语法结构,可以用代码快速提取关键信息,无需调用大模型,成本为零。

非结构化内容需要理解语义才能有效摘要,必须借助大模型。

任务感知

过滤器不只是机械地提取结构信息,而是根据用户的查询意图提取相关信息。

arduino 复制代码
用户查询:「这个网页上有哪些产品的价格?」
工具返回:一个 50,000 tokens 的网页 HTML

任务无关的过滤:提取所有 <div> 标签结构
任务感知的过滤:提取所有 class="price" 和 class="product-name" 的内容

过滤后的格式

makefile 复制代码
FILTERED: web_fetch_tool
URL: https://example.com/products
FILE: /workspace/.context/web_fetch_xxx.html
PREVIEW: [任务相关的摘要,约 500 tokens]
RECOVER: 需要完整内容时,读取上述文件

5.2 压缩:可逆的紧凑格式

当总上下文达到阈值时,系统将历史中的旧工具调用结果转换为紧凑格式。

触发条件

复制代码
总上下文 > 60,000 tokens(可配置)
且预计节省 > 3,000 tokens(避免小清理破坏缓存)

信息外部化

Manus 的解释:

「假设你有一个向文件写入的工具,它可能有两个字段:路径和内容。一旦工具执行完毕,你可以确保该文件已经存在于环境中。因此,在紧凑格式中,我们可以安全地去掉超长的内容字段,只保留路径。如果你的智能体足够聪明,当它需要再次读取该文件时,只需通过路径即可检索。」

「这样一来,没有任何信息真正丢失,只是被外部化了。我们认为这种可逆性至关重要,因为智能体需要基于之前的行为和观察进行链式预测,你永远不知道哪个过去的行为会在十步之后突然变得极其重要。这是无法预测的。」

完整格式 vs 紧凑格式

vbnet 复制代码
【完整格式 - 5,000 tokens】
{
  "tool": "web_search_tool",
  "arguments": {
    "query": "Claude API pricing 2024"
  },
  "result": {
    "sources": [
      {
        "title": "Claude API Pricing | Anthropic",
        "url": "https://www.anthropic.com/pricing",
        "snippet": "Claude 3.5 Sonnet: $3/M input tokens, $15/M output tokens..."
      },
      // ... 20 个搜索结果,每个 200 tokens
    ],
    "summary": "Claude API 定价如下:..."
  }
}

【紧凑格式 - 150 tokens】
COMPACTED: web_search_tool
QUERY: Claude API pricing 2024
FILE: /workspace/.context/web_search_20241220_153000.txt
RECOVER: cat <FILE> with bash_tool
META: tokens_saved=4850 time=2024-12-20T15:30:00

压缩策略

不同工具类型有不同的压缩规则:

工具类型 保留字段 移除字段
web_search_tool query, source_count 完整搜索结果
file_write_tool path content(文件已存在)
file_read_tool path content(可重新读取)
code_execute_tool code_summary, exit_code 完整输出
web_fetch_tool url, status_code 网页内容

部分压缩策略

压缩不意味着压缩整个历史记录。Manus 的做法是:

  • 只压缩最旧的 50% 的工具调用
  • 保留最近 5 次调用的完整格式
  • 这样模型仍然有新鲜的示例来学习如何正确使用工具
yaml 复制代码
对话历史(20 次工具调用):
┌──────────────────────────────────────────────────────────┐
│  调用 1-10: 转为紧凑格式(节省约 40,000 tokens)          │
│  调用 11-15: 转为紧凑格式(节省约 20,000 tokens)         │
│  调用 16-20: 保留完整格式(作为少样本示例)               │
└──────────────────────────────────────────────────────────┘

5.3 摘要:不可逆的最后手段

压缩也有其极限。最终,上下文仍然会增长并触及上限。这时,Manus 会将压缩与更传统的摘要结合起来,但做得非常谨慎。

触发条件

复制代码
总上下文 >= 150,000 tokens(可配置)

为什么摘要是最后手段?

摘要是不可逆操作,一旦执行,原始消息结构被替换,无法恢复。这与压缩的本质区别在于:

  • 压缩:信息外部化到文件,可以随时读回
  • 摘要:信息被大模型重新生成,原始细节丢失

有损但可追溯策略

Manus 的做法:

「在进行摘要之前,我们可能会将上下文的关键部分卸载到文件中。有时,我们甚至会更激进,将整个摘要前的上下文转储为一个文本文件或日志文件,以便日后随时恢复。如果模型足够聪明,它甚至知道如何检索那些被摘要前的上下文。」

工作流程

  1. 备份 :将完整上下文转储到 /workspace/.context/summary_dump_xxx.txt
  2. 摘要:基于完整数据生成结构化摘要
  3. 替换:上下文中只保留摘要 + 备份文件路径
  4. 恢复:模型可用 grep 命令检索原始细节

结构化摘要格式

关于摘要格式,建议使用固定模式而非自由形式:

「不要使用自由形式的提示让 AI 生成所有内容,而是定义一个模式,就像一个有很多字段的表单,让 AI 去填写。如果你使用这种更结构化的模式,至少输出会比较稳定。」

json 复制代码
{
  "user_goal": "分析订单数据并生成销售报告",
  "completed_actions": [
    "读取 orders.csv(10,000 条订单记录)",
    "按月份统计销售额",
    "分析退货原因分布",
    "生成可视化图表 4 张"
  ],
  "key_findings": [
    "2024 年总销售额 $1.2M,同比增长 15%",
    "退货率 5.2%,主要原因是尺寸问题(占 65%)",
    "Q4 销售额占全年 40%"
  ],
  "files_modified": [
    "/workspace/reports/sales_2024.md",
    "/workspace/charts/monthly_sales.png",
    "/workspace/charts/return_reasons.png"
  ],
  "pending_tasks": [
    "生成 PDF 版本报告"
  ],
  "last_action": "保存 Markdown 报告"
}

保留最近调用的重要性

Manus 的经验:

「保留几个完整的工具调用和结果示例非常有帮助。因为这能让模型知道它上次停在了哪里,从而更平滑地继续工作。否则,你会发现摘要之后,模型有时会改变其风格和语气。」

5.4 一个关键洞察:紧凑格式也占空间

摘要不会「永远不触发」。关键在于紧凑格式本身也占空间(约 150 tokens/个):

工具调用数 紧凑格式累积 其他消息 总计 触发摘要?
100 ~15,000 20,000 35,000
500 ~75,000 30,000 105,000
1000 ~150,000 40,000 190,000

当对话非常长(上千次工具调用)时,紧凑格式累积最终会超过摘要阈值,此时必须触发摘要。

5.5 三者的执行顺序

markdown 复制代码
工具调用返回时
    │
    └─ 结果是否超大(> 5k tokens)?
           │
           ├─ 是 → 【过滤】
           │       ├─ 完整结果存入文件
           │       ├─ 生成任务相关预览
           │       └─ 预览进入上下文
           │
           └─ 否 → 完整结果进入上下文
                       │
                       ↓
每轮对话结束后检查总上下文
    │
    ├─ 总上下文 < 60k tokens
    │   └─ 正常运行,无需处理
    │
    ├─ 60k ~ 150k tokens
    │   └─ 【压缩】
    │       ├─ 检查压缩收益是否 > 3k tokens
    │       ├─ 压缩最旧的 50% 工具调用
    │       ├─ 保留最近 5 次完整格式
    │       └─ 原始结果存入文件系统
    │
    └─ > 150k tokens
        └─ 【摘要】
            ├─ 备份完整上下文到文件
            ├─ 生成结构化摘要
            ├─ 保留最近 5 次完整工具调用
            └─ 用摘要替换历史消息(不可逆)

5.6 阈值管理的经验值

关于阈值的经验值:

「通过大量的评估,确定那个'腐烂前'的阈值非常重要,通常在 128k 到 200k 之间。我们将它作为触发上下文缩减的信号。」

根据场景调整阈值:

场景 压缩阈值 摘要阈值 最小节省阈值 原因
短对话(<10 轮) 80k 180k 10k+ 剩余轮数少,缓存价值高
长对话(>30 轮) 60k 150k 2-3k 剩余轮数多,节省效果累积
研究任务 60k 150k 5k 大量工具调用,上下文增长快
实时交互 80k 180k 8k+ 低延迟优先,避免频繁压缩

六、上下文隔离:子智能体的独立上下文

这一策略来自 Anthropic 的多智能体架构设计。

6.1 为什么需要隔离?

核心思想是:子智能体拥有独立的上下文窗口,其探索过程中的「噪音」不会污染主智能体的上下文。

场景示例

用户请求:「调研一下 2024 年最新的 AI Agent 框架,给出对比分析」

如果不使用隔离,主智能体需要:

  1. 搜索「AI Agent 框架 2024」
  2. 访问 10+ 个网页,每个 5000 tokens
  3. 阅读多篇技术博客和论文
  4. 整理对比表格

整个过程产生的上下文:10 × 5000 = 50,000+ tokens,全部进入主智能体上下文。

使用隔离后

  • 主智能体调用 researcher_tool,传入研究主题
  • 子智能体在独立上下文中完成调研
  • 子智能体返回精炼的摘要(~1000 tokens)+ 完整报告文件路径
  • 主智能体上下文只增加 1000 tokens

6.2 隔离的工作原理

yaml 复制代码
主智能体上下文窗口
┌─────────────────────────────────────────────────────────────┐
│ [System Prompt] [Tools] [User: 调研 AI Agent 框架]           │
│ [Tool Call: researcher_tool(topic="AI Agent 框架 2024")]    │
│ [Tool Result: {summary: "...", file: "/reports/xxx.json"}]  │
│                                                             │
│ 上下文增量:~1000 tokens                                     │
└─────────────────────────────────────────────────────────────┘

子智能体上下文窗口(独立)
┌─────────────────────────────────────────────────────────────┐
│ [System Prompt: 你是研究员...]                               │
│ [web_search: AI Agent 框架 2024] → 结果 5000 tokens          │
│ [web_fetch: langchain.com] → 结果 8000 tokens               │
│ [web_fetch: crewai.com] → 结果 6000 tokens                  │
│ [web_fetch: autogen.com] → 结果 7000 tokens                 │
│ ... 更多搜索和访问 ...                                       │
│                                                             │
│ 上下文累积:~80,000 tokens(全部隔离,不影响主智能体)         │
└─────────────────────────────────────────────────────────────┘

6.3 隔离后的返回格式

json 复制代码
{
  "topic": "AI Agent 框架 2024 对比分析",
  "summary": "经过调研,2024 年主流的 AI Agent 框架包括 LangChain、CrewAI、AutoGen、Semantic Kernel 等。LangChain 生态最完善但学习曲线较陡;CrewAI 专注多智能体协作...",
  "key_points": [
    "LangChain: 生态最完善,社区活跃,但抽象层次多",
    "CrewAI: 多智能体协作首选,API 简洁",
    "AutoGen: 微软出品,与 Azure 深度集成",
    "Semantic Kernel: 企业级首选,支持 .NET/Python/Java"
  ],
  "comparison_table": "详见完整报告",
  "_isolated": true,
  "_full_result_file": "/workspace/research_results/ai_agent_frameworks_2024.json",
  "_full_result_tokens": 85000,
  "_read_instruction": "完整研究报告(85000 tokens)已保存,如需细节请读取上述文件"
}

6.4 隔离的类比

「子智能体可能累积数万 token 的过程噪音,但这些 token 被隔离在其独立上下文中------主智能体只接收精炼后的结论。这类似于组织中的层级汇报:基层员工处理大量细节,向上级仅提交摘要和结论。」


七、缓存优化的核心原理

7.1 Manus 的三条铁律

铁律一:保持提示前缀稳定

「由于大模型的自回归特性,即使是单个 token 的差异也会使该 token 之后的缓存失效。一个常见的错误是在系统提示的开头包含时间戳。」

❌ 错误做法

python 复制代码
system_prompt = f"""
当前时间:{datetime.now()}
你是一个专业的 AI 助手...
"""

每次调用,时间戳都不同,导致整个系统提示词的缓存失效。

✅ 正确做法

python 复制代码
system_prompt = """
你是一个专业的 AI 助手。
动态上下文信息会在用户消息中提供。
"""

user_message = f"""
[当前上下文]
时间:{datetime.now()}
用户偏好:{preferences}

[用户问题]
{user_query}
"""

铁律二:使上下文只追加

「避免修改之前的操作或观察。确保你的序列化是确定性的。许多编程语言和库在序列化 JSON 对象时不保证键顺序的稳定性。」

常见陷阱

  1. Python dict 序列化顺序不稳定(Python 3.7+ 已按插入顺序,但不同版本可能有差异)
  2. 浮点数精度问题0.1 + 0.2 可能产生 0.30000000000000004
  3. 随机 ID:每次生成不同的 UUID 或时间戳

解决方案

python 复制代码
import json

# 确保键顺序稳定
def stable_serialize(obj):
    return json.dumps(obj, sort_keys=True, ensure_ascii=False)

铁律三:明确标记缓存断点

「某些模型提供商或推理框架不支持自动增量前缀缓存,而是需要在上下文中手动插入缓存断点。」

Anthropic 的 cache_control 示例:

python 复制代码
messages = [
    {
        "role": "system",
        "content": [
            {
                "type": "text",
                "text": "你是一个专业的 AI 助手...",
                "cache_control": {"type": "ephemeral"}  # 标记缓存断点
            }
        ]
    },
    # ... 后续消息
]

7.2 工具定义的分层排序

工具列表动态变化会破坏缓存前缀,因为工具定义位于上下文前部,任何更改都会使后续所有内容的缓存失效。

解决方案:按稳定性分层排序

层级 稳定性 变化频率 缓存价值 工具示例
CORE 极高 几乎不变 最高 web_search, file_editor, shell
COMMON 很少变 web_fetch, memory_tool
EXTENDED 偶尔变 临时加载的技能、answer_user

分层结构

scss 复制代码
[System Prompt]           ← 完全稳定,永远缓存
[Layer 1: 核心工具定义]    ← 高度稳定,几乎不变 (web_search, file_editor)
[Layer 2: 常用工具定义]    ← 相对稳定
[Layer 3: 动态/扩展工具]   ← 易变区 (临时加载的技能)
[对话历史]                ← 增量增长

效果

ini 复制代码
第 1 轮: [System][CORE: search,file,shell][EXTENDED: planner][History 1]
第 2 轮: [System][CORE: search,file,shell][EXTENDED: memory][History 1-2]
                 |<----- 这部分可缓存 ----->|

虽然 EXTENDED 工具变了,但:
- System Prompt + CORE 工具部分仍然命中缓存
- 只有 EXTENDED 及之后需要重新计算

7.3 批量清理优于渐进清理

问题:渐进式清理(每次超过阈值就清理)会频繁破坏缓存。

yaml 复制代码
渐进式:
  轮 10: 上下文 62k → 清理到 55k → 缓存失效
  轮 15: 上下文 63k → 清理到 58k → 缓存失效
  轮 20: 上下文 65k → 清理到 60k → 缓存失效
  (3 次缓存失效,3 次重建缓存的成本)

批量式:
  轮 10-19: 上下文 62k → 80k,积累,不清理
  轮 20: 上下文 85k → 一次性清理到 50k → 缓存失效
  (1 次缓存失效,1 次重建缓存的成本)

实现方式

复制代码
tokens < 60k        → 正常运行,不处理
60k < tokens < 120k → 开始积累,等待 N 轮后批量清理
tokens > 120k       → 强制立即清理(紧急情况)

八、成本分析:压缩与缓存的博弈

8.1 核心公式

上下文工程与提示词缓存本质上是「数量减少」与「单价打折」之间的博弈。

定义两个核心变量

  • R_comp(压缩率):上下文工程把 token 数量减少了多少。例如压缩 50%,R_comp = 0.5
  • D_cache(缓存折扣率):命中缓存后的价格是原价的多少。例如 Claude 命中缓存后价格是原价的 10%,则 D_cache = 0.1
scss 复制代码
上下文工程的成本 = Input_orig × (1 - R_comp) × Price_base
缓存命中的成本   = Input_orig × D_cache × Price_base

盈亏平衡点:当 (1 - R_comp) = D_cache 时两者相等

8.2 结论:缓存通常完胜

目前主流厂商(DeepSeek、Anthropic)的缓存折扣率约为 10%。这意味着:

  • 除非上下文工程能达到 90% 压缩率,否则单纯从成本角度看,不如直接用缓存
  • 而 90% 的压缩率通常会严重丢失信息,导致模型变笨

示例计算

假设原始输入 100k tokens,Claude 原价 $3/百万:

ini 复制代码
不压缩 + 缓存命中:100k × $0.3/M = $0.03
压缩 50% + 无缓存:50k × $3/M = $0.15
压缩 50% + 缓存命中:50k × $0.3/M = $0.015(最优)

结论:压缩应该服务于缓存,而不是替代缓存。

8.3 场景化决策

场景 特点 策略 原因
静态长文本 系统设定、知识库、少样本示例 死磕缓存,不压缩 缓存打 1 折,压缩反而破坏缓存
动态长历史 50 轮对话,接近窗口上限 必须用上下文工程 物理塞不进去是首要矛盾
检索增强 每次检索内容不同 混合策略 + 前缀增厚 系统提示词缓存 + 检索内容按需处理

前缀增厚策略

对于检索增强场景,一个进阶技巧是让系统提示词变「厚」:

  • 加入 10-20 个高质量的少样本示例
  • 把前缀从 500 tokens 扩展到 3000-5000 tokens
  • 缓存收益从 5% 提升到 30-50%

九、各模型提供商对比

9.1 机制对比

提供商 触发方式 折扣率 写入成本 TTL 最小长度
Anthropic 自动 + 显式 90% 原价 25% 5分钟/1小时 1024-4096
OpenAI 纯自动 50% 5-10 分钟 1024
DeepSeek 纯自动 ~90% 动态 64
Google 显式 + 隐式 75% $1/M/小时 1小时-1天 1k / 32k
阿里云 隐式 + 显式 80%/90% 隐式无/显式125% 不确定/5分钟 256 / 1024

9.2 Anthropic (Claude) 的注意事项

Claude 的缓存并非 100% 保证命中,存在以下限制:

  1. 精确匹配要求 :缓存命中需要 100% 相同 的提示词段,包括所有文本和图像的字节级一致

  2. 20 块回溯窗口 :系统仅检查每个显式 cache_control 断点之前的最多 20 个块 。如果修改发生在 20 个块之外,将无法命中缓存

  3. 并发请求限制 :缓存条目仅在第一个响应开始后才可用。并行请求可能无法命中刚创建的缓存

  4. 组织隔离:缓存在组织之间隔离,不同组织永远不会共享缓存

最小缓存长度要求

模型 最小缓存长度
Claude Opus 4.5 4096 tokens
Claude Sonnet 4.x 1024 tokens
Claude Haiku 4.5 4096 tokens
Claude Haiku 3.x 2048 tokens

9.3 阿里云的特殊机制

阿里云的隐式缓存与显式缓存互斥,单个请求只能应用其中一种模式。

隐式缓存(自动模式):

  • 命中概率:不确定,由系统判定
  • 即使请求上下文完全一致,仍可能未命中
  • 优势:无额外写入成本

显式缓存(主动模式):

  • 命中概率:确定性命中
  • 需要在 messages 中加入 cache_control 标记
  • 写入成本:输入价格的 125%
  • 命中价格:输入价格的 10%

9.4 统一优化策略

好消息 :所有提供商都基于相同的核心机制------前缀匹配

这意味着优化策略具有通用性:

  1. ✅ 保持前缀稳定
  2. ✅ 工具定义分层
  3. ✅ 只追加不删除
  4. ✅ 批量清理

十、实践建议

10.1 系统提示词设计

python 复制代码
# ❌ 不推荐:每次调用都变化
system = f"""
当前时间:{datetime.now()}
你是 AI 助手,帮助用户 {user_name} 完成任务。
"""

# ✅ 推荐:系统提示词稳定,动态信息放在用户消息中
system = """
你是一个专业的 AI 助手。
动态上下文信息会在每次对话中提供。
请根据上下文信息个性化地回应用户。
"""

messages = [
    SystemMessage(content=system),  # 稳定的前缀
    HumanMessage(content=f"""
[当前上下文]
时间:{datetime.now()}
用户:{user_name}
偏好:{user_preferences}

[用户问题]
{user_query}
"""),
]

10.2 工具定义顺序

python 复制代码
# 按稳定性排序
tools = [
    # CORE 层:最稳定,放最前面
    web_search_tool,
    file_editor_tool,
    shell_execute_tool,
    
    # COMMON 层:相对稳定
    web_fetch_tool,
    memory_tool,
    
    # EXTENDED 层:可能变化,放最后
    *dynamically_loaded_skills,
    answer_user_tool,
]

10.3 序列化的确定性

python 复制代码
import json
from collections import OrderedDict

def stable_serialize(data: dict) -> str:
    """确保序列化结果的字节级一致性"""
    return json.dumps(
        data,
        sort_keys=True,        # 键排序
        ensure_ascii=False,    # 保留中文
        separators=(',', ':'), # 紧凑格式,无多余空格
    )

# 测试
data = {"b": 2, "a": 1}
assert stable_serialize(data) == '{"a":1,"b":2}'

10.4 监控指标

python 复制代码
# 关键监控指标
metrics = {
    "cache_hit_rate": "缓存命中率,目标 > 80%",
    "context_length_p95": "上下文长度 P95,监控增长趋势",
    "compression_frequency": "压缩触发频率,过高说明阈值设置不合理",
    "summarization_count": "摘要触发次数,应该很少",
    "ttft_p95": "首 token 延迟 P95,反映缓存效果",
}

十一、写在最后

上下文工程在智能体开发中越来越重要。

Manus 的实践数据:

  • 推理次数:从平均 4.3 次降至 1.2 次(-72%)
  • 总成本:降低 72%

这个效果来自两个层面的叠加:

  1. 缓存层:提高命中率,降低单次推理成本
  2. 准确率层:减少失败重试,降低调用次数

用 Manus 的话来说:

「上下文工程是一门艺术与科学,它要求在多个可能相互冲突的目标之间找到完美的平衡。这真的很难。」

如果只能做一件事,从监控缓存命中率开始。


参考资料

相关推荐
hg01182 小时前
靖州首次从非洲进口初加工茯苓
大数据·人工智能
跨境猫小妹2 小时前
2025 TikTok Shop:从内容爆发到系统化深耕的商业跃迁
大数据·人工智能·算法·产品运营·亚马逊
ccLianLian2 小时前
CASS总结
人工智能·深度学习
倔强的小石头_2 小时前
Python 从入门到实战(十):Pandas 数据处理(高效搞定表格数据的 “瑞士军刀”)
人工智能·python·pandas
三更两点2 小时前
构建企业级智能体AI系统的七层架构
人工智能·架构
探索宇宙真理.2 小时前
WordPress AI Engine信息泄露漏洞 | CVE-2025-11749 复现&研究
人工智能·经验分享·开源·安全漏洞
_妲己2 小时前
stable diffusion的MLSD直线(AI室内设计)
人工智能·stable diffusion
FF-Studio3 小时前
Ubuntu 24.04 磁盘爆满“灵异“事件:Btrfs, Snapper 与删不掉的空间
linux·运维·人工智能·ubuntu
2401_841495643 小时前
【自然语言处理】关系性形容词的特征
人工智能·python·自然语言处理·自动识别·特征验证·关系性形容词·语言学规则和计算