上下文工程:把有限的注意力花在刀刃上 · Lesson 2 of 6

第 2 课:上下文的解剖:系统提示、工具与示例

学习目标:

  • 能拆解一次 LLM 请求的上下文组成(系统提示、工具定义、示例、消息历史),并解释它们为什么在花同一份注意力预算
  • 能判断一段系统提示的「高度」问题——过度硬编码还是过度空泛——并把它改写到正确高度
  • 能从上下文成本的视角审计工具定义与示例:合并重叠功能、精简返回、用少数典范示例替代边角案例清单

前置要求:读完第 1 课,认同「上下文是有限资源」这个前提 | 上一课 第 1 课 << | 下一课 第 3 课 >>

先打开引擎盖:一次请求里到底装了什么

第 1 课我们借用了 Anthropic 的定义:上下文工程是「在 LLM 推理过程中,筛选并维护最优 token 集合的一整套策略」1。听起来还是有点抽象——「最优 token 集合」到底指哪些 token?本课就干一件事:把引擎盖打开,看看每次请求发出去时,窗口里究竟装了什么。

你在本系列第 7 门课手写过 harness 循环,对请求的形状应该不陌生。把它按内容归类,一次请求的上下文大致是这四块:

text
一次请求的上下文├── 系统提示(角色、规则、背景知识)├── 工具定义(每个工具的名字、描述、参数 schema)├── 示例(few-shot:刻画期望行为的输入输出样本)└── 消息历史(用户消息、模型回复、每一轮的工具调用与结果)

关键在于:这四块不是四个互相隔离的口袋,而是同一个窗口里的邻居。模型在解析大量上下文时,依赖的是同一份「注意力预算」,每个新进来的 token 都会把这份预算消耗掉一点1。系统提示多一段废话,留给消息历史的注意力就少一点;工具清单里躺着十个用不上的工具,示例能分到的注意力也被摊薄。而且第 1 课说过,token 堆得越多,模型从上下文里准确召回信息的能力会下降,这个退化是渐变而不是断崖1——所以每一块的浪费都不会立刻报错,它只是安静地让整体变笨一点,等你察觉时往往已经找不到「是哪一行搞的鬼」。

还要注意四块的增长速度不一样。系统提示、工具定义、示例基本是静态的,你写多长就是多长;消息历史却在循环里持续膨胀——跑在循环里的 Agent 会不断产生「下一轮推理可能用得上」的数据1。你在本系列第 7 门课的 harness 里,每轮把工具结果 push 进 messages 数组的那行代码,就是膨胀的现场。历史怎么治,本课不展开,第 4 课《压缩与笔记》专门讲;本课先管好三块静态的——它们是每一轮都要付的固定成本。

把「上下文是边际收益递减的有限资源」1这句话落到实处,就是对这几块逐一问同一个问题:这一块的每个 token,换回了多少行为上的改善?下面逐块来看。

系统提示的「高度」:两种失败姿势

Anthropic 的工程文章用「高度」(altitude)来描述系统提示的抽象层级,并点名了两个失败极端:一端是把复杂、脆弱的逻辑硬编码进提示;另一端是空泛的高层指导,给不出具体信号1。我们用同一个客服 Agent 写三版系统提示,把这两个坑和正确写法摆在一起看。

低空飞行版——把逻辑写死:

text
你是订单客服。严格按以下规则处理:1. 用户说「没收到货」,回复模板 A。2. 用户说「收到但破损」,回复模板 B,并发放 10 元优惠券。3. 订单号以 TB 开头的先查物流,以 JD 开头的先查库存。4. 用户消息里出现「投诉」二字,转人工。……(省略 14 条)19. 用户同时符合第 2 条和第 4 条时,第 4 条优先。20. 以上都不匹配时,回复「抱歉,我不太明白您的意思」。

每一条规则只覆盖写出来的那个情况。用户说「箱子被压扁了」而不是「破损」呢?没有一条规则接得住,只能落到第 20 条兜底装傻。更麻烦的是规则之间开始打架,于是你补了第 19 条来调解——这时你已经在用自然语言写一个 if-else 解释器了:每加一条,提示更长、更脆,注意力预算烧得更多,而没写到的情况永远比写到的多。

高空飘着版——只给口号:

text
你是订单客服。请表现得专业、友好,灵活处理用户的问题,让用户满意。

token 是省下来了,但模型拿不到任何具体信号:退款的边界在哪?补偿权限有多大?什么情况必须转人工?全靠模型自己猜。「让用户满意」在极端情况下可能意味着把不该退的钱退了——这不是模型不听话,是你什么都没告诉它。

正确高度版——按 Anthropic 的说法,好的系统提示要「具体到足以有效引导行为,又灵活到能给模型留下强启发式」1

text
你是订单客服,目标是在一次对话内解决用户的售后问题。
判断原则:- 未发货的订单,用户要求退款可直接操作;已发货的先引导拒收或走退货流程。- 破损、错发、漏发属于我方责任,主动提出补偿方案,不等用户开口。- 责任归属拿不准时,先向用户确认关键事实(订单号、照片),再下判断。
硬边界(必须遵守):- 单笔补偿不超过 50 元,超出转人工。- 涉及人身伤害或法律纠纷的对话,立即转人工并标记。

注意这版的结构:原则负责泛化,红线负责合规。「箱子被压扁了」没有被逐字写进任何规则,但它自然落进「破损属我方责任」这条原则;而真正不能商量的事(金额上限、法律纠纷)以少数几条硬边界写死。字数比 20 条版少了一大半,覆盖面反而更大。

判断高度有个快捷问题:**遇到一个没写到的情况,这段提示能给模型一个判断方向吗?**低空版给不了(只能兜底),高空版给了一个空方向(「满意」),正确高度版给的是可迁移的原则。

常驻的系统层:CLAUDE.md 的纪律

系统提示不只是你手写的那段字符串。很多 harness 会把项目级配置常驻在系统层,Claude Code 的 CLAUDE.md 就是典型:官方文档说它是「每次对话开始时 Claude 都会读取的特殊文件」2。「每次都加载」意味着它的每一行都在每个会话里花注意力预算,所以官方文档给它立的纪律很硬。

一条纪律:只放普遍适用的内容2。只有某个子目录才用得到的构建命令、只有某类任务才需要的规范,不配住在常驻层。

另一条纪律:逐行做删除测试——对每一行问「删掉它会不会导致 Claude 出错?」2答案是否,就删。这不是洁癖:文档明确警告,臃肿的 CLAUDE.md 会让你真正的指令被忽略2。这正是注意力预算的具象版本——塞进去的每行可有可无的内容,都在稀释那几行真正重要的规则。同一份文档甚至直接说「上下文窗口是最重要的待管理资源」,它「填满得很快,而且越满性能越差」2

那些不常用、但偶尔确实需要的内容放哪?Claude Code 的做法是技能(skills):按需加载,「不会让每个会话都变臃肿」2。「常驻最小化,其余按需取用」这个组合,正是第 3 课的主题,这里先记个账。

顺带一提:你自己项目里如果有 AGENTS.md、system_prompt.txt 之类的常驻文件,同样的删除测试照做。常驻层的膨胀最隐蔽——它不出现在任何一轮对话里,却在每一轮里收税。

工具即上下文:定义在花钱,返回也在花钱

工具在上下文里出现两次:定义跟着每次请求走,返回结果进入消息历史。两头都在花预算。

本系列第 4 门课讲过工具设计的功能面——参数怎么定、错误怎么处理。本课换一个视角:每个工具定义都是一段要被模型读懂的 token。Anthropic 对工具的要求是自包含、对错误健壮、用途极其清晰1,并且工具之间的功能重叠要最小1。看一对反例(schema 为了可读性做了简写):

两个工具都能查订单,描述又都含糊,它们之间的边界连写工具的人都没说清楚。「重叠最小」1这条要求的道理就在这:边界模糊的工具堆在一起,等于把「该用哪个」这道题在每一轮都重新抛给模型。合并成一个、写清楚用途和行为,问题就消失了:

这个描述不只说了「干什么」,还写明了返回形态和空结果时的行为——这是「对错误健壮」1的直接体现:模型不用猜「查不到会怎样」,也就不容易在查不到时做出奇怪的补救动作。

再看返回端。Anthropic 要求工具返回的信息做到 token 高效1。一个把订单的 40 多个内部字段加完整操作日志全部 dump 回来的工具,每调用一次就往消息历史里灌一大桶低价值 token——而且这些 token 会留在历史里,被后面的每一轮反复收税。上面例子里「默认精简、detail=true 按需拿全量」就是针对这件事的常用形态。

最后,工具清单本身也要做减法:上下文里躺着十个从来不被调用的工具,它们的定义照样每一轮全额计费。审计工具清单和审计 CLAUDE.md 是同一个动作——删掉它会出错吗?不会就删。

示例:挑典范,别堆清单

示例(few-shot)是第三块静态成本。它最常见的腐化路径是这样的:线上每出一个 badcase,就往提示里加一个对应的例子,半年之后你有了一份 30 条的边角案例清单——而 Anthropic 的说法很直接:别把一堆边角案例(laundry list)塞进 prompt,应该精心挑选一组多样、典范(canonical)的例子来刻画期望行为1

「典范」的意思是:一个例子代表一类行为,而不是一个具体情况。还拿客服 Agent 说,三个例子就能把行为空间的骨架撑起来:

  1. 标准流程示范:查订单、归因、给方案的完整走法;
  2. 主动担责示范:我方错发,不等用户开口就给出补偿方案;
  3. 升级示范:超出权限,礼貌说明并转人工。

「多样」则是指这三个例子分别覆盖判断的不同分支,而不是同一类行为的三个变体。

那边角案例去哪了?大多数应该升回系统提示的原则层:「用户辱骂怎么办」不需要一个 300 token 的完整对话示例,一条原则「遇到情绪激烈的用户,保持克制并聚焦解决问题本身」就够了。例子教的是「期望行为长什么样」,原则教的是「遇到新情况往哪个方向想」。你可能已经发现了:往示例清单里加边角案例,和往系统提示里加分支,是同一枚硬币的两面——都是在低空打补丁,而只有升到原则层才封得住口子。

逐块检查清单:把解剖用起来

把本课收拢成一张可执行的清单。往上下文里加任何东西之前,先过一遍对应的问题:

检查问题
系统提示高度对吗?遇到没写到的情况,它能给出判断方向吗?1
常驻文件这行普遍适用吗?删掉会导致出错吗?2
工具定义用途够清晰吗?和别的工具重叠吗?返回默认精简吗?1
示例每个例子代表一类行为吗?清单是不是又在变长?1
消息历史本课不处理——第 4 课《压缩与笔记》见

表格之外还有一条总原则值得带走。Anthropic 在谈构建 Agent 系统时给过一个分寸建议:只有当复杂度确实能改善结果时,才考虑增加复杂度3。它原本说的是系统架构,但用在上下文的每一块上同样成立——多一个工具、多一条规则、多一个示例都是复杂度,先证明它换得回行为改善,再让它上车。

到这里,三块静态上下文的配法都过了一遍。但还有一个更进一步的问题:有些信息根本就不该预先装进窗口——与其猜模型会用到什么,不如让 Agent 在运行时自己去找。这就是下一课的主题。

小结

  • 一次请求的上下文由系统提示、工具定义、示例、消息历史四块拼成;它们共享同一份注意力预算,每个新 token 都会把预算消耗掉一点1
  • 上下文是边际收益递减的有限资源1;逐块问「这些 token 换回了什么」,比笼统地「改改 prompt」可操作得多。
  • 系统提示有两个失败极端——硬编码脆弱分支与空泛无信号的口号;正确高度是具体到能引导行为、灵活到留下强启发式1,「原则+硬边界」是落地这个高度的实用结构。
  • 常驻内容(如 CLAUDE.md)每次会话都会加载:只放普遍适用的内容,每行做删除测试;臃肿的常驻文件会让真正的指令被忽略2
  • 工具定义与返回两头花预算:用途极清晰、功能重叠最小、返回 token 高效1;从不被调用的工具也在每轮全额计费。
  • 示例要挑多样、典范的少数几个,别把边角案例堆成清单1;边角情况多数应该升回原则层解决。
  • 给上下文加任何复杂度之前,先想想 Anthropic 的分寸建议:只有当它确实能改善结果时才考虑增加3

>> 第 3 课:即时检索:让 Agent 自己去找上下文

Footnotes

  1. Effective context engineering for AI agents — Anthropic Engineering — https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

  2. Best practices for Claude Code — Claude Code Docs — https://code.claude.com/docs/en/best-practices 2 3 4 5 6 7 8

  3. Building Effective AI Agents — Anthropic Engineering — https://www.anthropic.com/engineering/building-effective-agents 2

Exercises

01

下面是一个报销审批 Agent 的系统提示:

Level 1:把「规则清单」改写到正确高度
text
你是报销审批助手。按以下规则审批:1. 打车发票金额 < 100 元,直接通过。2. 打车发票金额 >= 100 元,要求补充行程说明。3. 餐饮发票在工作日 12:00-14:00 之间的,直接通过。4. 餐饮发票在周末的,退回。5. 办公用品类目为「电子设备」的,转主管审批。6. 发票抬头与公司名不符的,退回。7. 以上都不匹配时,退回并备注「请联系行政」。

按这套规则,出差住宿的发票没有任何一条能接住,会一律落到第 7 条被退回;工作日晚上的餐饮发票既不满足第 3 条也不满足第 4 条,同样落进兜底。请:(1)指出这段提示属于哪个失败极端,并说明这类症状为什么必然出现;(2)把它改写到正确高度,改写稿要能覆盖上面两种「没写到」的情况。

Done criteria · checked locally
02

你接手了一个电商客服 Agent。它的上下文里有下面三个工具定义(schema 已简写),另外还有一份 12 个示例的 few-shot 清单,示例内容全是「用户发错别字怎么办」「用户重复提问怎么办」这类边角情况:

Level 2:给工具和示例做一次上下文成本审计

请做一次上下文成本审计:(1)指出工具清单里的功能重叠并给出合并方案;(2)说明合并后的工具如何让返回做到 token 高效、同时保留必要信息可取;(3)给出示例清单的改造方案:应该留下什么样的例子,边角情况交给谁处理。

Done criteria · checked locally

My note

Jot down thoughts, sticking points, things you didn't get. Written to this course's appendix only — the lesson file is never touched.