别再卷Prompt了!这才是Al Agent落地的真正瓶颈

140 阅读13分钟

在这里插入图片描述

当 Shopify CEO Tobi Lutke在x上发出这条推文时,或许他自己也没想到,这短短一句话会迅速点燃AI技术圈对Context Engineering的讨论热潮。

紧接着,前OpenAI核心成员Andrej Karpathy第一时间转发并附议,认为为任务提供所有上下文的艺术将引领LLM能力的进一步提升。

在这里插入图片描述

随着LLM性能的进步,人们不再需要为了想出一个像咒语一样的prompt而绞尽脑汁了。但是,随着Agent的发展和任务场景的高度复杂化,如何把正确的信息给到LLM,已经不再是一件显而易见的事情。

我们的挑战逐渐从“如何写好一个prompt”,升级成为了“如何为Agent的每一步,动态组装一个正确、完整且高效的Context”。

为什么我们需要Context Engineering

随着Agent的发展,人们利用AI构建的东西已经逐渐从两三年前的Demo,逐步进化到真正落地到生产环境的产品,这对AI应用的稳定性提出了更高的要求。

但是,Agent的本质,利用的是LLM本身的动态规划和决策能力,自动规划任务,自动调用工具,自动读取记忆,最终一步步完成任务。然而,这种运行模式,很难直接拿到企业生产环境中稳定运行。

国外的一个著名开发工程师Dex Horthy描述,当前LLM的工具调用在连续运行10~20轮次之后一般就会进入非常混乱的状态,导致LLM再也无法从中恢复。Dex Horthy质疑道,即使你通过努力调试让你的Agent在90%的情况下都运行正确,这还是远远达不到“足以交付给客户使用”的标准。而这10%的崩溃情况,大部分都能归结到系统送给LLM的上下文不够准确。

所以说,Context Engineering背景是,AI技术落地已经进入了一个非常专业化的时代,我们需要非常精细地把控我们输入给LLM的东西,使它可以长期稳定的运行,适配生产环境的需要。

Prompt Engineering vs Context Engineering

很多人会把Prompt Engineering 和 Context Engineering 混淆,认为Context Engineering是Prompt Engineering在新时代的叫法,但是其实这根本是两个不一样的东西。

Prompt Engineering 注重如何激发LLM的能力,这是一种临时性的,单点的,甚至有点类似奇技淫巧的技巧。它关心的是在上下文信息已定的前提下,我们怎么样让LLM基于这些上下文信息给出更符合我们需求的结果。

大家提到Prompt Engineering最经常联想到的例子可能是奖励或者威胁:

如果你答对了,我会给你5美元小费 / 如果你答错了,90岁的老奶奶会…

或者是角色扮演:

你是一个代码专家,擅长编写Python代码

这些技巧在LLM的早期非常有用,但是发展到现在,除了In-context Learning等少数技巧还有效果,其他许多技巧都已经不再被用到。现在,我们只需要清晰地把我们的需求描述出来,LLM就能做的很好。

Context Engineering 则不同,它关注的重点不是提升单次问答的效果,而是如何构建一个系统性工程,以在在整个任务运行期间,给到LLM完成任务需要的所有信息。它关心的是如何给定和编排更全面的上下文,并认为如果给定了好的上下文信息,LLM自然能够给出符合我们需求的结果。

什么是Context Engineering

Context Engineering包含了所有对组装正确的上下文起到关键作用的技术组件。 Dex Horthy在AIE上的分享甚至认为Everything is Context Engineering。为了给模型更多更精确的参考资料,我们需要引入RAG;为了让模型正确使用工具,我们应该给它Tool Description和工具的历史调用结果;为了让模型记住之前的对话内容,我们需要进行记忆管理。Context Engineering需要考虑的内容** 至少包括**:

  1. 静态的prompt及instruction。
  2. RAG返回的片段。
  3. web搜索返回的页面内容。
  4. 对于工具候选集合的描述。
  5. 工具调用的历史结果。
  6. 长期记忆及短期记忆。
  7. 程序运行的报错信息。
  8. 系统执行过程中通过human-in-the-loop获取到的用户反馈。

AI应用开发在本质上可以看成是,从海量信息中找到恰当的有效信息,最终适配到LLM的上下文窗口上。我们需要一套完整的Context Engineering让这个漏斗工作得更高效。

在这里插入图片描述

Manus对Context Engineering的六个思考

Manus是全球首个通用Agent,几个月前发布的时候引起了轩然大波。与训练模型的道路不同,Manus押注于上下文工程,它已经重建了四次代理框架,每次都是因为发现了更好的塑造上下文的方式。它的联合创始人Yichao ‘Peak’ Ji在前段时间发布了一篇长博客,整理了他们在构建Manus时管理Context的经验和教训,非常值得一看。以下是带着自己理解的一些总结表述,详细内容可以看原文。

围绕KV缓存进行设计

如果只能选择一个指标,Yichao ‘Peak’ Ji认为 KV-cache命中率是生产阶段AI代理最重要的单一指标。因为这直接影响了成本和效率。

一个典型的AI Agent的运行模式是:用户输入 → 模型分析上下文,选择工具 → 工具执行 → 结果写回上下文 → 模型再次分析……,如此循环直到完成任务。

随着每一步的推进,输入的上下文不断增长(我们会附加许多信息,比如观察结果),而输出的长度几乎不变(仍是输出一个行动)。**这种特性使得AI Agent相比传统聊天机器人,其预填充和解码比例呈现高度倾斜的状态。**例如,在Manus平台中,平均输入与输出的token比例约为100:1。

幸运的是,有相同前缀的上下文可以利用KV缓存技术,这大大减少了首个token的生成时间(TTFT)和推理成本。以Claude Sonnet为例,缓存的输入token成本为0.3美元/百万token,而未缓存的成本高达3美元/百万token——相差整整10倍。

在生产环境中,高KV-cache命中率意味着更多的请求可以利用缓存中的计算结果,从而减少重复计算,降低延迟,并显著降低推理成本。

在这里插入图片描述

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

1.保持提示前缀稳定。 由于LLM的自回归特性,我们要保证前缀的任何一个token都不能改变,否则缓存就会失效。一个常见的错误是在系统提示的开头包含时间戳这会灾难性地使得之后的所有缓存命中率归零。

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

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

避免动态添加或者移除工具

随着代理能力的增强,工具数量的爆炸式增长,Agent的行动空间变得越来越复杂。最近流行的MCP只会火上浇油。如果你允许用户自定义工具,相信我:总会有人将数百个神秘工具插入到你精心策划的行动空间中。结果,模型更可能选择错误的行动或采取低效的路径。简而言之,你武装过度的代理变得更加愚蠢。

一个自然的反应是像 RAG 那样,按需加载工具,每次只给模型提供当前最可能用到的那一小部分。Manus中也尝试过这种方法。但实验表明了一个明确的规则:除非绝对必要,避免在迭代过程中动态添加或移除工具。这主要有两个原因:

1.在大多数 Agent 的实现中,工具定义经常放置在系统提示的之前或者之后,处于上下文的最前端因此任何更改都会使后续所有的KV缓存失效。

2.当先前的动作和观察仍然引用当前上下文中不再定义的工具时,它看到过去的行为,却找不到对应的定义,极易导致它幻觉出无效的动作,造成新一轮的计算资源浪费。

Manus 提出了一种优雅的解决方案:工具集在上下文中保持“全量”且“稳定”,但在解码环节做文章。

具体来说,它通过一个上下文感知的状态机来管理工具的可用性。但它并非通过增删上下文中的工具定义,而是在生成下一个 Token 时,直接在 Logits 层面进行掩码操作以基于当前上下文阻止(或强制)选择某些动作

在这里插入图片描述

在实践中,大多数模型提供商和推理框架支持某种形式的响应预填充,这允许我们在不修改工具定义的情况下约束动作空间。函数调用通常有三种模式:

  • 自动:模型可以选择调用或不调用函数。 通过仅预填充回复前缀实现:<|im_start|>assistant
  • 必需:模型必须调用函数,但选择不受约束。 通过预填充到工具调用令牌实现:<|im_start|>assistant<tool_call>
  • 指定:模型必须从特定子集中调用函数。 通过预填充到函数名称的开头实现:<|im_start|>assistant<tool_call>{“name”: "browser_
    通过直接掩码token的logits来约束动作选择。

使用文件系统作为上下文

现代前沿LLM现在提供128K令牌或更多的上下文窗口。但在真实世界的Agent场景中,这通常不够。有三个常见的痛点:

  1. 观察结果可能非常庞大, 尤其是当代理与网页或PDF等非结构化数据交互时。很容易超出上下文限制。
  2. 超过一定的上下文长度后,模型性能往往会下降, 即使技术上支持该窗口大小。
  3. 长输入成本高昂, 即使使用前缀缓存。你仍然需要为传输和预填充每个token付费。

为了解决这个问题,许多Agent系统实现了上下文截断或压缩策略。但过度激进的压缩不可避免地导致信息丢失。这个问题是根本性的:代理本质上必须根据所有先前状态预测下一个动作——而你无法可靠地预测哪个观察结果可能在十步之后变得至关重要。从逻辑角度看,任何不可逆的压缩都带有风险。
这就是为什么Manus将文件系统视为终极上下文大小不受限制,天然持久化,并且代理可以直接操作。模型学会按需写入和读取文件——不仅将文件系统用作存储,还用作结构化的外部记忆。

Manus的压缩策略始终设计为可恢复的。例如,只要保留URL,网页内容就可以从上下文中移除;如果沙盒中仍然保留文档路径,则可以省略文档内容。这使得Manus能够缩短上下文长度,而不会永久丢失信息。

通过复述操控注意力

如果你观察过像 Manus 这样的 AI Agent 如何处理复杂任务,你会发现一个有趣的模式:它会创建一个todo.md文件,并在任务推进中不断勾选、更新它。 这并非简单的任务管理,而是一种高明的注意力操控机制。

Manus中的一个典型任务平均需要大约50次工具调用。这导致它在长上下文或复杂任务中很容易偏离主题或忘记早期目标

通过不断重写待办事项列表,Manus将其目标复述到上下文的末尾。这将全局计划推入模型的近期注意力范围内,避免了"Lost in the middle"的问题,并减少了目标不一致。

在这里插入图片描述

保留错误内容

代理会犯错,语言模型会产生幻觉,环境会返回错误,外部工具会出现异常行为,意外的边缘情况随时都会出现。在代码执行任务的过程中,出现错误是不可避免的。

我们一个常见的直觉是,如果遇到错误了,就把状态回退到错误发生前的步骤然后重新执行,希望通过抽卡的方式让模型输出正确的结果。这会让我们感觉到整个流程更可控更安全,但是这样做的代价是抹除掉了模型从错误中学习的可能。

根据Manus的经验,改善代理行为最有效的方法之一出奇地简单:将错误的尝试保留在上下文中。 当模型看到一个失败的行动——以及由此产生的观察结果或堆栈跟踪——它会隐式地更新其内部信念。这会改变其先验,降低重复相同错误的可能性。

谨慎使用few-shot

少样本学习是提高LLM输出质量的常用技术。但在Agent中,这可能导致LLM过度遵循上下文中提到的模式,即使这不再是最优的。

这在涉及重复决策或行动的任务中可能很危险。 例如,当使用Manus帮助审查简历时,它可能会局限于它看到的若干份,并被此影响,进而这导致偏离、过度泛化,或有时产生幻觉。

解决方法是增加多样性。Manus在行动和观察中引入少量的结构化变化 —— 不同的序列化模板、替代性措辞、顺序或格式上的微小噪音。这种受控的随机性有助于打破模式并调整模型的注意力。
换句话说,不要让自己陷入少样本学习的陷阱。你的上下文越单一,你的智能体就变得越脆弱。

总结

Context Engineering仍然是一门新兴的科学 —— 但对于Agent系统来说,它已经是必不可少的。模型可能变得更强大、更快速、更经济,但再多的原始能力也无法替代对记忆、环境和反馈的需求。塑造上下文的方式最终决定了我们智能体的行为方式:它运行的速度、恢复的效果以及扩展的范围。