从 Prompt Engineering 到 Context Engineering:2026 年 Agent 性能提升的隐藏杠杆

标签 :#AI Agent #上下文工程 #Prompt工程 #ContextRot #RAG #多智能体

数据口径:数据与结论均来自文末列出的公开资料(Anthropic 工程博客、Chroma 研究、斯坦福 Lost in the Middle 论文等),未标注出处的数字均为工程经验值,仅供对比参考
2025 年年中,"Context Engineering(上下文工程)"这个说法火了起来:Andrej Karpathy 说它是"为下一步把上下文窗口填进恰到好处信息的那门手艺",Shopify 的 CEO Tobi Lütke 把它定义成"让 LLM 能解决任务的上下文供给艺术"。同年 9 月,Anthropic 工程团队发了篇长文《Effective context engineering for AI agents》,把这套说法正式体系化。

为什么 2026 年这事成了 Agent 开发者的头号技能?因为很多人发现:Prompt 已经写到头了,性能瓶颈不在"怎么问",而在"模型每一步看到了什么"。 一个跑 30 轮的 Agent,第 20 步的表现不取决于系统提示词写得多漂亮,而取决于这 20 步里累积的工具返回、检索片段和历史对话是怎么被组织、压缩、取舍的。

这篇文章从 Prompt Engineering 的边界讲起,解释 Context Engineering 是什么、为什么它是有限资源,然后给出四个能直接上手的实战手段(上下文预算、JIT 加载、Compaction、Contextual Retrieval),最后聊缓存成本杠杆和 2026 年的新动向。

适合读者:正在做 Agent/RAG 的开发者;Prompt 调优进入瓶颈期的人;想搞清楚"上下文"全貌的架构师。


总体

  • Prompt Engineering 的边界:它优化的是"指令怎么写",是一次性的;Agent 是持续运行的循环,决定性能的是每一轮"组装进上下文的信息"
  • Context Engineering:Anthropic 的定义是"在推理期间,选择并维护最优 token 集合的策略集合"------它管的是系统提示词之外的所有东西:工具返回、检索结果、历史对话、记忆、草稿状态
  • Context Rot(上下文腐化):token 越多,模型准确回忆信息的能力越低(Chroma 2025 年对 18 个模型的实测),上下文是"边际收益递减的有限资源",不是越大越好
  • 四个经典失败模式(Drew Breunig 归纳):毒化(Poisoning)、分心(Distraction)、混淆(Confusion)、冲突(Clash)
  • 四个上手手段:上下文预算与组装、JIT(Just-In-Time)动态加载、Compaction(压缩续跑)、Contextual Retrieval(给 chunk 加"身份证")
  • 成本杠杆:Prompt Caching 缓存命中时输入成本约打一折(Anthropic 官方称最高可省 90% 成本、延迟降一半以上)
  • 2026 新动向:Anthropic 给 Claude Code 删掉 80%+ 系统提示词评测不掉点;Compaction、上下文编辑正在成为平台级能力

目录

  • [一、引言:Prompt 写到头了,性能卡在上下文](#一、引言:Prompt 写到头了,性能卡在上下文)
  • [二、Prompt Engineering 的边界:它能做什么,不能做什么](#二、Prompt Engineering 的边界:它能做什么,不能做什么)
  • [三、Context Engineering 是什么:把上下文窗口当资源来经营](#三、Context Engineering 是什么:把上下文窗口当资源来经营)
  • [四、为什么上下文是"有限资源":Context Rot 与注意力预算](#四、为什么上下文是"有限资源":Context Rot 与注意力预算)
  • [五、一个 Agent 的上下文窗口里到底有什么:七个槽位](#五、一个 Agent 的上下文窗口里到底有什么:七个槽位)
  • 六、四种失败模式:上下文是怎么"坏掉"的
  • 七、实战一:上下文预算与组装
  • [八、实战二:JIT 动态加载 vs 全量预载](#八、实战二:JIT 动态加载 vs 全量预载)
  • 九、实战三:Compaction------对话快满时的"存档续读"
  • [十、实战四:Contextual Retrieval------给每个 chunk 加"身份证"](#十、实战四:Contextual Retrieval——给每个 chunk 加"身份证")
  • [十一、成本杠杆:Prompt Caching 与"缓存友好设计"](#十一、成本杠杆:Prompt Caching 与"缓存友好设计")
  • [十二、更进一步的杠杆:结构化笔记、子 Agent 与上下文编辑](#十二、更进一步的杠杆:结构化笔记、子 Agent 与上下文编辑)
  • 十三、避坑指南
  • 十四、总结

一、引言:Prompt 写到头了,性能卡在上下文

2023~2024 年,大家的注意力都在 Prompt Engineering 上:怎么把指令写清楚、给几个 few-shot 例子、输出格式怎么约束。那套东西现在依然有效,但 2025 年起,越来越多做 Agent 的人撞上了同一堵墙:

我的系统提示词已经很"完美"了,为什么 Agent 跑上十几轮还是会失忆、跑偏、自相矛盾?

答案是:问题根本不在提示词。

举个典型的例子。一个"企业采购审批"Agent,第 1 步查库存、第 5 步查预算、第 12 步写报告。第 12 步那一刻,它看到的上下文是:系统提示词 + 用户请求 + 前 11 步的 4 次工具调用 + 4 次工具返回(可能是几千 token 的 JSON)+ 中间产生过的所有思考。如果第 3 步那次查库存返回了 5 万 token 的原始数据堆在历史里,第 12 步的模型注意力早被稀释光了------它大概率会忘掉目标、忽略关键约束,甚至开始编造数据。

一个措辞完美的系统提示词,补偿不了被 60% 噪音工具输出淹没的上下文。 这句话是 2026 年 Agent 工程的常识,也是这篇文章想讲清楚的第一件事。

打个比方:Prompt Engineering 是"把考题写清楚",Context Engineering 是"设计整个考场"------考生带什么参考资料、允许翻哪几页、时间怎么分配、评分标准是什么。考题只是其中一环,而且往往不是决定成败的那一环。


二、Prompt Engineering 的边界:它能做什么,不能做什么

先把它放对位置。

Prompt Engineering 擅长的事(这些结论 2026 年依然成立):

  1. 把任务目标、约束、输出格式说清楚
  2. 用 few-shot 示例让模型模仿期望行为
  3. 用角色设定稳定语气和行为边界
  4. 用结构化输出(JSON Schema)约束返回格式

它的边界:Prompt 只能说明"应该怎么做",不能自动提供完成任务所需要的实时信息------最新库存、企业规则、用户权限、工具执行结果、当前任务状态。这些东西在 Agent 的循环里每一轮都在变,没法靠一份"万能 Prompt"在启动时一次性塞进去。

还有一个结构性的区别,Anthropic 在官方文章里点得很透:

Prompt Engineering Context Engineering
关注点 如何编写有效指令(尤其是系统提示词) 每一轮推理时,把哪些信息以什么结构交给模型
作用对象 一次性的指令文本 整个上下文状态:系统提示、工具、MCP、外部数据、历史消息
生命周期 离散任务:写一次 迭代过程:每次推理都要重新决定
适用场景 单次分类、文本生成 多轮推理、长时间运行的 Agent

说白了:Prompt Engineering 是离散的,Context Engineering 是迭代的。前者优化一条指令,后者优化每一轮模型看到的整个世界。


三、Context Engineering 是什么:把上下文窗口当资源来经营

Anthropic 在 2025 年 9 月的《Effective context engineering for AI agents》里给出定义:

Context engineering 是"在 LLM 推理期间,选择并维护最优 token 集合(信息)的策略集合",包含可能进入上下文的提示词之外的一切信息。

模型每生成一个字之前,都要先"看"一遍你塞进去的所有 token。塞什么、塞多少、按什么顺序、什么该留、什么该丢,本身就是一门工程。

落到实操上,原则就一个:找到最小的、信号密度最高的 token 集合,而不是最多的。 上下文不是仓库,塞得越满,模型越糊涂。

这里有个观念转变:把上下文窗口当成预算 ,而不是存储。预算意味着:

  • 每个 token 都要花钱(按 token 计费)
  • 每个 token 都要抢注意力(Attention 是稀缺资源)
  • 越靠后的 token,对"本轮决策"的贡献越小,甚至为负

这个观念直接决定后面所有实战手段的设计思路。


四、为什么上下文是"有限资源":Context Rot 与注意力预算

4.1 Context Rot:上下文越长,记忆越差

2025 年 Chroma 发布的研究(以及 2023 年斯坦福的"Lost in the Middle"论文、各家 needle-in-a-haystack 类评测)反复指向同一个现象:

随着上下文窗口里的 token 数量增加,模型准确回忆其中信息的能力会下降。

Chroma 2025 年 7 月的报告对 GPT-4.1、Claude 4、Gemini 2.5、Qwen3 等 18 个主流模型做了系统性实测,结论是所有模型都随输入 token 数增加而出现非均匀的性能退化,且无关干扰信息会造成非线性衰减;综合相关综述,处于上下文中间位置的信息,准确率降幅可超过 30%。也就是说:模型不是到窗口上限才"崩溃",而是从很早开始,每多一个 token,平均每个 token 的"注意力"就被摊薄一点。

4.2 为什么?注意力预算与 O(n²)

Transformer 的注意力机制里,每个 token 要和上下文里每一个其他 token 做两两比较,n 个 token 就是 n² 对关系。上下文越长:

  • 注意力被摊得越薄,关键 token 的信号被稀释
  • 模型训练数据里短序列远多于长序列,"长上下文能力"本身就不是模型的舒适区
  • 结果是:信息检索和长程推理的精度随长度渐进下降

4.3 "Lost in the Middle":U 型曲线

2023 年斯坦福的经典实验:把 20 篇检索文档(约 4000 token)放进上下文,把目标答案放在不同位置。答案在开头或结尾时,准确率约 70%~75%;答案埋在中间时,掉到 55%~60%------只是位置不同,内容一字未变。这就是著名的 U 型曲线:开头和结尾的 token 被认真对待,中间的信息最容易丢。

4.4 更大的窗口能不能解决?

2026 年主流模型的窗口普遍进入百万 token 级别(Claude Opus 4.6 提供 1M token 窗口,Gemini 系列更早进入 2M 区间)。但两篇研究给出了残酷的结论:

  • 2026 年 1 月的 MECW 论文(arXiv 2509.21361)提出"最大有效上下文窗口"概念:标称 1M 的窗口,实际能保持稳定性能的有效长度往往远低于标称值,任务不同,有效长度可以差出几个数量级
  • 扩大窗口解决的是"放得下",Context Engineering 解决的是"该放什么"------两者根本不是同一个问题

打个比方:给你一个 1 万平米的仓库,不代表你该把什么都往里堆。仓库越大,找东西越难。上下文工程就是那个"仓库管理员"。


五、一个 Agent 的上下文窗口里到底有什么:七个槽位

要对上下文做工程,第一步是盘点它由什么组成。2026 年各家(Anthropic、OpenAI、LangChain、LlamaIndex)对上下文栈的划分已基本收敛,一个生产级 Agent 的上下文窗口由七个"槽位"组成:

槽位 内容 变化频率 谁负责
1. 系统提示词 角色、约束、目标、语气 常驻 开发者
2. 工具定义 工具的 JSON Schema 常驻 开发者
3. 长期记忆 用户画像、历史决策 检索后注入 应用
4. 检索知识 RAG 片段、SQL 结果、网页抓取 每轮动态 应用
5. 对话历史 之前的用户消息与回复 每轮追加 应用
6. 工作草稿 中间计划、笔记、思考 每轮读写 Agent
7. 本轮指令 当前这一步的具体任务 每轮 应用

其中 1、2 是"写一次",剩下的五个槽位是每一轮动态组装的------组装得对不对,就是 Context Engineering 的活。

注意一个坑:别把运行时数据(当前任务描述、今天的日期、会话配置)塞进系统提示词。系统提示词每一轮都在、每一轮都占地方,把只对某一步有用的数据放进去,等于让它在一整轮任务里白占注意力。这类数据应该放进对话历史或本轮指令槽,逐轮管理。


六、四种失败模式:上下文是怎么"坏掉"的

Drew Breunig 在 2025 年 6 月总结过长上下文 Agent 的四种失败方式,这四张图基本覆盖了生产环境里你能见到的所有"Agent 跑偏":

失败模式 表现 例子
毒化(Poisoning) 早期的一个错误/幻觉/过期事实被当成"事实",后续所有决策都被它带偏 第 3 步 Agent 记错了预算数字,第 12 步还在引用它
分心(Distraction) 上下文太长,模型在噪音里丢了当前目标 30 轮对话后,Agent 忘了用户最初要的是"汇总报告"
混淆(Confusion) 无关内容把模型往错误方向带 工具清单里混进了与任务无关的工具,模型"手痒"去调
冲突(Clash) 两段信息互相矛盾,模型选错了 系统提示要求 A 流程,检索回来的文档说 B 流程

关键点:这四种失败都不是靠"把 Prompt 写得更好"能修的,而是靠上下文组装规则修的。 比如:

  • 对抗"毒化":压缩历史时,把早期关键结论固化到摘要里并定期核对
  • 对抗"分心":每轮只保留当前子任务相关的片段(JIT 加载)
  • 对抗"混淆":工具最小集,不相关的工具不进上下文
  • 对抗"冲突":给信息标注来源和优先级,冲突时按规则裁决

七、实战一:上下文预算与组装

7.1 思路

给上下文窗口分预算,本质是回答三个问题:放什么、放多少、放哪里。 一份常见的预算分配(经验值,按需调整):

优先级 内容 预算占比
关键 系统提示词、本轮任务 10%~15%
相关文档、示例、当前工作草稿 20%~30%
对话历史(含压缩后摘要) 30%~40%
参考资料、大段检索片段 20%~30%

7.2 代码:一个简单的上下文组装器

下面用一段 Python 演示"预算感知的上下文组装"。核心思路:每一轮先统计各槽位的 token,超出预算的部分按优先级裁剪,历史超限则先走压缩(见第九节)。

python 复制代码
import tiktoken

enc = tiktoken.get_encoding("cl100k_base")

def count_tokens(text: str) -> int:
    return len(enc.encode(text))

# 上下文预算(按 token 计)
BUDGET = {
    "system": 2_000,      # 系统提示词
    "task": 500,          # 本轮指令
    "tools": 3_000,       # 工具定义
    "retrieval": 4_000,   # 检索片段
    "history": 6_000,     # 对话历史(含摘要)
    "scratchpad": 1_000,  # 工作草稿
}
TOTAL_BUDGET = sum(BUDGET.values())  # 约 16.5k,可扩展


class ContextAssembler:
    """按预算组装上下文的简单实现"""

    def __init__(self):
        self.system = ""
        self.task = ""
        self.tools = []
        self.retrieval = []   # (score, text),score 为相关性分
        self.history = []     # 已处理的消息
        self.scratchpad = ""

    def set_system(self, text: str):
        # 系统提示词超预算直接告警(它是常驻的,最不该膨胀)
        if count_tokens(text) > BUDGET["system"]:
            raise ValueError("系统提示词超出预算,请精简")
        self.system = text

    def add_retrieval(self, score: float, text: str):
        self.retrieval.append((score, text))

    def assemble(self) -> list[dict]:
        """按优先级组装,超预算的部分裁剪/丢弃"""
        messages = [{"role": "system", "content": self.system}]

        # 1. 本轮任务(优先级最高,先放)
        messages.append({"role": "user", "content": f"[任务]\n{self.task}"})

        # 2. 工具定义:只保留描述行,压缩体积
        tools_part = "\n".join(f"- {t}" for t in self.tools)
        if count_tokens(tools_part) > BUDGET["tools"]:
            tools_part = tools_part[: BUDGET["tools"] * 3]  # 粗略截断,生产应做结构化裁剪
        messages.append({"role": "system", "content": f"[可用工具]\n{tools_part}"})

        # 3. 检索片段:按相关性分数排序,填满 retrieval 预算为止
        retrieval_tokens = 0
        retrieval_part = []
        for score, text in sorted(self.retrieval, key=lambda x: -x[0]):
            t = count_tokens(text)
            if retrieval_tokens + t > BUDGET["retrieval"]:
                break
            retrieval_tokens += t
            retrieval_part.append(text)
        if retrieval_part:
            messages.append({"role": "system", "content": "[参考资料]\n" + "\n\n".join(retrieval_part)})

        # 4. 对话历史:从后往前放,放不下就把前面的换成摘要(Compaction 的入口)
        history_tokens = 0
        for msg in reversed(self.history):
            t = count_tokens(msg["content"])
            if history_tokens + t > BUDGET["history"]:
                # 生产环境:把更早的历史交给 LLM 压缩成摘要后放在最前
                messages.insert(1, {"role": "system", "content": "[历史摘要]\n(此处由 Compaction 生成)"})
                break
            history_tokens += t
            messages.insert(1, msg)

        # 5. 工作草稿:Agent 自己的笔记(Claude Code 的 to-do list 就是这个槽位)
        if self.scratchpad:
            messages.append({"role": "system", "content": f"[工作草稿]\n{self.scratchpad}"})

        return messages


if __name__ == "__main__":
    ca = ContextAssembler()
    ca.set_system("你是企业采购审批助手,只依据提供的资料回答。")
    ca.task = "汇总本季度采购支出并给出风险提示"
    ca.tools = ["query_erp(表, 条件) - 查询 ERP 数据", "check_budget(部门) - 查预算"]
    ca.add_retrieval(0.92, "Q3 采购支出总额 1,240 万元,其中办公耗材占比 34%......")
    ca.add_retrieval(0.45, "供应商名录(低相关,预算充足时才纳入)......")
    ca.history = [
        {"role": "user", "content": "先查一下 ERP 里 Q3 的采购数据"},
        {"role": "assistant", "content": "好的,已查询 ERP,Q3 采购支出 1,240 万元。"},
    ]
    ca.scratchpad = "已确认数据口径:Q3=7~9月;待办:核对办公耗材单价异常。"

    messages = ca.assemble()
    for m in messages:
        print(f"[{m['role']}] {m['content'][:40]}...")

这个组装器演示了三件事:按优先级排序、按预算裁剪、给历史留出压缩入口。生产环境里你可以在此基础上接 MCP(工具槽位改成动态发现)、接向量库(检索槽位)、接记忆层(历史槽位),骨架是一样的。

注:[工作草稿] 槽位在真实 Agent 里通常由模型自己写入(如 Claude Code 的 NOTES),此处用 system 角色简化演示;严格说它更接近 assistant 侧的"草稿纸",具体放哪个角色按你的评测结果定。


八、实战二:JIT 动态加载 vs 全量预载

8.1 思路

传统的 RAG 思维是"预载":把可能相关的资料一次性塞进去。Context Engineering 的思维是 JIT(Just-In-Time):手里只保留轻量标识符(文件路径、存储的查询、链接),需要的时候才去加载具体内容。Anthropic 称之为"渐进披露(Progressive Disclosure)"------先给模型一个目录,它自己决定展开哪一页。

典型对比:

复制代码
❌ 预载:把整份 50 页文档塞进系统提示词
✅ JIT:告诉模型"文档在 docs/contract-2026.pdf",需要时再读对应章节

❌ 预载:把所有工具的完整描述 + 全部示例都放进去
✅ JIT:先只给工具名和一句话描述,细节写进文档,模型按需查看

8.2 代码:一个"先列清单、再按需读取"的工具

python 复制代码
import asyncio
from dataclasses import dataclass


@dataclass
class DocRef:
    """轻量标识符:只占几个 token,不占正文"""
    doc_id: str
    title: str
    path: str


# 模拟文档库
DOCS = {
    "contract-2026": DocRef("contract-2026", "2026 年框架合同", "docs/contract-2026.pdf"),
    "procurement-policy": DocRef("procurement-policy", "采购合规制度 v5", "docs/policy.md"),
}


def list_docs() -> str:
    """给模型看的目录:只含标题,占极小 token"""
    return "\n".join(f"- {d.doc_id}: {d.title}" for d in DOCS.values())


def load_doc(doc_id: str, sections: list[str] | None = None) -> str:
    """按需加载正文;可以只加载指定章节(JIT 的关键)"""
    ref = DOCS[doc_id]
    # 真实项目:这里调用文档解析服务 / 检索,只返回请求的片段
    return f"《{ref.title}》内容({ref.path})...仅返回被请求的章节"


# Agent 循环里的用法:先给目录,等模型点名再展开
SYSTEM = f"""
你是一个合同审查助手。
可用文档:\n{list_docs()}
请先调用 read_doc 按需读取,不要一次性加载所有文档。
"""

async def agent_turn(user_input: str):
    # 第一轮:模型看到的是目录 + 任务,正文为 0 token
    print(SYSTEM)
    print("用户:", user_input)
    print("→ 模型:先读 contract-2026 的『付款条款』章节")
    print(load_doc("contract-2026", sections=["付款条款"]))


if __name__ == "__main__":
    asyncio.run(agent_turn("审查合同中的付款风险"))

JIT 的代价是慢一点(多一轮工具调用),收益是上下文永远是"够用但不臃肿",模型不会在 5 万 token 的无关正文里迷失。对于长任务,这个取舍几乎总是划算的。


九、实战三:Compaction------对话快满时的"存档续读"

9.1 思路

Compaction 是 Anthropic 明确点名的第一个长任务手段:把接近窗口上限的对话总结成摘要,然后用"摘要 + 最近内容"重新开一个上下文窗口继续跑。

它分两个阶段:

  1. 先保召回(Recall):把关键决策、未解决问题、实现细节、当前状态全部收进摘要------宁多勿漏
  2. 再提精度(Precision):去掉冗余输出,让摘要高信号化

最容易上手、最安全的压缩起点是"工具结果清理"(tool result clearing):某个工具在历史深处被调用过后,它的原始返回很少需要再看第二遍------清掉它,通常就能省出大量空间,而且几乎无损。

9.2 代码:一个最小的 Compaction 实现

python 复制代码
from openai import OpenAI

client = OpenAI(api_key="sk-xxx")  # 换成你的 key

COMPACTION_PROMPT = """请把下面的对话压缩成一份"续跑摘要",要求:
1. 保留:当前任务目标、已做出的关键决策、未解决/待办问题、重要数据结论
2. 丢弃:寒暄、重复表述、已被后续消息覆盖的内容
3. 输出结构:
   [任务目标]
   [已确认事实]
   [待办]
   [风险]
只输出摘要本身。"""


def compact(messages: list[dict]) -> list[dict]:
    """把超长对话压缩成 摘要 + 最近 N 条消息,返回新的 messages"""
    # 1. 定位压缩点:这里简单取"最后 10 条保留完整,前面全部压缩"
    KEEP_TAIL = 10
    head, tail = messages[:-KEEP_TAIL], messages[-KEEP_TAIL:]

    # 2. 用 LLM 生成摘要(真实项目应把 head 分批压缩,避免一次塞爆)
    resp = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "system", "content": COMPACTION_PROMPT}] + head,
    )
    summary = resp.choices[0].message.content

    # 3. 新上下文 = 摘要(system) + 最近完整消息,Agent 无缝续跑
    return [{"role": "system", "content": f"[历史摘要]\n{summary}"}] + tail


if __name__ == "__main__":
    long_history = [
        {"role": "user", "content": f"第{i}轮问题......"} for i in range(50)
    ]
    new_messages = compact(long_history)
    print(f"压缩前 {len(long_history)} 条 → 压缩后 {len(new_messages)} 条")
    print("首条为摘要:", new_messages[0]["content"][:60], "...")

Anthropic 在《How we built our multi-agent research system》中报告:多 Agent 架构配合 Compaction 等上下文管理手段,任务完成率相对单体 Agent 提升 90.2%(具体评测口径以原文为准)。这不是说"压缩能提升 90%"------而是说上下文管理对长任务 Agent 的影响是数量级的。

2026 年,主流平台都开始把上下文压缩做成平台级能力------例如 Claude 的 auto-compaction 与服务端压缩(Beta)、Claude Code 里的 /compact 命令与自动压缩。你可以在不自己造轮子的前提下先接入官方能力,再逐步自定义压缩策略。


十、实战四:Contextual Retrieval------给每个 chunk 加"身份证"

10.1 思路

传统 RAG 有个著名缺陷:分块破坏了上下文。一个 chunk 被从原文里切出来后,往往"失忆"了------"公司收入环比增长 3%" 这句话,脱离文档后没人知道是哪家公司、哪个季度。于是检索时,用户问"ACME 2023 Q2 收入增长",这个 chunk 可能根本召不回。

Anthropic 2024 年 9 月发布的 Contextual Retrieval 解决方式非常朴素:在把 chunk 存入索引之前,先用 LLM 给每个 chunk 生成一段"身份说明",拼在 chunk 前面。这样嵌入向量和 BM25 关键词都能"看懂"这个 chunk 属于谁、在讲什么。

官方给出的检索失败率数据(failure@20,混合语料):

方案 失败率 相对基线降幅
仅 Embedding(基线) 5.7% ---
+ BM25 混合检索 4.7% 17.5%
+ Contextual Embeddings 3.7% 35%
+ Contextual BM25 2.9% 49%
+ 重排序(Rerank) 1.9% 67%

10.2 代码:上下文化 + Prompt Caching

python 复制代码
from anthropic import Anthropic

client = Anthropic()

CONTEXT_PROMPT = """Here is the chunk we want to situate within the whole document:
<chunk>
{chunk_content}
</chunk>

Please give a short succinct context to situate this chunk within the overall
document for the purposes of improving search retrieval of the chunk.
Answer only with the succinct context and nothing else."""


def contextualize_chunk(whole_document: str, chunk: str) -> str:
    """给单个 chunk 生成身份说明(50~100 token)"""
    resp = client.messages.create(
        model="claude-haiku-4-5",   # 轻量模型即可
        max_tokens=200,
        system=[{
            "type": "text",
            "text": f"<document>\n{whole_document}\n</document>",
            # 缓存整篇文档:同一文档的所有 chunk 只需一次写入、多次命中
            "cache_control": {"type": "ephemeral"},
        }],
        messages=[{
            "role": "user",
            "content": CONTEXT_PROMPT.format(chunk_content=chunk),
        }],
    )
    return resp.content[0].text.strip()


def index_document(document: str, chunks: list[str]) -> list[dict]:
    """索引期:为每个 chunk 生成 context,拼好后交给向量库/BM25 入库"""
    out = []
    for i, chunk in enumerate(chunks):
        context = contextualize_chunk(document, chunk)
        out.append({
            "chunk_index": i,
            "original": chunk,
            "context": context,
            "combined": f"{context}\n\n{chunk}",  # 这才是真正入库的文本
        })
    return out

10.3 成本为什么能接受

每个 chunk 都要带整篇文档去生成 context,如果没缓存,成本会高到不可接受。Prompt Caching 是这套方案能跑起来的唯一原因:同一文档只在第一个 chunk 时"写入"一次缓存,后面所有 chunk 都命中缓存读(读价格约为正常输入的一折)。Anthropic 官方测算下,索引期成本约 1.02 美元/百万文档 token(使用 Haiku + 缓存),不缓存则要高约一个数量级。

生产建议:检索侧用"BM25 + 向量"混合取 top,再 RRF 融合,最后过一遍 reranker------也就是官方管道的完整版。上述 67% 的降幅是在加了 reranker 之后测得的。


十一、成本杠杆:Prompt Caching 与"缓存友好设计"

上下文工程的另一面是钱。长上下文请求的按 token 计费模式,让"塞满窗口"直接变成成本事故。Prompt Caching 是 2026 年最不该忽略的成本杠杆:

  • Anthropic:官方称缓存命中时延迟降低一半以上、成本最高可省 90%;缓存读约为正常输入价格的 1/10
  • OpenAI:提供 prompt caching,相同前缀自动命中,命中部分打折
  • DeepSeek:提供上下文硬盘缓存,命中时输入成本大幅降低(约 1/10,以官方计价页为准)

缓存友好设计的规则很简单:把静态内容放在消息序列前面,动态内容放后面,别混在一起。

复制代码
✅ 缓存友好:system(静态角色+规则) → 检索片段 → 历史 → 本轮任务
❌ 不友好:system 里混入每轮都变的日期/用户信息/临时数据 → 前缀每次都变,永远不命中

一个小技巧:工具定义、few-shot 示例这类"每轮都一样"的内容,集中放在最前面,让它们成为缓存前缀的一部分;每轮变化的任务指令放在最后。这样既保证命中率,又不牺牲灵活性。


十二、更进一步的杠杆:结构化笔记、子 Agent 与上下文编辑

12.1 结构化笔记(Structured Note-Taking)

Compaction 是"被动"的(满了才压缩),结构化笔记是"主动"的:Agent 把关键状态写到上下文之外的文件里(NOTES.md、to-do list),需要时再读回。Claude Code 维护待办清单、Anthropic 让 Claude 玩 Pokémon 时记账,都是这个套路。它的价值在于:状态持久化在上下文之外,即使上下文被压缩、重置,关键信息也不丢。这也是我在《从聊天记录到长期记忆:Agent Memory 四层架构实战》一文里讲的"会话记忆之外那一层"(外部长期记忆)的实现基础。

12.2 子 Agent 架构(多智能体隔离上下文)

给每个子任务开一个干净的上下文窗口:主 Agent 只做编排,子 Agent 在独立上下文里干活,把结果压缩成摘要回报给主 Agent。这样主 Agent 的窗口不会因子任务的原始输出而膨胀,context rot 被"隔离"在子 Agent 的窗口里。这不是简单的编排风格,而是上下文管理的一等公民手段。

12.3 上下文编辑(Context Editing)

2025 年 9 月,Anthropic 随 Claude Sonnet 4.5 发布了上下文编辑能力(Claude API 的 clear_tool_uses 清理过期工具结果、clear_thinking 管理思考块),核心是服务端主动清理/改写上下文中的过期内容,而不是被动等压缩。Anthropic 公布的数据:上下文编辑单独使用带来约 29% 的性能改进,与记忆工具(memory tool)配合使用约 39%(具体评测口径以官方原文为准)。Cursor 等工具也采纳了类似策略。它的理念是"上下文里的每条内容都应有保质期"。

12.4 "删减"同样在发生:Claude Code 删了 80% 系统提示词

2026 年 7 月,Anthropic 工程师 Thariq Shihipar 在《The new rules of context engineering for Claude 5 generation models》中披露:面向 Claude 5 代模型(Opus 5 / Fable 5),他们删掉了 Claude Code 超过 80% 的系统提示词,编码评测没有可测量掉点(提示词从约 2686 词缩减到约 514 词)。原因是:旧提示词里大部分是"护栏"------为阻止旧模型犯的错而写的规则,新模型不再犯这些错,护栏反而互相冲突、变成噪音。

官方把这次转变总结为六条"规则反转":从规则到判断、从示例到接口设计、从前置加载到渐进披露、从重复指令到简洁工具描述、从手动记忆到自动记忆、从简单规格到丰富参考。

这对普通开发者的启发不是"我也删 80%",而是:上下文里的每条规则都应有存在的证据,删除要绑定回归样本。这是 Context Engineering 进入精细化阶段的标志。


十三、避坑指南

  1. 别把"塞满窗口"当能力强项:1M 窗口是上限不是目标。Context Rot 让"更多 token"经常是负收益,先做减法再做加法
  2. 别把运行时数据写进系统提示词:日期、任务详情、会话配置放历史/本轮指令,放系统提示词=全任务白占注意力+破坏缓存前缀
  3. Compaction 别"一刀切"丢细节:先保召回(决策、待办、数据结论),再提精度;工具结果清理是最安全的起点
  4. Contextual Retrieval 不配缓存=烧钱:每 chunk 带全文生成 context 的成本,必须靠 Prompt Caching 压下来,否则索引期成本直接劝退
  5. JIT 不是万能:多一轮工具调用=多一次延迟和失败点。短任务、低风险场景,预载反而更简单;长任务才值得 JIT
  6. 子 Agent 不是越多越好:每个子 Agent 都是一次上下文往返。编排层要控制扇出,否则"隔离污染"变成"放大延迟"
  7. 别照抄 Anthropic 的"删 80%":那是 Claude 5 + Claude Code 特定场景的结果。删规则必须配回归样本,先低风险(重复说明、过时示例)再高风险(安全、合规约束)
  8. 上下文清单先于上下文优化:动手前先盘点一条规则/一段数据"从哪来、何时加载、删了会怎样",没有证据的内容优先删

十四、总结

  1. Prompt Engineering 是子集,Context Engineering 是全集:前者优化指令文本,后者管理模型每一轮看到的整个信息环境。Agent 时代,决定成败的是后者
  2. 上下文是"有限资源"不是"存储":Context Rot、注意力预算、Lost in the Middle 三件事共同说明------token 越多,每个 token 的价值越低,上下文必须当预算来经营
  3. 四个上手手段:上下文预算组装(放什么/放多少)、JIT 动态加载(渐进披露)、Compaction(存档续读)、Contextual Retrieval(chunk 身份证),从易到难,都能立刻落地
  4. 成本是一等公民:Prompt Caching 命中可省 90% 输入成本,缓存友好设计(静态前缀)是每个 Agent 都要做的基础功课
  5. 2026 的方向:结构化笔记、子 Agent 隔离、上下文编辑、删减回归------上下文工程从"堆内容"走向"精细化治理",模型越强,这个杠杆越值钱

参考资料

相关推荐
许彰午23 分钟前
07-SqlBuilder六法
java·开发语言·低代码·架构
孙69034228 分钟前
Spring 注入多例 Bean
java·spring
Jul1en_30 分钟前
【Java 脚手架】封装通用工具类-3
java·开发语言·redis·缓存·ai·bootstrap·rabbitmq
学习星球37 分钟前
2026年AI Agent全栈开发实战——从Prompt到Production
开发语言·人工智能·prompt
半夢半醒11 小时前
查看 Oracle 数据库中的定时任务执行情况
人工智能·prompt
边境悍匪1 小时前
springboot常用注解
java·spring boot·学习
captain3762 小时前
文件与IO(2)
java·开发语言·windows·java-ee
mqiqe2 小时前
AgentScope Java 2.0 集成 Chat Completions Web:一行依赖让你的 Agent 变身 OpenAI 兼容服务
java·开发语言·前端
qq_589666052 小时前
Java微服务介绍及应用
java·开发语言·微服务