做 Agent 开发的人大概都经历过这样一个阶段:为了让模型"懂行",我们把能想到的领域知识全部塞进系统提示。我第一次做 Claude Code Agent 的时候也是这样,把公司的计费规则、售后流程、常见问题、内部规范整理成一份八千多字的指引,全部塞进系统提示里,觉得这样 Agent 就"无所不知"了。结果呢?第一次跑通 demo 的时候确实惊艳,但越往后越不对劲——上下文越用越满、响应越来越慢、成本越来越高,更糟糕的是,它开始在一些基础问题上犯低级错误。
后来我才想明白一个很朴素的道理:系统提示不是知识库,它是决策框架。领域知识应该按需加载,而不是全塞进去。这篇是"Learn Claude Code Agent 开发"系列的第五篇,专门聊按需技能加载这个话题——它到底解决了什么问题、底层逻辑是什么、怎么落地、有哪些坑。适合正在用 Claude Code 搭 Agent、或者被上下文爆炸和提示词维护折磨过的开发者参考。
1. 为什么"全塞系统提示"会把 Agent 拖垮
先说结论:把领域知识全塞进系统提示,不是"笨"的问题,而是违反了大模型应用的一条基本经济原则——系统提示里的每一条内容,都会在每一次请求中重复参与计算。这条原则决定了"全塞"这个方案的不可持续性。
1.1 上下文账单:你为"可能用到的知识"付了"每次调用"的钱
假设你的系统提示塞了 8000 个 token 的领域知识。在 Claude Code 跑一个任务的过程中,Agent 往往会经历多轮"思考 → 调用工具 → 观察结果 → 继续思考"的循环。一个稍微复杂的任务,二十轮交互不算夸张。每一轮交互,系统提示的 8000 个 token 都是固定开销,跟着每一轮请求一起发送、一起参与计算。
这意味着什么?你为 8000 个 token 付的不是一次钱,而是二十次钱。跑一个任务,光系统提示的"运输成本"就是 8000 × 20 = 160000 token 的计算量。而这 8000 个 token 里,真正对这个任务有用的可能只有 2000。剩下 6000 个 token 属于"带了但没用到"的行李,却每轮都在拖慢响应速度、推高成本。
用大白话打个比方:你请了一个顾问,他从第一天起就拖着一个装满所有领域资料的大行李箱,不管你今天聊的是芝麻还是西瓜,他都得先拖着这个箱子走到你面前。这个"拖着箱子走"的动作,每次对话都在重复,而箱子里大部分资料今天根本用不上。
所以,全塞系统提示的第一个问题不是"效果不好",而是成本结构极其不健康。任务越复杂、交互轮数越多,这个固定开销被放大的倍数就越高。我在一个内部项目里实测过,同样的任务,系统提示从 8000 token 降到 1500 token 之后,单任务的 token 消耗直接降了 30% 以上,响应速度也明显快了。
1.2 指令稀释:规则一旦超过三条,第四条就开始失效
把系统提示当成一个装知识的容器,还容易踩中另一个问题:指令稀释。
大模型遵循指令的能力是有限的,尤其在提示词很长的时候。研究员早就发现过一个被称为"lost in the middle"的现象——处在提示词中段的内容,被模型遵循的概率明显低于开头和结尾。当你把 8000 字的领域知识塞进去,真正重要的行为准则("遇到歧义必须追问""不要臆造参数""输出必须附引用来源")会被淹没在大量参考资料里。模型不是不聪明,而是它真的分不清:哪些是必须遵守的铁律?哪些是遇到问题时再查的资料?
这个问题的后果很隐蔽。它不会让你的 Agent 直接崩溃,而是会微妙地降低一致性。比如你明确写了"所有退款金额必须四舍五入到分",但紧接着后面就是三四段计费规则细节,模型在处理时可能把这条准则当成"背景知识"而不是"核心约束",偶尔忽略掉。这种"偶尔犯错"在 Agent 场景里是最难排查的——它不规律、不可复现、看起来像随机抖动,实际上是你把约束和参考信息混在了一起。
打个比方:你把交规和驾校教材装订在同一本书里,让司机每次开车前从第一页读到最后一页。司机不是不想遵守交规,而是在 8000 页的阅读量里,"红灯停"这条规则和"某路段施工绕行"的公告混在一起,优先级被拉平了。
1.3 维护噩梦:知识一变,整个提示词就要返工
领域知识是高频变化的东西。计费规则改了、售后流程调整了、新版产品上线了、某条政策到期了,任何一个变化都意味着你要去动那篇巨型提示词。
改一次大型提示词是什么体验?首先,你要在八千字里定位到准确的位置,这本身就不容易。其次,改完你要重新验证全量功能,因为你没法确定这次改动会不会和提示词里其他内容产生冲突。改多了还容易改出隐蔽矛盾——旧规则没删干净、新规则又覆盖上去,模型到时候听谁的?规则之间互相打架,Agent 的行为就完全不可预期。
我在实际项目里见过最惨烈的一次:某次维护时把两条计费规则写成了互斥状态,Agent 开始在不同任务里给出完全相反的答案。排查了一个下午,最后定位到是提示词内部的逻辑冲突——两条规则在文档里相隔不到两百字,但没有任何一方提到"本规则不适用于另一条规则覆盖的场景"。这段经历让我彻底明白:巨型系统提示是有维护债的,而且这个债是复利计算的。
2. 按需技能加载的核心思路:把知识从提示词里搬出去
既然全塞的问题这么明显,那正确的做法是什么?核心思路其实一句话就能说完:把"行为准则"和"领域知识"分开,行为准则留在系统提示里,领域知识变成按需加载的技能文件。
2.1 系统提示只留决策框架,不负责背书
系统提示的最佳状态,是你在 1 分钟内能读完的东西。它应该只包含:
- Agent 的角色定位(你是谁、替谁干活)
- 核心行为准则(遇到歧义怎么办、输出格式要求、安全边界)
- 决策框架(什么时候该调用什么工具、什么情况下要停下来追问)
- 少量必须每次都知道的全局约定(比如项目路径、常用命令)
换句话说,系统提示回答的是"你该怎么做事",而不是"领域里有哪些知识"。知识是会变的,做事的方法相对稳定。把稳定的决策框架放在系统提示里,把易变的领域知识放在技能文件里,两边各自独立更新,互不干扰。
我自己的经验是:系统提示压缩到 1500 token 以内是一个比较健康的目标。如果压缩到这个量级还有内容塞不下,那说明塞不下的那些内容大概率本来就该属于技能文件。
2.2 领域知识进技能文件,变成可触发的资产
Claude Code 里有一个很实用的技能机制:你可以在项目里建一个技能目录,把每一块领域知识整理成独立的技能文件。每个技能包含一份描述文件(说明这个技能是干什么的、什么时候该用)和具体的知识正文(操作步骤、规则细节、注意事项)。
关键在于,这些技能文件不是系统提示的一部分。它们平时躺在目录里,不占上下文。只有当 Agent 判断当前任务和某个技能的描述匹配时,才会把那个技能的文件读进上下文里。这就好比一个专家不会把整本法规汇编背下来,但他清楚地知道"遇到退款争议该翻哪一章,遇到发票问题该翻哪一章"。
这种设计的好处是显而易见的:上下文里永远只有当前任务真正需要的知识。任务 A 只需要退款规则,那就只加载退款技能;任务 B 涉及物流时效,那就只加载物流技能。互不干扰,各自清爽。
2.3 不是让它背,是让它查:加载的本质是一次工具调用
理解按需加载,最关键的一点是转变心态:你不需要让 Agent"记住"领域知识,你只需要让它"知道去哪里查"。
在 Claude Code 的交互模型里,技能加载本质上就是一次文件读取操作。Agent 面对任务时,先浏览技能目录,看看有哪些技能可用、各自的描述说了什么,然后根据任务内容决定读取哪个技能文件。这个"决定"本身就是模型推理的一部分——它不需要事先知道技能文件里写了什么,只需要理解"这个任务的场景和那个技能描述的场景是匹配的"。
这也意味着,技能加载是天然可观测、可调试的。Agent 读了哪个文件、读了几次、读完有没有用到,都可以通过日志看到。相比之下,全塞系统提示是一条"黑盒路径"——你永远不知道那 8000 个 token 里到底哪些真正影响了行为。
3. 技能资产库怎么组织:目录、描述与加载链路
说完了思路,来看具体怎么落地。技能资产库的组织方式,直接决定了按需加载的命中率。组织得不好,技能机制反而会成为新的负担。
3.1 SKILL.md 的规范结构:description 才是触发信号
每个技能对应一个目录,目录里必须有一个入口文件(常见的命名是 SKILL.md)。这个文件由两部分组成:YAML 格式的元信息和 Markdown 格式的正文。
元信息里最关键的两个字段是名称和描述。名称不用多说,描述才是真正的触发信号。描述的质量,直接决定了这个技能被正确加载的概率。写描述的时候,最重要的不是"这个技能包含什么",而是"什么情况下该用这个技能"。
举个对比例子。一个退款规则技能,如果描述写成"包含退款计算规则、退款状态说明、特殊场景处理",这是一份目录式的描述。Agent 看到之后,能理解"哦,退款相关的东西在里面",但遇到具体问题时,它未必能把"用户问订单金额不对"和"退款规则技能"关联起来。
更好的写法是场景式的:"当用户询问退款金额计算、订单部分退款、退款状态异常、退款时间预估,或需要判断某笔订单是否满足退款条件时,使用此技能。不适用于换货、补差价场景。"这种描述给了 Agent 明确的匹配条件,还给出了反向排除条件,命中率会高很多。
3.2 知识文件的分层:入口文件、参考资料与示例
单个技能目录内部的资源,不建议全部堆在 SKILL.md 里。SKILL.md 只是入口,它的作用是给 Agent 一个"处理该类任务的决策路径"。具体细节应该分层存放:
- SKILL.md:入口文件,包含任务处理的流程框架、关键判断条件、异常情况处理逻辑
- references/:参考资料目录,放详细的规则文档、政策原文、内部规范
- examples/:示例目录,放典型输入输出对、历史处理案例
- templates/:模板目录,放输出格式模板、回复话术模板
这样的分层有个好处:Agent 加载技能时,先只读 SKILL.md,形成一个"这个任务的通盘处理思路";在执行过程中如果遇到具体细节问题,再按需去读 references 或 examples 里的对应文件。这就是"加载的加载"——连技能内部的知识都做到按需,而不是一次全读出来。
我在实践中的体感是:一个技能目录的入口文件控制在 300-600 行以内,知识细节尽量下沉到子文件,是最舒服的状态。入口太长的技能,加载起来还是会拖慢上下文;入口太短的技能,又容易让 Agent 在处理任务时"不知道下一步该干嘛",然后频繁去翻子文件,反而增加了调用次数。
3.3 一条完整的加载链路长什么样
把整个机制串起来,一个按需技能加载的典型链路是这样的:
- 用户向 Agent 提出一个任务,比如"客户 A 的订单申请了部分退款,帮忙算一下应退金额"
- Agent 在自己的决策框架指引下,判断这个任务需要领域知识支持
- Agent 浏览技能目录,看到退款规则、发票规则、物流规则等多个技能的描述
- Agent 把任务关键词和技能描述做匹配,选中退款规则技能
- Agent 读取退款规则技能的 SKILL.md,获得处理退款类任务的流程框架
- 执行过程中遇到具体计算细节,再读取 references 里的计算规则文档
- 任务完成,这部分知识不再占上下文,下一个任务重新匹配
这个链路的精髓在第 4 步:匹配是靠任务场景和技能描述之间的语义匹配完成的,而不是靠硬编码规则。这意味着你不需要在系统提示里写"如果遇到退款就打开展退款技能"这种脆弱的条件判断,模型自己就能完成这个映射。这也意味着,技能描述写得越好,这个链路就越可靠。
4. 一次真实改造:售后服务 Agent 从 8000 token 提示词到按需加载
理论讲了一堆,还是用一个真实的改造案例来收收尾。这个案例是某公司的售后服务 Agent(模拟项目,内部代号"售后帮"),我从接手到完成改造大概花了三周,效果非常直观。
4.1 改造前的症状:慢、贵、答非所问
这个 Agent 最初就是"全塞"方案的典型:系统提示里塞了 8000 多 token 的售后知识,涵盖退款、换货、发票、物流、优惠券、投诉升级六大块。它上线后陆续出现了三类症状。
第一是慢。一次普通的售后咨询,响应时间经常要十几秒,遇到复杂任务甚至更久。第二是贵。月底一看 token 消耗账单,发现很大一部分都花在"运输"系统提示上。第三是更隐蔽的答非所问:用户问退款,它突然蹦出物流规则;用户问发票,它又扯到优惠券叠加。虽然每一条单独看起来都"沾边",但组合在一起就是不对路。
我还做了一个很小的统计:随机抽取 50 条真实咨询记录,逐条看 Agent 的回答里有多少内容是"和本次任务无关但存在于系统提示里"的。结果触目惊心——接近一半的回答都混入了无关领域的知识。这就是指令稀释在真实业务里的样子:不是模型蠢,是它手里的规则太多了,多到它自己也分不清优先级。
4.2 拆解步骤:先分类,再抽取,后验证
改造的第一步,不是写代码,而是做知识分类。我把系统提示里的 8000 多个 token 逐条过了一遍,分成三类:
第一类是行为准则,比如"回答必须附订单号""遇到金额争议先安抚再核实""不确定时不得臆造规则"。这类内容留下,总共压缩到大约 1200 token。第二类是领域参考知识,比如退款计算规则、发票开具标准、物流时效承诺。这类内容全部搬出去。第三类是重复和冗余信息,比如同一规则在多个地方出现、新旧版本的规则混在一起,这类内容直接删除。
第二部,把搬出去的领域知识按业务域拆成五个技能:退款规则、换货规则、发票规则、物流规则、优惠券叠加规则。每个技能按前面说的结构组织,SKILL.md 里写处理流程,references 里放具体规则,examples 里放典型问答对。
第三步是最容易忽略的验证环节。我没有直接全量替换,而是先让新旧两个版本的 Agent 并行跑同一组测试任务,对比输出。这组测试任务覆盖了六类售后场景、每个场景 5 到 10 条典型问题,总共四五十条。对比下来发现,新版在绝大多数任务上的表现不低于旧版,但在跨领域的任务上(比如"退款之后用户又问发票怎么办")表现明显更清晰——因为它不会被无关知识带偏。
4.3 改造后的数据对比:省下的不只是 token
改造完成之后,我记录了半个月的数据,和改造前对比:
| 指标 | 改造前 | 改造后 |
|---|---|---|
| 系统提示大小 | 约 8000 token | 约 1200 token |
| 单任务平均上下文占用 | 约 12000 token | 约 8000 token |
| 单任务平均 token 消耗 | 约 35000 token | 约 24000 token |
| 答案与任务相关性(人工抽检) | 约 70% | 约 90% |
| 规则更新后回归验证耗时 | 2 小时起 | 30 分钟内 |
数据背后的逻辑很清楚:系统提示瘦身之后,每一轮交互的固定开销都降下来了,叠加起来就是单任务 token 消耗 30% 以上的降幅。更关键的是相关性和一致性上来了,跨领域串场的问题基本消失。而维护体验的提升更是立竿见影——计费规则改动时,只需要改"退款规则"技能目录下的对应文档,跑一遍回归测试集就行,再也不用在八千字海洋里捞针。
5. 实测中的边界与三个最容易踩的坑
按需技能加载不是什么银弹,它有自己的边界。下面这三个坑,是我自己和身边做 Agent 开发的同行都踩过的,分享出来帮大家提前绕开。
5.1 触发过载:技能太容易被激活,等于没有技能
第一个坑是技能描述写得太宽泛,导致它几乎每次都被加载。比如某技能描述写的是"处理各种订单问题",那碰到任何和订单沾边的任务,Agent 都会把它读进来。结果就是,这个技能从"按需加载"变成了"事实上的全塞",上下文爆炸的问题原样回来了。
触发过载的本质是偷懒:你没有认真想清楚这个技能真正适用的边界,于是用一句大而全的话概括。解决方法是强制自己在描述里写清楚边界条件,尤其是"不适用于什么场景"。我当时是怎么做的?我给每个技能写描述时,要求自己至少写两个反向排除条件,写不出来就说明我对这个技能的边界还没想清楚。
5.2 触发失灵:描述写成目录而不是"何时该用"
第二个坑和第一个正好相反:描述写得太像目录了。"包含退款计算规则、退款状态说明、特殊场景处理"这种写法,信息量其实很低。Agent 看到这个描述,能知道"哦,退款相关的知识在这个技能里",但它未必能判断"当前这个具体问题该不该加载这个技能"。
描述的正确写法,是从"提问者的视角"出发,写清楚什么话术、什么场景、什么意图会触发这个技能。用口语说就是:这个技能是"用来回应什么问题的"。比如"当用户询问退款金额怎么算、为什么退款金额不对、部分退款和全额退款有什么区别时使用"。写完之后,我还会自己模拟十几个真实问题,逐个问自己"看到这个描述,我会不会想加载这个技能",用这种方式来检验描述的有效性。
5.3 技能之间的依赖循环与知识版本管理
第三个坑更隐蔽:技能 A 的处理流程里需要用到技能 B 的知识,但 B 的描述里完全没有提到这种联动场景。结果是 Agent 只加载了 A,处理到一半发现缺知识,再去翻 B,有时候翻了 B 又发现需要 C,来回折腾之下,加载链路乱成一团。
解决方法是显式声明依赖。在技能 A 的 SKILL.md 里明确写一句"处理此任务时,如涉及优惠券叠加,需同时加载优惠券叠加规则技能",把跨技能的知识依赖显式化。依赖声明写清楚之后,Agent 的加载路径就稳定多了。
知识版本管理是另一个容易被忽略的问题。领域知识是持续变化的,技能文件也需要自己的版本管理。我的做法是:每个技能目录的 references 下放一个变更记录文件,每次更新知识时记录变更内容、生效时间和影响范围。这样如果 Agent 突然行为异常,我能很快定位到"是哪个技能文件在什么时间点被改过"。
6. 让加载命中率持续提升的调试方法
按需加载不是一次性的改造,它和写代码一样,需要持续调试和优化。最后分享三个我一直在用的方法。
6.1 给每次加载打日志:让命中率可见
我在技能目录里设计了一个简单的调试模式:让 Agent 在处理任务时,把"浏览了哪些技能、加载了哪些技能、加载后被用于什么环节"记录下来。这些日志不需要很复杂,一两行文字就行,但积累起来价值很大。
每周末,我会把这些日志汇总过一遍,关注两个数字:技能加载率和技能有效使用率。加载率是"多少任务加载了技能",有效使用率是"加载的技能里有多少真的被用上了"。如果某个技能加载率很高但有效使用率很低,那说明它的描述有误导性,Agent 被"骗"进来读了半天没用;如果某项技能加载率很低,那说明它的描述太隐蔽,或者它对应的场景确实很少出现。
6.2 维护一组回归测试任务:改完不慌
领域知识一变,技能文件就得改。但改完之后,怎么确认这轮改动没把别的场景搞坏?我的做法是维护一组回归测试任务,大概二三十条,覆盖每个技能的典型场景、边界场景和跨技能联动场景。
每次改完技能文件,我不做人工抽检,而是直接把这组测试任务跑一遍,重点看两点:技能命中率有没有变化(比如某个本来该加载的技能这次没加载),回答内容有没有回归。跑完有异常,结合加载日志定位问题;没问题就直接上线。这套流程跑顺之后,规则变更的风险就基本可控了。
6.3 把技能当代码维护:定期评审与删减
技能文件也是代码,它需要评审、重构、删减。我曾经整理过一个从系统提示里拆出来的技能,当时觉得很有用,结果三个月过去了,它在真实任务里的加载次数一只手数得过来。这种技能就应该考虑合并进其他技能,或者干脆删除——它存在的意义不是"有这个知识",而是"这个知识在合适的场景能被正确调用",如果调用不了,文件写得再详细也是无效资产。
我给自己定了一个规则:每季度做一次技能资产评审,逐个技能看过去三个月的加载率和有效使用率,低于阈值的标记出来,要么重写描述、要么下沉合并、要么直接删掉。这个习惯坚持下来之后,我的技能资产库一直保持精简,每个技能都有它明确的、不可替代的用途。
从我自己的体会来说,按需技能加载带来的最大收益其实不是省了多少钱、快了多长时间,而是让 Agent 重新变得可预期、可维护了。全塞提示词的 Agent 像一个装满杂物的房间,你总觉得它什么都有,但真正需要某个东西时反而找不到;按需加载的 Agent 像一套分类清晰的档案柜,东西还是那些东西,但每样都待在它该待的位置,需要时一抽就出来。最后再分享一个小技巧:技能描述里的触发词,尽量用你真实业务里的高频词汇,别用书面化的抽象表达。我见过有人把退款技能描述成"资金返还流程处理模块",结果 Agent 死活不加载它——因为它和用户口中的"退钱"之间,隔了一整个语义鸿沟。