跳转到内容
Kewei Yan
返回

让 Agent 自己决定什么时候加载技能

写 coding agent 的人早晚会撞上一个问题:技能(skill)到底是塞进 system prompt,还是让 Agent 自己决定什么时候要。

我们最近把技能加载从「自动注入 system prompt」切到了「让 Agent 按需 tool call」。改完 token 省了一大截,Agent 的行为也更可控。这里把设计思路和取舍记一下。

技能是什么

技能就是给 Agent 的专项指令。比如「排查报错用这套流程」「写前端页面按这个规范来」。技能本身不跑代码,就是一段 prompt,告诉 Agent 在这种场景下该怎么想、怎么做、注意什么。

问题在于:这东西什么时候送到 Agent 手里。

自动注入,简单但贵

最直接的做法是自动匹配。用户一发消息,系统拿关键词去撞技能列表,撞上了就把技能全文塞进 system prompt。

比如用户问了句「帮我看一下这个报错」,系统识别到「报错」匹配了排查技能,几百行的排查流程直接打进 system prompt。不管 Agent 这轮用不用,它都在上下文里了。

好处是省事。不用给 Agent 多加一个 tool,也不用让它学会怎么加载技能,用户发完消息系统自己搞定。

问题有三个:

浪费 token。一个 coding agent 通常十几套技能,加一块可能上万字。用户问「帮我格式化这段代码」,系统匹配到三五个技能,全会注入。这轮对话 Agent 可能一个都没用到,但每个技能都在占上下文。

行为不可控。技能注入是静默的,用户看不到。Agent 输出变了,到底是技能生效了、还是模型自己想出来的,很难讲清楚。调试的时候也是一笔糊涂账。

长对话更糟。第一轮自动注入的技能内容留在 system prompt 里,后续每一轮都复用。用户第一轮问了排查报错、第二轮转头开始写前端,排查技能还赖在上下文里不走。

匹配是怎么做的

旧方案里,匹配发生在系统侧。用户消息过来,走一个 skills.Match,拿关键词去撞技能名称和描述,撞上了就把技能全文注入 system prompt。

这个匹配是纯规则驱动的。用户说「报错」,能撞上排查技能;用户说「帮我写段文案」,能撞上写作技能。但用户说「这段代码跑起来慢了,帮看看」,可能什么都撞不上,也可能撞上好几个。

规则匹配最大的问题是:它不知道用户真正想干什么。同一句话,用户可能在要代码评审,也可能在要性能分析,也可能只是想聊聊。规则没有语义理解,它只是撞字。

按需加载把这个环节交给了 Agent 自己。system prompt 里放着技能清单,每个技能一行描述。Agent 读过用户的消息,自己判断:

没有匹配函数了,没有关键词撞库了。Agent 靠语义理解做判断,跟它判断要不要读文件、要不要跑测试是同一套能力。LLM 看十几行技能清单,理解每个技能是干什么的,跟理解「这个用户想要什么」用的是同一个推理链路,不需要额外训练或调参。

按需加载,Agent 自己开口要

改法不复杂:不自动注入了。system prompt 里只放一份技能清单,每个技能一行,带上名称和简短描述。Agent 觉得需要的时候,自己调 loadSkill 工具加载完整内容。

## Available Skills
- check: 代码评审,合并前检查
- design: 做页面、做 UI
- hunt: 排查报错、定位根因
- write: 改写文档、去 AI 味

Agent 看到用户说「帮我审查这段代码」,判断自己需要 check 技能,发一个 tool call loadSkill("check"),系统返回完整的技能内容。后续对话里它就有了这份专项指令。

技能要不要进上下文的决策权,从系统手里交到了 Agent 手里。

几个取舍

技能清单本身也占 token。十几行清单大概一两百 token,跟动辄几千字的技能全文比是小头。我们放清单是因为 Agent 得知道有哪些可选,不然没法主动加载。如果技能数量很少(三四套以下),这个开销几乎可以忽略。

手动激活路径还在。有些场景用户明确知道要用哪个技能,不需要 Agent 猜。skill: 命令保留,用户直接敲 skill:write,技能全文注入 system prompt,跟之前一样。

加载后技能内容不走 system prompt。loadSkill 返回的内容出现在对话消息里,不在 system prompt 里。原则上技能更适合待在后者的位置,但实测下来 tool result 也够用,Agent 能正确遵循技能指令。唯一的区别是系统重置后需要重新加载,但这是语义上合理的行为。

效果

切完之后 system prompt 从几百行变成几十行,不再有匹配不匹配的问题,每次都是固定大小。

Agent 用技能的决策变透明了。看对话记录能知道它在哪个节点主动加载了哪个技能,什么时候用的。

用户那头没感知变化,正常对话。

什么时候不适用

按需加载依赖 Agent 的 tool call 能力。如果模型本身不支持 function calling,或者 tool call 的准确率很低,自动注入可能更稳。

另外,如果技能数量很少(三四套)、每套都很短,自动注入的 token 开销不大,加一层 tool call 反而是多此一举。

还有一个边界:有些技能 Agent 不认识就不会主动加载,但用户可能觉得它应该默认带着。这种情况需要在技能描述上多花一点功夫,让 Agent 知道什么场景该加载什么。


选哪条路取决于 Agent 架构更在意什么。在意省 token、行为可控,按需加载更合适。在意简单、不想加复杂度,自动注入也够用。我们选按需加载,是因为十几套技能全塞进 system prompt 的话,一轮对话光技能就占了上下文大半,留给代码和对话的空间太少。Agent 只加载真正要用的那几个,省下来的空间够它在复杂任务里多撑好几轮。


分享这篇文章:

上一篇
给 Coding Agent 设计 Tool:从能跑到好用
下一篇
从零做一个 Coding Agent:我这几天真正学到的东西