Agent Skills 实操指南:从概念到落地,让大模型稳定处理复杂长任务

📅 发布时间:2026/9/18 0:59:25
Agent Skills 实操指南:从概念到落地,让大模型稳定处理复杂长任务
最近几个月agent-skills这几个字在 AI Agent 工程圈子里出现的频率越来越高。我陆陆续续在几个开源项目里看到类似的设计也在一线把这种思路落地到了实际业务里。今天这篇不聊虚的直接拆开讲讲 agent-skills 到底解决什么问题、底层逻辑是什么、怎么从零手写一个能用的 skill以及我在实操中踩过的一些坑。如果你正在用大模型搭智能体或者感觉现有的 agent 在长任务、复杂流程面前有点力不从心那我强烈建议你认真看完这篇。它可能比你在多个模型之间反复调 prompt、堆 tool 更管用。这篇文章会从概念、设计、实战代码到排错经验一条线讲透。1. 先搞清楚agent-skills 到底解决什么问题1.1 大模型应用开发被长任务卡住了过去一年里做大模型应用的朋友应该都有一个共同感受单轮问答、简单的文本生成已经没什么挑战了真正难的是让 agent 稳定地完成一个多步骤的复杂任务。什么叫复杂任务比如帮我把这周的运营数据拉出来生成一份带图表的周报再发到群里这里涉及取数、分析、写报告、生成图表、发送等多个环节。用传统的方法做通常是把所有指令塞进 system prompt再挂上一堆 function calling 工具。问题很快就暴露了。模型的上下文窗口有限指令一长、逻辑一复杂模型就开始迷失要么遗漏关键步骤要么用错了工具要么在某个环节的格式上反复出错。我见过不少团队在 prompt 里写了几百行的复杂业务规则最后模型跑出来的结果依然不稳定。说白了把怎么做一个完整的任务全部塞进对话里让模型自己临场发挥本身就是一个非常脆弱的设计。这时候就需要一种机制把完成某个任务所需的全部知识、步骤、工具调用方式、输出规范打包成一个结构化的、可复用的模块让模型在需要的时候自动加载调用。这就是 agent-skills 的核心思路把单个任务的完整解决路径固化下来而不是每次让模型在长的像论文一样的指令里自由发挥。1.2 skills 为什么比 tool、prompt 都更接近能力单元很多人会问这不就是 tool工具吗不完全是。工具tool通常只代表一个原子操作比如查询某个城市的天气、计算两个日期的差值。而 skill 代表的是一个完整的工作流程可能包含多个工具的协作、规则的约束、输出模板的定义、示例数据的引导。举个例子天气查询是一个 tool但为一次户外活动制定天气应对计划就是一个 skill它需要调用天气查询工具还要结合活动时间、地点、备选方案规则才能输出最终建议。也有人说skill 不就是把 prompt 拆开吗对了一半。传统 prompt 是一次性的写在 system prompt 里模型每次对话都要占用上下文。而 skill 是按需加载的只有触发相关任务时才会被注入用完就算。这意味着你可以准备几十个、上百个 skill而不用担心上下文被撑爆。从我个人的理解看skill 应该被看成连接用户意图和底层工具之间的业务中间层。它既包含如何做的过程性知识也包含做成什么样的结果要求。这个定位让它比单纯的 tool 更接近一个完整的能力单元。2. skills 系统的整体设计思路2.1 一个最小可用的 skill 系统有几层我在自己项目里落地 agent-skills 时一般会把整个系统分成四层技能注册层、技能发现层、技能执行层、技能治理层。每一层都有明确的职责也都有对应的实现要点。技能注册层负责把写好的 skill 登记到一个统一的位置可以是一个目录结构也可以是一个配置文件列表核心是给每个 skill 一个唯一的标识和基础描述。技能发现层负责判断当前用户的请求应该触发哪个或者哪几个 skill这一层一般靠在每个 skill 的清单文件里写清楚触发场景再由上层调度器做语义匹配。技能执行层负责把 skill 定义好的步骤跑起来包括解析参数、调用底层工具、按照模板生成结果这一层往往跟具体的代码执行框架绑定。技能治理层则是管理技能的版本、更新、复用、废弃到了一定规模之后这层的价值会越来越明显。这四层看起来复杂但落地时完全可以先用最简单的目录加约定来实现并不需要一开始就上重型的框架。我见过一些团队把 skills 直接放在一个文件夹里每个子文件夹是一个技能里面包含一个说明文件和一个实现文件调度时用向量检索做匹配效果就已经非常好了。2.2 核心构成清单文件、技能内容、引用方式一个规范的 skill 在结构上是有讲究的。我推荐最少要包含三个部分技能元信息也叫 frontmatter、技能正体主说明或实现代码、技能参考资源可选。技能元信息通常包括技能名称、描述、适用的任务场景、触发关键词、依赖的工具或技能、版本号。这一段信息的主要作用有两个一是帮助调度器在用户请求到来时快速判断该不该用这个技能二是方便人维护时一眼看清技能边界。技能正体是这个 skill 最核心的指令部分通常是一段结构化的指令文本写清楚任务目标、执行步骤、禁止事项、输出格式复杂一点的可以附带 Python 脚本或模板文件。技能参考资源一般是一些可选内容比如样例数据、历史成功案例、额外的参考文档用来在需要的时候给模型提供更充分的引导。这三个部分各司其职合起来就是一次完整任务执行的行动手册。在 Anthropic 的公开设计里它们更是直接推动了Agent Skills标准的流行很多开源项目的组织方式都开始向这套模式看齐。2.3 为什么这种设计比堆提示词更靠谱核心原因有三个。第一责任隔离。传统提示词把任务目标和完成步骤混在一起导致模型难以抓重点。skills 模式下调度逻辑和任务逻辑分开了模型拿到的是清晰的、边界明确的执行指令成功率自然高。第二上下文控制。按需加载的最大好处是节省 tokenskill 只在相关时进入上下文其余时间完全不占资源。我的实测里同一个业务场景下引入 skills 之后平均 token 消耗可以下降 40% 到 60%。第三可迭代性。单个 skill 是一个独立模块改一个技能不需要动整个系统这在大规模应用里是奢侈的便利。还有一个平时不太容易被注意到的点skill 的存在等于给模型提供了一个参考答案。模型在最开始做的时候是自由发挥但一旦你给过一个历史成功案例作为样张后续的行为就会明显稳定下来。这一点在下面写周报生成器的例子时会非常明显。3. 实操从零编写一个可复用的 agent skill3.1 环境准备与一个最小 skill 结构我日常使用的是 Python 3.10 环境配合 LangChain 或者是直接用原生的 OpenAI 接口来调度模型。其实 agent-skills 本身对框架没有强依赖重要的是目录约定和调用逻辑。先看一个我推荐的最小目录结构skills/ ├── weekly_report/ │ ├── SKILL.md │ ├── skill.py │ └── template.md └── data_query/ ├── SKILL.md ├── skill.py └── examples/ └── sample_output.md每个技能一个文件夹SKILL.md 是技能的说明文件skill.py 是技能的具体执行逻辑template.md 是输出模板examples 下面放参考样例。这个结构对人和机器都足够友好。调度逻辑我一般写成这样import os import json from typing import Optional class Skill: def __init__(self, name: str, skill_dir: str, description: str): self.name name self.skill_dir skill_dir self.description description def load(self) - str: 加载技能的完整说明内容 path os.path.join(self.skill_dir, SKILL.md) with open(path, r, encodingutf-8) as f: return f.read() def load_template(self) - Optional[str]: path os.path.join(self.skill_dir, template.md) if os.path.exists(path): with open(path, r, encodingutf-8) as f: return f.read() return None def load_all_skills(base_dir: str) - list[Skill]: skills [] for entry in os.listdir(base_dir): sub os.path.join(base_dir, entry) if not os.path.isdir(sub): continue skill_file os.path.join(sub, SKILL.md) if not os.path.isfile(skill_file): continue description entry skills.append(Skill(nameentry, skill_dirsub, descriptiondescription)) return skills这个代码看起来很简单但它已经完成了一个基础能力把所有技能目录结构扫描出来并支持按名加载技能内容。简单有效是起步阶段最好的选择。3.2 手写一个周报生成器 skill为了更直观地讲清楚一个 skill 应该怎么写我拿一个我自己在项目中用过的周报生成器来拆解。它要做的事是根据用户的原始工作记录自动生成一份规范的周报包含本周完成、下周计划、风险与协调事项三个模块。SKILL.md 的内容大概是这样的--- name: weekly_report description: 根据流水账式工作记录生成结构化周报适用于团队周报、项目周报等场景。 trigger: 周报、周总结、weekly report、本周工作汇总 version: 1.0.0 dependencies: [] --- # 周报生成技能 ## 任务目标 将用户提供的零散工作记录整理成一份正式的周报必须包含三个模块本周完成、下周计划、风险与协调事项。 ## 执行步骤 1. 从用户消息中提取原始工作记录若缺少必要信息主动询问用户补齐。 2. 将工作记录按照本周完成进行归类每个核心事项包含事项名称、进展说明、结果产出。 3. 从用户补充中识别下周计划若没有根据本周进展合理推导 2-3 条待办。 4. 识别风险事项或需要跨部门协调的事项放入第三模块。 5. 严格按照 template.md 中的格式输出禁止自行增减模块。 ## 注意事项 - 语气使用第三人称客观描述比如完成了日志埋点方案设计而不是我做了日志埋点。 - 每条完成事项控制在 1-2 句话突出结果。 - 未确认的计划不要写得太绝对用计划、预计这类词。template.md 也很简单# 周报{report_type} 周期{start_date} ~ {end_date} ## 本周完成 {completed_items} ## 下周计划 {next_plan} ## 风险与协调事项 {risks}skill.py 里我做了三件事解析用户输入中的日期、调用大模型按 SKILL.md 规则生成内容、校验输出格式是否符合模板要求。伪代码大概是这样的def generate_weekly_report(raw_input: str): # 1. 解析时间范围 start_date, end_date extract_date_range(raw_input) # 2. 组装完整指令 skill_content skill.load() template_content skill.load_template() user_content f原始工作记录\n{raw_input} messages [ {role: system, content: skill_content}, {role: system, content: f输出模板\n{template_content}}, {role: user, content: user_content}, ] # 3. 调用模型并做格式校验 result call_llm(messages, temperature0.3) result validate_output(result, template_content) return result我一般固定 temperature 在 0.2 到 0.3 之间。周报这种讲究准确性的场景不需要模型太发散温度调太高容易出现离谱内容。3.3 引用与自动调用逻辑skill 写好了怎么自动触发这一步是整个系统能否真正跑起来的关键。我这里采用的是两段式设计第一段用轻量模型或者基于规则的匹配器做初筛第二段再把匹配到的 skill 内容和用户输入一起发给主模型。初筛阶段可以简单做不一定需要大模型。我用的方法是给每个 skill 定义一组 trigger 关键词然后用包含关系、同义词扩充来做匹配。比如周报这个 skill可以匹配周报、周总结、summary、工作汇总等词。这套逻辑优点是快、便宜缺点是可能漏掉语义复杂但没有关键词的情况。所以我加了第二段兜底如果初筛没有命中任何 skill就把用户请求和所有已注册 skill 的描述做一次向量相似度计算选用相似度最高的技能。实际效果比纯关键词匹配好不少尤其是用户换了表达方式时也不容易落空。from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import cosine_similarity def match_skill(query: str, skills: list[Skill]): # 第一步关键词匹配 for skill in skills: meta parse_frontmatter(skill.load()) # 解析SKILL.md中的元信息 for trigger in meta.get(trigger, ).split(,): if trigger.strip() and trigger.strip() in query: return skill # 第二步向量相似度兜底 descriptions [skill.description for skill in skills] corpus [query] descriptions vectors TfidfVectorizer().fit_transform(corpus) sims cosine_similarity(vectors[0:1], vectors[1:]).flatten() best_idx sims.argmax() if sims[best_idx] 0.2: return skills[best_idx] return None这套实现不算复杂但工程上够用。整套流程跑下来一个周报类任务从请求到输出通常在 3 到 5 秒内能完成成本也不高。4. 让技能真正稳定跑起来的关键细节4.1 模板参数的约束与边界很多人写完第一版 skill 后发现效果时好时坏。排查到最后大量问题出在模板参数没有约束清楚。模型是根据模板理解输出结构的如果模板里的占位符没有说明每个字段的取值范围和写法模型就会产生各种创造性输出。我建议在模板中不仅给出结构还给出字段说明。比如风险与协调事项这个模块如果没有说明模型可能会写成暂无风险也可能写需要注意进度风险写法差异很大。但如果你在 SKILL.md 里明确写了无风险时写暂无有风险时注明影响范围和应对措施输出稳定性会大幅提升。还有一个细节是日期格式的问题。模型经常把日期写成下周一、本周五这类相对表达这在报告类场景里非常不友好。我习惯在 skill 的指令里直接要求所有日期输出格式必须为 YYYY-MM-DD并在校验阶段用正则做一次检查不合格直接让模型重新生成。这样一个简单的约束能够把周报类输出的可交付度提升一大截。4.2 怎么设计高质量样例在 agent-skills 的设计里样例few-shot examples是非常关键的组成部分。很多团队写 skill 时只写指令不写样例这就像教新人做事只讲抽象规则不给案例效果完全看天赋。而一个实际可复现的样例能直接把模型的输出质量拉到一个可验收的水平。我常用的做法是在 examples 目录下放 2 到 3 组完整案例每一组都包含原始输入和期望输出。比如周报生成器里我会放一个本周工作日志零散记录对应的完整周报示例里面展示三种不同风格的工作记录如何被规范化处理。模型看到样例后对格式、语气、颗粒度的把控会明显更稳定。也有一个副作用需要注意模型有时会直接模仿样例中的措辞导致不同的用户产出的周报风格雷同。我的解法是样例尽量覆盖不同的写法风格并在指令中声明严禁照抄样例内容只参考格式和表达逻辑。这个细节看似小但在面向外部客户的场景里非常重要。4.3 技能间的依赖与链接技能多了之后很快会遇到一个问题技能之间会有依赖。比如周报生成器需要数据查询技能的输出而数据查询技能可能又依赖数据库连接技能。如果这些依赖关系不整理清楚调度时会反复出错。我的处理方式是在 SKILL.md 的 frontmatter 中显式声明 dependencies 字段。调度器在加载一个技能时会递归检查它依赖的其他技能并优先加载。如果发现某个依赖缺失就直接报错提示而不是等到模型执行到一半才暴露问题。还有一个实用技巧设计复合技能。比如我单独写了数据查询和周报生成两个基础技能然后写了一个数据周报的复合技能专门负责把两个基础技能串联起来。这样做的好处是基础技能可以复用到其他场景而复合技能保持了业务入口的单一性。5. 常见问题与排查经验5.1 模型就是不按我的思路走怎么办这是我在群里被问得最多的问题。模型不按设计走通常有三类原因指令本身描述模糊、样例与指令冲突、外部依赖的数据格式与预期不一致。如果是指令模糊解决办法是给每个步骤加上可验证的完成标准。比如分析数据就是模糊指令换成至少输出三个维度的数值变化并针对每个变化解释可能原因模型就会更听话。如果是样例冲突通常是因为样例出现了模型觉得是对的但与你新指令矛盾的格式这时候优先修改样例而不是加强指令。如果是外部数据问题一定要在 skill 执行前做数据格式校验提前拦截异常数据否则模型再聪明也会被脏数据带偏。我的一个批量排查技巧是把同一份输入分别运行五次比较输出差异。如果五次输出结果差异很大一定是指令的确定性不够需要加约束样例如果输出高度一致但都不理想那就要看是不是方向性错误。5.2 技能数量多了之后怎么管理技能一旦超过 10 个光靠人肉维护就开始吃力了。我见过一个团队硬生生积累了 80 多个 skill最后调度成功率反而下降了因为检索环节几乎每回都能命中多个相似技能用户不知道选哪个模型也不知道用哪个。我的经验是第一建立命名规范。技能名称尽量用动词对象的结构比如query_sales_data而不是sales_data这样调度和检索都更清晰。第二定期合并细分技能。如果多个技能的任务流程相似度超过七成就考虑合并成带参数的单个技能。第三给重要技能添加清晰的使用场景说明减少歧义命中。第四要有技能废弃机制不能只增不减。另外建议每个 skill 都记录版本和负责人信息。我在前面的 SKILL.md 里写了 version 字段这看起来是可有可无的元信息但在多人协作时是救命的。它能够避免这个问题我明明修了的经典纠纷。5.3 上下文窗口不够怎么处理虽然 skills 本身就是按需加载的但某些复杂 skill 加上各种样例、模板后占用的 token 依然可观。这个时候有几个优化手段可以尝试。第一个手段是精简指令。把 SKILL.md 里每个句子的长度控制住能说输出为JSON就不要说请把结果以JSON格式输出。第二个手段是缩减样例把 3 个样例减到 1 个高质量样例往往效果差距不大但 token 消耗却少很多。第三个手段是做分步执行。复杂技能不一定要一次把所有内容都加载进上下文可以先加载第一阶段的 skill 内容完成后再加载第三阶段的输出规则。这种方式工程上稍微复杂一点但对 token 压缩的效果非常明显。我自己实测过一个内容生成技能优化前一次调用大约消耗 6000 个 token优化后降到 2200 个同时生成质量不降反升因为模型可以更专注于当前的核心步骤。6. 从单个技能到技能体系6.1 技能的组织方式决定你能走多远当技能数量增长到一定规模你的组织方式会直接决定这个项目是越用越好用还是越用越混乱。我目前比较推崇的是按业务域划分目录而不是按能力类型划分。比如一个团队里同时有运维助手和分析助手两个产品线那它们各自的技能应该分别放在 ops_skills 和 analysis_skills 下面而不是把所有的查询类技能堆在一起。这样做的好处是权限、版本、发布都可以绑定到业务域上避免交叉污染。坏处是不同的业务域可能有一些通用能力重复建设比如两个域都需要发邮件技能。我的折中方案是抽一个 common_skills 目录放公共技能业务流程类技能都往业务域里放。既有复用又有隔离。还有一个我越来越看重的点技能的可测性。一个好技能不应该只靠模型输出好不好来判断好坏还要能在技术上做回归验证。比如周报生成器可以准备一份测试输入和一份期望输出每次改动技能后跑一遍对比看看输出是否还是符合预期。这个机制一开始成本不高但在长期迭代过程中收益极大。6.2 把技能开发当成软件工程来做agent-skills 听起来是一个很轻的概念但真正落地到业务里你会发现它本质上是一套工程体系。版本管理、测试、灰度发布、监控缺一不可。我见过很多团队把技能当成高级提示词来管理最后都会在某个阶段付出代价。我现在的做法是把每个 skill 当成一个微服务来看待有自己的版本、有单元测试、有自己的负责人、有变更记录。这样做的直接后果是技能之间的迭代不会互相踩踏也不会出现某个技能改完另几个技能全挂的情况。当然我不建议一开始就把体系建得很重。如果你的技能数量在 5 个以内一个目录加一个说明文件就够了。但当你的项目开始跨团队、跨产品线时尽早把工程化的规范建立起来远比亡羊补牢来得划算。6.3 我的实操建议从一个小场景跑通闭环如果这篇文章看到这里你也想在自己的项目里引入 agent-skills我最后的建议是不要贪多先找出一个你日常重复率最高、步骤最繁琐、目前最不稳定的任务把它做成第一个 skill。跑通用上感受一下模型行为的改变。然后隔一周回过头来重新读一遍你写的 SKILL.md大概率会想改掉一半的内容——这是正常的也是好事说明你开始用工程思维来理解这个能力了。我个人的体会是技能系统的威力不在于某一个技能多么花哨而在于把不可控的大模型行为变成尽可能可控的模块化决策。这个过程不会一蹴而就但它带来的稳定性、可复用性、团队协作效率是这个阶段最值得投入的方向之一。