逐日AI
第 1 周 · D2约 3 小时

系统提示与指令层次:高度适中、持久指令文件、渐进披露,少即是多

系统提示写太细会脆、写太粗会飘。这一天找中间那个高度:把提示分块组织、把长期规则搬进持久指令文件、让细节靠渐进披露按需展开。

今日目标 0/3

登录后可以勾选并保存进度。

今日目标

  1. 能判断一段系统提示是写太细还是写太粗,并各给出一个具体修法
  2. 能把系统提示分块组织,并说出哪些内容该下沉到持久指令文件
  3. 能用最小起步加失败驱动的方式迭代系统提示,并记录每条规则的保留理由

昨天量出来的四块里,系统提示是每一轮都原样重发的那一块。它的特殊之处在于复利:改一次,整场会话每一轮都受益。所以先从它下手。读完回来把三条目标勾掉。

小白版讲解

行李清单该写到袜子,还是只写类别

出差前你会列一张清单。清单有两种写法,各有各的坏。

第一种写得极细:「白衬衫两件,蓝色那件放最上面;袜子三双,其中一双是运动袜,放在鞋子里;充电器放侧袋左边第二格。」这张清单的问题不是啰嗦,是。行程改成四天,整张清单作废;酒店提供洗衣服务,一半条目失效。你要么每次重写,要么照着一张已经不对的清单收拾。

第二种写得极粗:「带够衣服,带好电子设备,别忘了重要文件。」这张清单不会过时,因为它什么也没说。你照着它收拾,结果和不看它收拾完全一样。

系统提示就是这张清单,而且两种坏写法在真实项目里都极其常见。写太细的那种,是把业务逻辑硬编码进了自然语言——本该由代码判断的分支,被写成了一串「如果……就……」;写太粗的那种,是一段读起来无可指摘、但删掉之后模型行为不变的话。

中间那个位置,Anthropic 的工程博客给了一个很好的词:高度(altitude)。飞太低看得清每一棵树,但看不见路;飞太高整片森林都在视野里,但你不知道该往哪走。合适的高度是:具体到足以有效引导行为,又灵活到能给模型留下判断空间。

怎么判断自己飞在哪个高度?有一个几乎不会失手的自查:你能为这条规则写出一个自动检查它有没有被遵守的断言吗?

  • 写不出来(「语气要自然」「回答要清晰有条理」)——飞太高了,这条是废话。
  • 写得出来,但断言里要列举七种情况(「如果状态是 A 就说这句,是 B 就说那句……」)——飞太低了,这该是代码或者引用文件。
  • 写得出来且断言很短(「回复不含感叹号」「引用政策必须带条目编号」)——高度合适。

这条自查今天会用整整 43 次。因为下面要讲的东西,都建立在「先把每条规则判定一遍」之上——而绝大多数团队的系统提示,从来没有被逐条判定过。

两种失败模式,各有各的修法

先看写太低的那种。这是一段真实形态的客服系统提示片段:

如果 get_order 返回 status 是 pending,告诉用户正在备货。如果是 paid,告诉用户已付款待发货。如果是 shipped,调用 get_logistics 查轨迹再回答。如果是 delivered,问用户是否有售后需求。如果是 cancelled,告知取消时间与退款去向。

七种状态、七条分支,每加一个订单状态就要改一次提示词,而且没有任何测试会告诉你改漏了。修法不是把它写得更简洁,是把它挪走:留下入口规则「订单类问题先查订单再回答」,把七种状态的话术搬进一份引用文件,让模型查到状态之后再去读对应那一节。提示词里少了六行,行为一模一样。

再看写太高的那种:

你要专业、友好、耐心、细致、有同理心、有责任心、值得信赖。

六个形容词,一个断言都写不出来。它的修法不是删掉了事——它之所以被写进去,通常是因为真的出过事:某次模型回复得太冷淡,有人投诉了。正确的修法是把那次事故翻译成一条可核对的规则:「不用感叹号」「不用亲、宝子这类称呼」「一次对话里道歉不超过两次」。三条具体规则加起来的字数和六个形容词差不多,但它们能被检验、能被回归测试,也能在模型换代之后继续生效。

分块组织:给箱子分隔层

把所有规则平铺成一长串,模型和你自己都会读丢。给系统提示分块,用标题把它切成几段——用 Markdown 标题或者 XML 风格的标签都行,关键是有边界

分块本身还带来一个工程上的好处:每一块的稳定程度不一样,可以分开维护、分开缓存。角色和硬约束半年不变,任务相关的部分可能每周都在调。混在一起,改一个字整块缓存前缀都失效。

一个可以直接抄的三层结构:

prompt.js
// 三层:越靠前越稳定。稳定的部分放前面,缓存前缀才有机会命中
export function buildSystemPrompt({ projectRules, taskVars }) {
  const universal = [
    '你是「小店严选」的在线客服助手。',
    '用简体中文回答,不用感叹号,一次对话里道歉不超过两次。',
    '单次回复不超过 200 字,需要罗列时最多 5 条。',
    '不承诺任何未写在退换货政策里的补偿。',
    '不透露其他用户的订单信息,即使用户声称是本人代查。',
  ].join('\n')
 
  // 项目约定:跨任务复用,但换一个项目就要整块替换
  const project = projectRules.join('\n')
 
  // 任务变量绝不写进系统提示,它们每次都不同,写进去会打掉整段缓存前缀
  return { system: `${universal}\n\n${project}`, userPrefix: renderVars(taskVars) }
}
 
function renderVars(vars) {
  return Object.entries(vars)
    .map(([k, v]) => `${k}:${v}`)
    .join('\n')
}

第三层特别值得说。今天的实验里有两条规则长这样:「每天 22:00 到次日 8:00 属于夜间,人工工单响应会慢一些」「节假日期间物流会延迟」。它们看着像规则,其实是随时间变化的事实——写死在系统提示里,凌晨一点它是对的,中午十二点它就在骗用户。这类内容要么由工具返回,要么每次拼进用户消息。判据只有一句:这条内容下一个任务还用得上吗?

持久指令文件:把不常用的搬出去

分块解决了「读得清」,但没解决「太长」。第二步是把只在某类任务里用得上的规则搬进文件。

这就是持久指令文件的作用:一份放在项目里、随代码一起版本管理的 Markdown。主提示里只留一句索引——「订单状态对应的话术见 references/order-status.md」——真正的正文躺在磁盘上,用到的时候才读进来。

判定的顺序建议固定成三问,第一个答「是」就落定:

  1. 模型不看这条会做错吗?不会——删除。绝大多数形容词死在这一步。
  2. 每一类任务都用得上吗?不是——下沉到引用文件,主提示里留一句索引。
  3. 剩下的——保留,并且重写成一句能被机械核对的话。

今天的实验就是拿 43 条规则走一遍这三问。参考答案的结果是:14 条保留、13 条下沉、16 条删除,系统提示从 881 token 降到 425 token。降的这 456 token 是每一轮都在重发的,按一次会话 20 轮算,一场会话省下约九千 token。

渐进披露:先给目录,用到再展开

引用文件解决了「搬到哪」,还剩一个问题:模型怎么知道什么时候该去读哪一份?

答案是渐进披露(progressive disclosure):上下文里常驻的只有一份轻量索引——每份文件的路径加一句「什么时候该看我」——正文按需读取。这和你收拾行李时的做法一样:常用的放随身包,不常用的托运,需要时再取。

实现起来非常朴素:

disclosure.js
import { readFile } from 'node:fs/promises'
 
// 常驻上下文里只有这张索引表,每条不到 20 个 token
const INDEX = [
  { path: 'references/order-status.md', when: '拿到订单状态之后,需要对应话术时' },
  { path: 'references/recommend.md', when: '商品缺货,需要推荐替代品时' },
  { path: 'references/formats.md', when: '需要校验订单号或政策条目编号的格式时' },
]
 
export function renderIndex() {
  return INDEX.map((e) => `- ${e.path}:${e.when}`).join('\n')
}
 
// 模型真的决定要读了,才把正文取进来。这一步是一次工具调用,不是预加载
export async function readReference(path) {
  if (!INDEX.some((e) => e.path === path)) throw new Error(`未登记的引用文件:${path}`)
  return readFile(path, 'utf8')
}

索引里那句「什么时候该看我」是整套机制里最重要的一行字。写得含糊,模型要么该读的时候不读,要么每次都读——后者等于没做渐进披露,还多花了一次工具调用。触发条件要写成可判断的情形,不是文件内容的摘要。

这套「先看索引、需要时才展开」的思路已经被标准化成了一种能力封装形式,也就是 Agent Skills。它把渐进披露拆成三个明确的阶段,写法和边界另有一整天在讲,见 7 天 Agent Skills 的第 1 天。本课这里只用到它的思想内核:上下文里常驻的应该是指针,不是正文。

少即是多:从最小起步,按失败加规则

最后是方法论,也是最难执行的一条:系统提示应该从最小开始,只为真实发生过的失败增加规则。

大多数团队的做法正好相反:上线前先把能想到的规则全写上,「以防万一」。结果是一份没人敢删、也没人知道哪条真正生效的文件。今天实验里那 43 条就是这么来的。

正确的循环只有三步,而且第三步最容易被跳过:

  1. 从一份最小提示词起步:角色一句、硬约束几句,就这些。
  2. 跑一批真实用例,把失败的那些收集起来,按成因分类。
  3. 为每一类失败加一条规则,并且在旁边记下它是为哪个失败案例加的

第三步的那句注释,就是半年后你敢不敢删这条规则的唯一依据。没有它,任何一条规则都会变成不可动的遗产。今天实验的参考答案之所以敢删掉 16 条,就是因为它对每一条都能回答「当初是为什么加的、现在这个理由还成立吗」。

还有一条实践上的提醒:加规则之前先换一次模型试试。 很多规则是为了绕过某一代模型的具体毛病写的,模型换代之后它们不但没用,还在继续消耗每一轮的预算。

源码导读

动手实验

🧪 D2 实验:精简一份持久指令文件

代码位置:labs/context-engineering-5days/day-02-instruction-file

验收标准:

  1. 43 条全部有判定,没有留空的方括号,且每条都有一句理由。
  2. 三层结构填完,且每一条「通用规则」都能被机械核对。
  3. 「任务变量」那一层至少列出两条,它们在原文里是被当成长期规则写的,实际上每次都在变。
  4. 前后对比表填完,精简后的估算 token 至少降到原来的一半以下,参考答案是 881 降到 425。
  5. 能对着结果说出一句话:删掉的那些条目里,没有一条是模型真正需要的。

今天的产出是文档不是程序,所以没有 MOCK=1,也不需要安装依赖。但请先自己判完 43 条再看参考答案——分歧比一致更有价值,找出你和它判得不一样的条目,想清楚谁的理由更硬,这一步才是今天真正的训练。

  1. 只读 starter 的第 1 到第 8 节,凭自己的判断在每条后面填上保留、下沉或删除,并补一句理由。
  2. 按三问的顺序复核一遍:模型不看这条会做错吗、每类任务都用得上吗、剩下的怎么重写成可核对的一句话。
  3. 把保留下来的条目重排进三层结构,注意合并同类项——原文里至少有三组是同一件事的不同说法。
  4. 填完前后对比表,算出精简掉的 token 数,再乘以你预计的会话轮数,得到一场会话真正省下的量。
  5. 打开 solution 对答案,重点看你判「保留」而它判「删除」的那几条,问自己能不能为它写出断言。

面试题

今天 3 道题在下方题库区,侧重系统提示的高度判据、指令层次的划分、以及持久指令文件与渐进披露的边界。展开后先看「分析过程」再看要点——照着推导练,比背要点管用。标注「国内高频 / 海外高频」方便按目标市场取舍。

检查清单与明日预告

  • 能判断一段系统提示是写太细还是写太粗,并各给出一个具体修法
  • 能把系统提示分块组织,并说出哪些内容该下沉到持久指令文件
  • 能用最小起步加失败驱动的方式迭代系统提示,并记录每条规则的保留理由
  • 能用「写不写得出断言」这条自查,当场判断一条规则的高度是否合适
  • 实验的 5 条验收标准全部通过,且找出了至少一条与参考答案判定不同的规则
  • 3 道面试题不看要点也能答出至少 2 道

明天(D3)我们去治昨天量出来的真正大头:工具定义与工具结果。它们合起来通常占八成以上,而且比系统提示难对付——系统提示是你写的,工具结果是外部系统吐给你的,你事先不知道它会有多大。我们会给出一套按任务裁剪工具清单的判据,并动手写一个能量化前后差距的裁剪器。先治好写的,再治难写的,顺序还是按性价比排的。

面试题库

  • 系统提示的高度怎么把握?写太具体和写太笼统各会出什么问题?How do you calibrate the altitude of a system prompt, and what goes wrong at each extreme?
    国内高频海外高频进阶#system-prompt#altitude

    分析过程 · 先想清楚再作答

    1. 这题在考你有没有一把可操作的尺子。凡是答「要恰到好处」「要具体但不要太具体」的,都会被归到没做过工程那一类,因为这句话不能指导任何一次具体修改。
    2. 怎么拆:先把两端的病症说清楚。写太具体是把业务逻辑硬编码进了自然语言——七种订单状态写成七条分支,加一个状态就要改提示词,而且没有任何测试会告诉你改漏了。写太笼统是一段读起来无可指摘、删掉之后模型行为却完全不变的话。
    3. 给尺子:你能为这条规则写出一个自动检查它有没有被遵守的断言吗。写不出来说明飞太高;写得出来但断言要列举七种情况说明飞太低;写得出来且断言很短,高度就合适。这把尺子的好处是能当场逐条判定,不需要争论。
    4. 结论加修法:太低的修法是挪走而不是缩写——留下入口规则,把分支细节搬进引用文件按需读取;太高的修法是把它当初对应的那次事故翻译成可核对的规则,而不是直接删掉,否则同一个事故会再来一次。
    5. 可预期的追问:那怎么知道一条规则当初是为什么加的?答:加规则的时候就在旁边记下它是为哪个失败案例加的。没有这句注释,半年后没人敢删任何一条。

    How to reason about it · think before answering

    1. This tests whether you have an operational yardstick. Answering that it should be specific but not too specific fails, because that sentence cannot guide a single concrete edit.
    2. Name both failure modes. Too low means business logic hardcoded into prose: seven order states become seven branches, every new state forces a prompt edit, and no test tells you when you missed one. Too high means text that reads well and changes nothing if deleted.
    3. Give the yardstick: can you write an automated assertion that checks whether the rule was followed? If not, the rule is too high. If the assertion needs to enumerate seven cases, the rule is too low. A short assertion means the altitude is right.
    4. Then the fixes. For too low, relocate rather than shorten: keep the entry rule and push branch detail into a reference file loaded on demand. For too high, translate the incident that produced it into a checkable rule instead of just deleting it, or the incident recurs.
    5. Expect the follow-up: how do you know why a rule was added? Record the failing case beside the rule when you add it. Without that note, nobody will dare delete anything six months later.

    答题要点

    • 高度太低是把业务分支硬编码进自然语言,脆且改漏无人知;太高是删掉也不改变行为的废话。
    • 判据:能不能为这条规则写出一个自动断言,断言写不出或要列举七种情况都是高度不对。
    • 太低的修法是把细节挪进引用文件、主提示只留入口规则;太高的修法是把对应事故翻译成可核对的规则。
    • 每加一条规则就记下它对应的失败案例,这是将来敢不敢删它的唯一依据。

    Key points

    • Too low hardcodes business branches into prose: brittle, and silent when it goes stale. Too high is text that changes nothing when removed.
    • Test: can you write an automated assertion for the rule? No assertion, or one that enumerates seven cases, means the altitude is wrong.
    • Fix too low by relocating detail into on-demand reference files and keeping only the entry rule; fix too high by translating the originating incident into a checkable rule.
    • Record the failing case beside every rule you add; it is the only basis for deleting it later.
  • 哪些内容该进系统提示,哪些该进持久指令文件,哪些该按需加载?What belongs in the system prompt, what belongs in a persistent instruction file, and what should be loaded on demand?
    国内高频海外高频进阶#instruction-hierarchy#progressive-disclosure

    分析过程 · 先想清楚再作答

    1. 这题在考分层意识。只回答「常用的放系统提示」是循环论证——问题恰恰是怎么定义常用。面试官想听的是一条排序明确的判定流程。
    2. 怎么拆:给三问,第一个答是就落定。第一问,模型不看这条会做错吗,不会就删除;第二问,是不是每一类任务都用得上,不是就下沉到引用文件、主提示里只留一句索引;剩下的保留,并重写成能被机械核对的一句话。
    3. 补一层常被漏掉的划分:还有一类内容根本不属于以上三者——随时间变化的运行时事实,比如当前是不是夜间、是不是节假日延迟期、本次会话的订单号。它们看着像规则,写死在系统提示里就会在半天之后开始骗用户,应该由工具返回或每次拼进用户消息。
    4. 结论:判据是稳定程度加使用频率。越稳定越靠前,越少用越靠后;而每次都变的东西根本不进系统提示。附带一个工程收益——按稳定程度排序之后,缓存前缀才有机会命中,改一条任务变量不会打掉整段缓存。
    5. 可预期的追问:下沉是不是比删除安全?不是。搬进引用文件的内容仍然要维护、仍然会在需要时占上下文,只是晚一点少一点。真正没用的条目要删,「先留着以防万一」正是提示词膨胀的主因。

    How to reason about it · think before answering

    1. This tests layering. Answering that frequently used content goes in the system prompt is circular, since defining frequently used is the actual question. Give an ordered decision procedure instead.
    2. Three questions, first yes wins. Would the model get it wrong without this rule? If not, delete. Is it needed for every class of task? If not, push it into a reference file and leave a one-line index. Whatever remains stays, rewritten as a mechanically checkable sentence.
    3. Add the category people miss: runtime facts that change over time, such as whether it is currently night hours, whether holiday shipping delays apply, or this session's order id. They look like rules but start lying to users hours later. They belong in tool output or in the user message.
    4. Conclusion: sort by stability and frequency. The more stable, the earlier; the rarer, the later; anything that changes every call never enters the system prompt. Bonus: this ordering is what makes a cache prefix hittable, so editing a task variable does not invalidate everything.
    5. Expect the follow-up: is pushing content down safer than deleting it? No. Reference files still have to be maintained and still consume context when read, just later and less often. Keeping things just in case is the main cause of prompt bloat.

    答题要点

    • 三问定去处:模型不看会做错吗(不会就删)、每类任务都用得上吗(不是就下沉)、剩下的保留并重写成可核对的一句话。
    • 第四类是运行时事实(时间、节假日、本次订单号),不属于系统提示,应由工具返回或拼进用户消息。
    • 分层顺序按稳定程度排,稳定的在前,这样缓存前缀才有机会命中。
    • 下沉不是免罪符:引用文件仍要维护、仍会占上下文,没用的要删掉。

    Key points

    • Three questions decide placement: would the model err without it (no means delete), is it needed by every task class (no means push down), and the rest stays as a checkable sentence.
    • A fourth category is runtime fact (time of day, holiday delays, this session's order id); it belongs in tool output or the user message, not the system prompt.
    • Order layers by stability so the cache prefix stays hittable.
    • Pushing down is not free: reference files still cost maintenance and context, so genuinely useless rules should be deleted.
  • 系统提示越写越长是怎么发生的?你会怎么止住这个过程?How do system prompts keep growing, and how would you stop it?
    国内高频海外高频基础#prompt-bloat#maintenance

    分析过程 · 先想清楚再作答

    1. 这题看着像吐槽题,其实在考流程意识。能答出成因的人不少,能给出一条可执行的止损机制的人很少。
    2. 怎么拆:先讲成因,它是一条单向棘轮。每次线上出问题,最快的止血手段就是往系统提示里加一句;加的人当时知道为什么加,但没写下来;半年后没人敢删,因为删了万一那个事故重来一次,责任在删的人身上。于是只进不出。
    3. 再指出成因里最隐蔽的一层:很多规则是为了绕过某一代模型的具体毛病写的。模型换代之后它们不但没用,还在继续消耗每一轮的输入预算,而且没有任何信号提示你它们已经过期。
    4. 结论给三条机制:一是加规则时强制记录它对应的失败案例,这是将来敢删的唯一依据;二是最小起步——先用最少的规则跑一批真实用例,按观察到的失败逐条加,而不是上线前把能想到的都写上;三是定期做一次逐条判定,用「能不能写出断言」当尺子,并在换模型之后重跑一次。
    5. 可预期的追问:删规则的风险怎么控?答:把每条规则对应的失败案例沉淀成回归用例,删之前先跑一遍。没有用例支撑的规则,本来就没有资格待在那里。

    How to reason about it · think before answering

    1. It sounds like a complaint prompt but it tests process thinking. Many can name the cause; few offer a mechanism that actually stops the growth.
    2. The cause is a one-way ratchet. Every production incident is fastest to patch by appending a sentence to the system prompt. The person who added it knew why but did not write it down. Six months later nobody dares delete it, because if the incident recurs the blame lands on whoever deleted it.
    3. Name the subtle layer too: many rules exist to work around a specific model generation's quirks. After a model upgrade they are useless yet still consume input budget every turn, and nothing signals that they expired.
    4. Give three mechanisms. Record the failing case beside each rule when adding it. Start minimal and add rules only for observed failures rather than writing everything imaginable before launch. Periodically re-audit rule by rule using the can-you-write-an-assertion test, and re-run that audit after every model upgrade.
    5. Expect the follow-up: how do you de-risk deletion? Turn each rule's originating failure into a regression case and run it before deleting. A rule with no supporting case never earned its place.

    答题要点

    • 成因是单向棘轮:出事就加一句,加的理由没记录,之后没人敢删。
    • 隐蔽的一层:很多规则是为绕过某代模型的毛病写的,换代后过期却没有任何信号。
    • 止损三招:加规则时记录对应失败案例、最小起步按失败驱动增加、定期用断言尺子逐条重判。
    • 删除风险靠回归用例控制:每条规则对应的失败案例应沉淀成用例,删前先跑。

    Key points

    • The cause is a ratchet: incidents are patched by appending a line, the reason is never recorded, and nobody dares delete it later.
    • Subtle layer: many rules work around one model generation's quirks and silently expire after an upgrade.
    • Three fixes: record the originating failure with each rule, start minimal and add only for observed failures, and re-audit periodically with the assertion test.
    • Control deletion risk with regression cases derived from each rule's originating failure.

评论

登录后即可参与讨论

还没有评论,来说第一句。