从Prompt膨胀到Agent失控:Skills机制如何重构大模型知识管理

发布时间:2026/10/22 16:41:25
从Prompt膨胀到Agent失控:Skills机制如何重构大模型知识管理
上周我做了一件很蠢的事给内部客服助手加第七条退款规则。改完之后跑回归测试挂了三个用例其中一个两周前刚验收通过。把提交记录翻出来一算这条系统提示词已经膨胀到1200行每次加功能都是同一个剧本——改一处坏一片。这不是我一个人遇到的麻烦。过去一年里围绕大模型应用为什么 Prompt 和 Agent 都无法 Scale几乎是所有从原型走向生产的人都会撞上的墙。Prompt 把规则全塞进上下文Agent 把决策全部交给模型运行时看起来都解决了问题但规模一上来二者都以不同的方式崩掉。直到我认真拆了一遍官方推出的 Skills 机制才意识到真正的病灶不在我们写得不够好而在于我们选错了知识存放的位置。这篇文章想把背后的设计范式讲透它的触发机制、渐进披露、文件系统即知识库以及我们如何把这套思路移植到自己的项目里。1. Prompt 从 5 行膨胀到 500 行之后我看见了三个物理瓶颈1.1 注意力是稀缺资源超长上下文喂不饱规则先聊最容易被忽视的一点模型上下文窗口确实在变大但注意力并不会等比变多。业界反复验证过lost in the middle现象——关键信息放在超长文本的中间位置召回率会明显下降。换句话说规则行数越多每条规则实际分到的注意力权重就越低。我自己做过一次对照实验同一个任务800 行的规则压到 250 行之后准确率不降反升。原因很讽刺——被挤在中间的那两百行规则以前根本没被执行过。它们只是存在于上下文里白白占据了 token还干扰了其他规则的判断。打个生活化的比方你的桌面就那么大把所有手册全摊开在上面想找某一条规则的效率反而比只放三五页更低。上下文窗口放得下和放得下之后依然好用是两回事。1.2 规则冲突没有任何优先级机制只能人肉扫视第二个硬伤是隐式冲突。第 17 条说用户提到退款一律转人工第 23 条说有退款意向的用户直接发送表单。两条规则同时存在模型听谁的Prompt 里没有一个字段叫 priority没有任何内置仲裁机制只能靠人来查。人怎么查在 1200 行里上下扫视自己脑补冲突的可能。这不就是当年在老代码里改全局变量的感觉吗改了一处不知道哪里会被连带影响。而且这种冲突不是静态的——模型最终听谁取决于它对整个上下文的权重权衡输入一变结论可能就变。最折磨人的是回归策略。每次加规则都得全量测试因为冲突是隐式的你没法像单元测试一样只测新增的那部分。Prompt 的维护成本是超线性增长的功能翻一倍回归工作量翻的可不止一倍。1.3 token 成本是乘数级增长买到的却是更差的性能最后还得算钱。Prompt 膨胀的成本计算很简单每条请求都要发送全部系统提示词。假设一个 prompt 从 500 token 涨到 5000 token架上 1000 并发、每日 100 万次请求成本直接翻 10 倍。更难受的是这 10 倍成本买的不是更强的能力而是更差的注意力分配。我见过不少团队把这个问题归结为模型不聪明其实根本不是——你把规则写成那样再聪明的模型也发挥不出来。所以 Prompt 不是不能 Scale而是 Scale 的方向错了。它 Scale 的是规则的体积可我们真正需要的是 Scale 功能的数量。2. Agent 也没给出解药把决策交给运行时之后失控的代价更贵2.1 状态空间爆炸模型自由发挥的每一小步都在产生新路径Prompt 解决不了很多人转向 Agent。Agent 的主流形态是 ReAct 风格循环模型根据当前观察决定下一步工具调用。单看每一步都是合理的但组合起来的状态空间大到没法测。一个任务执行 10 步每一步假设只有 20 种合法选择那就是 20 的 10 次方条可能路径。没有人的测试集覆盖得了这种规模。我做过一次记录一个文件整理任务正常路径 8 次工具调用但有一次因为日志格式异常Agent 进入了解析失败→换个工具→又失败→尝试读原文件→发现权限不对→再换思路的循环跑了 37 次调用才停下结果还是错的。这不是模型笨而是这个架构天然允许自由探索。自由探索在真实数据的噪声面前根本无法保证收敛。2.2 不可复现是调试的噩梦同一个问题跑两次路径完全不同Agent 调试困难的核心原因是采样随机性叠加状态路径。第一次跑模型选了 A 分支第二次遇到同类输入它选了 B 分支。日志里明明有一长串工具调用记录但你很难判断哪一步开始走歪了。这种感觉就像让一个实习生自己摸索流程——他第一次成功了你说不清他是真懂了还是蒙对的他第二次失败了你也不知道他错在哪一步。你从头教他他还觉得自己的思路没错。我在实际调试中量化过这个成本一次故障排查要翻至少 50 条工具调用记录其中一大半是无关紧要的旁路。人肉找问题效率极低这也是很多团队把 Agent Demo 做得挺好、一上生产就崩的根本原因——Demo 环境的数据太干净路径太少问题暴露不出来。2.3 循环里的上下文费用比 Prompt 更失控Agent 模式里有一个很少被算清楚的成本循环中每一步都要重新发送完整上下文——对话历史、工具结果、中间推理全得带上。一旦工具调用出错触发重试成本是成倍涨的。我见过一个只需要三个工具调用的简单任务因为一次工具返回格式不符合预期触发了四轮重试最后烧掉 10 万 token。这不是偶发事故而是概率必然循环步数越多接触坏数据的概率越大重试链就越长。所以 Agent 把 Scale 的问题藏得更深了。表面上它不需要你维护规则但决策路径、测试边界、成本模型全部跟着模型运行时一起变得模糊。你失去的不是控制是知道自己在失控的能力。3. Skills 的设计核心把如何做从上下文里搬到文件系统里3.1 一个 Skill 就是一个文件夹SKILL.md 加上辅助脚本先描述基本形态。官方对 Skill 的定义很朴素一个文件夹包含一个 SKILL.md 文件以及任意你需要的辅助脚本、模板、数据文件。SKILL.md 开头是 YAML 格式的 name 和 description正文用 Markdown 写这个技能的完整用法适用场景、操作步骤、示例、注意事项还可以引用同目录下的脚本和子文件。这个文件夹的魔力在于它让技能变得像代码一样可以被管理——进 git、做版本化、走 code review、单技能独立测试全部成为可能。这在 Prompt 时代是不可想象的因为 prompt 是一坨文本块不是可组合的组件。3.2 渐进披露模型不需要背词典只需要知道怎么查词条整个设计里最值得琢磨的是渐进披露。模型第一眼只会看到每个技能的 description——一两句话相当于图书馆里的索书卡。只有当模型判断当前任务确实需要某个技能时它才打开完整的 SKILL.md。如果还不够正文可以再引导它去读 rules/01_disk_full.md 这种子文件。这个机制把上下文占用从所有技能全文之和降到了所有技能摘要之和加上实际用到的技能全文。系统里有 50 个技能时常态下只有几百 token 的 description 可见而不是把 50 个技能的正文全塞进窗口。这正是对 Prompt 范式最直接的回应知识仍然存在但它只在被需要的那一刻进入上下文。用的时候才加载不用的时候不占地方。3.3 触发条件是整个范式的命门渐进披露有个代价什么时候触发加载完全由模型自己判断。所以 description 写得好不好直接决定这个技能是否存在。我踩过的坑是第一版 description 写得太抽象模型遇到具体的日志文件时根本不会加载因为分析日志这个描述太宽泛了模型不知道它是不是当前任务的最佳选择。后来我改成当用户提供系统日志、磁盘告警、内存不足、OOM、进程崩溃等描述时使用触发率才明显上去。用一句话总结我的经验description 不是自我介绍是搜索索引。它要覆盖的是用户会怎么说出这个需求而不是这个技能能干什么。3.4 范式对比知识的位置决定了一切维度PromptAgentSkills知识存放位置上下文常驻模型运行时推导文件系统按需加载新增功能的成本修改全局文本隐患大新增自由工具路径更难预测新增一个文件夹可测试性全量回归路径穷举困难单技能独立测试成本模式每次请求全量 token循环中上下文反复重发常态只有摘要 token调试方式人肉读 prompt翻工具调用日志看技能是否触发、是否走对分支所以 Skills 能 Scale 的根本原因是把知识拆成了两层索引层description和实现层正文脚本子文件。索引层常驻实现层按需加载。功能增加等于新增索引条目不修改任何已有内容上下文成本被锁定在摘要级别。4. 一个完整示例我把服务器日志分类从提示词改成了技能4.1 先把问题拆成规则集再翻译成目录我挑了一个特别常见的任务来说实操服务器日志故障分类。传统 prompt 写法是在系统提示词里写 20 条 if-then 规则——如果日志里出现 No space left on device判断为磁盘满等等。写到后面又臭又长诊断顺序也表达不出来。改成技能之后我先把故障类型拆开磁盘满、内存泄漏、OOM Killer、慢查询、进程崩溃。每个类型单独一个文件放在 rules/ 目录下由 SKILL.md 正文统一指挥诊断流程先看磁盘再看内存再看 CPU最后查应用层。4.2 SKILL.md 的具体写法和加载逻辑直接贴一个简化后的模板--- name: server_log_triage description: 诊断服务器日志中的故障根因。当用户提供系统日志、错误日志或描述服务器异常磁盘告警、内存不足、OOM、进程被杀、接口超时时使用。 --- # Server Log Triage ## 适用场景 - 用户上传日志文件或直接贴出日志片段 - 用户描述服务器异常需要定位根因 ## 使用流程 1. 先运行 scripts/parse_log.py 日志路径获得结构化指标。 2. 根据指标输出决定下一步 - 磁盘使用率超过 90%读取 rules/01_disk_full.md - 内存持续增长读取 rules/02_memory_leak.md - 日志包含 OOM Killer读取 rules/03_oom_killer.md 3. 得出根因后按根因 / 证据 / 建议动作三段式输出。 ## 注意事项 - 不要直接读原始日志全文先让脚本做汇总 - 脚本执行失败就停止诊断请用户检查文件权限注意第 2 步的写法不是把每条规则完整写出来而是告诉模型什么情况读哪个文件。真正的规则细节在子文件里只有走到那个分支才会消耗 token。这才是渐进披露的正确姿势——SKILL.md 本身也只是一个入口。4.3 脚本负责确定性操作模型只做判断Skill 里不能只有文字脚本要接下确定性的脏活。parse_log.py 做的事情很朴素读日志文件、提取时间戳、统计 ERROR/WARN 等级、把磁盘和内存相关关键字做成结构化 JSON。模型拿到 JSON 之后再做语义判断——而不是让它去数日志行数那是它最不擅长的事。脚本入口我加了一层强制参数校验import argparse parser argparse.ArgumentParser() parser.add_argument(log_path, help待分析的日志文件路径) args parser.parse_args()为什么这么严格因为模型在调用脚本时偶尔会自己猜参数你不约束它就敢传一个不存在的路径。脚本发现自己干不了就该立刻报错把控制权交回给模型——绝不能硬着头皮输出垃圾结果那会带偏整个诊断链路。4.4 测试一个技能的正路和歪路我测技能通常走两条路径。一条叫剧本测试构造典型日志文件逐个验证分支——磁盘满的文件必须触发 01_disk_full.md内存泄漏的文件必须触发 02_memory_leak.md。走过一遍置信度就有了。另一条叫对抗测试丢一个没有明显特征的正常日志进去看模型会不会错误加载技能。这条最容易被忽略但我后来发现它比剧本测试更重要——description 写得太宽泛模型什么任务都试着加载token 照烧结论还都是未发现异常。触发对了是成本优化触发错了是成本浪费加误导。5. Skills、MCP、Prompt、Agent四者的分工边界5.1 MCP 管数据连接Skills 管领域智慧MCP 和 Skills 经常被放在一起讨论但它们解决的是完全不同的两层问题。MCP 解决的是模型能不能访问数据——数据库、API、文件系统这些外部资源怎么连进来用协议统一掉。Skills 解决的是模型知道怎么干活——面对这些数据应该按什么顺序、用什么方法、调用什么工具来完成任务。一个有 MCP 但没有 Skills 的系统模型能查数据库、能调 API但不知道该按什么 SOP 走一个有 Skills 但没有 MCP 的系统方法齐全但没有任何数据入口。真实项目里两者是配合关系MCP 把运维平台接进来Skills 提供故障处理的流程规范。5.2 什么时候别用 Skills二三十行规则就别折腾Skills 不是银弹。如果一个任务只有二三十行规则、场景单一、很久不变直接写 prompt 反而更划算——建目录、写 SKILL.md、做测试全是成本这些成本可能永远收不回来。我自己定的判断标准是规则超过一屏大约 150 行并且会持续演化才值得技能化。150 行以下直接写 prompt省心。5.3 什么时候仍然需要 Agent开放探索加混合范式真正的开放式探索任务比如帮我研究这份协议可能有哪些安全隐患没有任何固定流程可言这种场景 Agent 仍然合适。但我的建议是混着来把确定性的部分——扫描、解析、格式化输出——做成技能把探索方向交给 Agent。这样做的价值是把变量控制住技能负责必然Agent 负责可能。一条链路里的确定性部分越多整体就越可测试、可调试、可预算。5.4 把渐进披露移植到自己的系统里不是所有人都能直接用官方生态。但渐进披露这个设计思想完全可以在自己的应用里实现每次请求时先给模型传一份技能清单——名称加摘要总共几百 token模型声明需要某个技能时你再把完整内容注入上下文。加载时机由你自己控制成本模型就是可控的。技能目录可以直接进 git、走代码评审。这比改 prompt 文本先进太多了——你终于可以用软件工程的方法来管理模型行为而不是把希望寄托在一段不断膨胀的文本里。6. 实践中的翻车记录与三条铁律6.1 description 写不好技能等于不存在这个坑我反复踩。早期写技能description 用的是可用于分析日志这种描述。结果模型看到具体日志时就是不触发——因为分析日志太宽泛了模型无法确定这个技能是不是当前任务的最佳选择。后来我把用户可能出现的各种说法都列进去日志乱服务器挂了磁盘满了进程被杀了。触发率立刻上来了。这事的本质是模型是拿 description 做匹配的你得站在用户怎么说话的角度去写而不是站在我这个功能多厉害的角度去写。6.2 粒度不是越细越好越细越像新的规则冲突有人把所有判断分支都做成独立技能结果模型处理一个任务时频繁切换上下文被拆得七零八落description 集合本身成了新的冲突点。这等于把 Prompt 的规则冲突换了个形式搬了回来。我的原则是一个技能对应一个能说出口的完整任务——诊断服务器日志是一个技能判断磁盘是否满了不是。粒度太细时技能之间又会出现优先级、重叠、边界问题只是换了一种存在形式。6.3 技能里的脚本是代码得走安全边界技能脚本会接触用户数据这是个经常被忽略的风险点。用户传一个恶意构造的日志文件脚本如果没有边界检查可能把不该执行的指令带起来。我在项目里对脚本做了三条硬限制只允许读取和统计不允许执行任意命令所有输出在返回给模型之前必须过滤防止注入指令从侧面溜进上下文SKILL.md 和脚本本身都走代码评审。技能不是提示词了它是程序就要按程序的标准管。6.4 三条铁律折腾了这么久我把经验压缩成三条铁律现在团队里新加技能都按这个来第一description 按搜索引擎关键词标准写覆盖用户原话的变体。第二一个技能只做一件事一件大到值得单独交付的事。第三确定性操作全部交给脚本模型只做判断与表达不负责数数和解析。这三条看起来简单但每一条背后都有翻车案例撑着。我现在搭建技能库的方式已经彻底从写提示词切换到建系统了。它不解决所有问题但它终于让功能变多不再意味着上下文变乱。