Skip to content

上下文工程

上下文工程(Context Engineering)是提示工程的高阶延伸——不再只关注”怎么写 Prompt”,而是系统性地设计、组装和管理 LLM 上下文窗口里的全部信息。它是 LLM 应用生态概览中的核心工程能力,直接决定了 RAG、Agent、多轮对话等场景的成败。前置阅读:提示工程。

上下文工程 = 给 LLM 准备一份完美的”开卷考试资料包”。

LLM 的上下文窗口(context window,即模型单次能处理的最大 token 数量)就像一张有限大小的书桌(比如 128K token 的桌面)。你往桌上放的每一样东西——系统指令、用户问题、检索到的文档、历史对话、工具调用结果——都在争夺这块有限的空间。放对了,LLM 表现神勇;放错了,LLM 要么遗漏关键信息,要么被噪声淹没。

关键洞察:

  • 位置很重要:LLM 对上下文开头和结尾的信息注意力更高,中间部分容易被忽略——这就是”迷失在中间”(Lost in the Middle)现象。重要的信息要放在开头或结尾。
  • 顺序很重要:先给上下文再问问题,比先问问题再给上下文效果好——因为 LLM 是自回归模型(autoregressive,即从左到右逐个 token 生成,后面的 token 生成时能看到前面的全部信息),后面的 token 生成时能看到前面的信息。
  • 密度很重要:无关信息会稀释关键信息的注意力。与其塞 10 篇相关度一般的文档,不如精选 3 篇最相关的。
  • 结构很重要:用清晰的分隔符(XML 标签、Markdown 标题)把不同来源的信息分区,LLM 更容易定位。

2025 年趋势:随着 Gemini 支持 2M token 上下文窗口、Claude 支持 200K、GPT-4o 支持 128K,“能装”的问题大幅缓解。但”能用”的问题依然存在——研究反复表明,即使模型标注支持超长上下文,在 32K 以上就开始出现信息遗漏。2025 年的上下文工程重点从”怎么塞进更多”转向了”怎么让模型高效利用已有信息”,包括 Prompt Caching(上下文缓存)、Context Compression(上下文压缩)和智能检索策略。

LLM 基于 Transformer 的自注意力机制(详见 Transformer 架构),理论上每个 token 都能”看到”上下文中的所有其他 token。自注意力(Self-Attention)的核心操作是:对序列中每个位置的 token,计算它与所有其他 token 的相关性分数(attention weight),然后加权求和得到该位置的上下文表示。但实际运行中注意力是不均匀的:

  • 每一层、每个注意力头(attention head)都有有限的”注意力预算”。
  • 当上下文很长时,注意力被稀释——关键信息如果混在大段无关文本中间,分配到的注意力就少了。
  • 这就是为什么即使模型支持 128K 上下文,实际有效利用的信息可能远少于 128K。

KV Cache(Key-Value Cache) 是 Transformer 推理时的关键优化——把之前计算的 Key 和 Value 矩阵缓存起来,避免重复计算。上下文工程中的很多策略(如 Prompt Caching)本质上就是利用 KV Cache 来减少计算量和成本。详见 LLM 推理优化。

研究(Liu et al. 2023)发现了一个普遍现象:当关键信息放在长上下文的中间位置时,模型的表现显著下降。具体表现:

  • 信息在开头:表现最好(模型首先处理的信息,注意力分配最多)。
  • 信息在结尾:表现也很好(最近的上下文,距离生成位置最近,注意力天然高)。
  • 信息在中间:表现明显最差(被前面和后面的信息”夹击”,注意力分配最少)。

为什么会有这个效应? 这与 Transformer 的注意力分布有关——在长序列中,开头和结尾的 token 因为位置效应( positional bias,即注意力机制天然偏向序列边界的位置)获得更多注意力权重,而中间的 token 注意力被稀释。这个效应在大多数 Transformer 模型中普遍存在。

实践建议:把最重要的信息(任务指令、关键文档)放在上下文的开头或结尾,避免埋没在中间。

一个生产级 LLM 应用的上下文通常包含:

  1. 系统提示词(System Prompt):定义角色、能力边界、输出格式。通常放在最前面,固定不变。这是整个上下文的”宪法”。
  2. 检索内容(Retrieved Context):来自 RAG(Retrieval-Augmented Generation,检索增强生成)的文档片段。详见 RAG 检索增强生成。
  3. 工具定义(Tool Definitions):可用工具的 JSON Schema 描述——告诉 LLM 有哪些函数可以调用、参数格式是什么。详见 MCP 协议与工具调用。
  4. 少样本示例(Few-shot Examples):引导输出格式和风格的输入-输出范例。详见提示工程。
  5. 对话历史(Conversation History):多轮对话的历史消息。
  6. 用户输入(User Query):当前轮次的实际问题。

这六类信息需要按特定的优先级和顺序排列,而不是随便堆在一起。

当信息总量超过窗口限制(或为了控制成本和延迟)时,需要压缩和管理策略:

  • 对话历史摘要:将早期对话用 LLM 摘要压缩成短文本,只保留近几轮完整对话。这是 ChatGPT 长对话的核心策略。例如,把前 20 轮对话压缩成 200 token 的摘要,加上最近 3 轮的完整对话。
  • 检索后重排:用重排序模型(Reranker,一个专门训练来判断文档与查询相关性的模型)对检索结果按相关性重排,只保留 top-K。详见重排序模型。
  • 滑动窗口:只保留最近 N 轮对话,更早的直接丢弃。最简单但最粗暴。
  • 选择性保留:保留对话中的关键决策点和用户偏好,丢弃寒暄和中间过程。比滑动窗口更智能但需要额外的判断逻辑。
  • 文档分块优化:控制每块的大小和重叠度(overlap,即相邻块之间共享的内容量),确保检索回来的内容密度高。详见向量数据库。
  • 上下文压缩模型:2025 年出现了专门用于上下文压缩的技术——如 LLMLingua 系列,用一个小的 LLM 压缩长 prompt 中的冗余 token,可以在几乎不损失性能的情况下减少 50-80% 的 token 数量。

部分推理引擎支持 Prompt Caching(上下文缓存):如果多次请求的上下文前缀完全相同(如很长的系统提示词 + 固定文档),引擎会缓存这部分中间计算结果(KV Cache,即 Transformer 推理时缓存的 Key-Value 矩阵,避免重复计算之前 token 的注意力),后续请求只需计算新增部分。

  • Anthropic Claude 支持 Prompt Caching,可节省最高 90% 的输入 token 成本和 85% 的延迟(TTL 默认 5 分钟,支持 1 小时)。
  • OpenAI 自动对重复前缀(1024 token 以上)进行缓存折扣,输入价格减半。
  • Google Gemini 支持显式的 context caching,可以预缓存大量文档。
  • 效果取决于上下文前缀的稳定性——把固定内容放前面,变化内容放后面。

2025 进展:Prompt Caching 已成为生产级 LLM 应用的标配。对于 RAG 系统、Agent 等需要大量固定上下文的场景,缓存可以将成本降低一个数量级。关键原则是稳定前缀 + 变化后缀——系统提示、工具定义、知识库文档等不变的内容放最前面,用户输入放最后。

2025 年的顶级模型上下文窗口已经非常惊人:

模型上下文窗口约等于
GPT-4o128K token~100K 英文单词 / ~一本小说
Claude 3.5/4 Sonnet200K token~150K 英文单词 / ~一本半小说
Gemini 1.5/2.0 Pro2M token~1500K 英文单词 / ~一部《战争与和平》

但研究表明,窗口大 ≠ 利用率高。在长上下文检索任务(Needle-in-a-Haystack,即在长文本中找到一个特定事实)中:

  • 开头和结尾:几乎所有模型表现优秀(找到率 >95%)。
  • 中间位置:多数模型在 64K 以上开始出现明显下降。
  • 多事实检索:当需要在长上下文中同时找多个事实时,性能下降更显著。

最佳实践:即使有百万级窗口,也应该配合重排序和摘要策略——把最相关的信息放在开头和结尾,而不是盲目堆砌。

from anthropic import Anthropic
client = Anthropic()
# 上下文工程的实践:系统性地组装每一部分
system_prompt = """你是一个技术文档助手。
规则:
1. 只基于提供的文档回答
2. 找不到答案时说"文档中未提及"
3. 回答末尾标注引用来源编号 [1] [2]
格式: Markdown,代码用代码块包裹"""
# 检索到的文档(实际从向量数据库获取)
retrieved_docs = [
{"id": 1, "text": "Transformer 使用自注意力机制处理序列..."},
{"id": 2, "text": "梯度下降是模型训练的核心优化算法..."},
]
# 将文档用 XML 标签清晰分区(Claude 训练时见过大量 XML,分区效果好)
context_block = "\n".join(
f'<doc id="{d["id"]}">\n{d["text"]}\n</doc>' for d in retrieved_docs
)
user_query = "Transformer 的核心机制是什么?"
# 组装完整消息(固定内容在前,变化内容在后——利于 Prompt Caching)
messages = [{
"role": "user",
"content": f"参考文档:\n{context_block}\n\n问题: {user_query}"
}]
response = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
system=system_prompt,
messages=messages,
)
print(response.content[0].text)
import openai
client = openai.OpenAI()
def chat_with_compression(messages, max_history=6):
"""当对话过长时自动压缩早期历史"""
if len(messages) <= max_history:
return messages
# 把早期消息交给 LLM 生成摘要
old_msgs = messages[:-max_history]
recent_msgs = messages[-max_history:]
summary_prompt = "请用 3-5 句话总结以下对话的关键信息:\n\n"
for m in old_msgs:
summary_prompt += f"{m['role']}: {m['content'][:200]}\n"
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": summary_prompt}],
max_tokens=300,
)
summary = resp.choices[0].message.content
# 用摘要替代早期历史
return [
{"role": "system", "content": f"对话历史摘要: {summary}"}
] + recent_msgs
# 使用示例
history = [
{"role": "user", "content": f"问题 {i}"},
{"role": "assistant", "content": f"回答 {i}"},
for i in range(10)
]
compressed = chat_with_compression(history)
print(f"压缩前 {len(history)} 条 → 压缩后 {len(compressed)} 条消息")
from anthropic import Anthropic
client = Anthropic()
# 大量固定内容放在 system prompt 中,并启用缓存
# cache_control: ephemeral 表示标记缓存边界
system_prompt = [
{
"type": "text",
"text": """你是一个专业技术文档助手。
规则:
1. 只基于提供的文档回答
2. 找不到答案时说"文档中未提及"
3. 回答末尾标注引用来源编号 [1] [2]""",
},
{
"type": "text",
"text": f"<knowledge_base>\n{load_large_kb()}\n</knowledge_base>",
# 标记此处为缓存边界——前面的内容都会被缓存
"cache_control": {"type": "ephemeral"}
}
]
# 第一次调用:计算并缓存 system prompt 的 KV Cache
response1 = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
system=system_prompt,
messages=[{"role": "user", "content": "什么是 Transformer?"}],
)
# 第二次调用:system prompt 命中缓存,输入成本降低 90%
response2 = client.messages.create(
model="claude-sonnet-4-20250514",
max_tokens=1024,
system=system_prompt, # 完全相同的前缀
messages=[{"role": "user", "content": "什么是梯度下降?"}],
)
# response2 的 usage 中会显示 cache_read_input_tokens > 0
import tiktoken
def count_tokens(text: str, model: str = "gpt-4o") -> int:
"""精确计算文本的 token 数量"""
enc = tiktoken.encoding_for_model(model)
return len(enc.encode(text))
def budget_check(system_prompt, retrieved_docs, history, user_query,
max_context=8000, output_budget=2000):
"""检查上下文是否超出预算,并给出分配建议"""
available = max_context - output_budget
parts = {
"system_prompt": count_tokens(system_prompt),
"retrieved_docs": sum(count_tokens(d) for d in retrieved_docs),
"history": sum(count_tokens(m) for m in history),
"user_query": count_tokens(user_query),
}
total = sum(parts.values())
print(f"Token 预算分配:")
for name, count in parts.items():
pct = count / available * 100
bar = "█" * int(pct / 2)
print(f" {name:20s} {count:6d} ({pct:5.1f}%) {bar}")
print(f" {'TOTAL':20s} {total:6d} / {available}")
if total > available:
print(f" ⚠️ 超出预算 {total - available} token!需要压缩。")
return total <= available
# budget_check(system_prompt, docs, history, query)
  • 分层放置:把最稳定的固定内容(系统提示词、工具定义)放在最前面,把变化的用户输入放在最后面。这不仅有利于 Lost-in-the-Middle 效应,还能最大化 Prompt Caching 的收益。
  • 用分隔符分区:使用 XML 标签(<doc>...</doc>)或清晰的 Markdown 标题来分隔不同来源的内容。Claude 官方推荐 XML 格式,因为它训练时见过大量 XML 结构化文档;GPT 系列对 Markdown 标题和 XML 都支持良好。
  • 少即是多:不要一股脑把检索结果全塞进上下文。重排序后只取 top-3 到 top-5 最相关的片段,比塞入 20 篇文档效果好得多——信息密度比信息量更重要。
  • 显式引用而非隐式记忆:不要期望 LLM”记住”前面说过的规则。关键约束在最后再提醒一次(“再次强调:不要编造,只基于文档回答”),效果远好于只在开头说一次。
  • Token 预算管理:用 Tokenizer(分词器,将文本拆分为模型能处理的 token 序列的工具)精确计算各部分 token 占用。一个常见的预算分配:系统提示词 500 token,检索内容 2000-4000 token,历史 1000-2000 token,留足输出空间。详见 Tokenizer 分词器详解。
  • 长上下文 ≠ 无限有效:即使模型标注支持 128K/200K/2M 上下文,实际在 32K 以上就开始出现信息遗漏。长上下文要配合重排序和摘要策略,不能无脑堆砌。
  • 利用 Prompt Caching:对于 RAG 系统、Agent 等需要大量固定上下文的场景,Prompt Caching 可以将成本降低一个数量级。关键是保持前缀稳定——把不变的 system prompt、工具定义、知识库放在最前面。
  • Agent 场景要主动清理:Agent 的思考链、工具调用结果、观察记录会快速膨胀上下文。每隔几步做一次摘要压缩,只保留关键决策和最新状态。详见 Agent 设计模式。
  • 多轮对话系统:ChatGPT、Claude 等的长对话都依赖历史摘要 + 滑动窗口来管理上下文。ChatGPT 在对话过长时会自动提示”之前的对话已被压缩”。详见提示工程。
  • RAG 系统:检索内容如何组装进上下文直接决定回答质量。2025 年的先进 RAG 系统不仅做向量检索,还做重排序、去重、上下文压缩,确保进入上下文的每一条信息都是高密度的。详见 RAG 检索增强生成。
  • AI Agent:Agent 的思考链、工具调用结果、观察记录都需要上下文管理策略,否则几轮循环后上下文就满了。Agent 框架(如 LangGraph)提供了状态管理(state management)和消息裁剪(message trimming)功能。详见 AI Agent 与多智能体。
  • AI 编程助手:代码文件、函数定义、项目约定等信息如何高效塞入上下文。Cursor 的 Codebase Indexing 本质上是上下文工程在编程场景的应用。详见 AI 编程助手。
  • AI 搜索引擎:多步检索的结果如何压缩和重组。AI 搜索需要在有限上下文里容纳多个网页的提取内容,压缩和去重至关重要。详见 AI 搜索引擎。
类库语言说明
tiktokenPythonOpenAI 的分词器(tokenizer),用于精确计算各部分 token 占用
langchain ConversationSummaryMemoryPythonLangChain 内置的对话历史摘要管理组件
llama-index ContextPythonLlamaIndex 的上下文管理模块,支持节点压缩和重排
anthropic Prompt CachingAPIClaude API 的上下文缓存功能,大幅降低重复前缀的成本
LLMLinguaPython微软的上下文压缩工具,用小模型压缩长 prompt 中的冗余 token
lettuceDetect / trim_messagesPython开源工具,自动检测和裁剪超出窗口限制的历史消息
Mem0 / LettaPython开源记忆层框架,提供跨会话持久化记忆和上下文管理
术语英文解释
上下文窗口Context WindowLLM 单次能处理的最大 token 数量,如 GPT-4o 为 128K,Gemini 2.0 为 2M
上下文工程Context Engineering系统性设计和管理上下文窗口内容的工程方法论
迷失在中间Lost in the MiddleLLM 对长上下文中间位置信息利用率显著下降的现象
注意力稀释Attention Dilution上下文越长,每个 token 分配到的注意力越少
对话历史摘要Conversation Summary将早期对话压缩为短文本摘要以节省上下文空间
滑动窗口Sliding Window只保留最近 N 轮对话,丢弃更早的历史
提示词缓存Prompt Caching缓存重复上下文前缀的 KV Cache 以加速推理和降低成本
上下文压缩Context Compression通过摘要、过滤或压缩模型等手段减少上下文中的冗余信息
KV 缓存KV CacheTransformer 推理时缓存已计算的 Key/Value 矩阵以避免重复计算
自回归Autoregressive模型从左到右逐个 token 生成,每个 token 依赖之前所有的 token
TokenTokenLLM 处理文本的最小单位,大致 1 个英文单词 ≈ 1-2 个 token
重排序Reranking用专门的模型对检索结果按与查询的相关性重新排序
  • Liu et al.,「Lost in the Middle: How Language Models Use Long Contexts」(2023):经典论文,系统性地揭示了 LLM 在长上下文中”迷失在中间”的现象,是上下文工程的理论基础。
  • Anthropic,「Prompt Caching」官方文档:Claude 上下文缓存功能的官方指南,详细说明缓存机制和最佳实践。
  • Jiang et al.,「LongLLMLingua: Accelerating and Enhancing LLMs in Long Context Scenarios via Prompt Compression」(2024):微软的上下文压缩技术,在几乎不损失性能的情况下大幅减少 token 使用。
  • OpenAI,「Practices for growing LLM applications」:OpenAI 工程博客,讨论如何管理多轮对话上下文和缓存策略。
  • LangChain Memory 文档:LangChain 对话记忆模块文档,包含滑动窗口、摘要、向量存储等多种上下文管理策略的代码示例。
  • Peng et al.,「Yarn: Efficient Context Window Extension」(2023):讨论如何扩展 LLM 的上下文窗口长度,以及长上下文下注意力分布的变化。
  • Google,「Gemini 1.5: Unlocking Million-Token Context」技术报告 (2024):Google 展示 Gemini 百万 token 上下文能力的技术报告,包含 Needle-in-a-Haystack 评测结果。