AI Agent 的上下文工程:Anthropic 的实践方法

19 分钟

本文翻译自 Anthropic 工程博客 Effective context engineering for AI agents,原文发布于 2025-09-29,作者为 Anthropic Applied AI 团队的 Prithvi Rajasekaran、Ethan Dixon、Carly Ryan、Jeremy Hadfield,以及 Rafi Ayub、Hannah Moran、Cal Rueb、Connor Jennings 等团队成员。

过去几年,应用 AI 的注意力大多集中在 prompt engineering 上。现在出现了一个新的说法:context engineering(上下文工程)。和语言模型协作,重心正在从挑词造句转向一个更宏观的问题:什么样的上下文配置,最能引导模型生成期望的行为?

打个比方,写 prompt 像给同事写一封邮件,把意图说清楚就够了;做上下文工程像给同事配工位:决定他桌上摆什么资料、能查什么文档、忘了东西能问谁。前者关心“这次说什么”,后者关心“他随时能拿到什么”。

上下文指的是模型在采样时被一并送入的那一组 token。工程问题在于,如何在 LLM 的固有约束下,把这些 token 的利用率拉满,稳定地拿到想要的结果。处理 LLM 常常需要 thinking in context(在上下文中思考):把模型在某一时刻能看到的全部状态当作一个整体来考虑,并预判这种状态会引导出什么样的行为。

本文整理上下文工程这门正在成形的手艺,给出一套构建可控、有效 Agent 的心智模型。具体会分三层展开:静态配置(system prompt、工具、例子)决定 Agent 一上线就具备什么;运行时检索(按需取数据)决定每一步从外部取什么进来;长任务腾挪(compaction、结构化笔记、子 Agent)应对单个上下文窗口装不下的场景。

上下文工程 vs. 提示工程

在 Anthropic 看来,上下文工程是 prompt engineering 的自然延伸。Prompt engineering 关注的是怎么写、怎么组织 LLM 指令以拿到最好的结果(参考 官方文档)。上下文工程关注的范围更大:在 LLM 推理过程中,怎样筛选和维护一组最优的 token(信息),包括那些不在 prompt 里、但同样会进入上下文的内容。

LLM 工程的早期,prompt 是最大的一块。除了日常对话之外,大部分场景都是一次性的分类或文本生成任务,重点是把 prompt 写好,特别是 system prompt(开发者写给模型的角色设定与规则,用户看不到)。但随着工程目标转向 Agent(能自主调用工具、连续做几十步动作的 LLM 程序),需要管理的就不只是 prompt 本身,而是整个上下文状态:system instruction、工具、Model Context Protocol(MCP,给 Agent 接外部数据源和工具的协议)、外部数据、消息历史等等。

Agent 在循环中运行,每一轮都会产生大量可能与下一轮相关的新数据,这些数据必须被周期性地筛选。上下文工程的本质,就是从这堆持续膨胀的可能信息里,挑出真正要放进有限上下文窗口的那部分。Karpathy 在一条推文里把这件事概括为一门 art and science:“在每一步往上下文窗口里塞入恰好正确的信息”;多了、少了或者格式不对,模型的表现和成本都会偏离最优。

提示工程与上下文工程对比

写 prompt 是一次性的动作,上下文工程则是迭代式的,每一次决定送什么进模型,都要重新筛选一遍。

为什么上下文工程对 Agent 至关重要

LLM 速度越来越快,能处理的数据量越来越大,但和人类一样,它们到一定程度也会失焦或者混乱。大海捞针式的基准测试已经揭示出一种叫做 context rot(上下文腐烂)的现象:随着上下文窗口中的 token 数量增加,模型从中准确召回信息的能力会下降。Chroma 在 18 个主流 LLM(GPT-4.1、Claude 4、Gemini 2.5、Qwen3 等)上做过系统测试,即便是最简单的任务,输入越长,模型表现也会以并不均匀、甚至有些反直觉的方式衰减。

不同模型衰减的速度不同,但这个趋势在所有模型上都存在。Context rot 是表象,背后的机制是 LLM 有限的注意力预算被稀释。所以上下文必须当成一种边际收益递减的有限资源来对待。和人脑的工作记忆容量有限一样,LLM 在处理大量上下文时也会消耗一份“注意力预算”。每多塞进去一个 token,预算就被耗掉一点,进入上下文的内容因此必须被仔细挑选。

注意力稀缺背后是 LLM 的架构约束。LLM 基于 Transformer 架构每个 token 都能与上下文中其他任意 token 互相关注,n 个 token 对应 n² 量级的两两关系。上下文越长,模型有限的注意力容量就要分摊到这 n² 量级的关系上:候选 token 越多,每个 token 拿到的概率质量越稀,能聚焦到的关键依赖也就越少。还有一层因素:训练数据里短序列远多于长序列,模型的注意力模式正是从这种分布里学出来的,对于跨越超长上下文的依赖关系,它既经验不足,专门处理这种情况的参数也少。

位置编码插值这类技术(把推理时更长序列的位置索引线性压回原训练范围内)可以让模型处理比训练时更长的序列,但 token 位置的精度会有所牺牲。这些因素叠加起来,模型在长上下文里的表现并不会在某个长度上突然失效,而是平滑下降:上下文变长时模型依旧能用,只是信息检索和长程推理的精度会比在短上下文下有所损失。所以要构建有能力的 Agent,认真做上下文工程是绕不开的前提。

有效上下文的几个要素

LLM 的注意力预算有限,好的 上下文工程要做的,就是用 尽量少、含金量尽量高 的 token,把模型推向期望的结果。这件事在 system prompt、工具、例子等各个组件上都有自己的形态。

System prompt 应当极其清晰,使用直白的语言,把信息呈现在合适的“高度”上。所谓合适的高度,是两种失败模式之间那个恰到好处的中间地带(即 Goldilocks 区间)。一种极端是工程师在 prompt 里硬编码大量复杂、脆弱的逻辑,试图精确控制 Agent 的行为,这种做法长期维护负担很大。另一种极端是写得过于宽泛、含糊,假设模型已经掌握了大量共享上下文,结果模型拿不到足够明确的信号。合适的高度处在两者中间:指令具体到能引导行为,又留出余地让模型用启发式来填充细节。

校准 system prompt 的“高度”

推荐把 prompt 拆成几个清晰的区块,比如 <background_information><instructions>## Tool guidance## Output description,用 XML 标签或者 Markdown heading 划分边界。随着模型能力提升,格式的具体形态变得没那么重要,但结构感本身仍有价值。

无论怎么组织,目标都是用最少的信息把期望行为描述完整。注意,最少不等于最短,前期必须给模型足够的信息才能让它符合预期。一个好的起手是用当前最强的模型跑一遍最小 prompt,看初始表现,再根据观察到的失败模式补指令和例子。

工具让 Agent 能与环境交互,在工作过程中把新的上下文补充进来。工具是 Agent 与外部世界之间的接口,决定了它能获取什么信息、能执行什么动作。设计工具时要兼顾两件事:返回的内容要节省 token,使用方式要让 Agent 容易做出高效的选择。

Anthropic 在 Writing tools for AI agents – with AI agents 中讨论过这件事:好的工具应当像一份设计良好的代码库里的函数,具备三个特征:独立可用,不依赖外部状态遇到错误能优雅处理,不会让整个流程崩溃用途一眼能看懂,不需要猜。参数描述要具体、无歧义,并且贴合模型的能力特点。

最常见的失败模式之一,是工具集臃肿,功能边界互相重叠,模型在选哪个工具时会犹豫。如果一个人类工程师都说不清某个场景该用哪个工具,让 Agent 来选就更指望不上了。把工具集裁到最小可用集,对长会话中的上下文维护和裁剪也更友好。

提供例子(few-shot prompting)依然是值得坚持的实践。但常见的误区是在 prompt 里堆砌大量边界 case,试图穷尽规则。比起这样,更好的做法是挑出一组多样、典型的例子,把期望行为以“画面”的方式呈现给模型。对 LLM 来说,例子相当于一图胜千言。

不论是 system prompt、工具还是例子,整体原则都一致:上下文要丰富但要紧凑。

上下文检索与 Agentic 检索

今天的 Agent 拿上下文,大致有两条路:一条是推理之前预先把数据找好放进窗口(预检索,典型做法是 embedding 召回),另一条是边走边由 Agent 自己决定要拿什么(Agentic 检索,按需用工具取数据)。下面具体看 Agentic 检索这条路是怎么走通的。

构建高效的 AI Agent 一文中,Anthropic 区分了基于 LLM 的工作流和 Agent。那之后,业界对 Agent 的定义在向 一个简单的版本 收敛,Simon Willison 把它精简为一句话:“LLM agent runs tools in a loop to achieve a goal.”(LLM Agent 在循环中调用工具达成目标。)

Anthropic 与客户的合作中也看到整个领域正在向这个简单范式收敛。底层模型越强,Agent 能放手做的事就越多:聪明的模型能让 Agent 独立应对更复杂的问题,出了错也能自己恢复。

工程师设计 Agent 上下文的方式也在变。在 embedding 预检索之上,越来越多团队叠加“按需获取”的策略:不把所有可能相关的数据预先塞进去,而是让 Agent 持有一组轻量化的引用(文件路径、查询语句、网页链接等),运行时再通过工具动态把数据载入上下文。

Anthropic 自己的 Claude Code 走的就是这条路。模型写定向查询,把结果落到外部文件,再用 headtail 这类 Unix 命令查看数据,完整数据始终不进入上下文。这种模式更接近人类认知:人不会把全部资料背下来,而是建立文件系统、收件箱、书签这类外部组织和索引系统,到用的时候再去取。

除了节省存储,这些引用本身的元数据,也提供了一种隐式的行为引导方式。对一个在文件系统里活动的 Agent 来说,tests 目录下的 test_utils.pysrc/core_logic/ 下的 test_utils.py 暗示着不同的用途。目录层级、命名约定、时间戳,对人类和 Agent 都是重要信号,决定了一份信息何时、如何被使用。

这种结构信号正好补上预检索的短板。Embedding 召回有几个典型失败模式:目录关系、调用链这类结构信息在向量化时被压平,索引会随代码改动而陈旧,用户查询与文档片段之间的语义对齐也并不稳定。按需获取走的是另一条路:直接拿文件路径、调用图这类原始结构当线索,把这几个陷阱都避开了。

让 Agent 自己去导航和检索数据,还能开启一种渐进式披露:Agent 通过探索逐步发现相关上下文。每次交互都产生新的线索:文件大小提示复杂度,命名约定提示用途,时间戳近似代表相关性。Agent 一层一层地构建理解,工作记忆里只保留必要的东西,配合笔记策略来做持久化。这种自管理的上下文窗口让 Agent 始终聚焦在相关的子集上,而不是淹没在详尽但可能无关的信息里。

代价当然存在:运行时探索比预先取数据慢;而且要让 LLM 在复杂的信息环境里能正确处理问题,工具和启发式规则必须精心设计。指引不够时,用错工具、陷入死循环、错过关键信息,都会让 Agent 白白浪费上下文。

在某些场景下,最有效的方式是混合策略:一部分数据先取进来确保速度,剩下的让 Agent 自主探索。决策的边界取决于任务本身。Claude Code 走的就是这种混合路线,项目根目录的 CLAUDE.md 配置文件被原样塞进上下文,glob、grep 这类基础工具则让 Agent 按需在环境里取文件,绕开了陈旧索引和复杂语法树带来的麻烦。对于内容变动较少的场景,比如法律或财务工作,这种混合策略可能更合适。

随着模型能力提升,Agent 的设计趋势是把更多判断让渡给聪明的模型,让人工筛选越来越少。在这种快速变化下,“做能跑通的最简单的事”是给团队最稳妥的建议。

长任务下的上下文工程

长任务要求 Agent 在动作序列中保持连贯、保持上下文、保持目标,而整个过程的 token 量往往远远超过单个上下文窗口。对那些跨越几十分钟到几个小时连续工作的任务(大规模代码迁移、综合研究项目等),Agent 需要专门的技术来绕开上下文窗口的尺寸限制。

等待更大的上下文窗口似乎是直接的解法。但前面提到的 n² 注意力稀释决定了,无论窗口扩到多大,上下文污染和信息相关性的问题仍然会跟着出现,至少在需要最强 Agent 表现的场景下如此。Anthropic 总结了几种针对上下文污染的方法:compaction(压缩)结构化笔记子 Agent 架构

Compaction

Compaction 指的是,在对话快达到上下文窗口上限时,对当前内容做一次总结,再用这份总结作为起点重启一个新的上下文窗口。它通常是长任务上下文工程的第一道杠杆,目标是用高保真的方式浓缩上下文,把 Agent 的性能下降控制到最小。

在 Claude Code 里,这件事的实现方式是把消息历史交给模型,让它做总结和压缩。模型会保留架构决策、未解决的 bug、实现细节,丢掉冗余的工具调用输出和消息。压缩完成后,Agent 基于这份总结、外加最近访问过的五个文件继续工作,用户感受不到上下文窗口的限制。

Compaction 的艺术在于决定保留什么、丢弃什么。压缩过度,会丢失细微但关键的上下文,而它们的价值往往要到后续步骤才会显现。实施 compaction 系统的工程师,建议在一组复杂的 Agent trace 上反复打磨 prompt:先以最大化召回为目标,确保不遗漏任何相关信息;再迭代提升 precision,剔除多余内容。

最容易做的一项 compaction,是清理过期的工具调用和返回结果:某个工具在很多步之前调用过一次,得到的原始结果对当前推理已经没用,Agent 后续也不会再读,留在上下文里只是浪费 token。这种最轻量的 compaction 已经作为一项工具结果清理功能上线了 Claude 开发者平台。

结构化笔记

结构化笔记,也叫 agentic memory,是让 Agent 定期把笔记写到上下文窗口之外的持久化存储里。这些笔记可以在后续被读回上下文。

这种模式提供低开销的持久记忆。Claude Code 里维护的 todo 列表,或者自定义 Agent 里的 NOTES.md,都是这个范式:让 Agent 在跨数十次工具调用的复杂任务里追踪进度、保留关键依赖。

Claude 玩宝可梦(Anthropic 在 Twitch 上长期直播 Claude 通关《宝可梦》的实验)展示了笔记如何在非编码领域改变 Agent 的能力。Agent 在数千步游戏过程中维护精确的状态记录,比如“过去 1234 步在 Route 1 训练宝可梦,皮卡丘已升 8 级,目标 10 级”。没有专门关于笔记结构的提示,它自己就发展出探索过区域的地图、记住关键成就、记录战斗策略,并归纳哪种攻击对哪种对手更有效。

每次上下文被压缩刷新之后,Agent 重新读取自己写下的笔记,就能接着完成几个小时的训练或地下城探索。靠笔记把多次压缩之间的状态串起来,那些原本单个窗口装不下的长任务策略就有机会跑通。

Sonnet 4.5 发布时,Claude 开发者平台同步上线了一个 memory 工具(public beta),用文件系统的方式让 Agent 更方便地在上下文窗口之外存取信息。Agent 可以由此沉淀知识、跨会话维护项目状态、回溯过去的工作,而不必把所有东西都留在上下文里。

子 Agent 架构

子 Agent 架构是另一种绕过上下文限制的方式。让一个 Agent 维护整个项目的状态开销很大,不如让专门的子 Agent 用各自干净的上下文窗口承担聚焦的任务。主 Agent 负责高层计划,子 Agent 负责具体的技术深挖或工具调用。每个子 Agent 可能用掉上万 token 做探索,但回到主 Agent 时只交付一份高度浓缩的总结(通常 1000–2000 token)。这个比例之所以合理,是因为主 Agent 不需要原始证据链,它要的只是结论;细节在子 Agent 内部已经被消化掉了。

这种切分干净地分离了关注点:细节搜索的上下文留在子 Agent 内部,主 Agent 专注于综合与分析。这种模式在 How we built our multi-agent research system 中被详细讨论过,在复杂研究任务上相比单 Agent 系统有明显提升。

三种方法的选择取决于任务特性:

  • Compaction 适合需要大量来回对话、要保持流动感的任务;
  • 结构化笔记 适合有清晰里程碑的迭代式开发;
  • 子 Agent 架构 适合复杂研究和分析,需要并行探索带来回报的场景。

结语

上下文工程标志着 LLM 应用构建方式的根本转向。随着模型能力提升,挑战不再是把单个 prompt 写到极致,而是在每一步思考清楚:哪些信息进入模型有限的注意力预算。无论是用 compaction 处理长任务、设计节省 token 的工具,还是让 Agent 按需探索环境,原则都一样:找到能最大化期望结果的最小一组高信号 token。

本文给出的这些技术会随模型进步而继续演化。模型越聪明,工程上需要规约的细节就越少,Agent 自治的空间也越大。无论能力扩张到哪一步,把上下文当作一种有限资源来对待,都会是构建可靠 Agent 的稳定方法。

目录