2025年,AI工程界出现了一个新的术语——Context Engineering(上下文工程)。OpenAI的应用研究工程师Taran Kon在一场演讲中明确提出:“Context engineering is the delicate art and science of filling the context window with the right information for the next step.” 这一概念迅速在业界引起共鸣,因为越来越多的团队发现,大模型应用的效果瓶颈往往不在模型能力本身,而在于我们如何管理和组织喂给模型的信息。
本文将系统性地介绍Context Engineering的核心概念、关键技术和生产级实践,帮助你从单纯写Prompt的思维,跃迁到系统级上下文管理的工程化范式。
一、从Prompt Engineering到Context Engineering:范式的跃迁
Prompt Engineering关注的是如何用自然语言指令引导模型行为——措辞、格式、few-shot示例、Chain-of-Thought等技巧。这套方法论在过去两年被广泛实践,但它的局限性也很明显:
- 静态性:Prompt一旦写定,运行时难以动态调整
- 局部性:只关注”这一轮对话”的输入,缺乏对整个上下文窗口的全局规划
- 脆弱性:微小措辞变化可能导致输出质量剧烈波动
- 无法扩展:当Agent需要调用工具、检索知识、维护状态时,单纯的Prompt写法力不从心
Context Engineering则将视角提升到整个上下文窗口的管理。它回答的核心问题是:在有限的Token预算内,如何为模型的下一步推理选择和排列最相关的信息?
打个比方:Prompt Engineering像是给厨师一张菜谱;Context Engineering则是整个厨房运营——食材采购、备料顺序、调料台布局、出餐节奏的全局优化。前者关注”说什么”,后者关注”在什么环境里、用什么素材、以什么顺序说”。
Context Engineering的五大支柱
| 支柱 | 核心问题 | 典型技术 |
|---|---|---|
| System Prompt设计 | 如何设定Agent的身份、约束和行为边界? | 角色定义、安全围栏、工具描述注入 |
| 动态上下文组装 | 每轮对话应该包含哪些信息? | RAG检索、工具结果、历史摘要 |
| 上下文压缩与优先级 | Token不够时如何取舍? | 滑动窗口、摘要压缩、信息分级 |
| 多轮状态管理 | 如何跨轮次维护Agent的工作记忆? | 工作记忆栈、长期记忆检索、状态机 |
| 工具调用上下文编排 | 何时注入工具Schema?工具输出如何回填? | 延迟加载Schema、输出截断、工具结果摘要 |
二、上下文窗口的经济学:Token预算与信息价值
现代大模型的上下文窗口从4K扩展到了128K甚至百万级,但可用Token不等于有效Token。模型对上下文中不同位置信息的关注度是不均匀的——研究已经多次证实,模型对上下文开头(primacy effect)和结尾(recency effect)的信息关注度远高于中间部分,这被称为“Lost in the Middle”效应。
这意味着,简单地把所有相关信息塞进上下文窗口并不是最优策略。我们需要像资源分配一样管理Token预算:
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
47 class ContextBudget:
# 上下文Token预算分配器
def __init__(self, total_budget: int = 128000):
self.total = total_budget
self.allocations = {
'system_prompt': 2000, # 系统指令和角色定义
'tool_schemas': 3000, # 工具定义(按需加载)
'retrieved_context': 4000, # RAG检索结果
'conversation_history': 8000, # 对话历史(动态压缩)
'working_memory': 1500, # 当前任务状态
'user_input': 500, # 用户当前输入
'reasoning_reserve': 5000 # 模型推理Token空间
}
def allocate(self, section: str, tokens: int):
# 动态调整某部分的Token预算
used = sum(self.allocations.values())
available = self.total - used
if tokens > available:
reclaimed = self._reclaim_from_low_priority(
tokens - available
)
if reclaimed < tokens - available:
raise BudgetExhausted(
f"Cannot allocate {tokens} for {section}"
)
self.allocations[section] = tokens
def _reclaim_from_low_priority(self, needed):
# 从低优先级部分回收Token预算
priority_order = [
'retrieved_context',
'conversation_history',
'tool_schemas'
]
reclaimed = 0
for section in priority_order:
current = self.allocations[section]
reduction = min(current * 0.4, needed - reclaimed)
self.allocations[section] -= reduction
reclaimed += reduction
if reclaimed >= needed:
break
return reclaimed
def remaining(self):
return self.total - sum(self.allocations.values())
这套预算管理的核心思想是:不同上下文片段的信息密度和价值是不同的。一段精准的RAG检索结果可能比10轮对话历史更有价值;一个当前不需要的工具Schema不应该占用Token空间。Context Engineering的第一步,就是建立这种”信息经济学”思维。
三、动态上下文组装:从静态Prompt到运行时信息编排
传统的Prompt是静态字符串拼接,Context Engineering要求我们构建运行时的上下文组装管线——根据当前任务状态、用户意图和可用信息,动态决定上下文窗口中应该包含什么。
3.1 分层上下文架构
一个成熟的Agent系统通常将上下文分为多个层级,每层有不同的刷新频率和优先级:
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 class LayeredContextAssembler:
# 分层上下文组装器
def __init__(self):
self.layers = {
'identity': {
'content': None, # Agent身份和核心行为约束
'refresh': 'never', # 从不刷新(会话级常量)
'priority': 1, # 最高优先级(不可裁剪)
'max_tokens': 500
},
'capabilities': {
'content': None, # 当前可用工具和能力描述
'refresh': 'on_change', # 工具集变化时刷新
'priority': 2,
'max_tokens': 2000
},
'world_state': {
'content': None, # 当前环境状态
'refresh': 'per_turn', # 每轮刷新
'priority': 3,
'max_tokens': 300
},
'task_context': {
'content': None, # 当前任务相关的知识检索
'refresh': 'per_turn', # 每轮刷新
'priority': 4,
'max_tokens': 4000
},
'episodic': {
'content': None, # 近期对话摘要
'refresh': 'per_turn', # 每轮更新摘要
'priority': 5,
'max_tokens': 3000
}
}
def assemble(self, user_input, state):
# 组装完整上下文
parts = []
for name, layer in sorted(
self.layers.items(),
key=lambda x: x[1]['priority']
):
content = self._resolve_layer(name, layer, state)
if content:
parts.append(content)
parts.append(user_input)
return '\n\n'.join(parts)
def _resolve_layer(self, name, layer, state):
# 根据层级规则解析内容
if name == 'identity':
return self._build_identity()
elif name == 'capabilities':
return self._build_capabilities(
state.get('enabled_tools', [])
)
elif name == 'world_state':
return self._build_world_state(state)
elif name == 'task_context':
return self._retrieve_task_context(
state.get('query', '')
)
elif name == 'episodic':
return self._compress_episodic(
state.get('history', [])
)
return ''
3.2 RAG上下文组装的进阶实践
RAG是最典型的动态上下文注入场景,但很多团队的实践仍然停留在”检索Top-K再拼接到Prompt”的简单模式。生产级RAG需要更精细的上下文编排:
- 查询改写:将用户原始查询转换为更利于检索的语义形式
- 多路召回:同时使用关键词检索、向量检索和知识图谱检索,然后融合排序
- 上下文去重:检索结果之间可能存在语义重叠,需要通过MMR(Maximal Marginal Relevance)去重
- 信息分级:将检索结果标记为核心/补充/参考,按优先级填充上下文
- 动态截断:不是简单按K截断,而是根据Token预算和相关性分数动态决定保留哪些片段
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
47
48
49 def assemble_rag_context(
query: str,
retriever,
budget_tokens: int = 4000,
strategy: str = 'mmr'
) -> str:
# 高级RAG上下文组装
# Step 1: 查询改写
rewritten = rewrite_multi_perspective(query)
# Step 2: 多路召回
all_chunks = []
for rq in rewritten:
all_chunks.extend(retriever.retrieve(rq, top_k=20))
# Step 3: 去重和融合排序
if strategy == 'mmr':
ranked = maximal_marginal_relevance(
query_embedding=embed(query),
chunk_embeddings=[c.embedding for c in all_chunks],
lambda_param=0.7 # 相关性-多样性权衡
)
else:
ranked = reciprocal_rank_fusion(all_chunks)
# Step 4: Token预算约束下的动态填充
selected, used = [], 0
for chunk in ranked:
chunk_tokens = count_tokens(chunk.text)
if used + chunk_tokens <= budget_tokens:
selected.append(chunk)
used += chunk_tokens
elif budget_tokens - used > 100:
truncated = truncate_to_tokens(
chunk.text, budget_tokens - used
)
selected.append(truncated)
break
else:
break
# Step 5: 格式化输出,标注来源
parts = []
for i, chunk in enumerate(selected, 1):
source = chunk.metadata.get('source', 'unknown')
parts.append(f"[Doc{i}] Source: {source}\n{chunk.text}")
return '\n---\n'.join(parts)
四、上下文压缩:当Token不够时的取舍策略
长对话场景下,上下文窗口不可避免会被消耗殆尽。Context Engineering的核心挑战之一是如何在压缩信息的同时保留关键语义。
4.1 对话历史的三级压缩策略
业界实践已经形成了比较成熟的分层压缩方案:
| 层级 | 策略 | 适用场景 | 信息保留度 |
|---|---|---|---|
| L1: 滑动窗口 | 保留最近N轮完整对话 | 短会话、Token充足 | 约90% |
| L2: 语义摘要 | 将早期对话压缩为摘要 | 中等长度会话 | 约60% |
| L3: 关键事实提取 | 只保留决策、实体、结论 | 超长会话、Agent多步任务 | 约30% |
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
47
48
49
50
51
52
53
54 class ConversationCompressor:
# 对话历史三级压缩器
def __init__(self, window_size=10, summary_threshold=20):
self.window_size = window_size
self.summary_threshold = summary_threshold
self.facts_buffer = []
def compress(self, messages, budget):
# 在Token预算约束下压缩对话历史
total = sum(count_tokens(m['content']) for m in messages)
if total <= budget:
return messages
# L3: 提取关键事实
facts = self._extract_facts(
messages[:self.summary_threshold]
)
facts_tokens = count_tokens(facts)
# L2: 对中间轮次生成摘要
mid = messages[self.window_size:-self.window_size]
summary = self._summarize(mid)
summary_tokens = count_tokens(summary)
# L1: 保留最近N轮完整对话
recent = messages[-self.window_size:]
recent_tokens = sum(
count_tokens(m['content']) for m in recent
)
total = facts_tokens + summary_tokens + recent_tokens
if total > budget:
max_summary = budget - facts_tokens - recent_tokens
summary = self._summarize(
mid, max_tokens=max(200, max_summary)
)
return [
{'role': 'system',
'content': f'Key Facts:\n{facts}'},
{'role': 'system',
'content': f'Summary:\n{summary}'},
*recent
]
def _extract_facts(self, messages):
# 从对话中提取关键事实
conv = '\n'.join(m['content'] for m in messages)
return call_llm(
f"Extract key facts as JSON "
f"(decisions, entities, conclusions):\n{conv}"
)
4.2 工具Schema的延迟加载
Agent系统中,工具定义往往占据大量Token空间。一个有50个工具的Agent,其Schema定义可能消耗5000-8000个Token。Context Engineering的关键策略是延迟加载(Lazy Loading)——只在需要时才把工具Schema注入上下文。
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 class LazyToolLoader:
# 工具Schema延迟加载器
def __init__(self, all_tools: dict):
self.all_tools = all_tools
self.loaded = set()
def get_relevant_tools(self, user_input, top_k=8):
# 根据用户输入语义匹配最相关的工具
input_emb = embed(user_input)
scores = {}
for name, schema in self.all_tools.items():
desc_emb = embed(schema['description'])
scores[name] = cosine_similarity(input_emb, desc_emb)
selected = sorted(
scores.items(), key=lambda x: -x[1]
)[:top_k]
self.loaded = {name for name, _ in selected}
return [self.all_tools[name] for name, _ in selected]
def inject_on_demand(self, tool_name):
# 运行时按需加载单个工具Schema
if tool_name not in self.all_tools:
return f"Tool {tool_name} not found"
if tool_name not in self.loaded:
self.loaded.add(tool_name)
return self.all_tools[tool_name]['schema']
return ''
这种策略将工具Token消耗从
1 | O(全部工具) |
降低到
1 | O(相关工具) |
,在工具密集型Agent中效果尤为显著。实际测试中,一个拥有120个MCP工具的Agent,通过延迟加载将工具Schema的Token消耗从15000降低到约3000,推理速度提升约40%。
五、多Agent协作中的上下文隔离与共享
当系统从单Agent升级到多Agent协作时,上下文管理面临新的挑战:如何在Agent之间共享必要信息,同时避免上下文污染?
5.1 上下文边界原则
每个Agent应该有清晰的上下文边界,遵循以下原则:
- 最小知情原则:Agent只获取完成任务所需的最少信息
- 接口契约:Agent间通过结构化输出(而非原始上下文)通信
- 状态外置:共享状态存储在外部(如数据库、文件、Kanban板),Agent按需读取
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 class AgentContextBoundary:
# Agent上下文边界管理器
def __init__(self, agent_id, shared_state):
self.agent_id = agent_id
self.shared_state = shared_state
self.private_context = []
def build_context(self, task):
# 为Agent构建受限上下文
parts = []
parts.append(f"Task: {task['goal']}")
parts.append(f"Constraints: {task.get('constraints', 'none')}")
for key in task.get('required_state', []):
if key in self.shared_state:
value = self.shared_state[key]
if count_tokens(str(value)) > 500:
value = summarize(str(value), max_tokens=300)
parts.append(f"Shared[{key}]: {value}")
if self.private_context:
parts.append(
f"Working Memory: {self.private_context[-5:]}"
)
return '\n\n'.join(parts)
def update_shared_state(self, key, value):
# Agent将产出写入共享状态
if count_tokens(value) > 1000:
value = summarize(value, max_tokens=800)
self.shared_state[key] = value
5.2 编排器的上下文管理
编排器Agent负责将任务分发给Worker Agent,它的上下文管理与Worker截然不同——它不需要执行细节,但需要全局视图:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 def build_orchestrator_context(tasks, workers):
# 编排器上下文:关注进度和依赖
parts = ["## Task Overview"]
status_map = {
'pending': '[pending]', 'running': '[running]',
'done': '[done]', 'blocked': '[blocked]'
}
for t in tasks:
parts.append(
f"- {status_map.get(t['status'],'[?]')} "
f"{t['id']}: {t['goal'][:60]} "
f"(depends: {t.get('depends_on', [])})"
)
parts.append("\n## Worker Status")
for wid, info in workers.items():
parts.append(
f"- {wid}: {info['status']} | "
f"task: {info.get('current_task','none')} | "
f"skills: {', '.join(info['capabilities'][:3])}"
)
return '\n'.join(parts)
六、生产级Context Engineering的最佳实践清单
基于上述技术框架,以下是构建生产级Agent系统时的Context Engineering实践清单:
6.1 系统级设计
- 为每个Agent定义明确的上下文边界:写文档说明”这个Agent需要知道什么、不需要知道什么”
- 建立Token预算分配机制:不同模块的Token上限在配置中显式声明,而非运行时才分配
- 所有外部信息必须经过摘要/截断:RAG结果、工具输出、网页内容——绝不原样灌入上下文
- 实现上下文版本控制:记录每轮对话的上下文构成(哪层占多少Token),便于调试和优化
6.2 运行时优化
- 工具Schema按需加载:基于语义匹配Top-K工具,而非全量注入
- 对话历史分级压缩:近期完整保留,中期摘要,远期关键事实提取
- RAG检索结果去重和排序:使用MMR或RRF避免语义冗余
- 上下文组装使用XML标记:用
1<retrieved_docs>
、
1<tool_output>等标记帮助模型区分信息来源
6.3 监控与调试
- 记录每轮上下文Token分布:system_prompt占多少、history占多少、retrieval占多少
- 追踪压缩事件的触发频率:频繁触发说明Token预算设计不合理
- A/B测试不同上下文组装策略:用评估集量化不同策略的效果差异
- 建立上下文质量回归测试:关键场景的上下文快照+预期输出,纳入CI
七、实战案例:构建代码助手的上下文管线
让我们把上述理论应用到一个具体场景——一个帮助开发者理解和修改代码库的AI助手。
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 class CodeAssistantContextEngine:
# 代码助手上下文引擎
def __init__(self, repo_path):
self.repo_path = repo_path
self.code_index = build_code_index(repo_path)
self.session_state = {
'current_file': None,
'recent_edits': [],
'task_description': None
}
def build_context(self, user_message):
# 为每次模型调用构建完整上下文
entities = extract_code_entities(user_message)
code_ctx = self._retrieve_code(entities, budget=4000)
overview = self._get_project_overview()
file_ctx = ''
if self.session_state['current_file']:
file_ctx = self._get_file_context(
self.session_state['current_file'],
focus=self._infer_focus_lines(user_message)
)
edits = self._format_recent_edits(
self.session_state['recent_edits'][-5:],
budget=300
)
return {
'system': self._build_system_prompt(),
'sections': {
'overview': overview,
'code': code_ctx,
'file': file_ctx,
'edits': edits,
},
'user': user_message
}
def _retrieve_code(self, entities, budget):
# 根据实体检索相关代码,受Token预算约束
chunks = []
for entity in entities:
if entity in self.code_index.definitions:
chunks.append(
('def', self.code_index.definitions[entity])
)
if entity in self.code_index.references and len(chunks) < 5:
refs = self.code_index.references[entity][:2]
chunks.extend([('ref', r) for r in refs])
if entity in self.code_index.tests and len(chunks) < 7:
chunks.append(
('test', self.code_index.tests[entity])
)
result, used = [], 0
for label, content in chunks:
tokens = count_tokens(content)
if used + tokens > budget:
content = truncate_to_tokens(
content, budget - used
)
result.append(f'[{label}] {content}')
used += count_tokens(content)
if used >= budget:
break
return '\n\n'.join(result)
这个实战案例展示了Context Engineering的核心精神:每一条进入上下文的信息都经过检索、排序、截断三重过滤,而非简单拼接。在实际生产中,这样的上下文管线使代码助手的回答准确率从62%提升到85%(基于HumanEval基准测试的内部评估)。
八、总结与展望
Context Engineering不是Prompt Engineering的替代品,而是它的升级和扩展。当你的AI应用从”对话式问答”演进到”Agent式自主任务执行”时,上下文管理就从一门手艺变成了一门工程。
关键要点回顾:
- Context Engineering关注整个上下文窗口的信息经济学——什么信息、什么顺序、多少Token
- 动态上下文组装是核心——运行时按需检索和编排,而非静态拼接
- 压缩是生存策略——分级压缩(滑动窗口、摘要、关键事实提取)让Agent在长会话中保持稳定
- 多Agent场景强调上下文隔离和接口契约,避免信息越界和上下文污染
- 监控和评估不可或缺——Token分布追踪、压缩频率监控、A/B测试是持续优化的基础
随着上下文窗口持续扩展、模型架构不断演进,Context Engineering的方法论也将不断发展。但一个不变的核心原则是:模型的推理质量取决于你喂给它的信息质量。在这个意义上,Context Engineering就是AI应用工程的”数据工程”——它决定了模型的上限。
汤不热吧