AI智能体“上下文工程”实践:来自 Manus 项目的经验总结

转载:https://manus.im/blog/Context-Engineering-for-AI-Agents-Lessons-from-Building-Manus

在启动 Manus (manus.im/app) 项目之初,我的团队面临一个关键抉择:究竟是基于开源基础模型训练一个端到端的智能体模型,还是在前沿大模型的“上下文学习”(In-Context Learning)能力基础之上构建智能体?

在我从事自然语言处理 (NLP) 的第一个十年里,我们没有这样“奢侈”的选择。在久远的 BERT (arxiv.org/abs/1810.04805) 时代(没错,已经七年了),模型必须经过微调和评估才能迁移到新的任务上。即使当时的模型比现在的大语言模型(LLM)小得多,这个过程每迭代一次也常常需要数周时间。对于快速发展的应用,尤其是在达到产品市场契合点 (PMF) 之前,如此缓慢的反馈周期是无法接受的。这是我上一家创业公司留下的痛苦教训,当时我从零开始训练模型以实现开放信息提取(Open Information Extraction)和语义搜索。后来,GPT-3 (arxiv.org/abs/2005.14165) 和 Flan-T5 (arxiv.org/abs/2210.11416) 横空出世,我内部开发的模型一夜之间便失去了竞争力。讽刺的是,正是这些模型开启了上下文学习(In-Context Learning)的新时代,也为我们指明了新的前进方向。

这次来之不易的教训使我们明确了选择:Manus 将侧重“上下文工程”(Context Engineering)。这让我们能够将改进的交付时间从数周缩短到数小时,并使我们的产品与底层模型保持独立:如果说模型进步是水涨船高,那么我们希望 Manus 成为那艘随波而动的船,而不是深陷海底的柱子。

尽管如此,实践证明上下文工程绝非易事。它是一门实验性科学——我们已经四次重构了我们的智能体框架,每一次都是在发现了更好的上下文构建方法之后进行的。我们亲切地将这种架构搜索、提示词调整和经验性猜测的手动过程称为“随机梯度下降法”(Stochastic Graduate Descent)。它虽然不够优雅,但确实有效。

本文将分享我们通过这种“随机梯度下降法”找到的局部最优解。如果你也在构建自己的 AI智能体,我希望这些原则能帮助你更快地收敛。

围绕 KV-缓存(KV-Cache)进行设计

如果只能选择一个指标,我会认为 KV-缓存命中率是生产阶段 AI 智能体最重要的单一指标。它直接影响延迟和成本。为了理解原因,我们来看看一个典型智能体 (arxiv.org/abs/2210.03629) 的运行方式:

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

可以想象,上下文会随着每一步的执行而增长,而输出(通常是结构化的函数调用)则相对较短。这使得智能体中的预填充(prefilling)与解码(decoding)之间的比例,与聊天机器人相比,呈现出高度倾斜。例如,在 Manus 中,平均输入与输出 token 的比例大约是 100:1。

幸运的是,具有相同前缀的上下文可以利用 KV-缓存(medium.com/@joaolages/kv-caching-explained-276520203249),这大大减少了首个 token 的生成时间(TTFT)和推理成本——无论你是使用自托管模型还是调用推理 API。而且我们谈论的不是节省一小部分:以 Claude Sonnet 为例,缓存的输入 token 成本为 0.30 美元/百万 token,而未缓存的则需要 3 美元/百万 token——相差 10 倍。

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

  1. 保持提示词前缀的稳定性。 由于大语言模型 (LLM) 的自回归(autoregressive)特性,即使是单个 token 的差异,也可能导致从该 token 开始的缓存失效。一个常见错误是在系统提示词的开头包含时间戳——特别是精确到秒的时间戳。当然,这可以让模型告诉你当前时间,但也会极大地降低你的缓存命中率。

  2. 确保上下文只追加不修改。 避免修改先前的操作或观察结果。确保你的序列化是确定性的。许多编程语言和库在序列化 JSON 对象时无法保证键的顺序稳定,这可能会悄无声息地破坏缓存。

  3. 在需要时明确标记缓存断点。 某些模型提供商或推理框架不支持自动增量前缀缓存,而是需要在上下文中手动插入缓存断点。在设置这些断点时,请考虑潜在的缓存过期,并且至少要确保断点包含系统提示词的末尾。

此外,如果你使用像 vLLM (github.com/vllm-project/vllm) 这样的框架来托管模型,请确保前缀/提示词缓存(docs.vllm.ai/en/stable/design/v1/prefix_caching.html)已启用,并且你正在使用会话 ID 等技术来确保请求在分布式工作节点之间保持一致地路由。

遮蔽,而非移除

随着智能体功能变得越来越强大,其操作空间自然也变得更加复杂——简单来说,就是工具的数量呈爆炸式增长。最近 MCP(modelcontextprotocol.io/introduction)的流行更是火上浇油。如果你允许用户配置工具,相信我:总会有人将数百个神秘工具插入到你精心策划的操作空间中。结果就是,模型更有可能选择错误的操作,或者采取低效的路径。简而言之,你的“全副武装”的智能体反而变得更笨了。

一个自然的反应是设计动态动作空间——也许使用类似 RAG (en.wikipedia.org/wiki/Retrieval-augmented_generation) 的方式按需加载工具。我们在 Manus 中也尝试过。但我们的实验表明了一个清晰的规则:除非绝对必要,否则避免在迭代过程中动态添加或移除工具。这主要有两个原因:

  1. 在大多数大语言模型 (LLM) 中,工具定义在序列化后位于上下文的前部,通常在系统提示词之前或之后。因此,任何更改都将使后续所有动作和观察结果的 KV-缓存失效。

  2. 当之前的动作和观察结果仍然引用当前上下文中未定义的工具时,模型会感到困惑。如果没有约束解码(constrained decoding),这通常会导致模式(schema)违规或幻想出的动作。

为了解决这个问题,同时仍然改进操作选择,Manus 使用了一种上下文感知的状态机(state machine)来管理工具的可用性。它不是移除工具,而是在解码时遮蔽(mask)token 逻辑值,以根据当前上下文阻止(或强制)选择某些操作。

在实践中,大多数模型提供商和推理框架都支持某种形式的响应预填充,这允许你在不修改工具定义的情况下约束操作空间。函数调用通常有三种模式(我们以 NousResearch 的 Hermes 格式 [github.com/NousResearch/Hermes-Function-Calling] 为例):

  • 自动(Auto) – 模型可以选择是否调用函数。通过仅预填充回复前缀来实现:<|im_start|>assistant

  • 必需(Required) – 模型必须调用函数,但选择不受限制。通过预填充到工具调用 token 来实现:<|im_start|>assistant<tool_call>

  • 指定(Specified) –模型必须从特定子集中调用函数。通过预填充到函数名称的开头来实现:<|im_start|>assistant<tool_call>{"name": “browser\_

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

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

将文件系统用作上下文

如今前沿的大语言模型拥有 128K token 甚至更长的上下文窗口。但在实际的智能体应用场景中,这往往不够,有时甚至会成为一个负担。通常有三个痛点:

  1. 观察结果可能非常庞大,尤其是当智能体与网页或 PDF 等非结构化数据交互时,很容易超出上下文限制。

  2. 模型性能往往在超过一定上下文长度后下降,即使窗口技术上支持更长的上下文。

  3. 长输入成本高昂,即使有前缀缓存。你仍然需要为传输和预填充每个 token 付费。

为了解决这个问题,许多智能体系统会实施上下文截断或压缩策略。但是,过度激进的压缩不可避免地会导致信息丢失。这是一个根本性问题:智能体天生就需要根据所有先前的状态来预测下一个动作——而你无法可靠地预测十步之后哪一个观察结果可能变得至关重要。从逻辑角度来看,任何不可逆的压缩都带有风险。

这就是为什么在 Manus 中,我们将文件系统视为终极上下文:它大小无限、本质上是持久的,并且可以直接由智能体本身操作。模型学习按需写入和读取文件——将文件系统不仅用作存储,还用作结构化的外部化内存。

我们的压缩策略总是被设计为可恢复的。例如,只要保留 URL,网页内容就可以从上下文中删除;如果文档的路径在沙箱中仍然可用,其内容就可以省略。这使得 Manus 可以在不永久丢失信息的情况下缩短上下文长度。

在开发此功能时,我发现自己一直在思考,如何才能让状态空间模型(SSM)在智能体环境中有效工作。与 Transformer 不同,SSM 缺乏完整的注意力机制,并且在处理长距离向后依赖方面存在困难。但如果它们能够掌握基于文件的内存——将长期状态外部化而不是将其保存在上下文中——那么它们的速度和效率可能会开辟一类新的智能体。智能体 SSM 可能成为神经图灵机(Neural Turing Machines)的真正继承者。

通过复述(Recitation)操控注意力

如果你使用过 Manus,你可能会注意到一个有趣的现象:在处理复杂任务时,它倾向于创建一个 todo.md 文件——并随着任务的进展逐步更新,勾选已完成的项目。这不仅仅是“可爱”的行为,更是一种刻意为之的“注意力操控”机制。

在 Manus 中,一个典型的任务平均需要大约 50 次工具调用。这是一个漫长的循环——由于 Manus 依赖于大语言模型(LLM)进行决策,它很容易偏离主题或忘记早期的目标,尤其是在长上下文或复杂任务中。

通过不断重写待办事项列表,Manus 将其目标重复写入上下文的末尾。这会将全局计划推入模型的近期注意力范围,从而避免“迷失在中间”的问题,并减少目标错位。实际上,它正在使用自然语言来引导自己的注意力集中在任务目标上——而无需特殊的架构更改。

将“错误的数据”保留在上下文中

智能体(Agent)会犯错。这不是 Bug,而是现实。语言模型会产生幻觉,环境会返回错误,外部工具会表现异常,意想不到的极端情况也时常出现。在多步骤任务中,失败并非例外,而是循环的一部分。

然而,常见的本能是隐藏这些错误:清理轨迹、重试操作,或重置模型状态并将其留给神奇的“温度(temperature)”参数。这感觉更安全、更容易控制。但这会带来代价:抹去失败就等于消除了证据。而没有证据,模型就无法适应。

根据我们的经验,改进智能体行为最有效的方法之一出奇地简单:将错误的路径保留在上下文中。当模型看到失败的操作以及由此产生的观察结果或堆栈跟踪时,它会隐式地更新其内部信念。这使得它不再倾向于类似的操作,从而减少重复相同错误的机会。

事实上,我们相信错误恢复是真正智能体行为最清楚的指标之一。然而,这在大多数学术研究和公共基准测试中仍然关注不足,这些研究和测试通常侧重于理想条件下的任务成功率。

警惕少样本提示(Few-ShotPrompting)的陷阱

少样本提示(Few-Shot Prompting)是一种常见的提高大语言模型(LLM)输出质量的技术。但在智能体系统中,它可能以微妙的方式适得其反。

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

这在涉及重复决策或动作的任务中可能很危险。例如,当使用 Manus 协助审核一批 20 份简历时,智能体常常会陷入一种固定的节奏——重复执行类似的操作,仅仅因为它在上下文中看到了这些模式。这会导致偏离、过度泛化,有时甚至产生幻觉。

解决方案是增加多样性。Manus 在动作和观察中引入少量结构化变量——不同的序列化模板、替换措辞、在顺序或格式上添加微小的噪声。这种受控的随机性有助于打破模式,并调整模型的注意力。

换句话说,不要让少样本提示将你引入困境。你的上下文越统一,你的智能体就越脆弱。

总结

上下文工程仍然是一门新兴科学,但对于智能体系统而言,它已经至关重要。模型可能变得越来越强大、越来越快、越来越便宜,但再多的原始能力也无法取代对记忆、环境和反馈的需求。你如何塑造上下文,最终决定了你的智能体如何表现:它运行的速度、恢复的能力以及扩展的程度。

在 Manus,我们通过反复重写、走入死胡同以及在数百万用户真实世界中的测试,汲取了这些经验教训。我们在此分享的并非普遍真理,但这些模式对我们来说是行之有效的。如果它们能帮助你避免哪怕一次痛苦的迭代,那么这篇文章就完成了它的使命。

智能体的未来将通过一次又一次地构建上下文来实现。请精心地进行这些工程。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/92295.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/92295.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

day19 链表

定义链式存储的线性表头文件相关定义 typedef int datatype;//定义数据域类型 typedef struct Node {union{int len; //头结点数据域datatype data; //普通节点数据域};struct Node *next; //节点指针域 }Node,*Node_ptr;链表的函数 注意事项 1.创建节点时&#xff0c;需要初…

【第三节】Class与Style绑定

文章目录Class与Style绑定绑定HTML Class对象语法数组语法绑定内联样式对象语法数组语法自动添加前缀Class与Style绑定 数据绑定一个常见需求是操作元素的 class 列表和它的内联样式,因为它们都是属性&#xff0c;我们可以用 v-bind 处理它们:我们只需要计算出表达式最终的字符…

CMOS知识点 离子注入工艺

知识点8&#xff1a;离子注入是为了将掺杂剂&#xff08;如硼、磷等&#xff09;精确引入硅晶片的近表面区域&#xff0c;以改变其电学性质。工艺过程&#xff1a;电离与加速&#xff1a;掺杂剂原子在离子源中被电离&#xff08;带电&#xff09;&#xff0c;通过高压电场&…

从安装到上手:Ubuntu 22.04 玩转 Containerd 2.1.3 容器运行时

Containerd 是一款支持 OCI 规范的容器运行时&#xff0c;注重容器部署和生命周期管理的简单性、健壮性与可移植性&#xff0c;常被嵌入到 Docker 和 Kubernetes 等系统中。本文将详细介绍在 Ubuntu 22.04 服务器上通过二进制包手动安装 Containerd 的完整步骤&#xff0c;包括…

Hadoop与云原生集成:弹性扩缩容与OSS存储分离架构深度解析

Hadoop与云原生集成的必要性Hadoop在大数据领域的基石地位作为大数据处理领域的奠基性技术&#xff0c;Hadoop自2006年诞生以来已形成包含HDFS、YARN、MapReduce三大核心组件的完整生态体系。根据CSDN技术社区的分析报告&#xff0c;全球超过75%的《财富》500强企业仍在使用Had…

飞算科技:以创新科技引领数字化变革,旗下飞算 JavaAI 成开发利器

作为国家级高新技术企业&#xff0c;飞算科技专注于自主创新&#xff0c;在数字科技领域持续深耕&#xff0c;用前沿技术为各行业客户赋能&#xff0c;助力其实现数字化转型升级的飞跃。​飞算科技凭借深厚的技术积累&#xff0c;将互联网科技、大数据、人工智能等技术与实际应…

多线程Python爬虫:加速大规模学术文献采集

1. 引言 在学术研究过程中&#xff0c;高效获取大量文献数据是许多科研工作者和数据分析师的需求。然而&#xff0c;传统的单线程爬虫在面对大规模数据采集时&#xff0c;往往效率低下&#xff0c;难以满足快速获取数据的要求。因此&#xff0c;利用多线程技术优化Python爬虫&a…

NX717NX720美光固态闪存NX724NX728

美光NX系列固态闪存深度解析&#xff1a;技术、性能与市场洞察一、技术架构与核心创新美光NX系列固态闪存&#xff08;包括NX717、NX720、NX724、NX728&#xff09;的技术根基源于其先进的G9 NAND架构。该架构通过5纳米制程工艺和多层3D堆叠技术&#xff0c;实现了存储单元密度…

浅谈——C++和C#差异

虽然这个话题看着似乎有些关公战秦琼的味道&#xff0c;但是作为游戏开发者&#xff0c;C和C#一定是绕不开的两门语言。不过虽然说是比较二者差异&#xff0c;因为我学习的过程主要是先学C&#xff0c;所以我先基于C的认知&#xff0c;再来聊聊C#之中的不同。&#xff08;为什么…

rocky9-zabbix简单部署

目录 一、准备 1、&#xff08;rocky9&#xff09; 2、配置数据库 二、配置文件 1、导入初始架构与数据 2、配置相关文件 三、启动服务 1、浏览器访问 2、解决乱码问题 ​编辑 四、监控 ① 添加主机 1、修改配置文件 2、启动服务 3、网页添加 ②添加监控模块 1…

tabBar设置底部菜单选项、iconfont图标(图片)库、模拟京东app的底部导航栏

欢迎来到我的UniApp技术专栏&#xff01;&#x1f389; 在这里&#xff0c;我将与大家分享关于UniApp开发的实用技巧、最佳实践和项目经验。 专栏特色&#xff1a; &#x1f4f1; 跨平台开发一站式解决方案 &#x1f680; 从入门到精通的完整学习路径 &#x1f4a1; 实战项目经…

7.22总结mstp,vrrp

一、MSTP技术&#xfeff;&#xfeff;MSTI和MSTI域根&#xfeff;&#xfeff;MSTP中的端口角色3. MSTP工作原理 MSTP 计算方法• CST/IST的计算和RSTP类似 • MSTI的计算仅限于区域内 • MSTI计算参数包含在IST BPDU中&#xff0c;和IST的计 算同步完成&#xfeff;&#xfe…

【电脑】网卡的基础知识

网卡&#xff08;Network Interface Card, NIC&#xff09;是计算机中用于连接网络的关键组件之一&#xff0c;它负责管理和发送数据包到互联网或其他局域网设备。下面是一些关于网卡的详细知识&#xff1a;网卡的基本结构MAC地址&#xff1a;每个网卡都有一个唯一的物理地址&a…

IPv4枯竭时代:从NAT技术到IPv6的演进之路

&#x1f50d; 开发者资源导航 &#x1f50d;&#x1f3f7;️ 博客主页&#xff1a; 个人主页&#x1f4da; 专栏订阅&#xff1a; JavaEE全栈专栏 IPv4&#xff08;Internet Protocol version 4&#xff09;是互联网最核心的通信协议之一&#xff0c;自 1981 年正式标准化以来…

模式结构-微服务架构设计模式

需求&#xff08;Forces)结果上下文(Resulting context)相关模式(Related patterns)需求&#xff1a;必须解决的问题需求部分描述了必须解决的问题和围绕这个问题的特定上下文环境。需求有时候是相互冲突的&#xff0c;所以不能指望把他们全部都解决&#xff08;必须取舍&#…

30个常用的Linux命令汇总和实战场景示例

下面汇总常用的 30 个常用的 Linux 命令&#xff0c;每个都附有简要说明和典型示例&#xff0c;适合日常开发、服务器维护或系统学习使用。30 个常用的 Linux 命令汇总 一、文件与目录操作&#xff08;基础&#xff09;命令说明示例ls列出文件和目录ls -l 显示详细信息cd切换目…

Taro 网络 API 详解与实用案例

Taro 网络 API 详解与实用案例 在现代前端开发中&#xff0c;网络通信是不可或缺的一环。Taro 作为一款多端开发框架&#xff0c;提供了丰富且统一的网络 API&#xff0c;帮助开发者在小程序、H5、React Native 等多端环境下高效地进行数据交互。本文将详细介绍 Taro 的四大网…

Bitbucket平台的HTTP Access Tokens操作手册

在Bitbucket平台添加HTTP Access Tokens&#xff08;用于替代密码进行认证&#xff09;。 1. 登录Bitbucket并访问个人设置 打开 Bitbucket 并登录账号。点击右上角头像 → 选择 Manage account。 2. 生成Access Token 在左侧菜单中选择 Access tokens&#xff08;位于 Sec…

低成本、高泛化能力的无人机自主飞行!VLM-Nav:基于单目视觉与视觉语言模型的无地图无人机导航

作者&#xff1a;Gobinda Chandra Sarker1^{1}1, AKM Azad2^{2}2, Sejuti Rahman1^{1}1, Md Mehedi Hasan1^{1}1单位&#xff1a;1^{1}1达卡大学&#xff0c;2^{2}2伊玛目穆罕默德伊本沙特伊斯兰大学论文标题&#xff1a;VLM-Nav: Mapless UAV-Navigation Using Monocular Visi…

Docker Desktop 安装到D盘(包括wsl)

默认WSL虚拟机位置&#xff1a; C:\Users\<用户名>\AppData\Local\Docker\wsl重装DockerDesktop下载安装包Docker Desktop Installer.exe在D盘创建文件夹D:\Program Files\DockerDesktopD:\Program Files\DockerDesktop\data 在cmd运行 start /w "" "Dock…