概述

Agent 的能力上限不只取决于模型,也取决于每次调用模型时,我们给了它什么上下文。

早期做大模型应用时,很多工作集中在提示词工程:怎么写 system prompt、怎么约束输出格式、怎么给 few-shot 示例。但 Agent 不是一次性问答,它会持续读取资料、调用工具、写代码、运行命令、处理错误、更新计划。随着任务变长,真正困难的问题变成:

在每一轮模型调用前,如何决定哪些信息进入上下文、哪些信息留在外部、哪些信息被压缩、哪些信息被隔离到子任务里。

这就是上下文工程(Context Engineering)。

截至 2026-05-11,Agent 上下文工程已经不再只是“长上下文不够用”的补丁,而是在向一套系统工程方法演进:状态管理、检索、压缩、记忆、工具协议、子 Agent 协作和可观测性共同组成 Agent 的上下文层。

技术路径演进

阶段一:Prompt Engineering

最早的大模型应用主要依赖提示词工程。

典型做法是:

  • 写清楚角色和任务目标。
  • 规定输出格式。
  • 给几个示例。
  • 在 prompt 里塞业务规则。

这种方式适合短任务,比如分类、摘要、信息抽取、单轮问答。因为模型只需要在一次调用里完成任务,上下文结构相对简单。

但它的问题也很明显:提示词只能控制“指令表达”,不能解决“运行过程中产生的信息如何管理”。一旦任务变成多轮,prompt 本身就不够用了。

阶段二:RAG,把外部知识拉进上下文

第二个阶段是 RAG。它解决的问题是:模型参数里没有最新知识或私有知识,所以需要从外部知识库检索相关内容,再放进上下文。

这一阶段的上下文工程重点是:

  • 文档如何切块。
  • 如何建立索引。
  • query 如何改写。
  • top-k 取多少。
  • 如何 rerank。
  • 如何把检索结果拼进 prompt。

RAG 让上下文从“手写 prompt”扩展到了“动态知识注入”。但早期 RAG 仍然偏向单轮问答:用户问一个问题,系统检索一批文档,模型生成答案。

Agent 场景会进一步复杂。Agent 不只是读文档,还要在过程中不断产生新状态:计划、工具结果、错误、代码改动、用户反馈、长期偏好。这些内容不是普通 RAG 能完整处理的。

阶段三:Tool Use,工具结果成为上下文

当模型开始调用工具,上下文结构又变复杂了一层。

工具调用带来了新的上下文类型:

  • 工具定义:模型需要知道有哪些工具、什么时候用。
  • 工具参数 schema:模型需要按结构生成参数。
  • 工具返回值:模型需要理解外部系统的结果。
  • 工具错误:模型需要根据失败信息恢复。
  • 工具权限:模型需要知道哪些动作允许执行。

这个阶段的问题是:工具很容易制造上下文噪声。

例如搜索工具返回 50 条结果、浏览器工具返回整页 HTML、命令行工具返回几百行日志。模型看似获得了更多信息,实际上注意力被稀释了。

因此,工具时代的上下文工程开始强调两个原则:

  1. 工具描述要短而明确:不要把所有工具文档都塞给模型。
  2. 工具返回要结构化和裁剪:返回摘要、关键字段、错误类型、下一步线索,而不是无脑返回原始输出。

阶段四:Memory 与 Session,让上下文跨轮延续

多轮 Agent 必须记住过去发生了什么,于是出现了 session、memory、conversation state 等机制。

OpenAI Agents SDK 文档把 context 分成两类:

  • Local context:代码本地可访问的状态,例如用户 ID、依赖对象、数据库连接、运行时状态。它不直接进入模型。
  • LLM context:模型真正能看见的文本、消息、工具结果和检索内容。

这个区分很关键。不是所有状态都应该给模型看。

例如:

1
2
3
4
5
6
7
8
9
10
本地状态:
user_id = 1024
db = PostgresConnection
permissions = ["read_repo", "run_tests"]
trace_id = "run_abc"

模型上下文:
用户正在修复 Hexo 博客构建失败
最近一次 npm run build 报错是 front-matter YAML 缩进错误
允许读取 source/_posts 下的 Markdown 文件

Session 解决的是“历史如何延续”,但它也会带来新问题:如果每轮都把历史消息完整拼回去,Agent 会越来越慢,也越来越容易被旧信息干扰。

所以 session 之后,必然会走向 trimming、summarization、compaction。

阶段五:Compaction,压缩长任务历史

Anthropic 在 Agent 上下文工程文章里明确提到,长时间任务需要处理 context pollution 和信息相关性问题。它们在 Claude Code 中使用 compaction:当对话接近上下文限制时,把历史压缩成高保真摘要,再用摘要开启新的上下文窗口。

Compaction 和普通摘要不同。

普通摘要追求“读起来简洁”。Agent compaction 追求“压缩后还能继续干活”。它必须保留:

  • 原始目标
  • 当前进度
  • 已修改文件
  • 已确认事实
  • 已做出的技术决策
  • 已经排除的方案
  • 最新错误和失败命令
  • 下一步行动

一个可用的 compaction 输出可以长这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
## Goal

修复 Hexo 博客构建失败,并保证新增文章能生成页面。

## Confirmed Facts

- 项目根目录是 D:\MyBlog。
- Hexo 版本是 7.3.0。
- 文章目录是 source/_posts。
- 构建命令是 npm run build。

## Changes Made

- 新增 source/_posts/2026-05-11-Agent上下文工程.md。

## Latest Verification

- npm run build 已通过。
- 生成 public/2026/05/11/Agent上下文工程/index.html。

## Open Issues

- PowerShell 终端显示中文乱码,但 Hexo 能正常生成页面。

## Next Action

如果继续编辑文章,只修改 source/_posts/2026-05-11-Agent上下文工程.md,然后重新运行 npm run build。

这类摘要不是写给人看的,而是写给下一轮 Agent 继续工作的。

阶段六:Structured Note-taking,把中间状态写到窗口外

只靠 compaction 仍然不够。因为压缩通常发生在上下文快满时,而长任务从一开始就应该主动管理状态。

因此更进一步的做法是 structured note-taking:让 Agent 把重要状态写到外部结构化存储。

可以是简单文件:

1
2
3
4
TASK.md
DECISIONS.md
ERRORS.md
TODO.md

也可以是数据库表:

1
2
3
4
5
tasks(id, goal, status, acceptance_criteria)
facts(id, task_id, content, source, confidence)
decisions(id, task_id, decision, reason, created_at)
errors(id, task_id, command, summary, resolved)
artifacts(id, task_id, path, description)

关键点是:上下文窗口只保留当前需要推理的信息,完整历史放在外部状态里。

这种思路和传统软件工程很接近:模型上下文是工作内存,数据库和文件系统是持久存储。

阶段七:Sub-agent,把上下文隔离到子任务

当任务本身很复杂时,单个 Agent 的上下文管理会越来越困难。于是上下文工程进入多 Agent 或子 Agent 阶段。

Anthropic 提到 sub-agent architecture:主 Agent 维护高层计划,子 Agent 负责局部探索,最后只返回压缩结论。

例如做一次技术调研:

1
2
3
4
5
6
7
8
9
10
11
12
主 Agent:
维护文章结构、判断哪些结论能写入最终稿。

资料 Agent:
查询 Anthropic、LangChain、OpenAI、HumanLayer 等资料。
返回每个来源的关键结论和链接。

工程 Agent:
总结可落地的架构和数据结构。

审查 Agent:
检查是否有过度泛化、引用不准、缺少实践步骤。

每个子 Agent 可以消耗大量 token,但主 Agent 只接收 1000 到 2000 token 的结果。这本质上是用任务边界来做上下文隔离。

阶段八:Context Policy,把上下文工程产品化

再往后,上下文工程会从经验规则变成可配置策略。

也就是不再手写:

1
把最近 10 条消息放进去,再加 5 条检索结果。

而是定义上下文策略:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
context_policy:
always_include:
- system_instructions
- task_goal
- acceptance_criteria

retrieve:
project_rules:
source: AGENTS.md
when: always
long_term_memory:
top_k: 5
min_score: 0.72
code_snippets:
top_k: 8
max_tokens_each: 800

compress:
conversation:
trigger: token_usage > 70%
keep_last_turns: 4
tool_outputs:
trigger: output_tokens > 2000
strategy: structured_summary

isolate:
research:
use_subagent: true
return_max_tokens: 1500

这时 Agent 系统会有一个专门的 context builder,在每次模型调用前,根据策略组装上下文。

怎么做:一套可落地的上下文工程流水线

如果要实现一个较可靠的 Agent,不要从“写一个超长 prompt”开始,而应该从上下文流水线开始。

1. 先定义上下文分层

建议把上下文分成七层:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
L0 System Contract
模型角色、安全边界、输出格式、工具使用原则。

L1 Task Brief
用户目标、验收标准、当前阶段。

L2 Runtime State
当前计划、待办事项、已完成步骤、当前阻塞点。

L3 Retrieved Context
从文档、代码、知识库、记忆中检索到的相关内容。

L4 Tool Context
可用工具、工具调用结果、工具错误。

L5 Working Memory
最近几轮高相关对话和中间推理状态。

L6 Long-term Memory
用户偏好、项目约定、历史决策、可复用经验。

每一层都要回答三个问题:

  • 这层信息从哪里来?
  • 什么时候进入模型上下文?
  • 超过 token 预算时如何裁剪?

2. 给每类上下文设置生命周期

不要让所有信息都永久留在历史里。

可以这样设计:

上下文类型 保留位置 生命周期 进入模型的方式
用户目标 session/task 表 整个任务 每轮必带
验收标准 session/task 表 整个任务 每轮必带
最近对话 session 短期 保留最近 N 轮
工具原始输出 blob/log 短期 默认不带,按需读取
工具摘要 task state 中期 相关时带入
错误记录 errors 表 到解决为止 未解决错误必带
已确认事实 facts 表 长期 检索命中时带入
用户偏好 memory 长期 高置信度才带入
敏感信息 secure store 按权限 默认不进入模型

这样做的好处是,Agent 不会把所有东西混在聊天记录里。

3. 实现 Context Builder

Context Builder 是上下文工程的核心模块。它的职责是:在每次模型调用前,把外部状态转换成模型输入。

伪代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
def build_context(task_id: str, user_message: str, token_budget: int) -> list[Message]:
task = load_task(task_id)
state = load_runtime_state(task_id)
recent_turns = load_recent_turns(task_id, limit=4)

memories = retrieve_memories(
query=user_message,
task=task,
top_k=5,
)

docs = retrieve_documents(
query=build_retrieval_query(task, state, user_message),
top_k=8,
)

unresolved_errors = load_unresolved_errors(task_id)
tool_summaries = load_relevant_tool_summaries(task_id)

blocks = [
system_contract(),
task_brief(task),
runtime_state(state),
error_block(unresolved_errors),
memory_block(memories),
document_block(docs),
tool_summary_block(tool_summaries),
recent_turns_block(recent_turns),
current_user_message(user_message),
]

return fit_to_budget(
blocks=blocks,
token_budget=token_budget,
priority_order=[
"system",
"task_brief",
"runtime_state",
"unresolved_errors",
"current_user_message",
"recent_turns",
"tool_summaries",
"retrieved_docs",
"memories",
],
)

这里最重要的是 fit_to_budget。它不能简单截断,而要按优先级裁剪。

4. 工具返回必须结构化

工具返回是 Agent 上下文污染的高发区。

一个不好的工具返回:

1
2
3
4
返回整个网页 HTML
返回完整 npm install 日志
返回 200 条搜索结果
返回整份数据库表

一个更好的工具返回:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"status": "failed",
"tool": "shell",
"command": "npm run build",
"summary": "Hexo front-matter YAML parse failed.",
"important_lines": [
"YAMLException: bad indentation of a mapping entry",
"source/_posts/2026-05-11-Agent上下文工程.md"
],
"artifact_refs": [
"logs/build-2026-05-11.txt"
],
"suggested_next_actions": [
"Inspect front-matter indentation",
"Run npm run build again after fix"
]
}

原则是:原始输出可以存起来,但进入模型上下文的应该是结构化摘要和必要引用。

5. 检索不只检索文档,也检索状态

很多人把 retrieval 理解成只检索知识库。Agent 场景下,应该检索四类东西:

  • 文档知识:API 文档、设计文档、博客资料。
  • 代码片段:相关函数、配置、测试。
  • 任务历史:之前做过什么、失败过什么。
  • 长期记忆:用户偏好、项目规范、历史决策。

检索 query 也不应该只用用户原话。更好的 query 应该结合当前任务状态:

1
2
3
4
5
用户原话:
目前的总结过于泛化了,我需要的是目前上下文工程的一个技术路径演进以及怎么做

增强后的检索 query:
Agent context engineering technical evolution prompt engineering RAG tool use memory session compaction subagents implementation architecture

6. 压缩要分类型,不要统一摘要

不同内容需要不同压缩策略。

内容 压缩目标 推荐格式
对话历史 保留目标、决策、未解决问题 task summary
工具日志 保留结果、错误、关键行 structured JSON
资料调研 保留结论、来源、可信度 source notes
代码阅读 保留文件、函数、依赖关系 code map
子 Agent 输出 保留结论、证据、风险 brief report

例如资料调研的压缩结果可以是:

1
2
3
4
5
6
7
8
9
10
11
12
13
## Source

Anthropic, Effective context engineering for AI agents, 2025-09-29

## Key Points

- Context is finite and has diminishing returns.
- Long-horizon agents need compaction, structured note-taking, and sub-agent architectures.
- Claude Code compaction preserves architectural decisions, unresolved bugs, implementation details, and recent files.

## Relevance

用于说明上下文工程从提示词优化转向状态管理和长任务连续性。

7. 子 Agent 不是越多越好

子 Agent 的价值是隔离上下文,不是制造复杂性。

适合拆子 Agent 的情况:

  • 需要并行查多个来源。
  • 需要探索大量代码,但主任务只需要结论。
  • 需要独立审查风险。
  • 子任务失败不会破坏主任务状态。

不适合拆子 Agent 的情况:

  • 下一步立即依赖它的结果。
  • 子任务边界不清楚。
  • 子 Agent 需要频繁修改同一批文件。
  • 汇总成本高于隔离收益。

一个实用原则是:子 Agent 应该消耗大量上下文,但只返回小结果。

8. 加入上下文评估指标

上下文工程需要评估,否则只能凭感觉调 prompt。

可以记录这些指标:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
token_usage:
input_tokens
output_tokens
tool_output_tokens
retrieved_tokens

context_quality:
relevant_blocks_count
irrelevant_blocks_count
duplicated_blocks_count
stale_blocks_count

agent_behavior:
tool_call_count
repeated_tool_call_count
failed_tool_call_count
task_completion_rate
human_intervention_count

compaction:
trigger_count
compression_ratio
lost_fact_count
post_compaction_failure_count

其中最有价值的是两个指标:

  • 重复工具调用次数:如果 Agent 反复查同一个信息,说明上下文没有保留好。
  • 压缩后失败次数:如果 compaction 后 Agent 经常丢目标或走错方向,说明压缩模板不合格。

一个最小可行实现

如果现在要做一个可用的 Agent 上下文系统,可以先实现这个版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1. TaskState
保存 goal、acceptance criteria、plan、done、open issues。

2. ToolResultStore
保存工具原始输出,但只把摘要放入模型上下文。

3. MemoryStore
保存用户偏好、项目规范、历史决策,并支持检索。

4. ContextBuilder
每轮按优先级组装上下文。

5. Compactor
token 超过阈值时,把旧对话压缩为结构化任务摘要。

6. SubAgentRunner
只用于资料搜索、代码探索、审查等上下文消耗大的子任务。

7. TraceLogger
记录每次模型调用时实际传入了哪些上下文块。

数据结构可以从简单的 JSON 开始:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"task_id": "agent-context-engineering-post",
"goal": "写一篇关于 Agent 上下文工程技术路径演进和落地方法的博客",
"acceptance_criteria": [
"说明从 prompt engineering 到 context policy 的演进",
"给出可实现的上下文流水线",
"包含状态分层、工具返回、压缩、子 Agent、评估指标"
],
"state": {
"phase": "drafting",
"done": [
"完成资料检索",
"确定文章结构"
],
"open_issues": [
"需要补充最小可行实现"
]
},
"facts": [
{
"content": "Anthropic 将 compaction、structured note-taking、sub-agent architectures 作为长任务上下文工程方法。",
"source": "https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents",
"confidence": 0.9
}
]
}

这个版本不复杂,但已经比“把所有历史拼进 prompt”可靠很多。

常见设计取舍

长上下文 vs 精准上下文

长上下文能减少检索遗漏,但也会增加噪声、成本和延迟。对于高可靠 Agent,长上下文应该是兜底能力,不应该替代上下文选择。

摘要 vs 原文

摘要便宜,但可能丢细节。原文准确,但占 token。实践中应该保留原文引用,模型上下文里放摘要;当需要细节时,再用工具按引用读取原文。

单 Agent vs 多 Agent

单 Agent 简单,状态一致性好;多 Agent 能隔离探索成本,但会增加协调成本。多 Agent 更适合研究、代码库探索、审查等高并行任务。

自动记忆 vs 人工确认

自动记忆方便,但容易写入错误偏好。对于长期记忆,最好有置信度、来源、过期时间,重要偏好需要用户确认。

总结

Agent 上下文工程的技术路径大致是:

1
2
3
4
5
6
7
8
Prompt Engineering
-> RAG
-> Tool Use
-> Memory / Session
-> Trimming / Compaction
-> Structured Note-taking
-> Sub-agent Context Isolation
-> Context Policy / Context Builder / Evaluation

它的本质不是“把 prompt 写得更长”,而是把 Agent 每一轮模型调用前的信息流管理起来。

真正落地时,可以抓住三句话:

  1. 上下文窗口是工作内存,不是数据库。
  2. 工具原始输出要存储,进入模型的是结构化摘要。
  3. 长任务要靠状态、压缩和隔离,而不是一直堆历史。

如果说提示词工程是在写“模型应该怎么做”,那么上下文工程就是在设计“模型每一步做事时能看见什么”。对于 Agent 来说,后者正在变成更核心的工程能力。

参考资料