AI Agent-Manus 构建经验解读(下)
前言
Manus 官网博客文章《Context Engineering for AI Agents: Lessons from Building Manus》为我们带来了 AI 智能体上下文工程领域的深度实践总结。该文浓缩了 Manus 团队在技术攻坚中的核心经验与深刻洞察,系统性梳理了智能体系统设计面临的关键问题及解决方案,其中涵盖 KV 缓存优化、动态动作空间管理、基于文件系统的上下文扩展等核心技术方向。
对于有 AI Agent 系统开发经验的从业者而言,阅读此文堪称 “句句切中痛点”。文中探讨的每一项技术细节,几乎都精准映射了实际开发中曾遭遇的 “技术陷阱”。尤为珍贵的是,文章不仅以实践印证了这些挑战的行业普遍性,更针对性地提供了经过验证的精妙解决方案 —— 这些思路恰恰是许多开发者此前未触及或仍在艰难探索的方向,为实际项目的优化升级提供了清晰指引。
基于此,本文将对该博文展开深入解读,助力读者精准把握核心技术要点,全面理解文中的实践经验与技术逻辑。
动态工具处理策略
问题概述
随着AI 智能体能力的提升,其动作空间(action space)会变得愈发复杂,工具数量也会呈爆炸式增长。最近MCP 的流行更是加剧了这一问题。如果允许用户配置工具,总会有人将大量来历不明的工具塞入精心设计的动作空间中,导致模型更容易选错行动或采取低效路径,从而使AI 智能体变得迟钝。
一种自然的想法是设计一个动态的动作空间,按需动态加载工具,在Manus中也尝试过这种方法。但实验表明,除非绝对必要,否则应避免在迭代中途动态增删工具,原因主要有以下两点:
-
在大多数模型中,工具定义在序列化后通常位于上下文的靠前位置,通常在系统提示词之前或之后。因此,任何更改都会导致后续所有动作和观测的KV 缓存失效。
-
当此前的动作和观测仍然引用着当前上下文中不再定义的工具时,模型会感到困惑。如果没有约束解码(constrained decoding),这通常会导致模式违规或产生幻觉动作。
处理策略
为平衡动作空间优化与上下文稳定性,Manus 采用上下文感知状态机精准管理工具可用性,同时集成推理框架的响应预填充功能 —— 基于状态机输出动态生成部分响应文本(如函数调用前缀)以锚定解码起点,再在模型解码阶段通过遮蔽非目标 token 的 logits 实现动作选择的动态约束。
1. 定义上下文感知状态机
-
状态机角色:状态机基于代理的当前上下文(如用户输入、上一步动作结果)决定工具可用性。状态示例:
-
IDLE
:用户刚提供输入,代理必须立即回复文本,不得调用任何工具。 -
TOOL_REQUIRED
:代理必须调用一个工具,但具体工具不限。 -
TOOL_SPECIFIED
:代理必须从特定工具组调用(如只允许浏览器工具)。
-
-
状态转换:状态机根据事件(如用户消息或工具执行结果)更新状态。例如:
-
用户发送新消息 → 状态转为
IDLE
(代理应优先回复)。 -
代理决定执行工具 → 状态转为
TOOL_REQUIRED
或TOOL_SPECIFIED
。
-
-
输出:状态机输出一个“遮蔽规则”,例如:
-
在
IDLE
状态:遮蔽所有工具调用token(只允许文本响应)。 -
在
TOOL_SPECIFIED
状态:只允许以browser_
开头的工具名称。
-
2. 集成响应预填充
利用推理框架的响应预填充功能,根据状态机输出预先生成部分响应,限制模型的解码起点。以 NousResearch 的 Hermes 格式为例,函数调用约束可通过三种模式实现:
-
自动模式(Auto):状态为“可选工具调用”,预填充至
<|im_start|>assistant
。模型可自由选择文本回复或工具调用(不应用遮蔽)。 -
必须模式(Required):状态为“必须调用工具”,预填充至
<|im_start|>assistant<tool_call>
。模型必须生成一个工具调用,但工具名称不受限(遮蔽只用于排除无效工具)。 -
指定模式(Specified):状态为“必须调用特定工具组”,预填充至函数名前缀,例如
<|im_start|>assistant<tool_call>{"name": "browser_
。模型只能完成以browser_
开头的名称(如browser_search
),遮蔽其他所有选项。
预填充固定了响应的起始部分,减少了模型的决策空间,logits遮蔽在此基础上进一步约束。
3. 应用Logits遮蔽
-
遮蔽机制:在模型解码时,logits处理器动态修改token logits:
-
对于不被允许的token(如特定工具名称),将其logits设为负无穷(或极低值),使模型无法生成这些token。
-
遮蔽范围:基于状态机输出的规则:
-
在
IDLE
状态:遮蔽所有工具调用相关token(如<tool_call>
和所有工具名称),只允许文本token。 -
在
TOOL_SPECIFIED
状态:遮蔽所有不以指定前缀(如browser_
)开头的工具名称token。
-
-
实现方式:
-
使用推理框架的API(如Hugging Face的
LogitsProcessor
类)在生成过程中注入遮蔽逻辑。 -
在编写工具名称的时候,带上结构化前缀(如
browser_
、shell_
)。由于工具名称有结构化前缀,遮蔽可以高效实现:只需检查token是否匹配前缀,无需维护复杂状态。 -
示例代码逻辑:
class ToolMaskLogitsProcessor(LogitsProcessor):def __init__(self, allowed_prefix):self.allowed_prefix = allowed_prefix # e.g., "browser_"def __call__(self, input_ids, scores):# 遮蔽所有不以allowed_prefix开头的工具名称tokenfor token_id in token_vocab:token_str = tokenizer.decode(token_id)if token_str.startswith("tool_name:") and not token_str.startswith(f"tool_name:{self.allowed_prefix}"):scores[token_id] = -float('inf') # 遮蔽return scores
-
-
-
无状态设计:遮蔽仅基于当前解码步骤的token,不依赖历史上下文,因此不会引入有状态复杂性。
这种 “遮蔽而非移除” 的策略确保了 Manus 代理循环的稳定性:既维护了上下文的一致性以保障 KV 缓存有效性,又通过动态 logits 掩码灵活优化动作选择,在模型驱动架构下实现了高效且可控的智能体交互。
上下文状态机工作流程总结
-
输入处理:用户发送消息,代理进入新状态。
-
状态机评估:状态机基于上下文(如消息内容)更新状态(例如,转为
IDLE
)。 -
预填充生成:根据状态,预填充响应前缀(例如,在
IDLE
状态,预填充至<|im_start|>assistant
)。 -
解码与遮蔽:模型开始解码;logits处理器应用遮蔽规则(例如,在
IDLE
状态,遮蔽所有工具token)。 -
输出生成:模型生成响应,仅限允许的动作(如纯文本或指定工具调用)。
上下文状态机场景处理示例
假设用户向Manus代理发送消息:“查一下今天的新闻。” 代理状态机处理如下:
-
上下文:用户新输入,代理应优先回复确认,而不是立即执行工具。
-
状态机决策:状态转为
IDLE
(必须立即回复文本)。 -
预填充:响应预填充至
<|im_start|>assistant
(自动模式,但遮蔽将强制文本回复)。 -
Logits遮蔽:
-
遮蔽规则:所有工具调用token(如
<tool_call>
、browser_search
、shell_exec
等)的logits被设为负无穷。 -
结果:模型只能生成文本token,例如输出:“好的,我将为您搜索新闻。请稍等...”,而无法生成任何工具调用。
-
-
后续动作:如果代理决定执行工具(如状态转为
TOOL_SPECIFIED
for 浏览器工具),状态机更新:-
预填充至
<|im_start|>assistant<tool_call>{"name": "browser_
。 -
遮蔽规则:只允许以
browser_
开头的工具(如browser_search
),遮蔽其他工具(如shell_ls
)。 -
模型输出:
<tool_call>{"name": "browser_search", "parameters": {"query": "今日新闻"}}
。
-
使用文件系统作为上下文
问题概述
现代前沿大语言模型(LLM)已能支持 128K 令牌甚至更长的上下文窗口,但在真实世界的智能体场景中,这样的容量往往仍显不足,甚至可能成为性能负担。这一矛盾主要体现在三个核心痛点:
-
容量瓶颈:当智能体与网页、PDF 等非结构化数据交互时,观察结果会急剧膨胀,极易突破即使是 128K 的上下文限制,迫使系统不得不舍弃部分关键信息。
-
性能衰减:研究表明当输入长度超过临界值(通常远低于模型理论支持的窗口大小)后,模型的理解能力与逻辑连贯性会显著下降
-
成本控制:即便采用前缀缓存等优化技术,长输入的传输成本与预填充计算开销仍随序列长度线性增长,导致在高频交互场景中花费巨大
为了解决这些问题,许多智能体系统选择采用上下文截断或压缩策略,但过度激进的压缩必然导致信息丢失。这一问题是根本性的:智能体的决策本质上依赖于对所有先前状态的完整感知,而我们无法提前预判哪些观察结果会在后续步骤中成为关键依据。从逻辑层面看,任何不可逆的压缩操作都潜藏着破坏决策链连贯性的风险。
处理策略
采用文件系统
基于这些考量,Manus 创新性地将文件系统重构为智能体的无限记忆层。其核心突破在于:
-
无限容量:文件系统天然支持TB级存储,解除上下文长度枷锁;
-
持久化状态:所有观察结果按需持久存储,规避模型内存限制;
-
结构化操作:智能体通过读写API直接操作文件,实现状态精准存取。
在这种架构下,模型能够自主学会按需读写文件,将文件系统不仅作为存储介质,更转化为结构化的外部记忆模块。
可恢复性压缩策略
我们的压缩策略始终设计为可恢复的:
-
网页内容可从上下文中移除,只需保留 URL 作为重新爬取的索引;
-
文档正文可暂时省略,通过沙盒中保留的文件路径即可实时加载原始内容。
这种设计实现了上下文长度的有效缩短,同时确保所有被移除的原始数据都能通过元数据(URL、文件路径)完整重建,从根本上避免了信息的永久丢失。
对于未来智能体架构的展望
在这一方案的探索过程中,Manus团队意外发现了它对状态空间模型(SSM)在智能体领域应用的特殊价值。文件系统方案意外地为状态空间模型(SSM) 开辟了新可能:
-
弥补SSM缺陷:SSM因缺乏全局注意力机制,难以处理长程依赖。而文件系统提供的精准外部记忆,可替代性地解决状态回溯问题;
-
释放效率潜能:SSM的线性复杂度结合文件系统的无限记忆,有望实现超长决策链的低成本推理。
与 Transformer 架构不同,SSM 因缺乏全局注意力机制,在处理长距离后向依赖关系时存在天然短板。但如果能让 SSM 掌握基于文件系统的外部记忆管理能力 —— 将长期状态存储于外部文件而非上下文窗口中,其线性计算复杂度带来的速度与效率优势将得到充分释放,有望催生一类全新的高效智能体系统。从这个角度看,融合文件系统记忆的 SSM 智能体,或将可能成为神经图灵机理念的真正实现路径。
通过复述操控LLM注意力
如果你使用过 Manus,可能会注意到一个有趣的现象:在处理复杂任务时,它会主动创建一个todo.md文件,并在任务推进过程中持续更新内容 —— 通过勾选已完成项目、补充新任务项等方式动态维护列表。这一看似简单的行为,实则是 Manus 为操控大语言模型(LLM)注意力而设计的核心机制。
当 Manus 智能体在任务中创建并更新 todo.md 时,其本质是在对抗 LLM 固有的注意力熵增定律。这种定律带来的挑战在长任务场景中尤为突出:
-
目标权重衰减:实验数据显示,在超过 20 步的决策链中,原始任务目标在 KV 缓存中的注意力权重衰减幅度超过 70%,导致模型对核心目标的感知能力显著弱化。
-
局部最优陷阱:模型容易陷入高频工具调用的循环(如反复执行搜索操作),这些局部动作会逐渐淹没全局任务意图,使智能体偏离预设路径。
-
传统方案失效:此前常见的 “在系统提示中重复目标” 策略难以奏效,由于提示文本位于上下文前端,会被后续大量中间状态信息干扰稀释,无法持续发挥引导作用。
Manus 处理的典型任务平均需要约 50 次工具调用,这意味着决策循环会经历较长的上下文积累过程。由于智能体的决策完全依赖 LLM 的推理能力,在长上下文或高复杂度任务中,极易出现主题偏离或早期目标遗忘的问题。
为解决这一痛点,Manus 通过持续重写待办事项列表 todo.md的方式,将核心目标与当前进度主动复述到上下文的末尾位置。这种设计能将全局计划精准推入模型的近期注意力范围,有效避免任务目标 “丢失在中间状态” 的问题,显著减少决策过程中的目标不一致现象。
值得注意的是,这一机制完全通过自然语言交互实现注意力偏向引导,无需对模型架构进行特殊改造,既保持了系统的轻量化特性,又实现了对 LLM 注意力的精准调控。
注意力锚点
层级 | 实现方式 | 认知作用 |
---|---|---|
结构锚 | 创建/plan/todo.md | 建立专用的待办列表-记忆文件 |
时间锚 | 每3步更新todo.md,同步处理进度 | 防止注意力漂移 |
语义锚 | 用相同句式描述所有任务项 | 降低模式识别负荷 |
保留错误
问题概述
在智能体执行任务的过程中,错误的出现并非技术缺陷,而是现实场景的必然产物。语言模型可能生成幻觉内容,外部环境可能返回错误数据,工具调用可能出现异常行为,各类意外的边缘情况更是随时可能发生。在多步骤任务中,失败绝非偶然现象,而是智能体决策循环中不可分割的组成部分。
面对错误时,一种常见的处理方式是刻意隐藏这些 “不完美”:
清理错误痕迹、直接重试操作,或是重置模型状态并依赖温度参数等随机性来规避问题。
这种做法看似能营造更安全可控的交互体验,实则需要付出沉重代价 —— 错误证据的丢失,意味着LLM失去了关键的学习素材。缺乏这些具象化的失败案例,智能体便无法形成有效的经验积累,自然难以实现行为模式的适应性优化。
处理策略
根据Manus团队实践的经验,改善智能体行为最有效的策略其实异常简单:将错误尝试完整保留在上下文中。当模型能够直接观察到失败的行动轨迹,以及由此产生的观测结果或堆栈跟踪信息时,其内部信念系统会进行隐性更新。这种基于真实反馈的学习过程,会动态调整模型的决策先验,显著降低重复相同错误的概率,推动智能体在试错中实现自我迭代。
事实上,错误恢复能力应当被视为真正智能体行为的核心标志之一。然而目前在大多数学术研究与公共基准测试中,这一关键维度仍未得到充分体现 —— 现有评估体系往往聚焦于理想条件下的任务成功率,却忽视了真实场景中错误处理与恢复能力的重要价值。这种评估偏差可能导致智能体技术在实验室环境中表现优异,却在复杂现实场景中难以发挥实效。
保持少样本学习的多样性
问题概述
在大语言模型(LLM)应用中,通过少样本学习引导LLM正确输出格式是减少大模型幻觉的常用技术。但在智能体系统中,这种策略可能在某些情况下产生相反的效果。
语言模型本质上是优秀的模式模仿者,它们会无意识地复刻上下文中的行为模式。当上下文长期充斥着相似的行动 - 观察对序列时,模型会倾向于遵循既定模式,即便该模式已不再适用于当前任务需求。这种模式依赖在重复决策类任务中尤为危险:例如在使用 Manus 审查 20 份简历的场景中,智能体常陷入机械性重复 —— 仅仅因为上下文历史中存在类似行动记录,就持续复现相同操作流程,最终导致任务偏离目标、过度泛化需求,甚至产生无依据的幻觉输出。
处理策略
破解这一困境的核心在于增加少样本学习案例的上下文多样性。Manus 通过在行动与观察数据中引入可控的结构化变化实现这一目标:
采用不同的序列化模板呈现信息,使用替代性措辞描述相似操作,在内容顺序或格式上注入微小差异。
这种受控随机性能够有效打破模型对单一模式的依赖,重新校准其注意力分配,使其更关注当前任务的具体需求而非历史模式惯性。
这一实践揭示了智能体设计的重要原则:避免LLM陷入某些固定结构的思维定式。上下文的同质化程度越高,智能体的适应性就越弱,面对新场景时的鲁棒性也越差。通过主动构建多样化的上下文环境,才能让智能体在复杂任务中保持灵活性与判断力。
参考文献
Context Engineering for AI Agents: Lessons from Building Manus