Effective Context Engineering:管理 Agent 的有限注意力

原始来源

Anthropic Applied AI 团队,2025-09-29:Effective context engineering for AI agents。

本页是原创学习导读,不含原文复刻;文章版权与完整内容以原始来源为准。

学完这一篇,你应该能做到什么

  • 区分 prompt engineering 与 context engineering,并列出一次推理中真正进入上下文的主要组成。
  • 把上下文理解为有限注意力预算,用“最小但足够的高信号 token 集”指导取舍,而不是追求越短或越长。
  • 为一个长任务设计完整生命周期:固定核心、按需检索、去重打包、执行反馈、外部笔记、压缩与恢复。
  • 判断哪些内容必须保留原文、哪些只留指针、哪些可以摘要、哪些应该丢弃。
  • 正确选择 compaction、structured note-taking 和 multi-agent,而不是把它们当成同一种“记忆”。

一句话抓住它

上下文工程不是把所有可能有用的材料塞进模型窗口,而是:在每一次推理前,从不断增长的信息宇宙中选择最小但足够的高信号 token 集,并在行动后持续更新、外部化和清理这套工作状态。

为什么“把 Prompt 写好”已经不够

Prompt engineering 主要关心指令的写法和组织方式。对一次分类或文本生成,这可能是主要工作;但长期 Agent 的一次推理还会看到:

  • 系统指令与权限边界;
  • 可用工具及其参数说明;
  • 少量示例;
  • 用户消息与对话历史;
  • 检索到的文档、代码和网页片段;
  • 工具调用结果、日志和测试输出;
  • 当前计划、待办和外部记忆;
  • 前几轮行动产生的新证据。

Agent 每执行一步,候选信息就继续增长。于是问题不再是“一开始怎样写一句完美指令”,而是“这一轮到底让模型看到什么;执行后什么进入下一轮;窗口接近上限时怎样保持连续性”。这就是 context engineering 的范围。

先定义四个对象

对象含义例子
信息宇宙当前任务可能访问的全部外部与历史信息整个仓库、全部日志、文档库、历史消息
候选上下文本轮可能有用、等待筛选的材料搜索命中、最近测试、相关规则、当前计划
已打包上下文真正作为 token 输入这一次模型采样的内容系统提示、工具定义、选定代码片段和摘要
外部状态不在本轮窗口内,但可通过指针再次读取的持久信息文件、数据库、结构化笔记、artifact URL

上下文窗口再大,也不代表所有 token 都同样容易被准确使用。原文用“有限注意力预算”和“边际收益递减”建立心智模型:无关、重复、冲突和低信号内容会与真正重要的信息竞争注意力。因此“最小”不是越短越好,而是没有多余负担,同时没有删掉完成任务所需的约束、证据和状态。

长窗口不是免做上下文工程

容量解决“能不能放进去”,不自动解决“模型能否在大量相似、冲突或陈旧信息中稳定找到并使用正确内容”。

上下文生命周期:不是一次组装,而是循环

flowchart TD
    U["信息宇宙:仓库、文档、历史、工具结果"] --> C["候选清单"]
    C --> H["固定高优先级:目标、权限、硬约束"]
    C --> R["按需检索:路径、查询、相关片段"]
    C --> N["近期状态:计划、未决问题、最新证据"]
    H --> F["去重、排序、预算打包"]
    R --> F
    N --> F
    F --> M["模型推理并选择行动"]
    M --> O["环境观察:结果、错误、测试、日志"]
    O --> J{"仍在当前阶段且窗口健康?"}
    J -->|"是"| C
    J -->|"接近上限 / 阶段结束"| X["外部化笔记 + Compaction"]
    X --> D["丢弃冗余原始输出,保留证据指针"]
    D --> C

一轮的输入、输出、状态、反馈与停止

部分应明确的内容
输入当前目标、硬约束、可用工具、候选材料、token 预算
状态当前子目标、已完成工作、未解决问题、最近证据、来源位置、剩余预算
控制流固定内容先放入;按相关性和新鲜度检索;去重;按优先级打包;超预算时降级为摘要或指针
输出一份用于本轮推理的上下文,以及对未选材料的可恢复引用
反馈模型行动后产生的新事实、错误、工具输出与状态变化,重新进入候选池
阶段停止当前验收完成、需要人工判断,或窗口接近阈值时先写检查点并压缩,再开启新的上下文阶段

这里的“停止”不一定结束整个 Agent。它可能只结束一个上下文阶段,然后由摘要、外部笔记和证据指针恢复下一阶段。

五类核心上下文应该怎样设计

1. 系统提示:选择正确抽象层级

原文指出两个极端:一端是把每个分支都硬编码进提示,形成脆弱的伪程序;另一端是只写“请做正确的事”,默认模型知道团队背景。合适的层级应该:

  • 清楚定义角色、目标、权限和不可违反的边界;
  • 给出足以指导行为的强启发式,而不是穷举所有路径;
  • 用明确章节区分背景、指令、工具说明和输出契约;
  • 从小而完整的版本开始,根据真实失败补充规则。

“minimal”不等于“short”。安全、数据格式或审批边界如果确实必要,就应该完整出现。

2. 工具定义:既提供动作,也控制新上下文

工具不仅改变环境,还把结果带回窗口。好的工具应当:职责单一、与其他工具少重叠、参数名清楚、错误可恢复、结果 token 高效。若两个工具连人类都难判断该选哪个,模型也会浪费注意力。

工具输出最好支持:过滤、分页、上限、摘要、来源位置和按需展开。一次返回十万行日志通常不是“信息丰富”,而是把检索工作推给有限窗口。

3. 示例:少量典型,不是边缘情况清单

Few-shot 示例能把抽象规则变成可模仿形状,但把所有边缘案例塞进提示会制造相互竞争的模式。应选择少量、差异化、典型的正反例,并让每个示例对应一个重要决策边界。

4. 检索:从预先塞满转向 just in time

Just-in-time 策略先给 Agent 轻量标识符,例如文件路径、保存的查询、网页链接或目录索引,再让它根据当前问题加载局部数据。文件名、目录、时间戳和大小本身也提供用途信号,Agent 可以逐步披露地形成理解:

看到目录地图 → 选择相关包 → 搜索符号 → 打开局部文件
             → 运行目标测试 → 只展开失败堆栈关联代码

优点是上下文保持聚焦、索引不必预装所有内容;代价是运行时探索更慢,而且需要可用的导航工具和启发式。现实中经常使用混合策略:稳定、必须遵守的内容预先放入;大体量和动态数据按需检索。

5. 消息历史与工作状态:保留因果,不保留所有字节

长期 Agent 需要知道“为什么走到这里”,但不必永久保留每次完整日志。历史中最有价值的是:目标、关键决定、已验证事实、失败路线及原因、未解决风险和原证据位置。旧工具输出如果已被新结果取代,可以清除正文,只留结论与指针。

一次完整案例:跨小时迁移 Python 配置格式

这是一个教学案例。目标是把仓库中旧版 YAML 配置迁移到新 schema,同时保持 CLI 兼容并更新测试。假设用“100 个注意力单位”表示相对预算;这不是任何模型的真实 token 数,只用于展示取舍。

阶段 1:建立任务地图

内容处理方式原因
用户目标、不能破坏旧 CLI、只改目标目录固定保留每一步都必须遵守
仓库入口说明、测试命令、目录地图保留摘要与路径支持导航,不预装所有文档
全部 800 个配置文件只保留目录和查询入口本轮不需要原文
搜索得到的 schema 定义和两个代表样本展开原文当前诊断直接依赖

Agent 先用约 35 个单位建立地图,而不是一次加载全部 800 个文件。

阶段 2:定位与设计

Agent 搜索解析入口、读取旧/新 schema、运行一个失败 fixture,并把结果写入结构化笔记:

## Goal
Migrate YAML to schema v2 without breaking legacy CLI input.
 
## Verified
- Parser entry: `src/config/load.py`
- Current failing fixture: `tests/fixtures/legacy_timeout.yml`
- `timeout` changed from scalar to `{value, unit}`.
 
## Decision
- Normalize legacy scalar at the parser boundary.
 
## Open
- Does export need to preserve legacy spelling?
 
## Evidence pointers
- Test run: artifact `runs/184/pytest.txt`
- Schema: `src/config/schema_v2.py:40`

这份笔记不复制整个测试输出,却保留了可追溯位置,也明确区分 Verified、Decision 与 Open。

阶段 3:实现时按需展开

实现阶段只加载解析器、相关类型、目标测试和最新失败摘要。旧搜索结果已经完成定位,被压成一行;无关的 README、成功安装日志和重复堆栈被丢弃。工具返回新的测试失败时,旧失败不会与新失败并列占据同等优先级,而是标记为 superseded。

阶段 4:窗口接近上限时压缩

在窗口临近阈值前,系统先写检查点,再执行 compaction:

  • 必须保留:目标、硬约束、架构决定、已改文件、尚未解决的 export 问题、最新测试状态;
  • 可摘要:探索过的三种方案及放弃原因;
  • 只留指针:长测试日志、完整 diff、原始 schema;
  • 可丢弃:重复成功输出、已修复的同一语法错误、被新状态覆盖的文件片段。

新窗口从压缩摘要、结构化笔记和最近访问的相关文件恢复,而不是从一段“我们做了很多修改,目前看起来不错”的空泛总结恢复。

阶段 5:隔离独立探索

若 export 兼容性需要深入查多个模块,可以把它交给独立 sub-agent。子 Agent 在干净上下文中阅读大量材料,只返回:结论、证据路径、置信度、未解项和推荐动作。主 Agent 不接收它的全部思考轨迹,从而隔离细节成本。

最终,主 Agent 运行完整验收、更新笔记为完成状态,并保留最终报告和测试 artifact;探索中的临时搜索结果不再进入以后任务。

三种长任务策略不是同一回事

策略核心动作最适合主要风险
Compaction把快满的消息历史压成高保真摘要,在新窗口继续需要保持连续对话和近期因果链过度压缩会丢掉后来才显得关键的细节
Structured note-taking定期把状态写到窗口外的结构化持久笔记,再按需读回有清晰里程碑、跨会话或长时间迭代笔记可能陈旧,推断可能被误写成事实
Multi-agent把聚焦子任务放入独立上下文,只返回压缩成果独立探索很深、并行收益高汇总丢证据、重复工作、子任务边界不清

原文建议 compaction 先追求 recall,再逐步提高 precision:首先保证关键信息都被保住,然后才删除冗余。最安全的轻量起点之一,是清理历史深处已经失效的原始工具结果。

一个可实现的上下文选择器

def build_context(task, candidates, budget):
    pinned = select_hard_requirements(task)  # goal, safety, permissions
    recent = select_latest_state(candidates)
    retrieved = retrieve_for_current_subgoal(task.subgoal, candidates)
 
    items = deduplicate(pinned + recent + retrieved)
    items = mark_provenance_and_freshness(items)
    items = rank_by(
        items,
        requiredness=True,
        relevance=True,
        recency=True,
        evidence_quality=True,
    )
 
    packed = []
    for item in items:
        view = item.full_text
        if not fits(packed, view, budget):
            view = item.summary_with_pointer
        if fits(packed, view, budget):
            packed.append(view)
 
    assert contains_goal_constraints_and_open_state(packed)
    return packed

真实系统会更复杂,但关键规则已经出现:硬约束先固定;围绕当前子目标检索;重复内容只保留最新可信版本;每个摘要带来源和新鲜度;超预算时先降级为“摘要 + 指针”,而不是随机截断。

什么该保留、什么该丢弃

内容类型默认策略
目标、权限、安全和数据契约固定保留;变更必须显式
最新失败、当前代码与未决阻塞高优先级保留
已验证事实保留结论、验证方式和证据指针
设计决定保留决定、理由、替代方案和日期
长原始日志保留最新相关片段,其余外部存储并留指针
已被新结果替代的观察标记过期后移出上下文
重复说明与寒暄通常丢弃
未验证推断可保留,但必须标记为 hypothesis,不能压成事实

丢弃标准必须与任务有关。“代码很长”不是删除理由;若它正是当前修改对象,原文可能比摘要更重要。“刚刚生成”也不是保留理由;一段新但无关的日志仍然是低信号内容。

这篇文章提供的证据与边界

原文是一篇面向 Agent 构建者的官方工程文章。它结合 Anthropic 的产品经验、客户实践和对长上下文研究的引用,提出“注意力预算”“最小高信号 token 集”、just-in-time 检索以及三种长任务策略。它不是一项把所有策略在统一任务上随机对照的实验,也没有给出适用于所有模型的固定 token 配方。

证据分层

“文章描述团队采用了某种策略”是已核验的来源事实;“你的系统采用后一定提升成功率”仍是待验证假设。应在代表性任务上测成功率、错误类型、成本、延迟和压缩后的连续性,而不只比较 token 数。

常见失败模式

  1. Context dumping:因为“可能有用”就预装全部仓库或全部日志,模型没有清晰优先级。
  2. 指令冲突:旧规范、新规范和示例同时出现,却没有版本或权威层级。
  3. 检索无来源:摘要进入上下文但没有路径和时间,无法验证,也不知道是否陈旧。
  4. 过度压缩:只剩结论,没有约束、放弃原因和未决项,下一阶段重复犯错。
  5. 压缩升级事实:把“模型猜测可能是缓存问题”写成“根因是缓存”。
  6. 结构化笔记腐烂:代码变了,笔记仍被当作 system of record;需要所有者、新鲜度和可执行校验。
  7. Sub-agent 回报过薄:只返回一句结论,没有证据路径、范围和置信度,主 Agent 无法复核。
  8. 只优化 token 数:上下文变短但任务成功率下降。真正目标是期望行为,不是最小账单。

历史贡献与今天的工程解释

原文贡献

文章把长期 Agent 的核心工作从“写一个好 prompt”扩展为“持续维护整个推理状态”,并把 context 明确定义为采样时真正进入模型的 token。它提供了一个统一框架,把系统提示、工具、示例、检索、历史、压缩、笔记和多 Agent 都放进同一注意力预算下讨论。

今天可直接采用的工程原则

  • 让原始证据留在外部可寻址存储,上下文只放当前需要的视图和指针。
  • 为上下文条目记录来源、时间、权威级别和是否被后续证据替代。
  • 把压缩设计成有测试的状态迁移:压缩后,目标、当前状态、下一步、阻塞和关键决定必须仍可回答。
  • 对检索与 compaction 建立任务级 eval,检查漏掉关键约束、使用陈旧信息和重复探索等失败。
  • 先选择最小可用工具集,结果支持按需展开;工具数量本身不是能力指标。

与“记忆”的区别

Generative Agents 重点研究哪些经历值得长期保存、检索和反思;本文的 context 包含一次推理时所有 token,记忆只是其中一个来源。窗口、外部存储和记忆策略三者不能混为一谈。

自测:你会真正做取舍了吗

  1. 为什么“最小高信号 token 集”不等于“把系统提示压到最短”?
  2. 在迁移任务中,完整测试日志什么时候应进入上下文,什么时候只留指针?
  3. Compaction 与 structured note-taking 分别解决什么连续性问题?
  4. 一个 sub-agent 回报至少应包含哪些字段,才能既压缩又可复核?
  5. 压缩后怎样检测“模型推断被升级成事实”?

上一篇:Building Effective Agents · 下一篇:Harness Engineering · 返回 里程碑原典目录