
1. 技能不是插件是Agent的肌肉记忆做Agent相关开发的人大概率都遇到过同一个尴尬明明大模型能力很强可一旦让它去执行一个包含多个环节的真实任务就会开始自由发挥。比如让它整理项目文档它可能先写个提纲然后突然开始生成代码最后又跳到总结结论。不是模型不行而是缺少一套约束和引导它的行为模板。这就是agent-skills要解决的问题。所谓技能可以理解为给Agent预先封装好的一组可复用、可组合、可观测的能力单元。它不是一段提示词不是一份API文档更像是一个行为模块——包含触发条件、输入输出规范、执行逻辑和错误处理。当Agent遇到匹配场景时能像调用肌肉记忆一样快速切换到正确的处理路径。做这一行的朋友应该已经发现了这其实是把传统软件开发里的函数封装和模块化思想搬到了Agent世界里。核心区别在于传统函数是给程序员调用的而技能是给大模型理解和调用的。这决定了技能定义不能只写做什么还得写清楚什么时候用输入长什么样输出是否符合预期。我自己在实践中的体会是凡是把技能体系做扎实的项目Agent的稳定性会有一个质变——从偶尔惊艳、经常翻车变成稳定输出、可预期交付。这篇文章就从设计思路、定义方法、编排策略到落地坑位系统拆一遍agent-skills的做法和踩坑记录适合正在搭建Agent应用、或者已经跑通Demo但想提升生产可用性的开发者参考。2. 技能定义从会说话到能干活2.1 一份可用技能描述的最小结构很多人一开始会把技能写成一两句话的提示词比如整理会议纪要并提取待办。这基本没用因为大模型理解到的只是任务名称执行时依然靠临场发挥。一份真正可用的技能描述至少应该包含六个部分技能名称与用途一句话说明这个技能做什么注意用动词开头比如提取结构化待办事项清单。触发场景明确什么情况下应该调用这个技能。写清楚场景特征例如当用户提供会议录音转写文本且要求整理纪要时。输入参数定义调用这个技能需要的字段名、类型、格式和约束条件让Agent能自己完成参数抽取。执行步骤用有序列表描述内部处理流程。这里要细化到第一步做什么、判断什么条件、输出什么中间结果每一步都要可执行。输出规范定义返回结果的格式比如JSON结构、字段说明、长度限制。边界与异常处理写清哪些情况不处理、哪些情况需返回特定错误码或引导用户补充信息。这六个部分缺一不可。尤其是触发场景和执行步骤这两块是很多人容易偷懒的地方但恰恰决定了技能是被正确调用还是会跑偏。2.2 让大模型读懂技能的写法技能描述最终是给大模型看的自然语言。写法和写给人看的说明文档有本质区别。人在阅读时会自动补充常识和上下文模型不会——它只依据文本里明确写出来的内容做判断。举个例子同样是描述文件整理写给人的版本可能是把散乱文件归类整理到对应目录给模型的版本则要写接收一个包含文件路径列表的JSON数组按扩展名映射到预设分类规则对于无法判定的文件返回unknown标记并将每个文件的源路径与目标路径写入输出结构。这里有个判断标准如果一段技能描述放给一个完全没接触过业务的新人看他不需要额外问任何问题就能照着操作且结果可复现这就算是合格了。我一般在写完技能描述后会专门找同事做冷读测试——只看文本不交流看他能不能准确说出来什么场景下用这个技能、输入是什么、输出长什么样。另外描述里的术语要一致。同一个概念要么通篇叫工作项别一会儿任务一会儿待办。大模型对术语漂移很敏感稍微不一致就可能把参数映射搞乱。2.3 一个技能定义的完整示例以会议纪要与待办提取为例展示一份结构化技能定义name: extract_meeting_actions description: 从会议转写文本中提取结构化纪要和可执行待办事项 trigger: - 输入为会议录音的转写文本且内容包含多人讨论 - 用户明确要求整理会议纪要、生成待办或行动项 input_params: transcript: type: string description: 会议完整转写文本保留说话人标记更佳 required: true meeting_title: type: string description: 会议名称用于纪要标题 required: false default: 未命名会议 steps: 1. 识别文本中的发言人按说话人分段整理 2. 提取讨论主题以时间线方式列出主要议题 3. 识别带有责任人和时间节点的句子转成结构化待办 4. 对模糊表述如尽快稍后标注为待确认优先级 5. 汇总输出未识别到待办的议题在待办列表中标注无需行动。 output_schema: summary: type: string description: 会议要点摘要不超过200字 topics: type: array items: string action_items: type: array items: owner: string task: string due_date: string or null priority: high/medium/low/unknown error_handling: - 如果输入文本长度不足50字直接返回文本过短无法提取有效纪要 - 如果没有识别到任何待办返回空数组并提示本次会议暂无待办事项这份定义看起来很啰嗦但每一个字段都有存在的理由。trigger决定了模型在什么情况下调用input_params告诉模型需要从对话里抽取哪些信息steps把执行过程拆到模型不需要想就能按顺序做的程度output_schema约束了输出格式方便后续程序直接消费。3. 技能编排让Agent拥有工作流思维3.1 三种基础编排模式单个技能只能解决单点问题真实业务场景往往需要多个技能接力配合。我在项目里常用的编排模式有三种分别对应不同的任务形态顺序编排——适用于流程固定、上一步输出是下一步输入的任务。典型场景是资料整理流水线先是fetch_document拉取文档再是extract_key_points提炼要点然后是generate_summary生成摘要最后send_report推送结果。每一步的输入输出都清晰对应像工厂流水线一样中间出问题可以精准定位在哪一环。条件分支编排——适用于同一任务因不同输入走不同路径的场景。比如classify_inquiry先判断用户问题类型分类结果是售后咨询就调用check_warranty是使用指导就调用load_manual是其他则走transfer_human。分支的核心是分类技能的输出必须严格可控最好限定为枚举值别让模型自由发挥。并行编排——适用于多个独立子任务可以同时推进的场景。比如做竞品分析时search_web、analyze_pricing、extract_reviews之间没有依赖关系可以并行执行再统一汇总。并行编排能显著缩短端到端耗时但要注意汇总阶段的输入量可能很大得设计好合并策略。这几种模式可以嵌套使用——条件分支内部可以包含顺序编排顺序编排的某一步也可以并行展开子任务。关键原则是每个节点的输入输出必须明确节点间的数据流要像接口契约一样清晰。3.2 编排中的上下文传递与数据流转技能编排最容易出问题的不是技能本身而是技能之间的上下文传递。我之前踩过一个典型的坑技能A输出的字段名是user_id技能B的输入参数里写的是uid两个技能单独测试都没问题编排在一起后Agent经常搞不清楚应该把A的哪个字段传给B。问题不在模型能力而在字段命名不一致导致模型需要做语义猜测一猜就容易错。解决方法是建立统一的上下文数据字典。定义一套贯穿所有技能的公共字段比如用户标识统一叫user_id任务标识统一叫task_id时间戳统一用ISO 8601格式。技能间的数据传递只允许使用公共字段不能各自定义别名。这套字典要写进技能定义的基础约定里所有技能编写者都必须遵守。另外还要控制单次传递的数据量。我有一次编排一个多技能流水线中间某个技能把整份原始文档传给了下一个技能结果模型处理时上下文迅速膨胀后面的步骤全乱了。现在我的做法是技能间只传结构化摘要和必要字段原始大文本尽量放到外部存储需要时再按索引读取。3.3 技能的生命周期管理技能不是写出来就能一直稳定工作的。模型版本在升级业务需求在变化技能描述里的触发条件可能逐渐失效。我把技能生命周期分成四个阶段管理注册——每个技能上线前必须经过格式校验和单技能测试。校验包括字段完整性、描述可读性、输入输出schema一致性。这个阶段可以用一组固定测试用例做回归用例不做随机变化。灰度——新技能或者修改过的技能先只对一小部分流量生效观察调用准确率和输出满意度。灰度期间要有日志记录每次调用的触发原因、参数抽取结果和最终输出方便复盘。全量——灰度数据达标后放开全量。此时重点监控的是调用频率和异常率比如某技能一周内没有被触发可能是描述里的触发条件写窄了异常率高则要检查边界条件是否漏写。下架——业务下线或者技能被其他技能替代时不要直接删定义先标记为deprecated保留一段时间让调用方完成迁移再彻底移除。这个习惯能避免很多线上事故。生命周期里还有一个容易忽略的点技能的版本要和模型版本联动记录。同一个技能定义在GPT-4上表现稳定换到其他模型上可能完全走样。我吃过这个亏换了一版模型后技能突然大面积不触发排查了半天发现是描述风格和模型理解偏好不匹配。把技能版本和模型版本绑定存档出问题才能快速回退定位。4. 场景驱动的技能库设计4.1 先找高频重复任务再抽象技能做技能体系最忌讳上来就规划我要做五十个技能。技能的设计应该从实际业务里长出来不是从概念里推出来。我常用的方法很简单把过去几周的用户对话日志翻出来找出那些反复出现的、执行路径高度相似的任务。比如每周都有大量用户要求把这份PDF里的关键数据提取到表格里而且每次的操作路径都差不多这就是一个值得沉淀为技能的场景。反过来那种一个月也碰不到一次的个性化需求就不值得做成技能。判断一个任务是否值得技能化的标准有三条频率够不够高、路径够不够一致、失败成本够不够大。频率高意味着投入产出比好路径一致意味着可以标准化失败成本大意味着值得用技能来兜底。三条里占两条就值得做。三条都没占就别浪费时间。4.2 五个适合优先沉淀的场景根据我这段时间的实践以下五类场景最适合先做技能沉淀文本清洗标准化。把用户输入的自由文本比如评论、留言、摘要转换成统一格式。包括错别字修正、表情符号清理、格式归一化。这个技能看起来简单却是很多下游任务的必备前置做扎实了能省大量后续麻烦。结构化信息抽取。从非结构化文档里提取实体、关系、关键指标。比如从简历里提取学历和工作经历从合同里提取金额和期限。这类任务大模型本身就能做但输出格式不稳定技能的价值在于把输出钉死在schema上。报告生成与格式转换。把零散数据点整理成结构完整的报告或者把一个格式的报告转成另一个格式比如Markdown转PDF、JSON转表格。技能里要写清楚报告的结构、风格、篇幅要求。多轮对话状态管理。处理需要多轮询问才能收集完整信息的场景。比如收集用户的项目需求先问目标再问预算再问时间线每一步校验信息完整性缺失就追问。这类技能能显著提升复杂信息的收集效率。代码修改与检查。接收代码片段或者仓库路径执行指定修改或者做静态检查。技能里要明确修改边界比如只修改传入文件不新增文件不修改测试代码避免模型过度发挥。选型逻辑其实很简单优先选那些如果不封装技能每次都要人工写提示词、调参数、清洗输出的脏活累活。一次投入长期复用。4.3 技能库的目录组织与治理技能数量超过十几个后管理就成了新问题。技能之间可能有依赖关系命名可能产生歧义描述可能互相矛盾。我目前的目录组织方式是按层级领域双维度划分基础技能层完全通用、不依赖具体业务场景的能力比如extract_dates、format_json、sentiment_analyze。这些技能可以被上层任意技能调用。领域技能层面向特定业务域的组合能力比如process_invoice会调用extract_dates、calculate_total等基础技能。场景技能层面向完整用户任务的端到端技能通常编排多个领域技能比如handle_refund_request。这套分层的好处是基础技能变化频率低领域技能跟随业务演进场景技能可以持续迭代。修改一个基础技能时要排查所有依赖它的上层技能是否有影响这要求技能库要有依赖关系记录不能靠人脑记。治理方面我坚持两条硬规矩第一所有技能变更必须走代码评审改描述就是改逻辑不能一个人改完直接上线第二定期做技能使用情况审计三个月没被触发过的技能要么优化描述、要么下架不养僵尸技能。5. 常见问题与排查技巧5.1 技能不触发的排查路径技能不触发是出现频率最高的问题。表现为用户输入明明符合触发条件Agent却完全无视技能定义用通用能力硬答。排查第一步看日志里模型实际读取的系统提示内容。技能不触发往往不是定义的问题而是技能根本没有被加载进模型上下文。很多框架默认只加载少量技能描述超出部分被截断了。这种情况要先确认技能是否在有效加载列表里。排查第二步检查触发条件的措辞。模型判断是否调用技能时是拿用户的当前输入和trigger字段做语义匹配。如果触发条件写得太抽象比如当用户需要帮助时模型反而不知道怎么匹配。更有效的写法是给出具体的匹配信号例如当用户输入中包含整理会议记录、会议纪要、记一下待办事项等表述时。排查第三步看是否有多个技能的触发条件重叠。如果两个技能都声明了相似的触发场景模型可能随机选一个或者在两个之间反复横跳。这种情况下需要给技能设定优先级或者在触发条件里加排除说明比如本技能仅处理销售类文档财务类文档请调用另一个技能。5.2 参数错配与上下文污染参数错配的问题在编排场景里特别突出。比如一个技能需要customer_email结果模型从对话里找到的是一串订单号填进了邮箱字段。我遇到这种情况先检查技能描述里是否明确给出了参数抽取的来源位置——应该写明从用户输入中提取……格式需包含符号这种校验提示。更隐蔽的问题是上下文污染。当Agent执行多技能任务时前一个技能的中间输出可能残留在上下文里影响后一个技能对输入参数的判断。比如先执行了analyze_contract输出里有大量金额数据紧接着执行format_invoice模型可能混淆了两者的输出字段。我的处理方式是每个技能在开头显式声明本技能仅使用输入参数中的字段忽略此前讨论中与这些字段无关的内容。同时在编排引擎层面技能切换时把系统上下文重置只保留必要的传递数据。双重保险下来参数错配的概率会大幅下降。5.3 缓存命中、模型选型与安全边界技能执行时的重复计算是个隐性成本。同一个文档被不同用户反复请求提取摘要每次都跑一次全量推理既不经济又慢。可以在技能执行层加缓存以技能名输入参数哈希作为缓存键命中直接返回历史结果。要注意的是缓存只适用于确定性输出技能比如文本清洗、信息抽取对于需要个性化、实时性的技能别开缓存。模型选型对技能效果影响极大。同样的技能描述在推理能力强的大模型上可能不需要太多步骤说明在轻量模型上则必须把所有路径都写得明明白白。我的经验是复杂技能用高能力模型简单技能用低成本模型通过框架的路由功能做分发。如果一套技能定义在多个模型间通用以能力最弱的模型为基准来写描述。安全边界也是技能设计里容易被忽略的部分。技能描述会被模型完整读取所以不要在描述里写任何敏感的内部信息、密钥或者未公开的业务规则。同时每个技能都要想清楚它的使用边界——比如execute_code这类高权限技能必须限定可执行的操作范围不能给模型留下自由发挥的空间。我一般会在技能定义里显式写上拒绝项例如本技能不接受删除文件操作本技能不执行任何涉及用户密码的请求。6. 技能质量评估与持续优化6.1 建立技能的量化评估指标技能做得好不好得拿数据说话。我常用的评估指标有四个调用准确率——判断该用技能的时候模型是否真的用了。统计方式是抽样审计日志把模型每次调用技能的记录拉出来人工标注这次调用是否正确再算比例。输出达标率——技能执行后的输出有多少比例是完全符合schema规范的。不达标的情况包括缺字段、类型错误、格式不合法。端到端任务成功率——从用户发起请求到最终交付结果用户是否满意。这个指标需要结合任务本身的验收条件来定不同技能标准不同。无效参数率——模型调用技能时抽取的参数中有多少是无效的比如缺失、错填、格式非法。这个指标能反映参数描述是否清晰。我每个迭代周期都会拉取这些指标做环比分析。技能出现退化时指标会先于用户反馈暴露问题这是持续优化最重要的数据支撑。6.2 用真实语料做回归测试技能优化最怕改一处崩一片。我有一次调整了extract_contact_info的输出字段顺序结果影响到依赖它的generate_customer_profile技能整个下游都乱了。从那以后我给每个技能都建立了一个回归测试集。测试集由三部分组成历史真实样本从线上日志里抽选的典型请求边界样本故意构造的极端输入比如空文本、超长文本、纯标点符号变异样本在真实样本上做小幅扰动比如拼写错误、格式松散考察技能的鲁棒性。每次修改技能定义后完整跑一遍测试集对比输出和预期结果的变化。只要有一个样本回归异常就说明改动有连带影响需要先排查清楚再放行。6.3 建立技能的版本与变更记录技能是代码就得有代码的管理规范。我在实践里给每个技能建立了一份变更记录包含以下字段版本号、变更日期、变更内容、变更原因、影响范围、测试结果。这份记录的价值在出问题时体现最充分。线上技能突然表现异常翻变更记录能快速定位是哪次改动引入的回归。没有这份记录只能靠猜效率太低。同时技能定义最好用Git管理。每次变更走提交记录和代码同库管理。回滚时可以直接切到上一个稳定版本而不是手工编辑文本恢复。还有一个细节技能和依赖它的编排流程最好放在同一个仓库里维护。分开管理容易出现版本对不上的情况——编排流程引用了新技能但技能定义还没上线线上表现就会混乱。同仓同步发布能规避这类问题。7. 一些个人实践体会讲了这么多方法最后分享一些我踩坑踩出来的体会。第一技能定义要写得笨一点。不要觉得步骤写得细是在侮辱模型的智商。恰恰相反把执行步骤写清楚模型就不用消耗能力去猜下一步该做什么能把智力集中在处理内容本身上。我见过太多技能翻车都是因为描述太聪明默认模型能理解那些没说出来的潜台词。第二技能设计要有小步迭代的心态。不要指望一版技能定义就完全稳定。第一个版本能覆盖80%的典型场景已经算成功。上线后收集失败案例针对性补充描述和边界条件迭代两三版后会逐渐进入稳定期。我自己最稳定的一个技能前前后后改了十一版。第三还要注意技能的命名规范。技能名称会被模型用来做意图分类命名不好会产生歧义。比如get_weather和check_weather在模型看来语义几乎一样容易混淆。给每个动作配置固定动词比如统一用get_、extract_、generate_、validate_开头降低模型理解成本。第四别忽视技能的执行日志。技能跑得好不好不打开日志永远不知道真相。我的习惯是每个技能关键节点都输出日志触发时记录原始输入摘要、参数抽取完记录参数明细、执行完记录输出摘要、异常时记录错误信息。这个日志习惯帮我解决过太多现象明显但无从下手的问题。关于agent-skills以上是我在项目里完整实践过一遍的方法体系。从定义、编排、治理到质量评估各个环节都有可以量化的标准和可直接落地的操作路径。如果你正在做Agent应用且觉得模型行为不可控试着从技能化这个角度切进去把能力边界显式地定义出来大概率会有不一样的体验。