上下文工程、AI 智能体·

AI 智能体的上下文工程:构建 Manus 的经验总结 [译]

在建立 AI 智能体的过程中,我们面临哪些关键选择,以及如何通过上下文工程改善系统性能。

Manus 项目的最初阶段,我的团队和我面临一个关键决策:我们应该使用开源基础构建一个端到端的智能体模型,还是基于前沿模型的上下文学习能力构建智能体?

在我进入自然语言处理(NLP)的前十年,我们并没有这样的选择。在那个遥远的 BERT 时代(是的,距离现在已经七年了),模型必须经过微调和评估才能转移到新的任务。尽管当时的模型相比现在的 LLM(大语言模型)小得多,但这一过程通常需要数周时间才能完成。对于快速变动的应用,尤其是在产品市场契合度(PMF)之前,这种缓慢的反馈循环是致命的。这是我在上一家公司创业时获得的苦涩经验,当时我从零开始训练模型用于开放信息抽取和语义搜索。然后,GPT-3Flan-T5 的出现使得我的内部模型在一夜之间变得无关紧要。讽刺的是,这些相同的模型标志着上下文学习的开端,以及一条全新的前进道路。

这段经历让我明确了选择:Manus 将致力于上下文工程。这使我们能够在几个小时内推出改进,而不是数周,并保持我们的产品与底层模型正交:如果模型进步是上涨的潮水,我们希望 Manus 是船,而不是固定在海床上的柱子。

然而,上下文工程的实施却并非简单。它是一门实验科学——我们已经对智能体框架进行了四次重建,每次都是在发现更好的上下文塑造方法后进行的。我们亲切地称这种手动的架构搜索、提示调整和经验猜测的过程为「随机梯度下降」(Stochastic Graduate Descent)。这并不优雅,但却有效。

本文分享了我们通过自己的「SGD」达到的局部最优解。如果您正在构建自己的 AI 智能体,希望这些原则能帮助您更快收敛。

设计围绕 KV-Cache

如果让我选择一个指标,我认为 KV-cache 命中率是生产阶段 AI 智能体中最重要的指标。这直接影响延迟和成本。为了理解其原因,让我们看看一个典型的智能体是如何运作的:

在接收到用户输入后,智能体通过一系列工具使用来完成任务。在每次迭代中,模型根据当前上下文从预定义的动作空间中选择一个动作。然后在环境中执行该动作(例如,Manus 的虚拟机沙箱),以产生观察结果。该动作和观察结果添加到上下文中,形成下一次迭代的输入。这一循环持续进行,直到任务完成。

如您所想,随着每一步的推进,上下文会不断增长,而输出(通常是结构化的函数调用)仍然相对较短。这使得预填充和解码之间的比率在智能体中相较于聊天机器人偏差很大。在 Manus 中,例如,平均的输入与输出的 Token 比率约为 100:1。

幸运的是,具有相同前缀的上下文可以利用 KV-cache,这大幅降低了首次 Token 的时间(TTFT)和推理成本——无论您是在使用自我托管模型还是调用推理 API。我们所谈论的不是小的节省:例如,使用 Claude Sonnet 时,缓存的输入 Tokens 成本为 0.30 USD/MTok,而未缓存的成本为 3 USD/MTok,差异达 10 倍。

从上下文工程的角度来看,提高 KV-cache 命中率涉及到几个关键实践:

  1. 保持提示前缀稳定。由于 LLM 的自回归性质,即使是单个 Token 的差异也会使得从该 Token 开始的缓存失效。一个常见的错误是在系统提示的开头包含时间戳——特别是精确到秒的。这样做确实可以让模型告诉您当前的时间,但同时也会降低您的缓存命中率。
  2. 使上下文只能附加。避免修改先前的动作或观察。确保您的序列化是确定性的。许多编程语言和库在序列化 JSON 对象时并不保证稳定的键顺序,这可能会悄然破坏缓存。
  3. 在需要时明确标记缓存断点。某些模型提供者或推理框架不支持自动增量前缀缓存,而需要在上下文中手动插入缓存断点。分配这些时,请考虑潜在的缓存过期,至少确保断点包括系统提示的末尾。

此外,如果您使用像 vLLM 这样的框架来自我托管模型,请确保启用前缀/提示缓存,并使用会话 ID 等技术在分布式任务之间一致地路由请求。

遮蔽,而非删除

随着您的智能体承担更多的能力,它的动作空间自然会变得更加复杂——简单来说,工具的数量激增。最近 MCP 的流行只使问题更加严重。如果允许用户配置工具,相信我:有人肯定会将数百个神秘的工具接入您精心策划的动作空间。结果,模型更有可能选择错误的动作或采取低效的路径。总之,您的武装到牙齿的智能体会变得更愚蠢。

一种自然反应是设计一个动态的动作空间——也许使用类似 RAG 的方式按需加载工具。我们在 Manus 中也尝试过这种方式。但我们的实验表明一个明确的规则:除非绝对必要,避免在迭代过程中动态地添加或删除工具。主要有两个原因:

  1. 在大多数 LLM 中,工具定义在序列化后通常位于上下文的前面,通常在系统提示之前或之后。因此,任何更改都会使随后的所有动作和观察的 KV-cache 无效。
  2. 当先前的动作和观察仍然引用当前上下文中不再定义的工具时,模型会感到困惑。在没有约束解码的情况下,这通常会导致架构违规或虚幻行为。

为了在改善动作选择的同时解决这个问题,Manus 使用一个上下文感知的有限状态机(state machine)来管理工具的可用性。它并不删除工具,而是在解码时掩蔽 Token 的 logits,以防止(或强制)根据当前上下文选择某些动作。

在实践中,大多数模型提供者和推理框架支持某种形式的响应预填充,这使您能够在不修改工具定义的情况下约束动作空间。一般有三种函数调用模式(我们将使用 NousResearch 的 Hermes 格式作为示例):

  • 自动 – 模型可能选择调用函数或不调用。通过仅填充回复前缀来实现:<|im_start|>
  • 必需 – 模型必须调用函数,但选择不受限制。通过填充到工具调用 Token 为止来实现:<|im_start|><tool_call>
  • 指定 – 模型必须从特定子集调用函数。通过填充到函数名称开头为止来实现:<|im_start|><tool_call>{"name": "browser_"

通过这种方式,我们直接通过掩蔽 Token logits 来约束动作选择。例如,当用户提供新输入时,Manus 必须立即回复,而不是采取行动。我们还有意设计了具有一致前缀的动作名称——例如,所有与浏览器相关的工具以 browser_ 开头,命令行工具以 shell_ 开头。这使我们可以轻松地强制智能体在给定状态下仅从某一组工具中选择,而无需使用有状态的 logits 处理器。

这些设计有助于确保 Manus 智能体循环保持稳定——即使是在模型驱动的架构下。

利用文件系统作为上下文

现代前沿 LLM 现在提供 128K Token 或更多的上下文窗口。但在现实世界的智能体场景中,这通常是不够的,有时甚至是一个负担。有三个常见的问题:

  1. 当智能体与像网页或 PDF 这样的非结构化数据交互时,观察结果可能会非常庞大。很容易突破上下文限制。
  2. 模型性能往往在达到某一定长上下文后下降,即便窗口从技术上讲支持。
  3. 长输入成本高,即便使用前缀缓存。您仍然需要为传输和预填充每个 Token 支付费用。

为了应对这一点,许多智能体系统实施了上下文截断或压缩策略。但过于激进的压缩最终不可避免地会导致信息丢失。这个问题是根本性的:智能体必须根据所有先前状态预测下一个动作,而您无法可靠地预测哪些观察结果在十步之后可能变得关键。从逻辑上讲,任何不可逆的压缩都带来了风险。

这就是为什么我们在 Manus 中将文件系统视为最终上下文:大小无限,天然持久,并且可被智能体直接操作。模型学会按需写入和读取文件——不仅将文件系统作为存储,同时也作为结构化的、外部化的记忆。

我们的压缩策略始终设计为可恢复。例如,只要保留 URL,网页内容可以从上下文中丢弃;如果路径保存在沙箱中,文档内容可以被省略。这使 Manus 能够缩短上下文长度,而不会永久丢失信息。

在开发这一功能时,我想象了在智能体场景中有效工作的状态空间模型(SSM)需要什么。与 Transformer 不同,SSM 缺乏完整注意力,并且在处理长距离反向依赖时挣扎。但如果它们能够掌握基于文件的记忆——外部化长期状态而不是将其保留在上下文中——那么它们的速度和效率可能会开启一类新的智能体。智能体 SSM 可能是 Neural Turing Machines 的真正继承者。

通过重述操控注意力

如果您曾经使用过 Manus,您可能会注意到一个有趣的现象:在处理复杂任务时,它往往会创建一个 todo.md 文件,并在任务进展过程中逐步更新,勾选已完成的项目。

这不仅仅是可爱的行为——这是操控注意力的有意机制。

一个典型的 Manus 任务平均需要大约 50 次工具调用。这是一个较长的循环——由于 Manus 依赖 LLM 进行决策,因此在长上下文或复杂任务中,它容易出现偏离主题或忘记早期目标的情况。

通过不断重写待办事项列表,Manus 正在将其目标重述到上下文的末端。这将全球计划推向模型的最近注意力范围,避免「中间迷失」问题并减少目标不一致。实际上,它通过自然语言来偏向于任务目标的关注——而无需特殊的架构更改。

保留错误信息

智能体会犯错误。这并不是一个缺陷——这是现实。语言模型会产生幻觉,环境返回错误,外部工具不正常工作,意外的边缘情况时常出现。在多步骤任务中,失败并不是例外,而是循环的一部分。

然而,一个普遍的冲动是隐藏这些错误:清理轨迹、重试动作,或者重置模型的状态并让其依赖神秘的「温度」。这看起来更加安全、更易于控制。但这会带来代价:抹去失败会移除证据。在没有证据的情况下,模型无法适应。

我们发现,改善智能体行为的一种有效方法却出奇简单:在上下文中保留错误。在模型看到失败的动作——以及随之而来的观察或堆栈跟踪后,它会隐含地更新其内部信念。这使得它的先验从类似的动作中移动,减少重复相同错误的可能性。事实上,我们认为错误恢复是灵活行为最清晰的指标之一。然而,它在大多数学术研究和公共基准测试中仍然显得被低估,后者通常关注于理想条件下的任务成功。

不要让自己陷入少样本困境

少样本提示(few-shot prompting)是一种常见的提高 LLM 输出的技巧。然而,在智能体系统中,它可能以微妙的方式适得其反。

语言模型是优秀的模仿者;它们会模仿上下文中的行为模式。如果您的上下文充满了类似的过去动作-观察对,模型往往会倾向于遵循这种模式,即使当它不再最优时。

在涉及重复决策或动作的任务中,这可能是危险的。例如,当使用 Manus 帮助审阅一批 20 份简历时,智能体往往会陷入一种节奏——仅仅因为它在上下文中看到的相似动作而重复类似的行为。这样会导致偏离、过度概括,有时甚至产生幻觉。

修复方法是增加多样性。Manus 在行动和观察中引入小幅度的结构变异——不同的序列化模板、替代措辞、顺序或格式上的小噪声。这种受控的随机性有助于打破模式并微调模型的关注。换句话说,不要让少样本使您陷入困境。您的上下文越统一,您的智能体越脆弱。

结论

上下文工程仍然是一门新兴科学,但对于智能体系统而言,它已经成为必需。模型可能正变得更加强大、更快速和更便宜,但无论多么强大的能力都无法替代对记忆、环境和反馈的需求。您如何塑造上下文最终决定了智能体的行为:它运行的速度、恢复的效率,以及它的扩展能力。

在 Manus,我们通过反复重写、遭遇死胡同和在数百万用户中进行真实世界测试吸取了这些教训。我们在这里分享的内容并不都是普遍真理,但这些是在我们身上奏效的模式。如果它们帮助您避免一次痛苦的迭代,那么这篇文章就达到了它的目的。

智能体的未来将是在一个个上下文中建造的。精心设计它们吧。

原文链接:https://manus.im/blog/context-engineering-for-ai-agents-lessons-from-building-manus


© 2025 智人飞扬