上下文工程:喂给模型什么,决定它的上限
上下文工程(Context Engineering):决定 AI Agent 上限的不是模型,而是你喂给它的上下文。讲透四大操作——写出/选取/压缩/隔离,RAG 还是长上下文、记忆、KV-cache 优化,附实战与自查清单。
做 Agent 的人迟早会撞上这堵墙:prompt 已经调到极致,效果还是不稳。问题往往不在那句话,而在模型每一步到底看到了什么。这篇讲的就是这件事——怎么管理模型的"视野"。面向工程师与技术决策者,从概念到实战,附照抄清单。
一、一个反直觉的事实
很多人调 Agent 的路径是这样的:效果不好 → 改 prompt → 还是不好 → 继续堆 prompt → 把所有可能用到的资料、历史、工具说明一股脑塞进去 → 效果反而更差。
问题往往不在 prompt 写得好不好,而在模型这一步看到的那段上下文是否对。
2025 年开始,业界有了一个共识性的词来命名这件事。Karpathy 把它讲得最直白:
"上下文工程"胜过"提示工程"。人们一提到 prompt,想到的往往是日常里随手给模型的一句简短指令;可在任何工业级的 LLM 应用里,上下文工程才是真功夫——它是一门精细的艺术与科学:在上下文窗口里,为下一步恰到好处地填入正确的信息。
「+1 for "context engineering" over "prompt engineering". People associate prompts with short task descriptions you'd give an LLM in your day-to-day use. When in every industrial-strength LLM app, context engineering is the delicate art and science of filling the context window with just the right information for the next step.」
Shopify 的 CEO Tobi Lütke 也公开表达过同样的偏好——他更喜欢"上下文工程"胜过"提示工程",因为它更准确地描述了那项核心技能:"为任务提供全部上下文,让大模型能合理地把事做成"("the art of providing all the context for the task to be plausibly solvable by the LLM")。
一句话区分:
提示工程:写好一段相对静态的指令。 上下文工程:在一个多步骤、不断变化的 Agent 循环里,动态地决定每一步往有限的上下文窗口里放什么、不放什么。
提示工程是上下文工程的一个子集。当任务从"一问一答"变成"自主跑几十步、调一堆工具、读一堆返回结果"时,主战场就从"那句话"转移到了"那整个窗口"。
二、为什么上下文是稀缺资源
直觉上,上下文窗口越大越好,1M token 听起来什么都装得下。但实践里,上下文是最稀缺的资源,原因有三:
1)注意力是有限的,且会"中间失忆"。 经典研究《Lost in the Middle》早就发现:把关键信息放在长上下文的中间,模型经常视而不见——它对开头和结尾敏感,对中段迟钝。后来 Chroma 等团队用更系统的实验把这个现象命名为 context rot(上下文腐坏):随着上下文变长,模型在同一任务上的表现会持续、平滑地下降,哪怕"大海捞针"测试还显示它"找得到"。能找到 ≠ 能用好。
2)无关信息会主动干扰。 上下文里多塞的每一段无关内容,都是在和正确答案抢注意力。常见三种病症:
- 干扰(distraction):被无关历史带偏;
- 污染(poisoning):一个早期的错误结论留在上下文里,后面一路将错就错;
- 冲突(clash):塞进去的资料彼此矛盾,模型无所适从。
3)成本和延迟随 token 线性增长。 每多一个 token,都要花钱、花时间。一个把上下文堆到满的 Agent,又慢又贵又不准。
结论很反直觉,但极其重要:
上下文不是越多越好,而是"信噪比"越高越好。上下文工程的本质,是在每一步只留下"刚好够用"的高信号信息。
三、上下文里到底有什么
先把"上下文"拆开。模型推理时看到的,远不止用户那句话,而是一整个被拼装出来的窗口:
| 成分 | 说明 | 谁来控制 |
|---|---|---|
| 系统提示 / 角色设定 | 身份、规则、输出格式 | 工程师,相对静态 |
| 工具定义 | 有哪些工具、怎么调 | 工程师 + 动态筛选 |
| 用户输入 | 当前这一轮的请求 | 用户 |
| 对话历史 | 之前几轮的来回 | 需要管理(压缩/裁剪) |
| 工具返回结果 | 检索到的文档、API 结果、报错 | 高度动态,最易爆炸 |
| 记忆 | 跨会话的长期信息 | 需要写入/取回 |
上下文工程,就是对这张表里每一格做"加什么、减什么、留多久"的决策。其中最容易失控的是"工具返回结果"——一次网页抓取、一次数据库查询,就可能糊上几千 token。
四、四种基本操作:写出、选取、压缩、隔离
LangChain 把上下文工程的全部手段,归纳成四类操作。这是目前最好用的一张心智地图:
1. Write —— 写出(把信息存在窗口之外)
不是所有东西都得待在上下文里。把中间结论、计划、笔记写到窗口外部,需要时再取回:
- 草稿区(scratchpad):当前任务内的临时笔记,让模型记住"我已经做到第几步、得出了什么"。
- 记忆(memory):跨任务、跨会话的持久信息(用户偏好、项目约定)。
2. Select —— 选取(只在需要时取回相关的那一点)
这是 RAG(检索增强) 的本质,也是工具选择、示例选择的本质:
- 不把整个知识库塞进去,而是按当前问题检索出最相关的几段;
- 工具太多时,先用一层检索筛出"这一步可能用到的几个工具",而不是把上百个工具定义全摆上;
- 连 few-shot 示例都可以按相似度动态挑选。
3. Compress —— 压缩(用更少的 token 表达同样的信息)
上下文快满时,不是粗暴截断,而是压缩:
- 摘要 / compaction:把前面几十轮对话总结成一段"目前进展纪要",丢掉原始过程、保留结论和未决事项;
- 裁剪:按规则删掉最老、最不相关的部分。
4. Isolate —— 隔离(把上下文拆到多个独立空间)
把一个大任务拆给多个子 Agent,每个子 Agent 拿一个干净的、专属的上下文窗口去做子任务,只把"浓缩后的结论"返回给主 Agent。主 Agent 的窗口因此始终清爽。沙箱、独立运行环境也属于这一类——把可能产生海量输出的操作隔离在外。
记住这四个动词——写出、选取、压缩、隔离——你就有了应对几乎所有上下文问题的工具箱。
接下来几节,挑出这套框架里最常被追问、也最容易做错的几处,逐个深入。
五、RAG 还是长上下文?2026 年的答案
长上下文窗口刚出现时,很多人断言"RAG 要死了——既然能把整本书塞进去,何必检索?"两年过去,结论清晰了:长上下文没有杀死 RAG,反而让两者各归其位。
- 长上下文的软肋就是第二节说的 context rot:塞得越满,越容易中间失忆、越慢越贵。把 50 万 token 全灌进去,不等于模型真的"读懂"了 50 万 token。
- RAG 的价值恰恰是把"50 万里相关的 5 千"挑出来,喂高信号、低噪声的上下文——这正是上下文工程想要的。
实务上的选择:
| 场景 | 倾向 |
|---|---|
| 知识库巨大、且持续更新 | RAG(检索 + 引用来源) |
| 单份文档、需要全局理解(如审一份长合同) | 长上下文直接读 |
| 既大又要精 | 混合:先检索粗筛,再把候选段落放进长上下文精读 |
更前沿的做法是 agentic retrieval(智能体式检索):不再是"一次检索、拿结果就走",而是让 Agent 自己多轮搜索、自己判断"够不够、要不要再查一次",把检索变成一个有反馈的循环。
六、记忆:让 Agent 跨会话变聪明
记忆是上下文工程里专门处理"时间"的部分,分两层:
- 短期记忆:单次会话/任务内的连续性,主要靠第四节的 compaction 和 scratchpad 维持。
- 长期记忆:跨会话保留的信息——用户是谁、偏好什么、项目有哪些约定。你在 ChatGPT、Claude 里看到的"记住了你的偏好",背后就是一套写入(什么值得记)+ 取回(这次该想起什么) 的机制。
记忆的难点从来不是"存",而是两个判断:
- 写什么:不是所有对话都值得记,乱记会污染未来的上下文;
- 取回什么:这一次该想起哪几条,取多了又是噪声。
说到底,记忆系统就是"Write + Select"这两个操作在时间维度上的应用。
七、一条最容易被忽略的实务:让上下文对缓存友好
前面几节都在谈"放什么、不放什么",还有一条几乎直接决定成本的路径常被忽略——KV-cache(提示缓存)。
模型处理上下文时,会把前缀算成一份可复用的中间状态(KV-cache)。只要这一轮的上下文前缀和上一轮完全一致,这部分就能直接命中缓存、不必重算——又快又省(命中的 token 通常只按原价的几分之一计费)。在多步 Agent 里,每一步都带着几乎相同的系统提示和历史,缓存命中率几乎直接决定了账单和延迟。业界甚至有一句话:"KV-cache 命中率,是生产级 Agent 最重要的单项指标。"
由此引出几条和"信噪比"同等重要的构造原则:
- 稳定的放前面,易变的放后面。 系统提示、工具定义这些每轮不变的内容固定在最前,保证前缀稳定。
- 只追加,不改写。 新信息往尾部追加;一旦回头去改前面的内容(哪怕只动一个时间戳),其后的缓存会全部失效。
- 压缩要挑时机。 compaction、裁剪会改写前缀、使缓存失效,所以别太频繁触发,挑划算的点一次性做。
一句话:上下文工程不只决定"模型看得准不准",也直接决定"这套系统跑得贵不贵"。
八、实战:两个最常用的模式
模式一:Compaction(上下文压缩续跑)。 这是长任务的命脉。以 Claude Code 为例,当上下文窗口快满时,它不会直接断档,而是自动把前面的工作总结成一段紧凑纪要(改了哪些文件、当前状态、下一步要做什么),然后用这段纪要"接着跑"。要点是:总结要保留"未决事项和关键决策",丢掉"原始过程"。压缩没做好,Agent 就会"失忆",反复做已经做过的事。
模式二:Sub-agent 上下文隔离。 主 Agent 负责编排,遇到一个重活(比如"把这 20 个文件逐个读一遍找出 bug"),就派一个子 Agent 去做。子 Agent 在自己干净的窗口里读完 20 个文件、得出结论,只把一句话结论返回给主 Agent。主 Agent 的窗口完全不会被那 20 个文件的内容淹没。这正是"隔离"的威力——也是多 Agent 系统最被低估的好处:它本质上是一种上下文管理手段,而不只是"并行干活"。
九、它和这些概念是什么关系
上下文工程周围围着一圈容易混淆的词。不必死记,按"关系的类型"理一遍,边界自然就清楚了:
① 上下游——MCP 在下面供料,Harness 在外面兜底。
- MCP(模型上下文协议):解决"能接什么",用一套统一协议把外部工具和数据源标准化地接进来,是上下文的"来源"。
- Harness(智能体的工程外壳):模型权重之外、决定智能能否稳定落地的全部工程(工具、状态、验证、上下文管理……)。上下文管理只是其中一个子系统,却往往是最先翻车的那一个。
② 子集——提示工程和 RAG,都是它的一部分,不是它本身。
- 提示工程:上下文工程的前身和子集。任务从"一问一答"变成"自主跑几十步",主战场就从"写好那句话"扩大到了"管好整个窗口"。
- RAG(检索增强):只是"选取"这一类操作里的一个具体手段,负责"按需把相关知识取进来"。它是上下文工程的子集,不是同义词——把两者画等号,就漏掉了写出、压缩、隔离另外三大类。
③ 替代路线——上下文工程 vs 微调。
给模型注入知识和能力,有两条根本不同的路:把它喂进上下文(in-context,本文讲的全部),还是训进权重(in-weights,即微调 fine-tuning)。
- 知识更新快、要带来源、要随时增删 → 走上下文(RAG / 长上下文);
- 要固化一种稳定的风格、格式或专有能力,且数据足够 → 考虑微调。
多数成熟系统两者并用:微调定"底色",上下文工程喂"当下这件事"。
一句话收束:上下文工程决定模型每一步的"视野"——提示工程、RAG 是它的局部,微调是它的另一条路,MCP 在下面供料,Harness 在外面兜底。
十、照抄即用:上下文自查清单
- 我能说清模型在每一步看到的上下文里,到底有哪些成分吗?
- 工具返回结果有没有做"瘦身"(只留结论、截断超长输出)?还是原样塞回去?
- 上下文里有没有"已经没用的历史"?有没有定期压缩/裁剪?
- 长任务有没有 compaction 机制?压缩时保留了未决事项和关键决策吗?
- 知识检索是"全量塞入"还是"按需检索"?检索结果带不带来源?
- 工具/示例是不是"全量摆上"?能不能按当前步骤动态筛选?
- 有没有把重活拆给子 Agent,用独立窗口隔离它产生的海量中间信息?
- 上下文是不是"稳定内容在前、只在尾部追加",以尽量命中 KV-cache、压住成本?
- 我有没有在"上下文越长越好"和"信噪比越高越好"之间,站对了队?
模型会越来越聪明,上下文窗口会越来越大。但只要"注意力有限、信息有噪声"这两条还成立,决定 Agent 上限的,就永远不只是模型本身,而是你喂给它的那段上下文。
参考
- Andrej Karpathy,关于 "context engineering" 的推文(2025)
- LangChain,Context Engineering for Agents(Write / Select / Compress / Isolate 框架)
- Anthropic,Effective context engineering for AI agents
- Chroma,Context Rot: How Increasing Input Tokens Impacts LLM Performance(2025)
- Liu et al.(arxiv),Lost in the Middle: How Language Models Use Long Contexts(2023)