什么是上下文工程
系列入口:如何使用 Agent。本篇是 M3 上下文工程模块的第一篇。
一、一句话回答
提示词工程解决「这句话怎么说」,上下文工程解决「它能看到什么」。 前者打磨的是你发出去的那一条消息;后者管理的是 Agent 在完成任务时能接触到的全部信息——系统给它的指令、项目规则、你们的对话历史、它读过的代码、工具返回的结果、跨会话保留的记忆。
这个区分为什么重要?因为用过 Agent 的人都会有个体会:同一个模型、同一句提示,放在 A 项目里很聪明,放到 B 项目里像失忆。差别几乎不在那句话,而在它有没有被喂到对的信息。上下文工程,就是把「喂什么、喂多少、什么时候喂」变成你能主动设计的东西。
为什么这个能力在今天格外突出?有几个背景同时出现。一是模型本身的语言能力已经很强,单条提示还能压榨出的差距在缩小,而决定成败的变量越来越偏向「它接没接上正确的信息」。二是任务规模在变大,人们开始让 Agent 处理跨多文件、多服务、持续很久的任务,这种任务里信息怎么组织,直接决定可不可控。三是工具链成熟了,规则文件、子代理、记忆、MCP、钩子这些零件齐备,让「主动设计信息」第一次成为普通人够得着的事,而不只是研究概念。
这也带来一个新的分水岭:早期大家比的是谁提示写得巧,而现在更能拉开差距的,是谁能让 Agent 在复杂任务里持续看到恰当的信息。你会在真实工作中反复观察到,两个水平相近的人用同一个模型,交付质量和能驾驭的任务规模却差很多——根子往往不在谁更会「说」,而在谁更会「布置信息流」。理解了这一点,你就明白为什么这个模块值得花整整六篇来讨论。
二、上下文的六个来源
- 系统指令(System Prompt):Agent 工具出厂时内置的行为设定,比如「你是一个编程助手」「改完要跑测试」。这部分一般不归你管,但框定了它的基本风格。
- 规则文件(Rules /
CLAUDE.md):项目级的长期约定,每次任务自动注入。怎么写见 CLAUDE.md 指南。 - 当前对话(Chat):你这次发的消息和之前几轮的来回,是最直接的上下文。
- 代码文件(Code):它为了完成任务读入的源码。给得准不准,直接决定它改得对不对。
- 工具结果(Tool Outputs):跑命令、搜代码、查接口返回的内容。这些结果会占窗口,也是它判断下一步的依据。
- 记忆(Memory):跨会话留下来的背景,让它不用每次重新认识你的项目。
理解这六个来源的意义在于:当 Agent 表现不好时,你能判断是哪一路信息出了问题——是规则没写、代码没读全、还是工具结果把窗口塞满了。为了让这六类信息更具体,下面分别给一个典型场景和管理要点。
- 系统指令:场景,解释了为什么它不用你提醒就会先规划、为什么默认会谨慎对待命令。要点:基本不可改,你只要知道它的存在,别把工具的默认行为误当成模型「随机发挥」。
- 规则文件:场景,你在项目规则里写了「包管理器用 pnpm、提交信息中文、不要动生成代码目录」,每次任务它都看得到。要点:这是你投入产出比最高的一路信息,一次写好长期生效,写法上只写它容易搞错、又不是常识的事。
- 当前对话:场景,你前面几轮已经确认过的方案、补充的背景,都在对话里。要点:对话是最自然但也最容易膨胀的一路,长任务里早期信息会被后浪推挤、注意力被稀释,需要靠压缩来管理。
- 代码文件:场景,你让它改函数,它读了函数本体和调用方,判断才有依据。要点:这一路最忌讳「给得不全」和「给得太泛」——没读到关键文件会瞎改,一口气塞几十个文件又会淹没重点。精准选文件是核心技能。
- 工具结果:场景,一条搜索可能返回几百行、一次构建日志可能上千行,这些都会真实占据窗口。要点:要意识到工具输出本身也是上下文,命令越啰嗦、搜索范围越大,塞进来的噪音越多,必要时让它只返回结论而非原始输出。
- 记忆:场景,它记住了部署目录、你偏好用某个测试框架。要点:记忆能省重复交代,但要定期清理过期信息,错误记忆会持续误导。
把六类信息放在一起看,你会发现它们的性质不同:系统指令和规则相对稳定,对话、代码、工具结果是随任务动态变化的,记忆则是跨任务沉淀的。上下文工程很大程度上,就是在有限窗口里安排好这几路信息的比例和时机。
三、和提示词工程到底怎么分工
两者不是替代关系,而是层次不同:
| 提示词工程 | 上下文工程 | |
|---|---|---|
| 关注 | 这一条消息怎么表达 | 整个任务它能接触到什么 |
| 范围 | 单轮、局部 | 跨轮、跨文件、跨会话 |
| 典型动作 | 说清 What/Where/约束 | 选文件、定规则、控预算、裁剪压缩 |
| 失效表现 | 它答非所问 | 它「不知道有这个东西」 |
一个实用的判断顺序:Agent 做错事,先怀疑上下文(它是不是没看到关键信息),再去打磨提示词。多数「它怎么连这个都不知道」的问题,根子都是信息没进窗口,而不是措辞不够好。
三·补、一个直观对照:同一句话,两种喂法
看一个能直接说明上下文威力的对照。同一句指令:「帮我给 calculate 加上对负数的处理。」
喂法 A:只发这句话,不提供任何文件。Agent 既不知道 calculate 在哪个文件、做什么用、现在对非法输入是什么行为,只能凭函数名猜,给出一段通用代码,很可能和你的真实场景对不上,甚至改错文件。
喂法 B:发指令的同时让它先读 src/pricing/calculate.ts 和对应测试,规则文件里还写了「本项目金额以分为单位、非法输入返回特定错误码而不是抛异常」。它读了真实实现,知道返回约定,给出的处理精确贴合现有代码风格和错误约定,还顺手补了测试。
指令本身一字不差,结果天差地别。差距全部来自「它看到了什么」。这就是为什么本模块主张:遇到表现不佳,先别动那句提示,先检查该进窗口的信息进没进。 新手最常做的无用功,就是在信息缺失的情况下反复改写同一句话,期待措辞能让它「猜」出自己没看到的事实——那是不可能的,模型无法靠修辞获知一个根本没提供的路径、约定或背景。
四、为什么上下文是「工程」
叫「工程」是因为它有明确的约束和取舍,而不只是灵感:上下文窗口有容量上限(token 预算),信息给少了它不懂,给多了既浪费又可能让它抓不住重点;不同来源的信息还有优先级,项目规则显然比一段无关的命令输出更重要。
这意味着上下文工程包含一组可操作的动作:预算管理(见 上下文窗口预算)、精准选取代码、用规则注入长期信息、用子代理隔离子任务上下文、以及压缩和摘要。本模块后面几篇就是逐个把这些动作讲透。
举个具体的取舍场景,让你看到「工程」二字落到实处:你要让 Agent 修一个跨服务的 bug。你面对几个选择——是让它直接读全部相关服务的所有文件(信息全但窗口瞬间被占满、重点淹没),还是先让一个子代理去摸排、只返回「问题涉及这三个文件的这几个函数」的结论(主窗口保持干净),然后主 Agent 只读这几个精确文件?一个大的工具日志,是把上千行原始输出都放进窗口,还是让它只提炼出关键的几行错误?这些都不是非此即彼的对错题,而是要结合「窗口还剩多少、什么信息最关键、哪一步可验证」来权衡的决策。所谓工程,就是你在这些约束下有意识地做安排,而不是默认把所有东西一股脑塞进去,或碰运气地随便给。随经验增长,这些取舍会越来越快地变成你的直觉。
五、为什么信息一多,表现反而会变差
很多人凭直觉以为「上下文越多,它知道得越多,结果越好」。在信息不足阶段确实如此,但超过某个点后,继续往里塞信息会带来几个反效果,理解它们是做好上下文工程的认知前提。
第一,窗口预算是硬约束。 一个窗口能装的 token 有上限,无关内容占掉的每一份预算,都是在挤掉真正有用信息的空间。塞到接近满时,要么触发粗暴截断(最早、有时恰恰是最关键的背景被切掉),要么被迫压缩,而压缩本身可能丢细节。
第二,注意力会被稀释。 模型并不是「读得越多越清醒」,当大量相关与不相关的信息混在一起,它对关键约束的关注度反而下降。一个典型现象:你在对话开头明确说过的要求,在二十轮、几万 token 之后被它忽略——不是它没读到,而是信息洪流把那个信号淹没了。少而准的上下文,往往比大而全的上下文让它表现更好。
第三,噪音会诱发「脑补」。 当窗口里堆满似是而非、彼此不完全一致的信息(几段不同时期的代码、几份过期文档),它可能从噪音里拼出一个错误结论。给它清晰的少量事实,比给它一堆需要自行甄别和取舍的材料更可靠。
第四,成本和速度。 更多 token 直接意味着更贵、更慢。这不仅是钱的问题,过长上下文还会拖慢每一轮的响应,影响你和它协作的节奏。
所以上下文工程的精神不是「尽量多喂」,而是像一个好的信息编辑一样做减法和编排:判断当前阶段真正需要哪些、把无关的挡在外面、让关键信号清晰突出、并随任务推进动态调整。 这也是它被称为「工程」而不是「收集」的原因——核心是取舍。你可以用一句话提醒自己:目标是让它「恰好看到该看的」,而不是「看到一切」。
六、上下文工程的核心动作一览
把本模块要讲的动作先列在这里,让你有张整体地图,后面逐篇展开:
- 定预算:知道窗口多大、各部分大概占多少,避免不知不觉塞满。
- 选文件:精准挑出真正相关的代码,而不是整目录倾倒。
- 写规则:把长期、稳定的约定沉淀下来自动注入。
- 控输出:让工具返回结论而非冗长原始日志,主动降噪。
- 做隔离:用子代理把大量中间信息挡在主窗口之外。
- 勤压缩:长任务及时摘要,给后面腾空间。
这些动作不需要你一次掌握,先建立一个总意识即可:信息进窗口是要「挑」和「管」的,不是越多越好。
为了帮你把抽象原则落到今天就能用的动作,这里给一个「下一次任务前花一分钟」的小检查:开工前先想清楚——这次任务真正依赖哪几个文件或规则?这些我是否明确让它读到了?有没有我准备顺手丢进去、但其实无关的东西?任务如果会很长,我打算在什么节点做一次压缩?就这四个问题,已经覆盖了上下文工程最核心的日常决策。你不必现在就精通所有零件,只要养成「先想信息流、再发任务」的习惯,后面几篇讲的具体技巧就有了挂靠的地方。很多质量差异,恰恰来自有没有在开工前多这一分钟的思考。
七、常见问题
Q:我不做上下文工程,直接多聊几句不也能用? 简单任务可以,对话本身就会带入上下文。但任务一复杂、文件一多,缺乏管理就会出现该看的没看到、没用的塞满窗、长对话失忆等问题。上下文工程是让你在更大任务上仍能稳定交付的手段,小任务未必要刻意做。
Q:上下文是不是给得越多越保险? 不是。窗口有限,无关信息会挤占预算、稀释注意力,让它更难抓住重点,还更贵。关键不在量多,而在「相关、及时、比例合理」——把真正用得上的信息在合适阶段放进去。
Q:我怎么判断它表现差是上下文问题还是能力问题? 先做个简单验证:把它缺的关键文件、规则或事实明确喂进去,再看结果。如果信息补齐后明显改善,就是上下文问题;若该看的都看到了、仍做不到,才更可能是任务难度或能力边界。这个排查顺序能避免你误判模型。
Q:系统指令我也需要自己写吗? 一般不需要,它由工具预置。你主要可控的是规则、对话、所选代码、工具使用方式和记忆这几路。把精力放在自己能管的部分即可。
总结
想要 Agent 稳定发挥,别只盯着提示词怎么写——把它能看到的信息(规则、对话、代码、工具结果、记忆)当作一个有预算、要取舍的系统来设计,这就是上下文工程,也是高手和普通使用者拉开差距的地方。