Context Engineering:从提示词到上下文工程
AI 导读
Context Engineering:从提示词到上下文工程 作者:Maurice | 灵阙学院 引言:一个范式正在悄悄替换另一个范式 2023年,"Prompt Engineering"(提示词工程)风靡一时。每个人都在学写更好的提示词,争论零样本还是少样本,研究思维链(Chain-of-Thought)的奇效。 2025年之后,顶级AI工程师开始使用另一个词:Context...
Context Engineering:从提示词到上下文工程
作者:Maurice | 灵阙学院
引言:一个范式正在悄悄替换另一个范式
2023年,"Prompt Engineering"(提示词工程)风靡一时。每个人都在学写更好的提示词,争论零样本还是少样本,研究思维链(Chain-of-Thought)的奇效。
2025年之后,顶级AI工程师开始使用另一个词:Context Engineering(上下文工程)。
这不是换个马甲、炒概念。这是一次真实的工程范式转移——从"如何写出好提示词",升级到"如何设计模型决策时能看到的整个信息空间"。
Andrej Karpathy在2025年的分享中说得直白:"The bottleneck is not the model, it's the context."(瓶颈不是模型,是上下文。)
本文是面向AI产品经理、技术负责人和创业者的完整技术指南。读完之后,你会理解:为什么这个范式更本质、如何设计高质量上下文、以及如何用Prompt Caching把推理成本砍掉90%。
第一章:范式转移——从提示词到上下文工程
1.1 提示词工程的局限
提示词工程的核心关注点是:写给模型的那段文字。
它的思维框架是线性的:
用户问题 --> [精心设计的提示词] --> 模型 --> 输出
这个框架在早期GPT-3时代是够用的,那时候模型窗口只有4K token,RAG还不成熟,Agent概念还停留在论文里。
但今天的现实是:
- 模型上下文窗口已达200K(Claude)、1M(Gemini)甚至更长
- 生产级Agent系统需要工具调用、数据库查询、多轮记忆
- 同一个Session里可能涉及10+次工具调用、数百页文档
- 推理成本已经成为产品核心竞争力的一部分
提示词工程管不了这些。它只盯着"那段文字",却忽略了一个更基本的问题:模型在回答你之前,它的上下文窗口里到底装了什么?
1.2 上下文工程的定义
上下文工程的定义可以用一句话概括:
上下文工程是关于"模型在每次推理时,其上下文窗口中应该包含什么、以什么顺序排列、以什么格式呈现"的系统性设计学科。
它的思维框架是立体的:
┌─────────────────────────────────────────────────────────┐
│ Context Window │
│ │
│ [指令层] System Prompt + 角色定义 + 约束规则 │
│ [知识层] RAG检索结果 + 工具输出 + 文档注入 │
│ [记忆层] 对话历史 + 摘要压缩 + 长期记忆 │
│ [格式层] Few-shot示例 + 输出结构约束 │
│ │
│ ↓ │
│ 模型推理(一次) │
└─────────────────────────────────────────────────────────┘
模型在推理的那一刻,能看到的"全部"就是这个窗口。上下文工程师的任务,就是让这个窗口里的每一个token都物尽其用。
1.3 两者的本质差异
| 维度 | 提示词工程 | 上下文工程 |
|---|---|---|
| 关注范围 | 单次输入文本 | 整个上下文窗口生命周期 |
| 核心问题 | 怎么问才能得到好答案 | 模型需要知道什么才能做出正确决策 |
| 技能要求 | 语言表达、心理学技巧 | 系统设计、缓存策略、信息架构 |
| 成本意识 | 几乎不涉及 | 核心竞争力(缓存命中率直接影响账单) |
| 适用场景 | 单轮对话、简单任务 | Agent系统、长期记忆、多模型协作 |
| 演进方向 | 技巧积累 | 工程化、可测量、可优化 |
第二章:上下文工程的四大支柱
2.1 指令层(Instruction Layer)
指令层是上下文窗口的"宪法"。它定义了模型是谁、能做什么、不能做什么。
设计原则:
一、明确而非模糊
差的写法:
You are a helpful assistant.
好的写法:
You are a senior compliance analyst at a Chinese fintech company.
Your role: review financial documents for regulatory violations under CBIRC guidelines.
Response language: Chinese for explanations, English for code/identifiers.
Decision format: always output {verdict, evidence, regulation_reference, risk_level}.
二、约束优先于期望
与其说"请给出准确的答案",不如说"如果你不确定,必须明确说明不确定的原因,不得猜测"。负向约束比正向期望更可靠。
三、角色锚定要具体
角色不是装饰,而是激活模型内部的特定"知识分布"。"资深Python工程师"和"帮我写代码的AI"会给出风格截然不同的输出。
2.2 知识层(Knowledge Layer)
知识层解决的核心问题是:模型训练截止后,或者领域专业知识不足时,如何补充?
三种主要方式:
RAG(检索增强生成)
# 典型RAG上下文构建
def build_knowledge_context(query: str, top_k: int = 5) -> str:
# 1. 向量检索
results = vector_store.similarity_search(query, k=top_k)
# 2. 重排序(reranker提升精度)
reranked = reranker.rerank(query, results)
# 3. 注入格式化
context_blocks = []
for doc in reranked:
context_blocks.append(f"""
[来源: {doc.metadata['source']}]
{doc.page_content}
""")
return "\n---\n".join(context_blocks)
工具调用(Tool Use)
工具调用的本质也是知识层补充——只不过知识来自实时API而非预存向量库。工具调用结果会被追加到上下文中,成为模型下一步推理的依据。
文档注入(Document Injection)
对于需要精确分析特定文档的场景(合同审查、报告生成),直接将文档全文注入上下文往往比RAG更准确——前提是文档不超过窗口限制。
2.3 记忆层(Memory Layer)
记忆层是多轮对话和长期Agent系统的核心挑战。
┌──────────────────────────────────────────────┐
│ 记忆系统架构 │
├──────────────────────────────────────────────┤
│ 工作记忆(Working Memory) │
│ - 当前对话窗口内的完整消息 │
│ - 容量:受上下文窗口限制 │
│ - 特点:实时、完整、不持久 │
├──────────────────────────────────────────────┤
│ 短期记忆(Short-term Memory) │
│ - 会话摘要(LLM压缩) │
│ - 关键决策节点记录 │
│ - 容量:可控(摘要通常500-2000 token) │
├──────────────────────────────────────────────┤
│ 长期记忆(Long-term Memory) │
│ - 向量存储(用户偏好、历史事实) │
│ - 知识图谱(实体关系) │
│ - 结构化存储(用户画像、配置) │
│ - 特点:持久、语义可检索 │
└──────────────────────────────────────────────┘
实用记忆管理代码示例:
class ContextMemoryManager:
"""三层记忆管理器"""
def __init__(self, max_working_tokens: int = 8000):
self.max_working_tokens = max_working_tokens
self.working_memory = [] # 当前对话消息列表
self.short_term_summary = "" # 摘要压缩结果
self.long_term_store = VectorStore() # 长期向量存储
def add_message(self, role: str, content: str):
self.working_memory.append({"role": role, "content": content})
# 超出预算时触发压缩
if self._estimate_tokens() > self.max_working_tokens:
self._compress_oldest_messages()
def _compress_oldest_messages(self):
"""将最旧的消息压缩为摘要"""
oldest = self.working_memory[:10] # 取最旧10条
summary = llm.summarize(oldest)
# 写入短期记忆
self.short_term_summary = summary
# 重要事实持久化到长期记忆
facts = extract_key_facts(oldest)
self.long_term_store.upsert(facts)
# 从工作记忆移除
self.working_memory = self.working_memory[10:]
def build_context(self, current_query: str) -> list:
"""构建完整上下文,按层拼装"""
messages = []
# 1. 摘要前置(如果有)
if self.short_term_summary:
messages.append({
"role": "system",
"content": f"[对话历史摘要]\n{self.short_term_summary}"
})
# 2. 长期记忆检索(按相关性)
relevant_facts = self.long_term_store.search(current_query, top_k=3)
if relevant_facts:
messages.append({
"role": "system",
"content": f"[相关历史记忆]\n" + "\n".join(relevant_facts)
})
# 3. 工作记忆(当前对话)
messages.extend(self.working_memory)
return messages
2.4 格式层(Format Layer)
格式层通过Few-shot示例和输出结构约束,告诉模型"应该输出什么形状的内容"。
Few-shot示例的黄金法则:
示例的质量远比数量重要。3个高质量示例胜过10个平庸示例。示例应该覆盖:正向案例、边界案例、以及你最担心模型出错的场景。
FEW_SHOT_EXAMPLES = """
[示例1 - 标准合规违规]
输入:该贷款产品年化利率显示为"月息1.5%"
输出:{
"verdict": "违规",
"violation_type": "利率披露不规范",
"regulation": "《商业银行互联网贷款管理暂行办法》第27条",
"risk_level": "高",
"required_action": "必须以年化利率形式展示,即18%/年"
}
[示例2 - 无违规]
输入:年化综合资金成本为15.6%(含手续费),已在首页显著位置展示
输出:{
"verdict": "合规",
"violation_type": null,
"regulation": "符合《网络小额贷款业务管理暂行办法》第15条",
"risk_level": "低",
"required_action": null
}
"""
第三章:KV-Cache友好设计
这一章是很多工程师最容易忽视、但对成本影响最大的部分。
3.1 KV-Cache的工作原理
大语言模型在处理token时,会为每个token生成Key和Value向量(即KV-Cache)。当相同的token序列再次出现时,模型可以复用已计算的KV向量,无需重新计算——这就是缓存命中。
关键洞察:KV-Cache是基于前缀匹配的。只有上下文的开头部分完全相同,缓存才能命中。
3.2 设计规则:静态内容前置,动态内容后置
BAD(动态内容在前,每次请求都不同,缓存永远不命中):
┌─────────────────────────────┐
│ 当前时间: 2026-02-27 14:32 │ <- 动态(每次不同)
│ 用户ID: u_12345 │ <- 动态
│ System Prompt(长篇指令) │ <- 静态
│ Few-shot 示例 │ <- 静态
│ 用户问题: ... │ <- 动态
└─────────────────────────────┘
GOOD(静态内容前置,缓存可命中静态部分):
┌─────────────────────────────┐
│ System Prompt(长篇指令) │ <- 静态(前缀,可缓存)
│ Few-shot 示例 │ <- 静态(可缓存)
│ 用户ID: u_12345 │ <- 动态(后置)
│ 当前时间: 2026-02-27 14:32 │ <- 动态(后置)
│ 用户问题: ... │ <- 动态(末尾)
└─────────────────────────────┘
绝对禁忌:在System Prompt开头加入时间戳、请求ID或任何每次变化的内容。这会让数千token的静态指令永远无法缓存,成本直接翻倍。
3.3 多轮对话的缓存策略
轮次1: [System][历史Ø][用户消息1]
↑可缓存 ↑新增 ↑新增
轮次2: [System][用户消息1][助手回复1][用户消息2]
↑命中缓存 ↑命中缓存 ↑命中缓存 ↑新增
轮次3: [System][用户消息1][助手回复1][用户消息2][助手回复2][用户消息3]
↑全部命中缓存(前缀相同) ↑新增
随着对话进行,缓存命中率越来越高,边际成本越来越低。这就是为什么在长对话场景下,Prompt Caching的ROI极高。
第四章:Prompt Caching实战
4.1 三大平台对比
| 特性 | Anthropic Claude | OpenAI | Google Gemini |
|---|---|---|---|
| 支持模型 | Claude 3.5+, Claude 3+ | GPT-4o, GPT-4o-mini | Gemini 1.5+, 2.0+ |
| 缓存机制 | 显式cache_control断点 | 自动前缀匹配 | 显式context cache API |
| 输入折扣 | 缓存命中:-90%(读取价) | 缓存命中:-50% | 缓存命中:-75% |
| 最小缓存token | 1024 token | 1024 token | 32768 token |
| 缓存TTL | 5分钟(活跃刷新) | 5-10分钟 | 1小时(可配置) |
| 缓存写入成本 | 原价 x1.25 | 无额外成本 | 按存储计费 |
| 最佳场景 | 长System Prompt,固定Few-shot | 标准对话,自动生效 | 超长文档,批量分析 |
4.2 Anthropic Prompt Caching实战
import anthropic
client = anthropic.Anthropic()
# 构建带缓存断点的消息
response = client.messages.create(
model="claude-opus-4-6",
max_tokens=1024,
system=[
{
"type": "text",
"text": LONG_SYSTEM_PROMPT, # 数千token的指令
"cache_control": {"type": "ephemeral"} # 此处设置缓存断点
},
{
"type": "text",
"text": FEW_SHOT_EXAMPLES, # Few-shot示例
"cache_control": {"type": "ephemeral"} # 第二个断点
}
],
messages=[
{
"role": "user",
"content": user_query # 动态部分,不缓存
}
]
)
# 查看缓存效果
usage = response.usage
print(f"缓存读取token: {usage.cache_read_input_tokens}")
print(f"缓存写入token: {usage.cache_creation_input_tokens}")
print(f"普通输入token: {usage.input_tokens}")
# 成本计算示例(claude-opus-4-6)
# 普通输入: $15/1M tokens
# 缓存写入: $18.75/1M tokens(+25%)
# 缓存读取: $1.50/1M tokens(-90%)
实际场景成本测算:
假设你有一个法律合规Agent,System Prompt为5000 token,每天处理1000次请求:
无缓存:
5000 token x 1000次 = 5,000,000 token
成本 = 5M x $15/1M = $75/天
有缓存(第1次写入,后续命中):
写入: 5000 x 1 = 5000 token x $18.75/1M = $0.09
读取: 5000 x 999 = 4,995,000 token x $1.5/1M = $7.49
总成本 = $7.58/天 ← 节省90%
4.3 OpenAI自动缓存
OpenAI的缓存是全自动的,无需修改代码。但有几个关键点需要理解:
from openai import OpenAI
client = OpenAI()
# OpenAI自动缓存——无需任何特殊标记
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": STATIC_SYSTEM_PROMPT}, # 会自动缓存
{"role": "user", "content": dynamic_user_message} # 动态,每次不同
]
)
# 查看缓存使用情况
cached_tokens = response.usage.prompt_tokens_details.cached_tokens
print(f"命中缓存: {cached_tokens} tokens")
OpenAI缓存的关键限制:最小1024 token才会触发缓存。如果你的System Prompt很短,可以考虑适当补充更详细的指令或示例来达到阈值。
4.4 Google Gemini Context Caching
Google的缓存更适合超长文档场景(如完整合同、法规全文、技术手册):
import google.generativeai as genai
# 创建持久化缓存(适合多次查询同一文档)
cache = genai.caching.CachedContent.create(
model="gemini-2.0-flash-001",
display_name="合规法规全文缓存",
contents=[
{
"parts": [{"text": FULL_REGULATION_TEXT}], # 超长文档
"role": "user"
}
],
ttl="3600s" # 1小时缓存
)
# 使用缓存内容进行查询
model = genai.GenerativeModel.from_cached_content(cached_content=cache)
response = model.generate_content("分析第三章关于数据保护的条款")
第五章:上下文窗口管理策略
5.1 三种主流策略对比
策略一:滑动窗口(Sliding Window)
保留最近N条消息,丢弃更早的历史
优点:实现简单,延迟低
缺点:丢失早期重要信息,可能导致"失忆"
适用:短期任务,上下文连贯性要求不高的场景
策略二:摘要压缩(Summarization)
定期将旧消息压缩为摘要,保留关键信息
优点:保留信息精华,节省token
缺点:压缩本身有LLM调用成本,可能丢失细节
适用:长对话客服、持续任务追踪
策略三:RAG检索(Retrieval-Augmented)
将历史对话存入向量库,按相关性检索
优点:理论上记忆无限,精准检索
缺点:架构复杂,检索延迟,需要向量基础设施
适用:长期用户画像、企业知识库查询
5.2 Lost-in-the-Middle问题
这是一个被实验反复验证的现象:模型对上下文开头和结尾的内容记忆最清晰,中间部分容易被忽视。
上下文位置与模型注意力分布(示意):
注意力
↑
高 |████ ████
| ██ ██
| ██ ██
| ██ ██
低 | ████████████████
└─────────────────────────────────→
开头 结尾 位置
应对策略:
- 关键信息首尾原则:最重要的约束放在System Prompt,最关键的问题放在用户消息末尾
- 分块标注:对长文档使用清晰的章节标题,帮助模型定位
- 显式引用:在问题中直接引用关键段落,而不依赖模型自行找到
5.3 Token预算分配框架
对于一个典型的128K窗口Agent系统,推荐的预算分配:
┌─────────────────────────────────────────────────┐
│ 128K Token 预算分配 │
├─────────────────────────────────────────────────┤
│ 指令层(System Prompt) 8K 6.25% │
│ 知识层(RAG/工具结果) 40K 31.25% │
│ 记忆层(对话历史) 60K 46.88% │
│ 输出预留(max_tokens) 16K 12.50% │
│ 安全缓冲 4K 3.12% │
└─────────────────────────────────────────────────┘
实际分配需根据任务特性调整。知识密集型任务(文档分析)应给知识层更多预算;对话连贯性要求高的任务应给记忆层更多预算。
第六章:记忆系统深度设计
6.1 主流记忆框架对比
| 框架 | 类型 | 存储后端 | 语义检索 | 自动提取 | 适用规模 |
|---|---|---|---|---|---|
| Mem0 | 托管+自建 | 向量DB+图DB | 是 | 是(LLM自动) | 中到大 |
| Zep | 自建优先 | PostgreSQL+向量 | 是 | 是(提取器) | 中到大 |
| LangGraph Memory | 框架内建 | 可插拔 | 可选 | 手动 | 任意 |
| 文件驱动记忆 | 自建轻量 | Markdown文件 | 否 | 手动 | 小到中 |
推荐选型:
- 快速原型/个人项目:文件驱动记忆(Markdown + 关键词检索)
- 中型产品:Zep(开源、可自托管、成熟)
- 企业级Agent平台:Mem0(托管方便)或自建(向量DB + 知识图谱)
6.2 Mem0实战代码
from mem0 import MemoryClient
client = MemoryClient(api_key="your_mem0_key")
# 存储记忆(自动提取关键事实)
client.add(
messages=[
{"role": "user", "content": "我是一家合规科技公司的CTO,主要服务银行客户"},
{"role": "assistant", "content": "了解了,您在金融合规领域有丰富经验"}
],
user_id="user_001"
)
# 检索相关记忆(用于构建上下文)
memories = client.search(
query="用户的公司背景和技术偏好",
user_id="user_001",
limit=5
)
# 将记忆注入上下文
memory_context = "\n".join([m["memory"] for m in memories])
system_prompt = f"""你是专业的合规顾问。
[用户历史背景]
{memory_context}
基于以上背景为用户提供个性化建议。"""
6.3 轻量级文件驱动记忆
对于不需要完整向量基础设施的场景,文件驱动记忆是一个务实选择:
import json
from datetime import datetime
from pathlib import Path
class FileMemorySystem:
"""
轻量级文件驱动记忆系统
长期记忆 -> MEMORY.md(稳定事实)
日记记忆 -> memory/YYYY-MM-DD.md(增量追加)
"""
def __init__(self, base_dir: str):
self.base_dir = Path(base_dir)
self.memory_file = self.base_dir / "MEMORY.md"
self.diary_dir = self.base_dir / "memory"
self.diary_dir.mkdir(exist_ok=True)
def append_diary(self, content: str):
"""每日日记追加(只追加,不覆盖)"""
today = datetime.now().strftime("%Y-%m-%d")
diary_file = self.diary_dir / f"{today}.md"
timestamp = datetime.now().strftime("%H:%M:%S")
with open(diary_file, "a", encoding="utf-8") as f:
f.write(f"\n## {timestamp}\n{content}\n")
def update_long_term(self, key: str, value: str):
"""更新长期稳定记忆"""
# 读取现有内容
existing = self.memory_file.read_text(encoding="utf-8") if self.memory_file.exists() else ""
# 更新或追加
if f"## {key}" in existing:
# 更新现有条目(简化实现)
lines = existing.split("\n")
new_lines = []
skip = False
for line in lines:
if line.startswith(f"## {key}"):
new_lines.append(f"## {key}")
new_lines.append(value)
skip = True
elif skip and line.startswith("## "):
skip = False
new_lines.append(line)
elif not skip:
new_lines.append(line)
self.memory_file.write_text("\n".join(new_lines), encoding="utf-8")
else:
with open(self.memory_file, "a", encoding="utf-8") as f:
f.write(f"\n## {key}\n{value}\n")
def load_context(self, max_diary_days: int = 7) -> str:
"""加载记忆用于上下文注入"""
parts = []
# 长期记忆
if self.memory_file.exists():
parts.append("### 长期记忆\n" + self.memory_file.read_text(encoding="utf-8"))
# 最近N天日记
recent_diaries = sorted(self.diary_dir.glob("*.md"))[-max_diary_days:]
if recent_diaries:
diary_content = "\n".join([f.read_text(encoding="utf-8") for f in recent_diaries])
parts.append("### 近期对话记录\n" + diary_content)
return "\n\n".join(parts)
第七章:多模型上下文路由
7.1 为什么需要多模型路由
单一模型无法兼顾所有场景:
- 高精度任务(合同分析、代码生成)→ Claude Opus / GPT-4o,贵但准
- 高并发快速响应(实时对话、简单分类)→ Claude Haiku / GPT-4o-mini,快且便宜
- 多模态任务(图表分析、文档OCR)→ Gemini Vision / GPT-4V
- 代码专项任务(调试、重构)→ Claude Sonnet / Codex
7.2 上下文在多模型路由中的传递
class MultiModelRouter:
"""多模型路由器,上下文标准化传递"""
ROUTING_TABLE = {
"compliance_analysis": {
"model": "claude-opus-4-6",
"max_tokens": 4096,
"context_budget": 100000
},
"quick_classification": {
"model": "claude-haiku-3-5",
"max_tokens": 256,
"context_budget": 8000
},
"document_ocr": {
"model": "gemini-2.0-flash",
"max_tokens": 2048,
"context_budget": 50000
}
}
def route(self, task_type: str, context: dict) -> str:
config = self.ROUTING_TABLE.get(task_type, self.ROUTING_TABLE["quick_classification"])
# 根据目标模型的上下文预算裁剪上下文
trimmed_context = self._trim_context(context, config["context_budget"])
# 调用对应模型
return self._call_model(config["model"], trimmed_context, config["max_tokens"])
def _trim_context(self, context: dict, budget: int) -> dict:
"""按优先级裁剪上下文以适配目标模型的预算"""
# 优先级:指令 > 当前问题 > 知识 > 历史记忆
priorities = ["instruction", "current_query", "knowledge", "memory"]
total_tokens = 0
trimmed = {}
for key in priorities:
if key in context:
tokens = estimate_tokens(context[key])
if total_tokens + tokens <= budget:
trimmed[key] = context[key]
total_tokens += tokens
else:
# 按比例截断
remaining = budget - total_tokens
trimmed[key] = truncate_to_tokens(context[key], remaining)
break
return trimmed
7.3 上下文传递的标准化格式
当上下文需要在多个模型之间传递时,建议使用标准化的中间格式:
# 标准化上下文对象
context_schema = {
"session_id": "sess_abc123",
"task_type": "compliance_analysis",
"instruction": {
"content": "...",
"token_count": 2048,
"cacheable": True
},
"knowledge": {
"sources": ["rag_result_1", "tool_output_2"],
"content": "...",
"token_count": 15000,
"cacheable": False
},
"memory": {
"summary": "...",
"recent_turns": [...],
"token_count": 8000
},
"current_query": "请分析第三章关于数据保护的合规风险"
}
第八章:上下文工程的度量体系
优秀的工程实践必须可度量。上下文工程的核心度量指标如下:
8.1 效率指标
缓存命中率(Cache Hit Rate)
缓存命中率 = 缓存命中token数 / 总输入token数
目标:> 60%(成熟系统)
警戒:< 30%(需要检查上下文结构)
上下文利用率(Context Utilization)
上下文利用率 = 实际使用token数 / 上下文窗口容量
目标:70-85%(预留输出空间)
警戒:> 95%(频繁截断风险)或 < 40%(资源浪费)
信息密度(Information Density)
这是一个主观但重要的指标:每个token是否都在为模型决策提供价值?
低密度上下文(需要优化):
- 大量重复说明
- 冗长的背景介绍
- 不相关的历史消息
- 格式化废话("以下是...的分析:\n\n")
高密度上下文(目标):
- 精准的指令约束
- 直接相关的知识片段
- 压缩后的历史摘要
- 结构化的Few-shot示例
8.2 监控代码示例
class ContextMetricsCollector:
"""上下文工程度量收集器"""
def __init__(self):
self.metrics = {
"total_requests": 0,
"total_input_tokens": 0,
"cached_tokens": 0,
"context_sizes": []
}
def record_request(self, response):
usage = response.usage
self.metrics["total_requests"] += 1
self.metrics["total_input_tokens"] += usage.input_tokens
if hasattr(usage, "cache_read_input_tokens"):
self.metrics["cached_tokens"] += usage.cache_read_input_tokens
self.metrics["context_sizes"].append(usage.input_tokens)
def report(self) -> dict:
total = self.metrics["total_input_tokens"]
cached = self.metrics["cached_tokens"]
return {
"cache_hit_rate": f"{cached/total*100:.1f}%" if total > 0 else "N/A",
"avg_context_size": sum(self.metrics["context_sizes"]) / len(self.metrics["context_sizes"]),
"total_requests": self.metrics["total_requests"],
"estimated_cost_saving": f"{(cached * 0.9 / 1_000_000) * 15:.2f} USD"
}
第九章:完整案例——合规Agent的上下文架构
以下是一个完整的生产级合规审查Agent的上下文架构设计,综合运用本文所有技术。
9.1 系统架构图
┌─────────────────────────────────────────────────────────────────┐
│ 合规审查 Agent 架构 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 用户输入(合同/条款文本) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 上下文构建层(Context Builder) │ │
│ │ │ │
│ │ ①指令层 ②知识层 ③记忆层 ④格式层 │ │
│ │ System RAG检索 用户历史 Few-shot │ │
│ │ Prompt 法规数据库 审查记录 示例 │ │
│ │ [CACHED] 工具调用结果 [CACHED摘要] [CACHED] │ │
│ │ │ │
│ └───────────────────────┬─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 路由决策层(Task Router) │ │
│ │ │ │
│ │ 简单分类 → claude-haiku 复杂分析 → claude-opus │ │
│ │ 图表解析 → gemini-vision 代码生成 → claude-sonnet │ │
│ └───────────────────────┬─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 推理层(Model Inference) │ │
│ │ │ │
│ │ KV-Cache命中检查 → 实际推理 → 结构化输出解析 │ │
│ └───────────────────────┬─────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 记忆更新层(Memory Update) │ │
│ │ │ │
│ │ 事实提取 → 向量存储更新 → 日记追加 → 长期记忆更新 │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
9.2 完整上下文模板
class ComplianceAgentContextBuilder:
"""合规审查Agent上下文构建器"""
# 静态指令层(全局缓存,使用cache_control)
SYSTEM_PROMPT = """
你是一位资深金融合规顾问,专注于中国银行业和互联网金融监管合规。
[核心职责]
- 审查金融产品条款、合同文本、营销材料的合规性
- 识别违反CBIRC、PBOC、证监会等监管机构要求的内容
- 提供具体的合规改进建议和法规依据
[输出格式要求]
必须返回结构化JSON:
{
"overall_verdict": "合规|部分违规|严重违规",
"risk_level": "低|中|高|极高",
"findings": [
{
"issue": "问题描述",
"regulation": "违反的具体法规条款",
"severity": "轻微|中等|严重",
"recommendation": "改进建议"
}
],
"compliant_items": ["合规亮点1", "合规亮点2"],
"summary": "总体评估摘要(200字内)"
}
[约束规则]
- 不确定时必须注明"需进一步核实",不得猜测法规条款编号
- 所有法规引用必须准确,包含条款号
- 发现极高风险问题时必须在findings首位列出
"""
def build_full_context(
self,
user_id: str,
document_text: str,
document_type: str
) -> list:
"""构建完整上下文,返回messages数组"""
messages = []
# 1. 指令层(带缓存断点)
messages.append({
"role": "system",
"content": [
{
"type": "text",
"text": self.SYSTEM_PROMPT,
"cache_control": {"type": "ephemeral"} # 缓存此静态指令
},
{
"type": "text",
"text": self._load_few_shot_examples(document_type),
"cache_control": {"type": "ephemeral"} # 缓存Few-shot示例
}
]
})
# 2. 记忆层(用户历史,如有则注入)
user_memory = self.memory_store.load_context(user_id)
if user_memory:
messages.append({
"role": "system",
"content": f"[用户历史审查背景]\n{user_memory}"
})
# 3. 知识层(RAG检索相关法规)
relevant_regulations = self.regulation_rag.search(
query=document_text[:500], # 用前500字检索
doc_type=document_type,
top_k=5
)
if relevant_regulations:
messages.append({
"role": "system",
"content": f"[相关法规参考]\n{relevant_regulations}"
})
# 4. 用户消息(动态内容,末尾)
messages.append({
"role": "user",
"content": f"""请审查以下{document_type}的合规性:
---
{document_text}
---
请按照规定格式输出审查结果。"""
})
return messages
9.3 度量看板示例
合规Agent上下文工程度量看板(月报)
缓存命中率: 87.3% ████████████████████░░░
上下文利用率: 74.2% ██████████████████░░░░░
平均请求成本: $0.023(vs 无缓存 $0.187,节省87.7%)
P95延迟: 1.8s(缓存命中)/ 3.2s(缓存未命中)
信息密度评分: 8.2/10(人工季度抽样评估)
本月Top问题:
1. 2月14日 — System Prompt前追加时间戳导致缓存失效(已修复)
2. 记忆层过载:部分请求memory超出8K预算触发截断(待优化)
结语:上下文是认知的载体
提示词工程教会我们"如何问",而上下文工程让我们思考更根本的问题:在模型推理的那一刻,它的认知空间里有什么?
这个问题的答案,决定了AI产品的质量上限。
上下文工程不是一门玄学,它是可以度量、可以优化、可以工程化的学科。缓存命中率告诉你钱花得是否值当;信息密度告诉你上下文是否精准;记忆架构决定了产品是否有"长期人格"。
对AI产品经理来说,理解上下文工程意味着:你不再只是写需求,你在设计认知空间。每一次模型推理,都是在你精心构筑的信息舞台上进行的表演。
最后留一个思考题:你的产品中,当前的缓存命中率是多少?如果你从未测量过,那就从今天开始。
延伸阅读
- Anthropic Prompt Caching 官方文档:https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching
- OpenAI Prompt Caching 文档:https://platform.openai.com/docs/guides/prompt-caching
- Google Context Caching 文档:https://ai.google.dev/gemini-api/docs/caching
- Nelson Elhage et al., "In-context Learning and Induction Heads" (Anthropic, 2022)
- Andrej Karpathy, "The State of LLM Engineering" (2025)
- Mem0 官方文档:https://docs.mem0.ai
- Zep 官方文档:https://docs.getzep.com
Maurice | maurice_wen@proton.me