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问题

这是一个被实验反复验证的现象:模型对上下文开头和结尾的内容记忆最清晰,中间部分容易被忽视。

上下文位置与模型注意力分布(示意):

注意力
  ↑
高 |████                          ████
   |  ██                        ██
   |    ██                    ██
   |      ██                ██
低 |        ████████████████
   └─────────────────────────────────→
         开头              结尾      位置

应对策略

  1. 关键信息首尾原则:最重要的约束放在System Prompt,最关键的问题放在用户消息末尾
  2. 分块标注:对长文档使用清晰的章节标题,帮助模型定位
  3. 显式引用:在问题中直接引用关键段落,而不依赖模型自行找到

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产品经理来说,理解上下文工程意味着:你不再只是写需求,你在设计认知空间。每一次模型推理,都是在你精心构筑的信息舞台上进行的表演。

最后留一个思考题:你的产品中,当前的缓存命中率是多少?如果你从未测量过,那就从今天开始。


延伸阅读


Maurice | maurice_wen@proton.me