2025年7月18日 - 纪一超(Peak Ji)

在Manus项目启动之初,我和团队面临一个关键抉择:是基于开源基础模型训练一个端到端的智能代理,还是利用前沿模型的上下文学习能力构建代理?

回顾我在自然语言处理领域的早期经历,那时我们没有这样的选择。七年前的BERT时代,模型必须经过微调和评估才能迁移到新任务,每次迭代往往耗时数周,且模型规模远小于现今的大型语言模型。对于快速发展的应用,尤其是产品市场匹配前期,这种缓慢的反馈周期是致命的。我的上一家创业公司便深受其害,当时我从零训练模型用于开放信息抽取和语义搜索。随后GPT-3和Flan-T5的出现使得自研模型一夜之间变得无关紧要。讽刺的是,这些模型也开启了上下文学习的新纪元,带来了全新的发展路径。

这段宝贵的经历让我们做出了明确选择:Manus将押注于上下文工程。这使我们能够在数小时内发布改进,而非数周,同时保持产品对底层模型的独立性:如果模型进步是涨潮,我们希望Manus是船,而非固定在海底的柱子。

然而,上下文工程远非简单。它是一门实验科学,我们已经重构了四次代理框架,每次都是在发现更优上下文构造方式后进行。我们戏称这种手动的架构搜索、提示调试和经验猜测为“随机梯度下降”(Stochastic Graduate Descent),虽然不够优雅,但确实有效。

本文分享了我们通过“SGD”达到的局部最优解,希望对构建AI代理的你有所帮助,加速收敛。

围绕KV缓存设计

如果只能选一个指标,我认为KV缓存命中率是生产阶段AI代理最重要的指标,它直接影响延迟和成本。代理的典型工作流程是:接收用户输入后,模型基于当前上下文从预定义动作空间中选择动作,执行后产生观察结果,动作和观察被追加到上下文中,形成下一轮输入,循环直到任务完成。

上下文随着步骤增长而变长,输出通常是结构化函数调用,长度相对较短。以Manus为例,输入与输出的token比例约为100:1。

幸运的是,具有相同前缀的上下文可以利用KV缓存,大幅降低首次token生成时间和推理成本。以Claude Sonnet为例,缓存token成本为0.30美元/千token,非缓存token则高达3美元/千token,差距达10倍。

提升KV缓存命中率的关键做法包括:

  • 保持提示前缀稳定。LLM的自回归特性使得即使一个token的差异也会使缓存失效。常见错误是系统提示开头包含精确到秒的时间戳,虽然能让模型报告当前时间,但严重破坏缓存命中率。
  • 上下文仅追加,避免修改之前的动作或观察,确保序列化过程确定性。许多语言和库在序列化JSON对象时不保证键顺序稳定,可能无声破坏缓存。
  • 必要时显式标记缓存断点。一些模型提供商或推理框架不支持自动增量前缀缓存,需手动插入断点,且断点至少应包含系统提示末尾。
  • 自托管模型时,确保启用前缀缓存,使用会话ID等技术保证请求在分布式工作节点间一致路由。

遮蔽而非移除动作

随着代理能力增强,动作空间复杂度增加,工具数量激增。MCP的流行更是推波助澜。若允许用户配置工具,必然有人会接入数百个工具,导致模型更易选错动作或走低效路径,代理反而变“笨”。

自然反应是设计动态动作空间,如按需加载工具(类似RAG)。我们也尝试过,但实验表明:除非绝对必要,避免在迭代中动态增删工具。原因有二:

  • 大多数LLM中,工具定义序列化后位于上下文前端,任何变动都会使后续动作观察的KV缓存失效。
  • 之前动作观察引用了当前上下文中已移除的工具,模型会混淆,缺乏约束解码时易导致格式违规或幻觉动作。

Manus采用上下文感知状态机管理工具可用性,通过在解码时遮蔽token概率,防止或强制选择特定动作,而非移除工具。

大多数模型提供商和推理框架支持响应预填充,可在不修改工具定义的情况下约束动作空间。以NousResearch的Hermes格式为例,函数调用有三种模式:

  • 自动(Auto):模型可选择调用函数或不调用,预填充回复前缀。
  • 必须调用(Required):模型必须调用函数,但选择不受限,预填充至工具调用token。
  • 指定调用(Specified):模型必须从特定子集调用函数,预填充至函数名开始。

我们通过直接遮蔽token概率实现动作选择约束。例如用户新输入时,Manus必须立即回复而非执行动作。动作名称设计统一前缀(如浏览器工具均以browser_开头,命令行工具以shell_开头),方便在特定状态下限制动作选择,无需状态化概率处理器。

这些设计确保Manus代理循环稳定,即使在模型驱动架构下。

利用文件系统作为上下文

现代前沿LLM支持128K及以上token的上下文窗口,但实际代理场景中仍不足,甚至成负担。常见问题包括:

  • 观察结果庞大,尤其是处理网页或PDF等非结构化数据,易超出上下文限制。
  • 模型性能在超过一定上下文长度后下降,即使窗口技术上支持。
  • 长输入成本高昂,即使有前缀缓存,传输和预填充每个token仍需付费。

许多代理系统采用上下文截断或压缩策略,但过度压缩必然丢失信息。问题根本在于代理需基于所有先前状态预测下一动作,无法预知哪条观察十步后会关键。任何不可逆压缩都存在风险。

因此,Manus将文件系统视为终极上下文:容量无限、持久且可由代理直接操作。模型学会按需读写文件,文件系统不仅是存储,更是结构化的外部记忆。

我们的压缩策略均可恢复。例如网页内容可从上下文中移除,只保留URL,文档内容可省略,只保留沙箱中的路径。这样既缩短上下文长度,又不丢失信息。

开发过程中,我设想状态空间模型(SSM)在代理场景的潜力。SSM缺乏全注意力机制,难以处理长距离依赖,但若能掌握基于文件的记忆,将长时状态外部化而非保存在上下文中,其速度和效率或能催生新一代代理。代理型SSM或许是真正继神经图灵机后的继任者。

通过复述操控注意力

使用Manus时,你可能注意到它在处理复杂任务时会创建todo.md文件,并随着任务进展逐步更新,勾选完成项。

这不仅是巧妙设计,更是操控注意力的机制。

Manus的典型任务平均需调用约50次工具,循环较长。依赖LLM决策的Manus易出现偏题或遗忘早期目标,尤其在长上下文或复杂任务中。

通过不断重写待办列表,Manus将全局计划复述到上下文末尾,推动模型关注近期内容,避免“中间遗失”问题,减少目标偏差。实质上,它用自然语言引导自身聚焦任务目标,无需特殊架构改动。

保留错误信息

代理会犯错,这不是缺陷,而是现实。语言模型会产生幻觉,环境返回错误,外部工具异常,边缘情况频出。多步任务中,失败是常态。

但常见做法是掩盖错误:清理轨迹、重试动作或重置模型状态,寄希望于“温度”参数。这看似安全可控,却代价巨大:抹去失败即抹去证据,模型无法据此调整。

我们的经验表明,提升代理行为最有效的方法之一是保留错误路径。当模型看到失败动作及其观察或堆栈信息时,会隐式更新内部信念,降低重复犯错概率。错误恢复是代理行为的关键指标,然而学术和公开基准多聚焦理想条件下的任务成功,忽视了这一点。

避免少样本陷阱

少样本提示是提升LLM输出的常用技巧,但在代理系统中可能适得其反。

语言模型擅长模仿上下文中的行为模式,若上下文充满类似的动作-观察对,模型倾向重复该模式,即使不再最优。

这在涉及重复决策的任务中尤为危险。例如Manus在批量审阅20份简历时,代理常陷入固定节奏,重复类似动作,导致偏移、过度泛化甚至幻觉。

解决方案是增加多样性。Manus在动作和观察中引入结构化变异,如不同序列化模板、替代措辞、顺序或格式的微小扰动。这种受控随机性打破模式,调整模型注意力。换言之,不要让少样本提示把自己套入死胡同,越单一的上下文,代理越脆弱。

结语

上下文工程仍是新兴科学,但对代理系统已至关重要。模型虽日益强大、快速且廉价,但无论能力多强,都无法替代记忆、环境和反馈的作用。上下文的构造决定了代理的运行速度、恢复能力和扩展性。

在Manus,我们通过反复重写、探索死胡同和数百万用户的实战测试学到了这些经验。这里分享的不是普适真理,而是我们有效的模式。若能帮你避免一次痛苦迭代,本文即达目的。

代理的未来,将由一个个精心设计的上下文构建。请用心打造。