
做 AI agent 的人多半都经历过这种尴尬同一个任务第一次教模型怎么做它完成得不错第二次换个输入它忘了前提第三次你为它补了一大段规则结果它开始在无关的地方自由发挥。问题往往不在模型本身而在于我们一直在每次重新教没让 agent 真正学会做一件事。agent-skills 就是为解决这个问题出现的思路——它不是一个特定产品的名字而是一套把 agent 的可复用能力沉淀成结构化技能的方法论和工程实践。简单说就是把 agent 反复要执行的任务包装成有边界、有描述、有验证的技能模块让 agent 像人一样有手艺而不是每次即兴表演。这篇文章不聊虚的。我会从自己折腾 agent 项目的实际经历出发讲清 agent-skills 到底是什么、怎么设计、怎么写、怎么排坑。适合正在开发 agent 应用的工程师想给产品接入 agent 能力的业务侧同学以及刚开始接触 agent、想少走弯路的人。1. 搞懂agent-skills它是什么为什么值得认真对待1.1 从每次重新教到一次学会的范式变化传统做法里agent 的行为主要靠 prompt 驱动。你在系统提示词里写清楚角色、任务、步骤、注意点然后让大模型自由发挥。这个方式的代价是提示词越长模型越容易抓不住重点任务描述藏得越深模型越容易在细节上偷工减料。更麻烦的是一旦业务逻辑变了你要去改一大段自然语言文本还得重新测一遍所有可能出现的行为。整个流程非常脆弱。agent-skills 的思路是把任务怎么完成从 prompt 里抽出来变成独立的、可命名的技能单元。每个技能包含自己的描述、输入参数、执行步骤、工具调用和输出格式。agent 的主提示词只需要负责决定当前该用哪个技能技能内部的细节由技能自身闭环处理。这样就把思考和执行拆开了主模型负责选择和调度技能负责稳定地干好一件事。我举个例子。以前我做一个会议纪要 agentprompt 里写了请提取会议中的待办事项、负责人、截止时间结果模型有时候把讨论背景也当成待办有时候漏掉截止时间不写。后来我把能力拆成一个技能集合一个技能负责会议转写文本清洗一个技能负责按角色提取议题与结论一个技能专门负责待办与负责人匹配。每个技能只干一件事输入输出都是结构化的。效果立刻稳定了很多而且任何一步出问题我都能单独定位和修。提示判断一个任务该不该做成 skill最简单的标准是——这件事你是否已经在 prompt 里写了第二遍如果同一个处理逻辑要被多个 agent 复用或同一个任务反复出现且结果要求稳定它就值得沉淀成技能。1.2 skill与prompt、function calling、微调的本质差异很多人会把 agent-skills 和几个相近的概念搞混。我直接做一张对比表按自己的理解来说明你看完就能分清它们各自的角色。对比维度大段PromptFunction Calling微调Agent Skills本质用自然语言约束行为暴露可调用的函数接口修改模型权重结构化的技能模块改动成本低但极易失控中需维护接口契约高需要数据和算力中按技能独立增删改稳定性差文本越长越不稳定较好但只解决调用哪个函数好但覆盖不了长尾较好技能内部闭环可测试性弱靠人工看输出可测接口参数需要评估集每个技能有独立测试用例适用场景一次性、探索型任务需要精确入参出参的原子操作行为大量固化且数据充足可复用的多步骤能力看这张表就明白了function calling 解决的是模型该调哪个 APIprompt 解决的是模型应该按什么风格和顺序做事而 agent-skills 在这个基础上多做了一层组合与沉淀。一个 skill 内部可以包含多个 function call、一段执行逻辑、若干人为总结的经验规则甚至包含失败情况的兜底。它是介于纯提示词和微调之间的一种工程化能力封装。我自己对 skill 的定位是它是 agent 的操作手册 工具清单 检查表。手册告诉 agent 这件事按什么步骤做工具清单告诉 agent 可以调哪些函数检查表告诉 agent 做完之后怎么判断结果对不对。三者打包成一个单元agent 只需要知道什么时候取出这个单元。2. 设计一个skill之前先把这三件事想透2.1 边界skill到底该管多宽设计 skill 最容易犯的错就是什么都往里装。我见过很多第一次写 skill 的人把数据分析做成一个技能里面既要做清洗、又要做统计、还要画图、还要写结论。结果就是技能描述写得极长agent 根本搞不清楚什么场景该用它用起来也经常在内部步骤里跳来跳去。我现在的经验法则很简单一个 skill 只负责一个可独立验收的结果。比如从原始会议转写稿中提取全部待办事项输出 JSON就是一个合格的边界处理会议相关的一切事情就是不合格的边界。边界越窄描述越短agent 的命中率和执行成功率越高。判断边界是否合适可以问三个问题这个技能的输出能被一个明确标准验收吗它的输入能不能用结构化参数描述清楚它是否只依赖有限的几个工具如果答案都是肯定的边界基本就合理。2.2 契约输入输出怎么定才不会被 agent 玩坏skill 的输入输出契约本质上是在和 agent 这个不太守规矩的调用方打交道。大模型生成参数时天然存在填错字段、多填字段、类型不对的风险。所以设计契约时我有几条硬规矩参数尽量少最好不超过 5 个。参数越多模型填错的概率越高。每个参数都要有清晰的描述、类型和示例。你不写示例模型就会自己编一个。输出必须结构化能 JSON 就 JSON并固定字段命名。必须定义失败输出。比如未找到待办时返回空数组而不是报错这样上层才能稳定处理。我见过一个反面案例某团队做了个周报 skill入参是原始素材结果 agent 有时候传一个文件路径有时候直接把文件内容整个贴进去还有一次传了个 URL。就是因为参数描述里没写清楚本参数接受 Markdown 格式的文本内容不接受路径与链接。后来补了描述和示例问题才彻底解决。2.3 复用为多个场景复用而设计skill 的另一个价值是跨场景复用。同一个信息抽取技能可以被客服 agent 用来抽用户诉求也可以被运营 agent 用来抽活动评论关键词。为了支持这种复用你在命名和描述上要刻意去场景化。我的做法是技能的内部名称用动词对象的通用结构比如extract_action_items、validate_csv_schema不要在名字里带具体业务名。业务差异通过参数层面体现而不是复制一个几乎一样的技能。这样维护成本会低很多技能库也不会越来越膨胀。当然去场景化有个前提——技能内部不能隐含对某一类数据的强假设。比如提取待办就比提取会议待办可复用性高但如果数据源差异太大导致内部逻辑完全不同那就不要硬复用拆成两个技能反而清晰。3. 实操从零实现一个可用的 agent skill3.1 skill的目录结构与元信息定义我以自己常用的结构为例。每个技能是一个独立的目录放在统一目录下结构大致如下skills/ extract_action_items/ skill.yaml instructions.md assets/ examples.json tools/ parse_timestamps.py tests/ cases.yamlskill.yaml是技能的身份证包含名称、描述、输入参数定义、输出格式声明。下面是一份我实际用过的元信息示例name: extract_action_items description: 从会议或聊天记录文本中提取所有待办事项。适合输入原始转写文本、聊天记录输出结构化待办列表。当用户提到记一下待办有哪些要做的事时使用。 version: 1.2.0 input: raw_text: type: string description: 原始会议转写或聊天记录要求是 Markdown 纯文本不接受文件路径 required: true example: 张三说周五前给客户发报价。李四点了个赞。 owner_required: type: boolean description: 是否需要为每个待办匹配负责人无法匹配时置为 unknown required: false default: false output: type: object schema: action_items: type: array items: task: string owner: string due_date: string source_line: integer注意description的写法它既要说明技能的能力边界又要给出触发信号。你可以把这段描述理解为 agent 的技能检索入口描述写得越贴近用户真实表达agent 命中率越高。我见过不少技能功能本身没问题但因为 description 里全是技术术语在真实对话里根本没机会被触发。3.2 核心逻辑与工具封装元信息之外真正干活的是instructions.md和配套工具。instructions.md给 agent 提供怎么做的指导但它不是一篇论文而是操作步骤加规则加例子。我的习惯是控制在 300 到 600 词以内重点写清楚三个部分处理流程、边界规则、失败处理。下面是我给extract_action_items写的 instructions 关键内容# 待办提取执行指引 1. 通读 raw_text先识别所有表示行动需求的句子包括但不限于需要、要、记得、安排、跟进、确认等表达。 2. 对每个候选句子判断是否满足全部条件主语或明确负责人、动作、时间或优先级信息。缺少任意一项则视为置信度不足。 3. 排除纯讨论性内容。只记录未来需要有人执行的事项不记录背景信息、已执行完成的事。 4. 时间表达规范化将周五下周一转为具体日期使用今天日期推算无法推断时保留原文并标记 null。 5. 输出严格按 output schema 生成 JSON不得添加额外字段。若没有待办action_items 返回空数组。这里的重点不是让模型发挥理解力而是给它一组明确可遵守的规则减少随机性。第 4 条里涉及日期推算这种操作我强烈建议不要靠模型心算而是在工具层封装一个parse_timestamps()函数做规则转换。凡是算得准不准影响结果的事尽量交给代码而不是模型。工具封装方面我的习惯是把外部依赖如文件解析、日期处理、文档转换都包成独立函数并给每个函数写清楚参数和返回值。这样技能内部就像一个微型应用模型只在关键决策点上做判断其他都走确定性代码路径。提示不要指望模型在 skill 内部做复杂计算。凡是可以用正则、解析器、库函数完成的事都先在工具层做掉只把真正需要语义理解的环节留给大模型。3.3 测试skill如何确定 agent 真的会用它技能写完之后第一件事不是接进主流程而是先做两个层面的测试。第一层是调用测试模拟 agent 的决策环境给出一系列用户请求看模型是否能在正确的时候选中这个技能、传入正确的参数。这层测的是元信息质量。我一般准备 20 到 30 条用户说法覆盖正向触发、近似触发、不该触发三类。正向示例像帮我记一下今天会上说的待办近似示例像这个文档里有什么行动项吗不该触发示例像帮我算一下这个月的预算。第二层是执行测试用固定输入跑技能内部逻辑验证输出格式和内容正确性。这一步我会准备一份cases.yaml每个用例包含输入、期望输出字段、允许的偏差范围。比如- input: 张三说周五前给客户发报价。李四点了个赞。 expect: action_items_length: 1 first_task_contains: 给客户发报价 first_owner: 张三执行测试跑过之后再考虑接入真实 agent 流程。很多人跳过了这两层直接上生产结果模型根本不知道怎么触发技能还以为是模型能力问题其实纯粹是元信息没写好。3.4 注册与热加载让 skill 进入 agent 的运行流程技能写好了、测好了最后一步是注册。不同 agent 框架的注册方式不同但核心思路一致把技能列表注入到 agent 的可用工具/技能集合中让主模型在每次决策时都能看到它。以常见的伪代码为例from agent import Agent from skill_registry import load_skills skills load_skills(./skills) # 扫描目录解析 skill.yaml agent Agent( modelyour-model, skillsskills, # 注入技能集合 memory_enabledTrue ) # 运行时自动路由用户请求 - 模型决策 - 触发对应 skill result agent.run(帮我记一下今天会议里的待办)这里有个容易踩的坑技能列表不是越多越好。每个技能的描述都会占据上下文窗口技能数量多了模型反而看不过来触发准确率会下降。我自己的经验是单个 agent 同时挂载的技能数最好控制在 10 个以内超过了就要考虑做按场景分组的动态加载。比如按用户意图先粗分类再加载对应分组下的技能类似先选工具箱再从箱子里拿工具。另外升级技能时建议保留版本号并做灰度。不要今天改完 description 明天就全量上线因为 description 一旦变了模型的触发行为会发生整体偏移可能会误伤其他技能的触发率。我一般会先在测试环境跑一轮调用测试确认新描述下所有正向、近似的用户说法都被正确路由才推到线上。4. 常见问题与排查速查表4.1 技能不被触发、触发错乱、上下文污染我在实际项目中遇到的第一个大问题就是技能不被触发。排查下来多数原因集中在description上要么描述太技术化和用户真实说法对不上要么描述太泛模型觉得用户的问题不配用这个技能。解决办法是回到 3.1 里说的——把用户可能的表达方式写进 description 的触发信号里并且用真实对话语料去验证。第二个常见问题是触发错乱。比如用户想提取待办模型却调了会议总结技能。这往往是两个技能的 description 有重叠边界没划清。我的排查方法是把所有技能的 description 拉到一个表里逐条比对凡是我自己都分不清边界的模型一定也分不清。这时候就要调整表述给每个技能一个排他性触发词。第三个问题是上下文污染。有的技能会往对话历史里回写大段执行日志导致后续轮次模型把这些日志当成用户内容行为异常。我的做法是技能的输出尽量精简只返回最终结果和关键中间量执行细节放在日志里异步落盘不要回灌给主模型。4.2 性能、安全与可维护性坑点性能方面一个常见坑是技能内部的工具调用串行太多。比如先调一次转写 API、再调一次待办抽取、再调一次日历写入每一步都是一次网络往返延迟叠加后用户体感非常差。优化方向有两个一是把能并行的调用改为并行二是减少大模型在技能内部的调用次数把可确定化的判断改成规则代码能省一次模型调用就省一次。安全方面要特别注意工具权限收敛。技能一旦可以自由调用外部工具模型就可能因为指令注入把用户输入里的恶意内容当成指令传给工具。我在技能里加了输入过滤和工具白名单技能能访问的 API 列表在元信息里声明运行时动态校验不在白名单的直接拒绝。可维护性方面技能库很容易腐化。新增技能没人清理旧的、说明文档过期、测试用例没跟上半年后技能库就变成了代码沼泽。我的建议是每个技能必须有 owner、version、测试用例并在 CI 里跑一轮基础调用测试保证元信息格式合法、输出 schema 不破损。4.3 快速定位问题一份排查清单我整理了一份很朴素但非常有效的排查顺序清单每次技能行为不对就按顺序过一遍技能是否被正确注册先看运行日志里有没有技能被调用的记录。description 是否清楚把用户原话和 description 放到一个模型里问它你会在什么情况下用这个技能看输出是否一致。入参是否符合 schema把模型实际传入的参数打印出来和 yaml 定义比对。instructions 是否被遵守检查完整输出看它有没有按你定义的步骤执行还是跳步了。输出是否符合 schema用校验脚本跑一遍别靠肉眼。是不是上下文太长导致模型丢失规则尝试把关键规则压缩到 1 到 2 条放到最前面。这份清单我贴在自己项目文档最前面每次出事照着走基本十分钟内能定位问题到底出在哪一层。5. 最后分享一点个人体会说实话agent-skills 这条路我也是踩了一堆坑才摸到门道。最早我也觉得给 agent 写 prompt 就够了后来发现凡是稳定产出的要求最终都得靠结构化技能来兜底。现在我做 agent 项目的流程基本固定先列出所有反复出现的任务再逐个设计技能边界和契约然后实现和测试最后才考虑主流程怎么串联。技能库越来越像一个内部小工具库agent 每次干活都像在调用经过验证的接口而不是临时发挥一段文字。如果这篇文章对你有一点帮助建议你从手头最痛的那个重复性任务开始试把它的执行步骤拆出来写成第一个 skill跑一遍调用测试。你很快就会感受到把能力真正沉淀下来之后agent 的稳定性会好很多。