AI代理上下文工程入门:从Manus构建经验学习

AI代理上下文工程入门:从Manus构建经验学习。早上看到 Manus的文章,发现很多内容属于“每个字都理解,但是需要大量的知识点来补充才能更好的消化和应用”,看到很多大佬的解读都是面向纯技术的,我打算在Gemini+Claude的帮助下写一个小白也能看到的解析。所有图片源于Manus的配图。

AI代理上下文工程入门:从Manus构建经验学习
Photo by Finn / Unsplash

早上看到 Manus的文章,发现很多内容属于“每个字都理解,但是需要大量的知识点来补充才能更好的消化和应用”,看到很多大佬的解读都是面向纯技术的,我打算在Gemini+Claude的帮助下写一个小白也能看到的解析。所有图片源于Manus的配图。

原文见:https://manus.im/blog/Context-Engineering-for-AI-Agents-Lessons-from-Building-Manus

什么是上下文工程?为什么重要?

上下文工程(Context Engineering)是指设计和优化输入给AI模型的文本内容(即"上下文"),使模型能够更好地理解任务并产生期望的输出。对于AI代理(能够自主执行任务的AI系统)来说,这一点尤为重要。

想象一下:上下文就像是AI的"工作记忆",包含了它需要了解的所有信息。如何组织这些信息,决定了AI的表现好坏。

选择方案:训练新模型 vs 利用现有模型的上下文学习能力

文章开头讨论了两种构建AI代理的方法:

  1. 训练专门的模型:从开源基础模型开始,针对特定任务进行训练和微调
  2. 上下文学习:利用现有大型语言模型(如GPT、Claude)通过上下文提示来完成任务

为什么Manus选择了上下文学习?

  • 历史教训:作者分享了他在NLP领域十年的经验。以前(BERT时代,约7年前),每次想让AI做新任务,都需要重新训练模型,这个过程可能需要数周时间。
  • 快速迭代:使用上下文学习,可以在几小时内而非几周内发布改进,这对于快速发展的产品至关重要。
  • 适应模型进步:如果模型发展是"涨潮",那么使用上下文学习的产品就像"船"一样能随之升高,而不是固定在海底的"柱子"。

作者幽默地将他们的开发过程称为"随机研究生下降"(Stochastic Graduate Descent),指的是通过不断尝试不同方案、调整提示词和进行实验来找到最佳解决方案的过程。

KV缓存:理解AI代理的性能关键

什么是KV缓存?

KV缓存(Key-Value Cache)是一种技术,用于存储模型已经处理过的文本对应的内部表示,避免重复计算。简单来说:

  • 当AI模型读取文本时,它会将文本转换为内部表示(键值对)
  • 如果下次遇到相同的文本,可以直接使用缓存的表示,而不用重新计算
  • 这大大提高了处理速度,降低了计算成本

为什么对AI代理特别重要?

AI代理工作方式如下:

  1. 接收用户输入
  2. 选择一个动作(如搜索网页、运行代码等)
  3. 执行该动作并获得结果
  4. 将结果添加到上下文中
  5. 基于更新后的上下文选择下一个动作
  6. 重复直到任务完成

在这个过程中:

  • 上下文不断变长(每次添加新的动作和结果)
  • 输出通常很短(如函数调用)
  • 因此,输入对输出的比例很高(Manus中约为100:1)

KV缓存的经济效益:

  • 使用Claude模型时,缓存的输入令牌成本为0.30美元/百万令牌
  • 未缓存的输入令牌成本为3美元/百万令牌
  • 这是10倍的差异!

如何提高KV缓存命中率?实用技巧:

    • 避免在系统提示开头包含时间戳
    • 即使一个字符的变化也会导致后续缓存失效
    • 确保序列化方式一致(例如JSON键的顺序)
    • 模型按字符串处理文本:AI模型不理解"JSON语义",它只看到一系列字符
    • 缓存比较是基于精确匹配:即使只有顺序不同,也会被视为完全不同的输入
    • 自回归处理机制:模型从左到右处理文本,任何差异都会导致从该点开始的缓存失效
    • 使用有序字典或对象
    • 实现一致的序列化函数
    • 考虑对键进行排序(如字母顺序)
  1. 明确标记缓存断点
    • 在系统提示结束处设置缓存断点

只追加内容,不修改已有内容为什么JSON键顺序如此重要?在AI代理系统中,JSON对象中键的顺序看似微不足道,但实际上可能对性能和成本产生巨大影响。这是因为:看看这个例子:

❌ 错误示例:
{
  "time": "14:30:22",
  "action": "search"
}

✅ 正确示例(保持一致的键顺序):
{
  "action": "search",
  "time": "14:30:22"
}

虽然这两个JSON对象从人类角度看是完全相同的(包含相同的信息),但从模型处理的角度看,这是两个完全不同的文本序列。如果系统不保持一致的键顺序,每次生成类似结构时可能会产生不同顺序,导致缓存命中率大幅下降。实用解决方案

# Python示例:确保一致的JSON序列化
def serialize_action(action, time):
    # 总是按相同顺序构建字典
    return json.dumps({
        "action": action,  # 始终将action放在前面
        "time": time
    })

# 或使用排序键
def consistent_json(obj):
    return json.dumps(obj, sort_keys=True)

保持提示前缀稳定

❌ 错误示例:
当前时间:2025-07-21 14:30:22
你是一个AI助手...

✅ 正确示例:
你是一个AI助手...

对于开发者来说,这意味着您应该设计一个稳定的系统提示结构,并确保只向上下文追加内容,而不是修改或重写它。

工具空间管理:遮蔽比删除更好

问题背景:

随着AI代理能力增强,可用工具数量会迅速增长。在Manus这样的系统中,用户甚至可以自行添加工具。但工具过多会导致:

  • 模型可能选择错误的工具
  • 执行效率降低
  • 推理路径变得复杂

为什么不能动态添加/删除工具?

直觉上,我们可能想要根据需要动态加载工具,类似于RAG(检索增强生成)。但这有两个问题:

  1. 缓存失效:工具定义通常位于上下文前部,修改会导致整个缓存失效
  2. 模型混淆:如果先前的动作引用了现在已不存在的工具,模型会感到困惑

Manus的解决方案:使用状态机和遮蔽

不是物理删除工具,而是:

  • 使用上下文感知的状态机管理工具可用性
  • 在解码阶段遮蔽(mask)某些选项,防止模型选择特定工具

函数调用的三种模式(以Hermes格式为例):

指定模式:模型必须从特定子集调用函数

<|im_start|>assistant<tool_call>{"name": "browser_

必需模式:模型必须调用函数,但可以自由选择哪一个

<|im_start|>assistant<tool_call>

自动模式:模型可以选择调用或不调用函数

<|im_start|>assistant

实用技巧:工具命名策略

Manus有意设计具有一致前缀的工具名称:

  • 所有浏览器相关工具以browser_开头
  • 所有命令行工具以shell_开头

这样可以轻松限制模型在特定时刻只能选择某一类工具,而无需修改上下文。

文件系统作为扩展上下文:解决记忆限制

问题背景:

即使现代大型语言模型有12.8万令牌的上下文窗口,在实际代理场景中仍面临三个问题:

  1. 观察结果过大:网页或PDF等内容可能非常庞大,轻松超过上下文限制
  2. 性能下降:模型性能在超过一定上下文长度后会下降
  3. 成本高昂:长输入意味着更高的API调用成本

传统解决方案的缺陷:

许多系统使用上下文截断或压缩策略,但这可能导致关键信息丢失。你永远不知道哪个细节在十步后会变得重要。

Manus的创新方法:文件系统作为上下文扩展

将文件系统视为无限大小的外部记忆:

  • 代理可以将信息写入文件
  • 需要时再从文件中读取
  • 信息永久保存,不受上下文窗口限制

实际应用示例:

# 当处理大型网页时
1. 代理下载网页内容
2. 将完整内容保存到文件:webpage_content.txt
3. 在上下文中只保留URL和文件路径
4. 需要信息时,读取文件内容

可恢复的压缩策略:

Manus的原则是:只要信息可以恢复,就可以从上下文中移除。例如:

  • 网页内容可以删除,只要保留URL(可以重新访问)
  • 文档内容可以省略,只要保留文件路径(可以重新读取)

这种方法启发了作者对未来AI模型的思考:如果状态空间模型(SSM,一种比Transformer更高效的模型架构)能掌握文件系统交互,它们可能成为更高效的代理基础。

通过复述维持注意力:解决长任务中的目标遗忘

有趣的观察:

如果您使用过Manus,可能会注意到它有个特别的习惯:当处理复杂任务时,它会创建一个todo.md文件,并随着任务进行不断更新,勾选已完成的项目。

这不只是一个可爱的习惯,而是一个精心设计的机制。

问题背景:

Manus中的典型任务平均需要约50个工具调用。在这么长的过程中,模型容易:

  • 偏离主题
  • 忘记最初的目标
  • 在复杂任务中迷失方向

解决方案:复述重点内容

通过不断重写待办事项列表,Manus实际上是在:

  • 将任务目标重复写入上下文末尾(最近的部分)
  • 将全局计划推入模型的注意力范围
  • 避免"迷失在中间"问题(模型对上下文中间部分的注意力较弱)

实用启示:

当设计需要多步骤完成的AI任务时,让AI定期"复述"当前目标和进度是保持任务聚焦的有效方法。这可以是:

  • 待办事项列表
  • 进度摘要
  • 目标回顾

保留错误信息:AI代理的"错题集"机制

现实认知:代理会犯错

AI代理会出错,这不是bug而是正常现象:

  • 语言模型会产生幻觉
  • 环境可能返回错误
  • 外部工具可能失败
  • 意外情况随时可能发生

常见但错误的做法

当错误发生时,开发者通常倾向于:

  • 清理错误记录
  • 重试失败的动作
  • 重置模型状态

这看起来更干净、更可控,但实际上弊大于利。

"错题集"概念:从失败中学习

Manus采用的方法类似于为AI建立一个"错题集"。就像学生通过错题集避免重复犯相同的错误,AI代理通过保留错误信息来改进自己的行为。

错题集机制的工作原理:

  1. 记录失败经验
    • 就像学生在错题集中记录做错的题目和正确答案
    • AI代理在上下文中保留失败的动作和错误信息
  2. 隐式学习
    • 学生复习错题集时会思考"我之前为什么做错了"
    • AI通过上下文中的错误记录隐式调整其行为概率分布
  3. 避免重复错误
    • 学生在下次遇到类似问题时会想起错题集中的教训
    • AI在决策时会自然降低选择相似失败路径的概率

与传统错题集的不同:

  • 无需显式标记:不需要明确告诉AI"这是错误示范"
  • 即时学习:不需要特意"复习"错题集
  • 概率性调整:不是硬性规则,而是概率调整

实例说明:为什么保留错误记录更有效

# 错误的方式
Agent: 我将运行命令 `rm file.txt`
[错误发生,但被清除]
Agent: 让我尝试其他方法...

# 更好的方式(错题集方法)
Agent: 我将运行命令 `rm file.txt`
System: 错误:文件不存在
Agent: 我明白了,文件不存在。让我先检查当前目录中有哪些文件...

编程任务中的错题集效应

AI: 我将编写一个解析JSON的函数。
[AI编写代码]
```javascript
function parseJSON(jsonString) {
  return JSON.parse(jsonString);
}

const data = parseJSON(userInput);
console.log(data.name);

用户测试: 当输入无效JSON时发生错误

AI: 我注意到如果输入无效JSON,会抛出异常。我应该添加错误处理。 [AI修改代码]

function parseJSON(jsonString) {
  try {
    return JSON.parse(jsonString);
  } catch (error) {
    console.error("Invalid JSON input:", error.message);
    return null;
  }
}

const data = parseJSON(userInput);
if (data) {
  console.log(data.name);
}

用户: 现在请编写一个解析XML的函数

AI: 根据之前的经验,我将确保包含错误处理。 [AI编写新代码]

function parseXML(xmlString) {
  try {
    const parser = new DOMParser();
    const xmlDoc = parser.parseFromString(xmlString, "text/xml");
    
    // 检查解析错误
    const parseError = xmlDoc.getElementsByTagName("parsererror");
    if (parseError.length > 0) {
      throw new Error("Invalid XML input");
    }
    
    return xmlDoc;
  } catch (error) {
    console.error("XML parsing failed:", error.message);
    return null;
  }
}

const xml = parseXML(userInput);
if (xml) {
  // 处理XML
}

在这个例子中,AI从第一个任务中"学到"了需要添加错误处理,并将这一教训应用到了第二个任务中,即使没有明确要求添加错误处理。

实际应用建议

如果您想在自己的AI代理系统中实现这种"错题集"机制,可以:

  1. 设计富有信息的错误消息
    • 不要只返回"错误",而是提供具体原因
    • 例如:"文件不存在"比"操作失败"更有帮助
  2. 保持上下文连贯性
    • 不要在错误发生后重置或清除上下文
    • 让错误成为上下文历史的自然部分
  3. 考虑长期任务的错误管理
    • 对于复杂任务,可以考虑定期"总结"遇到的错误
    • 例如:"到目前为止,我尝试了X、Y和Z方法,其中X和Y失败了,原因是..."

作者认为,错误恢复能力是真正智能代理行为的重要标志,但在大多数研究和评估中被忽视了。

避免少样本陷阱:多样性的重要性

什么是少样本提示(Few-shot prompting)?

少样本提示是一种常用技术:提供几个示例,让AI模型学习并模仿这种模式。例如:

问题:2+2=? 答案:4

问题:3+5=? 答案:8

问题:7+9=? 答案:

在代理系统中的问题:

语言模型是优秀的模仿者,但这在代理系统中可能适得其反。如果上下文中充满类似的动作-观察对,模型会倾向于重复这种模式,即使不再适用。

实际例子:简历审查任务

当使用Manus审查20份简历时,代理容易陷入机械重复:

  • 对第一份简历执行了一系列动作
  • 这些动作-观察对进入上下文
  • 处理后续简历时,倾向于执行完全相同的动作序列
  • 导致过度泛化、忽略每份简历的独特性

Manus的解决方案:引入结构化多样性

在上下文中有意引入小的变化:

  • 不同的序列化模板
  • 替代性措辞
  • 顺序或格式的微小变化

这种"受控随机性"打破了模式,防止代理陷入重复。

实用启示:

如果您设计一个需要处理多个类似项目的AI代理(如审查多份文档、分析多个数据集),确保:

  • 不要让上下文变得过于模式化
  • 鼓励代理为每个项目"重新思考"
  • 引入适当的多样性

实用总结:开发自己的AI代理系统

基于Manus团队的经验,以下是构建有效AI代理系统的关键原则:

  1. 优化KV缓存
    • 保持系统提示稳定
    • 只追加内容,不修改已有内容
    • 注意序列化的一致性
  2. 智能管理工具空间
    • 使用遮蔽而非删除工具
    • 采用一致的工具命名约定
    • 使用状态机控制工具可用性
  3. 扩展记忆能力
    • 将文件系统作为外部记忆
    • 设计可恢复的压缩策略
    • 只保留必要信息在主上下文中
  4. 维持长期目标关注
    • 让代理定期"复述"任务目标
    • 使用待办事项列表或进度摘要
    • 将重要信息放在上下文末尾
  5. 实现"错题集"机制
    • 保留失败尝试及其结果
    • 提供丰富的错误信息
    • 让AI自主从错误中学习
    • 培养错误恢复能力
  6. 避免过度模式化
    • 引入适当的多样性
    • 防止代理陷入重复模式
    • 鼓励针对每个任务"重新思考"

扩展阅读资源

如果您想深入了解文章中提到的概念,以下是一些入门资源:

  1. 上下文学习
  2. KV缓存
  3. 代理系统
  4. 少样本学习
  5. 状态空间模型