

前言
大家好,这里是程序员阿亮!上文跟大家讲解了一下Prompt工程,那么这一文就在Prompt工程的基础上讲解一波Context工程。
在上一篇关于提示词工程(Prompt Engineering)的探讨中,我们确立了一个核心观点:提示词是连接业务逻辑与大模型非确定性智能之间的纽带。然而,当开发团队尝试将 Agent 推向更复杂的生产环境时------例如处理多轮长对话、执行跨越数小时的多步骤任务,或者在包含数百万代码和文档的仓库中进行自主排障------往往会遇到以下问题:
Agent 运行几轮后开始丢三落四,忘记了最初的用户目标或核心安全规则。
RAG(检索增强生成)系统召回了大量的文档片段,但在大模型生成的回答中,真正核心的依据却被忽略了。
随着多轮对话和工具调用结果的积累,API 的 Token 费用急剧飙升,首包延迟(TTFT)变得难以忍受。
这时,开发团队往往会习惯性地去微调 System Prompt,试图通过更严厉的词汇(如"你必须仔细阅读以下所有资料,绝对不准漏掉任何细节")来解决。然而,这种努力通常收效甚微。
因为核心痛点可能根本不在于提示词的语气,而在于大模型的输入信噪比过低,关键信息被淹没在冗余的数据洪流中。
在大语言模型(LLM)的上下文窗口(Context Window)动辄支持 1M 甚至更高 Token 的今天,我们必须纠正一个常见的技术误区:"能放进去"并不等于"能用得好"。长上下文窗口绝不是一个无节制的垃圾桶。当大量无关或过时的信息涌入窗口时,模型在注意力机制的分配、事实推理精度、成本控制以及首字延迟等硬约束上,都会面临严峻的挑战。
这就是上下文工程(Context Engineering)要解决的硬核问题。
如果说 Prompt Engineering 是在"告诉厨师这道菜怎么做"(指令设计),那么 Context Engineering 就在给厨师准备厨房------决定食材放在哪里、工具怎么分类、调料如何更替,以及如何保障有限的工作台上始终保持最高的信噪比。
本文作为"LLM 时代系统级编程"系列的第二篇,将深入剖析上下文工程的核心内涵、装配逻辑、动态加载策略、代谢机制以及如何构建可预测的上下文管理系统。
一、 概念 Clarification:Context Engineering 与 Prompt Engineering 的本质区别
很多初涉 AI 领域的开发者容易将 Prompt Engineering 与 Context Engineering 混为一谈。在真实的 Agent 系统架构中,这两者虽然在同一个输入载体(即大模型的 Input Tokens)中汇合,但其底层的设计哲学与关注重点截然不同。

我们可以通过一个对比表格直观地理解两者的区别:
|-----------|----------------------------------------|----------------------------------------------|
| 维度 | 提示词工程 (Prompt Engineering) | 上下文工程 (Context Engineering) |
| 关注重点 | 指令的表达方式与引导技巧 | 上下文窗口的管理与内存优化 |
| 核心任务 | 告诉模型"怎么做"(类似于给厨师写菜谱) | 决定"装什么、换出什么、何时读写"(类似于操作系统对 RAM 的页面置换) |
| 处理对象 | 系统指令(System Prompt)、自然语言规则、Few-shot 示例 | 静态规则、历史对话、RAG 检索结果、工具描述、会话状态等所有输入源 |
| 生命周期 | 相对静态(通常在部署前固化,通过版本管理更新) | 高度动态(随着 Agent 的运行循环而实时变化、剪裁、代谢) |
| 核心硬约束 | 指令遵循度(Compliance)、安全越狱防线 | Token 预算、信息信噪比、注意力迷失(Lost in the Middle) |
| 衡量目标 | 输出的格式一致性、逻辑正确性 | 维持推理所必需的信息供给,同时降低运行成本与首字延迟 |
从软件工程的角度来看,Context Engineering 实际上是大模型时代的"动态内存管理"。我们需要在一个物理受限、成本敏感且注意力容易涣散的"内存空间"(Context Window)中,通过精密的设计,确保模型在进行下一步推理时,眼前摆放的是最精准、最高效的"数据与契约"。
二、 生产级 Agent 的上下文装配引擎(Context Assembler)
在实际研发中,Agent 在发起下一次模型调用(Inference)前,绝对不能只是简单地把历史聊天记录序列化拼接。系统需要通过一个被称为 Context Assembler(上下文装配器) 的中间件,对多源信息进行收集、分类、过滤、排序与裁切。
以下是一轮典型的生产级 LLM 调用前,系统进行上下文组装的逻辑架构图:

2.1 上下文装配器的核心伪代码实现
为了更清晰地说明这一过程在工程上的实现方式,我们以下面这段高度结构化的 Python 伪代码为例,展示如何在装配阶段保障上下文的合理性与预算控制:
python
import tiktoken
from typing import List, Dict, Any
class ContextAssembler:
def __init__(self, model_name: str, max_budget: int):
self.encoder = tiktoken.encoding_for_model(model_name)
self.max_budget = max_budget # 硬性 Token 预算上限
def count_tokens(self, text: str) -> int:
return len(self.encoder.encode(text))
def assemble(self,
base_prompt: str,
constraints: List[str],
raw_history: List[Dict[str, str]],
retrieved_chunks: List[str],
available_tools: List[Dict[str, Any]]) -> str:
# 静态上下文初始化
system_block = f"<system_instructions>\n{base_prompt}\n</system_instructions>"
constraint_block = "<constraints>\n" + "\n".join([f"- {c}" for c in constraints]) + "\n</constraints>"
# 工具定义描述(例如遵循 MCP 规范)
tools_block = "<available_tools>\n" + str(available_tools) + "\n</available_tools>"
# 核心保留区的 Token 消耗
core_tokens = self.count_tokens(system_block) + self.count_tokens(constraint_block) + self.count_tokens(tools_block)
# 留给动态内容(历史、RAG证据)的剩余预算
dynamic_budget = self.max_budget - core_tokens - 1024 # 预留1024给输出 Token
if dynamic_budget <= 0:
raise ValueError("静态 Prompt 与工具描述超出了物理窗口预算,请精简工具集或约束规则。")
# 动态 RAG 证据的预算分配与剪裁
evidence_text = ""
current_evidence_tokens = 0
# 给 RAG 证据分配 40% 的动态预算
evidence_budget = int(dynamic_budget * 0.4)
for chunk in retrieved_chunks:
chunk_xml = f"<evidence_chunk>{chunk}</evidence_chunk>\n"
chunk_tokens = self.count_tokens(chunk_xml)
if current_evidence_tokens + chunk_tokens <= evidence_budget:
evidence_text += chunk_xml
current_evidence_tokens += chunk_tokens
else:
break # 预算用尽,优雅截断
# 对话历史(History)的滑动窗口与老旧消息剔除
history_text = ""
history_budget = dynamic_budget - current_evidence_tokens
current_history_tokens = 0
# 从最近的消息向历史消息逆序装配,确保近期状态优先保留
for msg in reversed(raw_history):
msg_format = f"<{msg['role']}>\n{msg['content']}\n</{msg['role']}>\n"
msg_tokens = self.count_tokens(msg_format)
if current_history_tokens + msg_tokens <= history_budget:
# 保持原有的时序插入
history_text = msg_format + history_text
current_history_tokens += msg_tokens
else:
# 遇到预算瓶颈,进行折叠或摘要降级
break
# 基于 Attention 最佳布局进行 Rank 拼接
# 科学证据表明,模型对开头和结尾的 Token 敏感度最高。因此,核心约束与当前目标必须置于上下文的首尾,中间放置证据。
final_context = f"""
{system_block}
{constraint_block}
{tools_block}
<retrieved_evidence>
{evidence_text}
</retrieved_evidence>
<dialog_history>
{history_text}
</dialog_history>
<system_reminder>
注意:请仔细阅读 <retrieved_evidence>。若证据中无直接事实,你必须说"信息不足,无法回答",严禁使用历史外推。
请以 JSON 格式输出。
</system_reminder>
"""
return final_context
2.2 为什么 Rank(排序)与注意力布局至关重要?
在传统的分布式系统中,只要数据完整、网络可达,数据的物理存储位置并不影响计算结果。但大模型具有概率性特征(Probabilistic Nature) 。在长上下文场景下,模型存在明显的 "Lost in the Middle"(中间注意力迷失) 现象。
-
首尾效应:模型在处理极长序列时,往往能很好地记住开头的系统设定(Primacy Effect)与最末尾的用户指令(Recency Effect)。
-
噪声干扰:如果将高置信度的证据和低置信度的冗余段落无序地混杂在中间,模型会因为筛选难度陡增而开始产生幻觉。
因此,装配器的一项核心工作,就是通过排序逻辑,人为将关键约束、安全护栏以及当前的核心步骤目标(Goal)分布在上下文最显眼的两端,而将较为冗长的参考文档、辅助信息包埋在中间,从而最大化指令遵循率。
三、 运行时上下文加载策略:预检索与 Just-in-Time 的取舍
在 Agent 运行时,上下文中的参考事实和知识数据是如何灌入窗口的?这里存在两种主流的哲学:预检索(Pre-retrieval) 与 按需加载(Just-in-Time, JIT)。
3.1 预检索(Pre-retrieval)
-
执行机制:在将用户请求发送给 LLM 之前,外部工程管线一次性通过关键词或向量检索(Vector Search)召回最相关的 Chunk,然后将它们直接嵌入到 Prompt 的对应插槽中。
-
适用场景:问答助手(QA Chatbot)、单轮总结、明确且单一的静态文档检索。
-
缺点 :它无法处理探索性、多步骤的任务。例如,当一个 Agent 被派去分析一个复杂的软件项目 Bug 时,系统无法在调用大模型前提前预知需要读取哪几个底层源码文件。
3.2 按需加载(Just-in-Time, JIT)
-
执行机制 :大模型运行时不预装任何厚重的背景文档,其上下文窗口初始状态极度干净,只维护轻量级的"目录引用句柄"。当大模型在运行循环中发现需要某些数据时,它会主动调用特定的工具(例如 read_file、grep、http_get)来动态拉取部分片断,实现上下文的主动填充。
-
典型实践 :在自主开发 Agent 中,通常采用 Progressive Disclosure(渐进式披露) 的策略:
-
首先,仅将整个项目树结构装载入上下文。
-
模型分析目录,调用 grep 检索关键词。
-
根据检索结果,大模型在下一步中动态请求读取特定代码段(JIT 加载)。
-
这有效避免了一开始把几十个文件一股脑塞满上下文的窘境,大幅提升了推理质量。
-
3.3 预检索与 JIT 的横向技术对比
|-----------------|------------------------|---------------------------------|
| 维度 | 预检索 (Pre-retrieval) | Just-in-Time 按需加载 (JIT) |
| 典型场景 | 法律合规检索、常规客服、静态知识问答 | 源码库排障、大型复杂系统探索、自主 Agent 编程 |
| 上下文利用率 | 较低(经常引入相似但无关的废话 Chunk) | 极高(只加载模型明确需要看的部分) |
| 延迟(Latency) | 低(单轮往返即可解决) | 高(需要多次 Tool-Call 交互,存在 Loop 延迟) |
| 对模型能力要求 | 中等 | 高(模型必须具备极强的 Tool Use 稳定性) |
| 对环境工具依赖 | 低 | 高(必须提供高质量的检索、导航和沙箱环境工具) |
最佳实践建议: 在生产级复杂的 Agent 中,通常采用混合策略(Hybrid Strategy):对于高度确定性的静态前置背景知识(例如用户画像、当前的基础业务规章)采用预检索注入;而对于运行时探索发现的动态线索,交由模型通过工具链按需拉取。

四、 长期/长任务中的上下文代谢与生存状态控制
当一个 Agent 执行一项长任务(可能需要调用大模型 50-100 次)时,即便我们严格执行了 JIT 加载,每一次工具返回的原始数据(Tool Result)依然会不断累积。
如果不加干预,上下文很快就会因为数据膨胀而失真,导致 Agent 在循环中自我绕弯,甚至陷入死循环。
我们必须在工程上引入上下文代谢机制(Context Metabolism),通过以下三大核心武器来对抗长任务中的上下文腐化:
4.1 Compaction:窗口快满时的历史折叠与代谢
当系统监测到当前上下文的 Token 消耗已经逼近设定阈值(例如窗口的 75%)时,系统需要主动触发压缩代谢机制(Compaction):
-
历史消息摘要化(Summarization):将最早期交互的部分、冗长且已达成共识的对话交由一个轻量、便宜的模型生成高密度摘要,然后使用这段摘要替换掉该区间的全部原始对话 Token。
-
清理已消化使用的工具返回结果(Tool-Result Clearing) :
在 Agent 的运行轨迹中,最占空间的往往是工具执行的原始 Payload(比如调用编译工具返回的 2000 行编译日志,或者爬虫抓取的整页 HTML 文本)。
-
做法:当模型已经通过后续决策证实"已理解错误并完成了修复"后,工程管线可以无情地将历史消息中对应的 tool_result 字段抹除,仅保留极简的调用存根记录。
-
这种裁剪手段能在瞬间清空大比例的无用背景数据,释放出宝贵的注意力空间。
-
4.2 Structured Note-taking:让 Agent 建立"工作备忘录"
长期任务中,大模型频繁阅读漫长的历史聊天来确定"我刚才干了什么、下一步要干什么"是非常低效的。
更好的设计是在上下文环境中强制开辟一块高度结构化的工作备忘录。
XML
<scratchpad>
<current_task>重构用户鉴权逻辑并修复 CI 测试</current_task>
<accomplished_steps>
1. 定位到 test_auth.py 的第 45 行参数传递错误。
2. 使用 edit_file 工具修改了 auth_validator.py。
</accomplished_steps>
<discovered_findings>
- 历史用户 Token 校验逻辑实际上与新的签名算法存在不兼容。
</discovered_findings>
<remaining_todo_list>
- 重新运行 pytest 测试套件确认通过。
- 清理临时生成的 Debug 日志文件。
</remaining_todo_list>
</scratchpad>
在执行每一次模型调用时,系统要求模型在生成具体行动前,优先更新这块 <scratchpad> 区域。这样,即使在下一次交互中我们截断了过去 90% 的历史交互文本,模型只要读取到这个高保真的工作备忘录,就能瞬间接上之前的决策上下文,有效抵抗遗忘,且 Token 成本极低。
4.3 Sub-agent Architecture:多智能体上下文物理隔离
当一个主 Agent 试图同时处理业务分类、代码编写、安全扫描和日志上传等多重任务时,其上下文窗口的工具描述和不同任务上下文会发生严重的噪声干扰。

生产级的方法是采用 Sub-agent 架构进行关注点分离(Separation of Concerns):
-
隔离逻辑:主 Agent(Master Agent)只负责高阶调度、路线规划和结果汇聚,其上下文始终保持在极度干净的水平。
-
子 Agent(Sub-agents) :当遇到繁重、高 Token 消耗的专门任务时,临时孵化一个专一的 Sub-agent 去执行。Sub-agent 可以去读几万字的原始日志,产生海量的上下文往返。但在任务结束时,Sub-agent 被彻底销毁,仅向主 Agent 返回一段高密度结构化结论。
-
这种架构不仅在物理层面上对上下文噪声进行了隔离,还能够防止由于单个模型调用链崩溃而导致的整个任务死锁,是当前复杂 AI System 设计的推荐通路。
五、 落地微调:上下文工程的工具生态与可观测性
要让上下文工程真正落地,离不开配套的工具链和可观测性(Observability)系统。
5.1 上下文工程的技术栈
在当前的开源与商业生态中,围绕上下文控制已经涌现出了明确的分层技术栈:
-
1. 编排与状态层: LangGraph, Letta
负责长任务的状态机循环、会话级持久化与 Sub-agent 控制。
-
2. 统一通信与工具协议层: MCP (Model Context Protocol) 规范
统一 Tool/Resource 的暴露格式,提供标准化的上下文源接入。
-
3. 记忆与生命周期层: Mem0, Zep, Letta
向量与结构化记忆的读写、主动合并与冲突代谢。
-
4. 数据摄取与检索优化层: LlamaIndex
负责文档的高级分块(Chunking)、元数据绑定、去重与重排(Rerank)。
-
5. 物理存储层: Qdrant, Milvus, Chroma
快速语义检索,提供召回的基础保障。
特别需要提及的是 MCP(Model Context Protocol)协议。它解耦了外部工具与数据同 AI 宿主之间的绑定关系。通过标准的 MCP Server,外部资源(如数据库、本地日志流、API 接口)可以被标准化地抽象为 Resources、Prompts 和 Tools 挂载入 Agent,使得上下文装配可以像组装积木一样统一。
5.2 上下文质量的评测与监控(Observability)
在经过了一系列 Compaction、Rerank 和 JIT 的改动后,如何系统评估上下文质量的变化?在生产环境的工程评测中,推荐关注以下三项核心指标:
-
1. 证据覆盖率(Evidence Coverage) :
评估召回并注入上下文中的信息,与大模型最终输出所依赖的证据之间的对齐度。如果模型在回答中大量使用了非上下文提供的事实,说明提示词的证据边界约束失效了,或者装配了错误的冗余信息,诱发了模型本身的先验事实幻觉。
-
2. Token 信噪比(Token-to-Signal Ratio):
-

-
3. 针锋相对性测试(Needle in a Haystack Test) :
定期在极其冗长、复杂的真实业务上下文装配块(Haystack)中,随机位置隐蔽插入一条微小的核心业务指令(Needle),运行评测集观察模型在不同上下文深度下的召回率曲线。只有当这条"针"在任何物理深度下都能稳定被模型检索并执行时,系统的上下文装配和注意力布局才达到了生产级的要求。
六、 总结:上下文是 Agent 的真实世界
我们常用一句话来概括 Prompt Engineering 与 Context Engineering 在 Agent 场景下的关系:"Prompt 决定模型收到什么指令,而 Context 决定模型实际看到什么样的世界。"
当 Agent 进入到多轮、复杂工具调用的深水区时,管理好上下文的质量、顺序和代谢,其重要性往往超越了单独去精雕细琢几句提示词。
在上下文工程的世界里,我们主张:
-
克制:不要把模型当做无节制的垃圾桶,宁可上下文少一点、干练一点,也尽量不装填含糊不清的噪音。
-
动态:根据模型不同阶段的任务目标,以 JIT、渐进式披露、Compaction 等手段,实现内存的主动代谢与持续进化。
-
契约:使用强结构(XML、JSON)将控制指令与易被污染的外部数据切分隔离,并依靠严谨的评估集确保系统表现的稳定性。
上下文工程的健全,标志着一个 AI Agent 应用开始脱离原型概念,走向具备高确定性、高成本可控性的企业级工业软件范式。
