Context Engineering:上下文工程
摘要: Context Engineering 是 Prompt Engineering 的自然演进——关注点从'说什么'转向'让模型知道什么,以及怎么知道'。本文从范式演进的驱动力出发,分析了单次 Prompt 在复杂任务中的局限性如何催生上下文工程的需求。核心围绕三大支柱展开:RAG(检索增强生成)通过外部知识库突破模型训练数据的时效边界;记忆系统(短期、长期与工作记忆)赋予模型跨轮次的状态感知能力;工具调用(Function Calling)使模型从信息处理者升级为行动执行者。同时讨论了上下文窗口容量管理、信息密度优化与多轮对话中的注意力衰减等工程挑战,为企业级 LLM 应用的上下文架构设计提供方法论参考。
1. 引言
如果把 Prompt Engineering 比作"学会与 LLM 对话的语法",那么 Context Engineering 就是"学会为 LLM 准备一个认知环境"。
Context Engineering 是 Prompt Engineering 的自然演进——从关注"说什么",到关注"让模型知道什么,以及怎么知道"。2. 从 Prompt Engineering 到 Context Engineering 的演进
让我们先理解为什么会出现这个演进:
Prompt Engineering 时代(2022-2023):
关注点:如何写好一段 Prompt
典型工作:调教措辞、设计 Few-shot 示例、尝试 CoT
假设:模型的能力是固定的,Prompt 是唯一的杠杆
Context Engineering 时代(2024-至今):
关注点:模型在做出决策时"看到了什么"
典型工作:上下文组装、信息检索增强、动态上下文管理
假设:模型的能力 = 模型权重 × 上下文质量,两个都可以优化演进的核心原因:
- 上下文窗口急剧扩大:从 GPT-3 的 2K token 到 Gemini 的 2M token,可塞入的信息量增长了 1000 倍。"放什么进去"变成了关键决策。
- RAG 模式的成熟:模型不再只靠训练时记住的知识,而是从外部动态获取信息。上下文变成了一个"动态拼装的认知界面"。
- Agent 工作流的兴起:Agent 在执行任务过程中不断积累信息(工具返回、中间结果、错误反馈),这些信息如何组织直接影响 Agent 的表现。
一个形象的比喻:
Prompt Engineering = 写一封邮件(措辞、语气、结构)
Context Engineering = 布置一间会议室(谁在场、桌上有什么资料、墙上贴了什么便签、
窗户透进什么光、会议前发生了什么)3. 上下文组装策略
上下文组装解决的核心问题是:在一次推理请求中,应当选择哪些信息放入上下文窗口,以及如何合理安排它们的顺序?
3.1 典型的上下文组装结构
┌────────────────────────────────────────────────┐
│ 1. System Prompt (~500 tokens) │
│ 角色 + 规则 + 格式要求 │
├────────────────────────────────────────────────┤
│ 2. 持久化上下文 (~200 tokens) │
│ 用户偏好 / 历史关键事实 / 对话目的 │
├────────────────────────────────────────────────┤
│ 3. 检索到的参考信息 (~2000 tokens) │
│ 文档片段 / FAQ / 知识库条目 │
├────────────────────────────────────────────────┤
│ 4. 工具调用历史 (~500 tokens) │
│ 之前调用了什么工具,返回了什么结果 │
├────────────────────────────────────────────────┤
│ 5. 对话历史 (~2000 tokens) │
│ 最近 N 轮对话 │
├────────────────────────────────────────────────┤
│ 6. 用户当前输入 (~200 tokens) │
│ 最新一条消息 │
└────────────────────────────────────────────────┘3.2 组装原则
原则一:相关性优先于完整性
不是把所有能找到的信息都塞进去。上下文窗口就像桌面的工作区——东西放得越多,越难集中注意力。
错误做法:找到 20 个相关的文档片段,全部放进去
正确做法:只放最相关的 3-5 个,其他作为备选
原因:1) 过多的不相关信息会稀释关键信号
2) "Lost in the Middle" 现象证明,中间位置的信息最容易被模型忽略
3) 每多一个 token,推理延迟和成本都在增加原则二:重要信息放在两端
研究表明,LLM 对上下文中位置信息的注意力不是均匀分布的:
上下文窗口中的注意力分布示意:
注意力强度
↑
│ ████ ████
│ ████ ████
│ ████ ████
│ ████ ████
│ ████ ██ ████
│ ████ ██ ██ ████
│ ████ ██ ██ ██ ████
│ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████
└──┴───┴───┴────┴────┴───┴─────┴───┴───┴──┴─────→ 位置
开头 中间区域(注意力最低) 结尾
→ "Lost in the Middle": 中间位置的信息最容易被模型忽视
→ 把关键指令放在开头(System Prompt)或结尾(近期对话)
→ 参考资料放在开头(紧接 System Prompt 后),比放在中间效果更好原则三:结构化优于自然语言
同样的信息,用结构化的方式呈现比用自然语言描述更容易被模型准确理解:
自然语言(差):
"我们的产品有三种订阅计划,基础版每月 29 元包含 100 次调用和基本支持,
专业版每月 99 元包含 1000 次调用和优先支持还有自定义模型,
企业版需要联系销售包含无限调用和专属支持..."
结构化(好):
| 计划 | 月费 | API 调用数 | 支持级别 | 自定义模型 |
|--------|--------|-----------|---------|-----------|
| 基础版 | ¥29 | 100 | 基本 | 无 |
| 专业版 | ¥99 | 1,000 | 优先 | 有 |
| 企业版 | 联系销售 | 无限 | 专属 | 有 |4. 上下文压缩与缓存
4.1 上下文压缩
随着对话进行,上下文持续增长。如果不加处理,最终会超出窗口限制或导致推理成本爆炸。上下文压缩解决的就是这个问题。
策略一:滑动窗口(Sliding Window)
最简单的方式:只保留最近的 N 轮对话,丢弃更早的。
完整对话历史: [R1, R2, R3, ..., R45, R46, R47, R48, R49, R50]
滑动窗口 N=10: [R41 ... R50] ← 只保留最近 10 轮缺点:丢失了用户早期表达的重要偏好和上下文线索。
策略二:摘要压缩(Summarization)
用 LLM 对早期对话进行渐进式摘要:
持久化摘要(随时间更新):
阶段 1: [R1-R5 原始对话]
阶段 2: [摘要(R1-R5)] + [R6-R10 原始对话]
阶段 3: [摘要(摘要(R1-R5) + R6-R10)] + [R11-R15 原始对话]
...
最终上下文:
┌──────────────────────────────────────┐
│ 对话摘要(2-3 句,覆盖全部历史关键信息) │
├──────────────────────────────────────┤
│ 最近 10 轮原始对话 │
├──────────────────────────────────────┤
│ 用户当前输入 │
└──────────────────────────────────────┘策略三:关键词提取(Keyword Extraction)
从历史对话中提取关键实体和偏好,存入一个精简的记忆字典:
记忆字典:
{
"user_name": "小明",
"preferred_language": "中文",
"expertise_level": "高级 Python 开发者",
"current_project": "构建 RAG 系统",
"key_decisions": ["使用 ChromaDB", "嵌入模型选 BGE-large"],
"open_questions": ["如何处理多轮对话的上下文压缩"]
}这种方式的内存开销极小(通常 100-300 tokens),适合长期跨会话记忆。
4.2 上下文缓存
对于多轮对话或批量请求场景,上下文中有一部分是不变的(如 System Prompt):
请求 1: [System Prompt] + [User Msg 1]
请求 2: [System Prompt] + [User Msg 2] ← System Prompt 的 KV Cache 可以复用!
请求 3: [System Prompt] + [User Msg 3]
优化:
首次请求 → 计算 System Prompt 的 KV Cache 并缓存
后续请求 → 直接从缓存读取,跳过 Prefill 阶段中的 System Prompt 部分
→ TTFT 降低 30-50%(取决于 System Prompt 在总输入中的占比)Anthropic 的 Prompt Caching 和 Google Gemini 的 Context Caching 都提供了这一功能的 API 支持。开发者可以标记上下文中的哪些部分是可缓存的,API 会自动处理缓存命中和失效。
5. 动态上下文管理
最先进的 Context Engineering 实践是动态上下文管理:不是一次性地静态组装上下文,而是让 Agent 在执行过程中根据当前需求主动"拉取"所需上下文。
5.1 按需检索(Lazy Retrieval)
静态 RAG(传统):
用户提问 → 一次性检索所有相关文档 → 全部放入上下文 → 生成回答
问题:可能检索了 15 个文档片段,但回答只需要其中 3 个
动态 RAG(按需):
用户提问 → 初步检索 3 个文档 → 生成初步回答
→ 发现需要更多细节 → 根据中间推理结果二次检索
→ 补充 2 个针对性文档 → 完善回答
优势:上下文更精准,不被无关信息稀释5.2 上下文窗口的"信息预算"管理
把上下文窗口视为一种有限资源(信息预算),需要做预算分配:
128K 上下文窗口的预算分配示例:
知识检索: ████████████████░░░░░░ 60K (高优先级)
对话历史: ██████████░░░░░░░░░░░░ 40K
Agent 状态: ████░░░░░░░░░░░░░░░░░░ 16K
工具历史: ██░░░░░░░░░░░░░░░░░░░░ 8K
预留空间: ░░░░░░░░░░░░░░░░░░░░░░ 4K (应对突发需求)
└───────────────────────┘
128K 总额
当预算紧张时:
1. 先压缩对话历史(摘要代替原文)
2. 降低检索结果数(只保留 Top 3 而非 Top 10)
3. 精简工具调用历史(只保留最后一次调用的关键结果)5.3 上下文污染防控
动态上下文管理还需要考虑上下文污染问题——错误或误导性信息一旦进入上下文,可能在整个 Agent 执行链条中传播:
污染传播示例:
错误的工具返回 → 进入上下文
→ 模型基于错误信息做推理 → 生成错误结论
→ 错误结论保留在上下文中 → 影响后续所有决策
防控手段:
- 对进入上下文的外部信息标注来源和置信度
- 定期让模型"反思"当前上下文中的假设是否仍然成立
- 对关键决策点,要求模型引用上下文中的具体证据6. 总结
Context Engineering 的核心洞察是 LLM 的输出质量不止取决于模型本身有多强,还取决于它在做决策时"看到的世界"是什么样子。精心管理的上下文,可以让一个中等模型产生超越更大模型的效果;而混乱的上下文,可以让最强模型也犯低级错误。