提示词工程:System Prompt 怎么驯服模型
一句话结论(30s)
System Prompt 不是「聊天开场白」,而是写给模型的一份系统契约——它持久地规定「你是谁、能调什么工具、不能做什么、产出要长什么样」,并且每一轮请求都会被原样重发。工程上真正有效的只有四招:角色设定(角色 = 视角 + 权限 + 验收标准,不是语气)、约束注入(用「禁止」和「必须」写铁律,且每条要可检查)、few-shot(给样例不给定义)、结构化输出(用 schema 逼模型返回 JSON,而不是祈祷它返回 JSON)。而 Claude Code 给出的最大启示是:不要把厚厚的说明书全塞进 system prompt,而是把能力切成一份份 skill,按需注入——这解决了「提示词太长,模型抓不住重点」这一提示词工程的元问题。
一、System Prompt 的本质:不是「开场白」,是「系统契约」
上一篇讲 Tool Loop 时说过:Agent 每一轮都要把历史重发给模型。其实 system prompt 也一样——它是每一轮请求里永远排在最前面的那段文本。user prompt 回答「这一轮要干什么」,system prompt 回答「你一直是谁、你手上有什么、你不能干什么」。前者是易变的,后者是持久的。
一个 System Prompt 要解决四件事:
| 要素 | 回答的问题 | 写不好会怎样 |
|---|---|---|
| 角色 Role | 你是谁、站在谁的视角 | 模型不知道该用什么标准取舍 |
| 约束 Constraint | 绝对不能做什么、必须怎么做 | 模型自由发挥,触红线 |
| 工具 Tool | 你能调用哪些函数、各自干嘛 | 模型调错工具、编造参数 |
| 边界 Boundary | 什么算做完、什么该停下来 | 模型过度设计、越权改范围外代码 |
这四件事不是并列的选项,而是一份契约的四个必备条款,缺任何一个,都会以可预测的方式翻车:
- 缺「角色」:模型会退化成「通用聊天助手」。你让它审查代码,它会顺着作者的思路走,夸「逻辑清晰、命名规范」——因为它没有被告知「你现在是攻击者,不是建造者」。角色缺失时,模型永远用「最安全、最讨喜」的默认视角回答,而不是「最有用」的视角。
- 缺「约束」:模型会自由发挥。它天生倾向于「多做一点」——顺手重构、补充没要求的抽象、写一堆没人要的注释。约束缺失时,产出会稳定地「超纲」。
- 缺「工具」:模型要么编造能力(把「我查一下」说成真的查了),要么用错工具、编参数。工具定义(
name+description+parameters)是模型在 think 阶段唯一能看到的「说明书」,说明书含糊,模型就会在调用时瞎猜。 - 缺「边界」:模型不知道什么时候该停。没有「什么算做完」的定义,它要么过早收工(答了一半就说「完成」),要么无限发散(从修一个 bug 扩大到重构整个模块)。边界就是 Agent 循环的停止条件,没有它,循环不会优雅地收敛。
结论先行:System Prompt 的每一行都应该对应上面四要素之一。 如果你写了一段既不说角色、也不设约束、也不描述工具、也不划边界的话,删掉它——它只会稀释模型的注意力,而不是增加它的能力。
二、四个工程手法
2.1 角色设定:角色 = 视角 + 权限 + 验收标准,不是语气
新手写角色是「你是一位资深、严谨、乐于助人的工程师」——这是语气,对模型行为几乎零影响。「资深」「严谨」「乐于助人」这些形容词没有给出任何可执行的取舍标准,模型读完还是一样「自由发挥」。
工程上的角色设定要回答三个能真正改变输出的问题:
- 视角:站在谁的立场思考?一个代码审查技能的方法论第一句话是「从建造者切换到攻击者」——假设你第一次看到这段代码,且你不在乎它为什么这样写,你只在乎它哪里会坏。这一句话比「你是严谨的审查员」有用一百倍,因为它给模型换了一个认知框架:从「顺着作者的思路走」切换到「专门找哪里会坏」。
- 权限:你能做什么、不能做什么?写进角色里的权限,比写进约束更根本——因为它是「你是谁」的一部分,而不是「你被要求别做什么」。审查者被声明成「只读」,它就天然不会想着去改代码。
- 验收标准:什么算「干完了」?「改最少的代码,做最精准的事,让新入职三天的开发者能看懂」——这句话同时定义了工作方式和交付标准。没有验收标准的角色,是一个「永远干不完」的角色。
一句话:角色设定的产出不是一段人设,而是三个可执行的答案——视角、权限、验收标准。 语气是这三个答案的副产品,不是目标。
💭 思考:为什么角色设定要落到「视角 + 权限 + 验收标准」,而不是「资深、严谨」这类形容词?——从「角色到底改变什么」反推:模型读到的每句话,最终都要变成「下一轮它怎么取舍」。形容词不提供取舍标准,「严谨」在「多写点防御代码」和「少改点代码」之间没有倾向,模型读完还是自由发挥。而「视角」告诉它往哪看(攻击者 vs 建造者)、「权限」告诉它能伸手多长(只读 vs 可写)、「验收标准」告诉它做到什么算停——这三样才是真正能落在「下一个 token」上的约束。如果不这样,你写的是一段「听起来很专业、实际上不改变任何输出」的装饰语,反而稀释了真正有用的条款。
2.2 约束注入:铁律用「禁止」,不用「请」
约束要写成负面清单 + 可检查条款,而不是「请保持代码整洁」这种软话。模型对「禁止」的服从度远高于「尽量」——因为「禁止」给了它一个明确的、不可跨越的边界,而「尽量」意味着它可以自行权衡。
一组正面教材式的约束长这样:
1. 注释说明「为什么」,不是「做了什么」
2. 复用现有模式,不要造新轮子
3. 不为「可能以后有用」创建抽象
4. 边界止步——收住你的手(看到旁边代码有 bug,告诉用户,别自己改)
第 4 条尤其关键。它针对的不是模型的能力缺陷,而是模型的过度热情——模型倾向于顺手修一切它看到的问题,导致 diff 变脏、范围失控。这条铁律用一个具体场景(「看到旁边代码有 bug 怎么办」)直接封死了这个倾向。
这种「禁止 + 必须」不是修辞建议,真实工具的常驻提示词里就是这么逐字写的。Skill 工具的常驻提示词(src/tools/SkillTool/prompt.ts)里有两条原话:When a skill matches the user's request, this is a BLOCKING REQUIREMENT: invoke the relevant Skill tool BEFORE generating any other response about the task,以及 NEVER mention a skill without actually calling this tool。BLOCKING REQUIREMENT(阻塞性要求)、BEFORE(之前)、NEVER(绝不)——全大写不是排版,而是把「必须何时做、绝不做什么」写成不可协商的执行时序,而不是「尽量」「可以」。
而「可检查」是约束是否有效的判据。约束写成不可检查的「原则」,等于没有约束;写成可 grep、可 diff 的验证动作,才谈得上「被遵守」:
| 铁律 | 检查方式 |
|---|---|
| 禁止过度设计 | 代码行数 / 新增文件 = 需求复杂度匹配? |
| 禁止触碰范围外代码 | git diff 是否包含非目标文件? |
| 零魔法数字 | grep 出的裸数字是否全为常量引用? |
写「不要写烂代码」是不可检查的;写「无裸 except: pass,每个 except 至少记录日志」是可检查的。 可检查性,是把「希望模型遵守」升级为「能验证模型遵守」的关键一跃。
💭 思考:为什么约束必须「可检查」,光写「保持代码整洁」不行?——从「约束最终靠什么兑现」反推:约束写进提示词只是「请求模型遵守」,真正兑现它的是「你事后能验证它被遵守了」。一条不可检查的约束,模型违反了你也发现不了,等于没有约束。所以好约束的标准不是「读起来有道理」,而是「违反时能 grep / diff 出来」——「无裸
except: pass」可检查,「不要写烂代码」不可检查。如果不这样,你就把工程质量从「可验证的事实」降级成「祈祷模型自觉」——而模型天生会给自己找理由,没有验证动作的约束,最后都会在长上下文里被默默跳过。
2.3 Few-shot:给样例,不给定义
当你要模型产出某种格式或做某种分类时,一个样例胜过一百字的定义。因为模型是「看着上下文预测下一个 token」的机器——你给它一个正确的输出样例,它比读任何格式说明都更快进入那种生成模式。
但 few-shot 有代价,而且这个代价常被低估:每个样例都占 token,且样例本身会框定模型的输出空间——例子太窄,模型就只会模仿那种窄法;例子有隐含的瑕疵,模型会连瑕疵一起学走。
所以工程上要问两个问题,再决定用不用 few-shot:
- 这个任务的难点是「做什么」还是「长什么样」? 前者靠约束和角色解决,后者才值得 few-shot。如果难点是「理解要做什么」,给样例只会让模型去模仿样例里的具体做法,而不是理解你的意图。
- 能不能用 schema 代替样例? 结构化输出场景里,给一个 JSON Schema 通常比给三个 JSON 样例更经济、更不易跑偏。这正是下一节。
2.4 结构化输出:用 schema 逼模型,而不是祈祷
「请返回 JSON」是最常见的无效提示词——模型可能返回 JSON 如下:... 的前缀、可能带 Markdown 代码块、可能漏字段、可能改键名。正确做法是在 API 层面声明 schema,让运行时去校验和重试,而不是靠提示词软约束。
而 tool_calls 本身就是最强的结构化输出——模型不再输出自由文本,而是输出一个必须匹配 parameters JSON Schema 的结构化意图:
{"name": "get_weather", "arguments": "{\"city\": \"深圳\"}"}
「工具调用」和「结构化输出」本质是同一件事:把模型从「自由生成」关进「受约束的生成」里。这也是为什么写工具定义要像写文档——parameters 里的每个字段、类型、required、description,都是给模型画的一个「受约束的生成空间」。这个空间画得越清晰,模型越不可能跑出界。
真实工程里的工具定义,正是这么一份纯声明。以 Claude Code 的 Skill 工具为例(src/tools/SkillTool/SkillTool.ts):它的工具名是常量 SKILL_TOOL_NAME,值就是字符串 'Skill';输入 schema 只有 skill(string)和可选 args(string)两个字段;description 不是一段写死的文案,而是一个运行时模板,按传入的 skill 名展开成一句 Execute skill: ${skill}。工具去哪里读文件、如何解析 SKILL.md、怎么把内容展开注入上下文——这些执行细节全被藏进「工具被调用之后」,声明里一行都没有。
另一个对照是 Config 工具(src/tools/ConfigTool/ConfigTool.ts,常量 CONFIG_TOOL_NAME = 'Config'):description 是一句写死的常量 Get or set Claude Code configuration settings.,输入 schema 只有 setting(string)加可选 value(类型收窄成 string/boolean/number 三选一的 union)。连「只读还是可写」都不是靠提示词说,而是声明里直接推出来的——isReadOnly 判定 value === undefined 即只读,checkPermissions 对「读」自动放行、对「写」才弹权限确认。工具定义越接近「纯声明」,模型在 think 阶段看到的东西就越干净,也就越不容易跑偏。
💭 思考:为什么「请返回 JSON」没用,非要在 API 层声明 schema?——从「模型怎么生成 token」反推:模型是「预测下一个 token」的机器,一句「请返回 JSON」只是众多 token 里的一个软建议,它照样可能先吐「JSON 如下:」再包一层 Markdown、漏字段、改键名。而 schema 在生成层就画死「每个字段、类型、必填项」的边界,模型只能在这个受约束的空间里生成,跑出去运行时还能校验重试。这跟「写工具定义要像写文档」是同一件事:
parameters就是模型的「受约束生成空间」。如果不这样,你就把「格式正确」寄托在「模型这次心情好」上——而格式错误在 Agent 循环里会变成「下游解析失败 → 重试 → 更烧钱」。
三、防跑偏:模型会「合理化」地跳过步骤
对工程 Agent 来说,真正的威胁往往不是恶意的提示词注入攻击,而是模型自我合理化——它会在没有证据时假设接口行为、用「应该差不多」糊弄过去、用「用户没明说所以我猜」跳过必要检查。这不是模型坏,而是模型「省事」的天性:它天生倾向于给出一个「看起来合理」的答案,而不是一个「被验证过」的答案。
对抗它的手法有两条,一条软、一条硬:
软的是把「防跑偏」写成清单,而不是段落。 段落在长上下文里会被模型「扫一眼略过」,而勾选框式的一条条,才是模型逐项自问的触发器:
## 🛑 动工前自检
- [ ] 我是否真的读了相关文件,而不是凭印象?
- [ ] 我是否在没有证据的情况下假设了行为/接口/数据结构?
- [ ] 我是否把「不确定」如实标注,而不是用「应该/大概/差不多」糊过去?
<!-- 停止重读规则:如果你正在为「跳过某条」找理由,那正是必须执行该条的信号。 -->
有两个设计值得注意:
- 它是 checklist,不是段落。 一条条可勾选的检查项,逼着模型逐项面对「这条我做了吗」,而不是对着一整段话含糊地点头。
- 「停止重读规则」是防跑偏的最后一环。 它把「模型正在为自己找借口」这个行为本身,变成「必须停下来执行」的信号——用一句提示词,把模型的自我辩护倾向反转成执行指令。
硬的则是不让约束依赖模型自律。 提示词里的约束再严密,也终究是「请求模型遵守」——模型可能忘、可能漏、可能给自己找理由。所以真正的硬约束要放在运行时里,而不是放在提示词里。这正是 Claude Code 的 hook 机制做的事:PreToolUse hook 能在某个工具被调用之前拦截它,PostToolUse 能在调用之后校验结果。换句话说——「审查者只给只读工具」这句话,写在提示词里是软约束,写成权限配置或 hook 拦截才是硬约束。能在运行时封死的东西,就不要指望模型自觉。
四、Skill 注入:提示词太长怎么办
现在回到那个元问题。假设你手上有几十份「能力说明书」——每份几百行,讲一个专门任务的执行方法论。如果你把它们全部塞进 system prompt,会发生三件事:
- token 爆炸——每轮请求都重发这几十 KB,账单翻几倍,而其中绝大部分这次任务根本用不到;
- 注意力稀释——模型对每一条规则的注意力权重下降,关键规则被淹没(这就是「Lost in the Middle」现象:模型对长上下文中段的记忆最差,越靠中段的信息越容易被忽略);
- 指令冲突——不同能力的红线可能打架(比如「改最少的代码」和「彻底重构」同时在场),模型只能瞎权衡。
Claude Code 的答案是 Skill 按需注入。这是它的公开机制,而且是一个极漂亮的提示词工程解法:skill 不是启动时就全部加载,而是先只注册一个「索引」——每个 skill 的 name + description。只有当任务命中某个 skill 时,模型才去读那份 SKILL.md 的全文,把详细方法论注入上下文。用完即弃,下一个任务再换。
这套「发现 → 加载」在源码里是分开的,而且「常驻的要薄」被写成了硬常量,不是口号:
- 索引有预算,不是无限列的。 skill 列表占的上下文窗口被定为 1%(
SKILL_BUDGET_CONTEXT_PERCENT = 0.01),按CHARS_PER_TOKEN = 4(每 token 约 4 字符)换算成字符预算contextWindowTokens × 4 × 0.01,拿不到窗口大小时退回一个 8000 字符的默认值(DEFAULT_CHAR_BUDGET = 8_000,源码注释明写「1% of 200k × 4」)。每条 description 还有硬上限 250 字符(MAX_LISTING_DESC_CHARS);预算装不下时,内置 skill 的完整描述永不截断,其余按剩余预算均摊截断,截到连 20 字符(MIN_DESC_LENGTH)都保不住就退化成只列名字。系统提醒只列每个 skill 的name + description,全文要等Skill工具被真正调用时,才由运行时把 SKILL.md 展开成完整 prompt 注入上下文。 - 加载动作本身也是一个被约束的工具调用。
Skill工具的输入 schema 只有两个字段:skill(名字)和可选args,description 动态展开成Execute skill: ${skill}。模型不需要知道 SKILL.md 在哪个文件、怎么解析,它只需要报一个名字,剩下的由运行时完成。
这和「把整本说明书塞给员工」vs「给员工一张目录,用到哪一章才翻哪一页」的区别一样。提示词的分片化,本质是把「静态的大 system prompt」拆成「动态的、按需装载的提示词模块」。 它没有放弃约束——约束仍在,只是从「常驻」变成了「用到时才加载」。
这套机制有两层值得拆开看:
第一层:常驻的极薄,注入的才厚重。 Claude Code 项目里始终加载的是 CLAUDE.md——一份项目级的薄路由:规范、约定、反模式速查。它只存指针,不存内容:不写「代码审查怎么做」,只写「审查时用 code review 能力」。真正的执行细节在各自的 skill 里,命中才读。常驻层要保证「薄」——薄到模型扫一眼就知道「遇到什么该去翻哪一页」,厚到模型能把每一个细节都记住是不可能的。
这套「结构化声明」在 Codex 那边有一个平行的标准:AGENTS.md。它的解析模块(core/src/agents_md.rs)会从项目根(默认用 .git 定位,即 default_project_root_markers())一路向下走到当前目录,把沿途找到的每一份 AGENTS.md(默认文件名常量 DEFAULT_AGENTS_MD_FILENAME = "AGENTS.md")按顺序拼成一份模型可见的指令,中间用常量 AGENTS_MD_SEPARATOR 的值 --- project-doc --- 分隔,并受一个总字节预算(project_doc_max_bytes)截断。也就是说,连「常驻声明」本身也是分片的——每个目录各写各的,加载时再合成一份。Codex 自己的 skill 也走同一套声明:SKILL.md 的 YAML frontmatter 由 skills/src/parser.rs 强制解析成 name(不超过常量 MAX_NAME_LEN = 64 个字符)加一个非空的 description——缺 description 抛 MissingField("description"),连 --- 包裹的 frontmatter 都没有则抛 MissingFrontmatter;name 和 description 还要过一道 sanitize_single_line,把换行和连续空白压成单行;frontmatter 里另有两个可选字段 model 和 metadata.short-description。声明是结构化、可拼接、可校验的产物,而不是一段不可分割的死文本。
第二层:靠语义匹配,不靠关键词命中。 skill 的 description 是它的「语义锚点」,模型靠它理解「这个 skill 是干嘛的」,而不是靠穷举触发词。用户说「我有段代码感觉很乱但说不上来哪里有问题」,字面上没有命中任何关键词,但模型靠语义能定位到「代码审查 + 根因分析」这类能力。用 LLM 做路由器,你不需要穷举用户会说什么——你只需要给每个 skill 一个准确的语义描述。 这就是提示词工程和路由的汇合点:description 写得好不好,直接决定这些 skill 能不能被正确调用。
而且这套「发现」还不只靠常驻索引。Claude Code 给了模型一个独立的 discover_skills 工具(常量 DISCOVER_SKILLS_TOOL_NAME = 'discover_skills'):系统提示词里写着「Relevant skills are automatically surfaced each turn as “Skills relevant to your task:” reminders」,当这些自动浮出的提醒覆盖不到当前任务时(临时改道、少见的工作流、多步计划),模型可以用一句话描述自己正在做什么、主动去检索更多 skill,而「Skills already visible or loaded are filtered automatically」——已加载/已可见的会被自动过滤。这段引导写死在 src/constants/prompts.ts 里,常驻索引负责「兜住常见情况」,语义检索负责「接住没见过的情况」——两者拼在一起,才是一套完整的按需注入。
所以 Skill 注入解决的不是「提示词写得不好」,而是「提示词太长的元问题」:当信息量超出模型能稳定把握的范围,正确做法不是把提示词写得更精炼,而是改变信息的装载方式——按需注入。 这跟你写代码时分模块、按需 import 是同一个道理:一个几万行的单文件,永远不如一个清晰的模块索引加若干按需加载的模块。
💭 思考:为什么 Claude Code 不把几十份能力说明书全塞进 system prompt,而是切成 skill 按需注入?——从「塞进去会发生什么」反推:全塞进去,token 爆炸、注意力稀释(Lost in the Middle,中段信息最容易被忽略)、指令之间还会冲突,三个坏处都来自同一个根因——「常驻的信息量超出了模型能稳定把握的范围」。于是解法不是「把提示词写得更精炼」,而是「改变装载方式」:常驻层只留一份目录(
name+description),命中才读全文。如果不这样,你就是在用「加更多说明」去解决「说明太多」的问题——南辕北辙。这跟「几万行单文件」vs「模块索引 + 按需 import」是同一个权衡。
五、结论:System Prompt 的每一行都应对应四要素之一
把全文收拢成一个可操作的判据:写 System Prompt 时,逐行问自己——这一行是在定义角色、注入约束、描述工具,还是划定边界?
- 定义了角色(视角 / 权限 / 验收标准)→ 留下。
- 写了一条可检查的约束(「禁止 X」+ 能 grep 验证)→ 留下。
- 描述了一个工具的语义(
name/description/parameters)→ 留下。 - 划定了边界(什么算做完、做到哪停)→ 留下。
- 四者都不是 → 删掉,它是噪声。
反过来,用这个判据去审视任何一份现成的 System Prompt,你会发现大多数「看起来写了很久」的提示词,真正在干活的只有中间那一小段——剩下的都是「你是资深的、严谨的、乐于助人的」这类语气词,它们让提示词显得丰富,却在稀释模型对关键条款的注意力。
再把视角拉高一点:当提示词的内容多到四要素都写不完时,不要硬塞,要分片。 把超出常驻预算的部分切成一个个 skill,靠索引 + 语义匹配按需注入。这是 Claude Code 给整个提示词工程领域上的最重要一课:提示词不是「一段话」,而是一个需要分片、声明、路由、可检查、可注入的工程产物。 把它当代码来维护——而不是当文案来润色——这就是 System Prompt 驯服模型的全部秘密。
总结
- System Prompt 是系统契约,不是开场白。 它的每一行都应服务于四要素之一:角色(视角 + 权限 + 验收标准)、约束(铁律 + 红线)、工具(能调什么)、边界(做到哪停)。缺任何一个,都会以可预测的方式翻车。
- 四个工程手法:角色设定要落到「视角 / 权限 / 验收」而不是语气;约束要用「禁止」+「可检查条款」,而不是「请尽量」;few-shot 给样例,结构化输出给 schema,两者都要先问「难点是做什么还是长什么样」。
- 防跑偏靠两手:软的是把自检写成 checklist 而不是段落,并用「停止重读规则」把自我辩护反转成执行信号;硬的是把关键约束放到运行时(权限、hook 拦截),不依赖模型自律。
- Skill 注入解决了提示词过长的元问题。 把大 system prompt 切成按需注入的模块——常驻的极薄(只存索引 + 指针),注入的才厚重(命中才读全文)。
description是语义锚点,让模型自己完成「自然语言 → 正确能力」的映射。
章末提问
追问 1:System Prompt 的本质是什么?一份合格的它要覆盖哪几件事?
结论先行:它不是开场白,而是一份系统契约,必须覆盖角色、约束、工具、边界四要素。 因为:它每轮请求都被原样重发、永远排在最前,回答「你一直是谁、手上有什么、不能干什么」。四要素缺一个都会以可预测的方式翻车:缺角色退化成语调助手、缺约束自由发挥超纲、缺工具编造能力、缺边界不知道什么时候停。
追问 2:角色设定为什么强调「视角 + 权限 + 验收标准」,而不是「资深、严谨」?
结论先行:形容词是语气,不提供取舍标准;视角 / 权限 / 验收才是能落到「下一个 token」上的约束。 因为:模型读到的每句话最终要变成「下一轮怎么取舍」。「严谨」在「多写防御」和「少改代码」之间没有倾向,模型还是自由发挥;而视角告诉它往哪看(攻击者 vs 建造者)、权限告诉它能伸手多长、验收标准告诉它做到什么算停。语气是这三者的副产品,不是目标。
追问 3:约束怎么写才有效?「可检查」为什么是关键?
结论先行:用「禁止」写铁律、且每条都能被 grep / diff 验证,可检查才谈得上被遵守。
因为:约束写进提示词只是「请求模型遵守」,真正兑现它的是「事后能验证」。一条违反了你都发现不了的约束等于没有。所以「无裸 except: pass」可检查,「不要写烂代码」不可检查——可检查性是「希望遵守」升级为「能验证遵守」的关键一跃。
追问 4:为什么「请返回 JSON」没用,非要在 API 层声明 schema?它和工具调用什么关系?
结论先行:schema 在生成层画死字段边界,运行时校验重试;工具调用本身就是最强的结构化输出。
因为:模型是预测 token 的机器,一句软提示词它照样会吐前缀、包 Markdown、漏字段。schema 把「格式正确」从「祈祷」变成「约束」;而 tool_calls 要求输出必须匹配 parameters 的 JSON Schema,本质是把模型从「自由生成」关进「受约束的生成」——两者是同一件事。
追问 5:能力说明书太多塞不下,怎么解决提示词过长的元问题?
结论先行:切成 skill 按需注入——常驻层只留 name + description 索引,命中才读全文。
因为:全塞进去会 token 爆炸、注意力稀释、指令冲突,根因是「常驻信息量超出模型能稳定把握的范围」。解法不是写得更精炼,而是改变装载方式:把静态大 prompt 拆成动态按需加载的模块,靠语义匹配做路由。这跟「模块索引 + 按需 import」是同一个权衡。