“你天天挂在嘴边的 Agent Skill,到底有什么用?不就是把 Prompt 写得更长一点吗?”
上周某位朋友在微信上突然这么问我。他最近刚上手智能体开发,已经能让大模型按固定格式写周报、自动总结会议纪要,觉得自己玩得挺明白了。这个问题也不怪他——我在聊到智能体架构时总是把“Skill”说得轻描淡写,但没有一个清晰的解释告诉大家:Skill 解决的到底是什么问题,为什么要把它当成一个独立的工程概念,而不是一句轻飘飘的“给 Agent 加个工具”。
当时我解释了很久,然后意识到这件事值得写一篇完整的文章,而不是靠一段语音讲清楚。原因很简单:Agent Skill 这个名字听起来像“技能”,但它既不是单纯的函数调用,也不是普通 Prompt 模板,它是一套把指令、示例、代码、校验流程打包成独立单元的封装思路。这种封装的收益,只有自己动手做过一遍,踩过几个坑,才能真正体会到。
这篇文章写给已经会用智能体、但还没系统想过“如何设计可复用的智能体能力”的人。我会先用大家熟知的场景把概念讲明白,然后拆解一个技能的完整构成,最后带大家从一个实际技能出发,完整走一遍从设计到验证的过程。重点是我自己踩坑后总结出的细节和排查经验,这些在官方文档里通常找不到。
1. 先把“Agent Skill”这件事说透
1.1 它不是“更长的 Prompt”,而是“抽屉里的专用工具”
先打个比方。一个传统 Prompt 就像把使用说明书整本塞进大模型的脑子里,让它在任何对话里都记住全部内容。而 Agent Skill 更像家里的工具箱:电钻、螺丝刀、水平尺各占一个抽屉,抽屉外贴着一张标签,写着“什么时候用、大概能干什么”。当你需要拧螺丝时,才打开对应抽屉,拿出工具,用完再收回去。
这个差异在真实场景中体现得非常明显。我见过很多人把系统提示写到一万词,恨不得把公司所有业务规则、话术模板、代码规范全部堆进 system prompt。结果就是上下文占用高、大模型选择困难、输出质量反而不稳定。Skill 的核心逻辑恰好相反:默认情况下大模型只看到每个技能的“标签”(名称和功能描述),只有当任务与标签匹配时,才把技能内部完整的说明和脚本加载到上下文中。这种“按需加载”让信息使用效率高很多,也降低了大模型被无关信息干扰的概率。
我一直认为,Agent Skill 是对“如何和大模型协作”的一次重新定义:不是命令大模型“记住一切”,而是给大模型提供一套可检索、可调用、可复用的能力集合。它会变成一个像函数库一样的东西,只不过这个“函数库”的使用者是模型,而不是程序员。
1.2 它和函数调用、RAG 有什么本质区别
很多人在第一次接触 Agent Skill 时会把它和函数调用(Function Calling)或者 RAG 混淆。这里我直接给出一个简单的对照逻辑:
- 函数调用解决的是“大模型如何准确地表达意图并触发一段程序”,本质是“把自然语言翻译成 API 调用”。
- RAG 解决的是“大模型如何获取未在训练时见过的知识”,本质是“把外部文档转化为可检索的上下文”。
- Agent Skill 解决的是“大模型如何在不重新写 Prompt 的情况下,复用一套完整的行为流程”,本质是“把指令、知识、脚本、校验包装成一个可迁移的单元”。
举一个例子。如果你想给智能体增加“生成周报”的功能:
- 用函数调用,你写一个
generate_report(data)的接口,让模型在合适时机调用,但格式、风格、内容结构仍然需要靠 Prompt 约束。 - 用 RAG,你可以把历史周报塞进知识库,让模型参考风格,但它不知道应该调用什么脚本或工具来做字段提取。
- 用 Agent Skill,你可以创建一个
weekly-report-skill,里面既包含“何时生成、生成什么格式”的说明,也包含一个 Python 脚本来整理本周数据、渲染 Markdown 表格,还包含一个示例输出供模型参考。模型识别到“用户要周报”后,会读取整个技能说明,按步骤执行、运行脚本、校验结果,最后输出合规的周报。
三者的边界不是绝对的,很多时候会结合使用。但在架构设计上,一定要意识到 Skill 承担的职责和其余两个完全不同。
1.3 为什么“复用”会成为智能体开发的拐点
大模型应用开发初期,大家解决一个问题就写一个 Prompt,项目里散落着几十个长得差不多的提示词文件。某次改动业务术语,要手动同步五六个文件。这种复制粘贴式的开发方式,在单 Agent 的小项目里勉强能撑住,一旦进入多智能体协作、多个场景复用的阶段,就完全不可行了。
Agent Skill 之所以这阵子很火,正是因为它把长期被忽视的“复用”问题摆到了台面上。一个团队可以维护一个技能仓库,不同项目按需接入;甚至不同的智能体之间也能共享同一套技能,只需要在加载时指定技能目录。这意味着“智能体的能力”真正具备了工程意义上的独立性和版本管理能力。
你不需要从一开始就追求复杂的框架,但要尽早建立“凡是重复两次以上的能力,都应该做成技能”的思维。这个思维转变,能让你在后续项目里省掉大量返工时间。
2. Agent Skill 的核心组成与设计逻辑
2.1 一个技能包里到底该放什么
一个标准的 Agent Skill,通常以目录为整体单元。我习惯的目录结构长这样:
my-skill/ ├── SKILL.md ├── scripts/ │ ├── run.py │ └── helpers.py ├── assets/ │ └── templates/report_template.md ├── requirements.txt └── tests/ └── test_skill.pySKILL.md:技能的大脑,包含了技能名称、描述、使用步骤、注意事项、调用示例。这是大模型真正会“阅读”的核心文件。scripts/:实际执行任务的代码。它负责那些大模型不擅长或者不应该靠“想”来解决的事情,比如精确计算、字符串解析、文件操作、数据请求。assets/:固定资源,比如输出模板、参考样例、配置文件。技能内部需要引用但不直接展示给模型的静态资源。requirements.txt:技能运行所需的依赖清单,方便在正式环境里一键安装。tests/:用于验证脚本正确性的测试文件。很多人会省略这一步,但我强烈建议保留,因为 Agent 开发中最怕的不是模型不聪明,而是脚本悄悄出错你却不知道。
最好的理解方式是:SKILL.md 是说明书,scripts 是工具本体,assets 是耗材,requirements 是工具清单,tests 是质检报告。一个技能包就是一个人可以独立完成某类工作的“工作站”。
2.2 SKILL.md 的每一部分都有明确目的
现在重点拆解SKILL.md,因为它是 Agent Skill 的入口和灵魂。一个规范的 SKILL.md 用 YAML 头加正文组成,YAML 头主要包含这几个字段:
--- name: weekly-report-generator description: 生成周报的专用技能。适用于开发者周报、项目进展同步、团队管理汇报等场景。输入原始工作记录,输出结构化的 Markdown 周报。 when_to_use: 当用户要求生成周报、汇总本周工作、整理项目进展时使用;当用户只是询问某一天的工作内容而明确不需要周报时,不要使用本技能。 version: 1.2.0 ---name是技能的唯一标识,尽量简短明确。description是模型用来判断“要不要调用这个技能”的关键依据,必须写清楚“这个技能能做什么、适合什么场景”。最容易犯的错误是把 description 写成“一个周报工具”,这种描述会让模型在犹豫时直接跳过技能。更实操的做法是像上面那样,用“动词 + 对象 + 典型场景 + 输入输出格式”来描述。
when_to_use是我在其他地方很少看到有人强调、但作用至关重要的字段。它是在给模型划边界,告诉模型什么情况下可以用、什么情况下不要用。加了这一步之后,“误用技能”的概率能下降一半以上。
YAML 头之后是正文,我通常按照下面四个小节来组织:
- 目标:用两三句话说清楚这个技能完成什么任务,让模型对最终产出有预期。
- 步骤:明确列出执行步骤。注意要写“模型需要做的步骤”和“脚本需要做的步骤”,让分工清晰。
- 注意事项:列出常见错误、边界条件、输出格式硬性要求。这一部分特别有价值,因为模型在推理时会主动参考这些约束。
- 调用示例:附一个“用户的输入”和“完整的输出”示例,这是模型理解技能的最佳途径。
没错,一份好的 SKILL.md 本质上是一份“给模型看的标准作业流程”。它不是一个技术架构文档,不要用纯技术语言,要用模型容易理解的、结构清晰的、步骤化的人类语言来写。
2.3 为什么这种封装能显著降低“翻车率”
我在前面提到,Agent Skill 最大的价值之一是“按需加载”。但从工程实现角度深挖,它的设计还从三个维度显著降低了智能体开发的整体风险:
- 上下文可控性:把大量指令性内容从系统提示中移出,交给技能文件被动加载。这样主提示保持精简,模型可以集中注意力在当前任务,上下文长度也不容易失控。
- 决策确定性:技能中的“步骤”将模型可能走的路径收敛到明确流程中。比如让它先调用脚本解析数据,再根据解析结果生成正文,而不是让它自由发挥。这相当于给模型的“思维过程”加了轨道。
- 可测试性:脚本可以被单独测试,输出格式可以被程序校验。这样即使模型偶尔理解有偏差,最终产物也能通过后置检查来纠错。
我自己在实际开发中的体感是,加了 Skill 之后,同样任务的输出稳定性提升明显,尤其是那些流程复杂、格式要求严格、有固定处理步骤的任务。相比“全凭模型悟性”的做法,Skill 的确定性优势是压倒性的。
3. 实操:从零构建一个“客户反馈总结”技能
3.1 先想清楚技能的主流程
理论讲得再多,不如动手做一个。这一次我们来做“客户反馈总结”技能,目标是从客户发来的分散消息里,提取关键信息、打上问题分类、生成一份结构化的汇总报告。选择这个例子,是因为它覆盖了一个技能里几乎所有典型环节:文本解析、分类映射、格式化输出、调用外部脚本。难度适中,又能暴露大部分常见问题。
开始之前,我先确定主流程:
- 模型读取用户提供的原始反馈文本。
- 模型判断是否属于本技能适用场景(有没有客户反馈、投诉、报障)。
- 调用
scripts/parse_feedback.py,对原始文本做关键字分类和情感预判。 - 模型根据脚本输出结果,结合上下文补充备注,生成 Markdown 汇总报告。
- 校验报告必填字段是否齐全。
3.2 实际动手搭建技能目录
我先创建目录结构:
mkdir feedback-summary-skill cd feedback-summary-skill mkdir -p scripts assets tests然后写SKILL.md,这是整个技能能否被正确调用的关键文件,我直接把我实际用的模板分享出来:
--- name: feedback-summarizer description: 从客户原始反馈中提取关键信息,分类并生成汇总报告。适用于客服团队、产品运营团队处理用户投诉、功能建议、使用疑问等场景。 when_to_use: 当用户提供多段客户反馈并要求整理分析时使用;当用户只是询问如何写客服话术时不要使用本技能。 version: 1.0.0 ---正文部分:
## 目标 将杂乱的客户反馈转成结构化的汇总 Markdown 报告,报告包含问题分类、情绪倾向、处理建议、原始引用。 ## 执行步骤 1. 阅读用户提供的反馈原文,提取每条反馈的核心诉求。 2. 运行 `scripts/parse_feedback.py < input.txt`,传入原文路径,获得 JSON 格式的初步分类结果。 3. 将分类结果与用户补充信息合并,形成报告内容。 4. 严格按照下方“输出格式”生成 Markdown 报告。 5. 检查报告是否包含所有必填字段:分类、标题、情绪、建议、原文引用。 ## 注意事项 - 不要虚构用户没有说过的话。 - 若原始反馈涉及多个问题,拆成多条记录,不要强行合并。 - 分类只能从: ["功能问题", "体验问题", "建议", "咨询", "投诉"] 中选择。 - 如果脚本输出为空或报错,请停止流程并告知用户“反馈解析失败”。 ## 输出格式 ```markdown | 序号 | 分类 | 核心问题 | 情绪 | 处理建议 | 原文引用 | |------|------|----------|------|----------|----------| | 1 | 功能问题 | ... | 负面 | ... | > "原文摘要" | ### 本轮反馈总结 - 高频问题:... - 需要立刻处理:...示例
用户输入:"这个应用更新后老是闪退,我已经重启三次了。另外希望增加深色模式。"
模型输出:
| 序号 | 分类 | 核心问题 | 情绪 | 处理建议 | 原文引用 |
|---|---|---|---|---|---|
| 1 | 功能问题 | 应用更新后闪退 | 负面 | 优先排查崩溃日志 | > "这个应用更新后老是闪退" |
| 2 | 建议 | 希望增加深色模式 | 中性 | 加入需求池评估 | > "希望增加深色模式" |
本轮反馈总结
- 高频问题:稳定性问题。
- 需要立刻处理:崩溃问题影响面较大,建议第一时间响应。
你可能会注意到,我把输出格式写得非常具体,这是故意的。大模型在看见一个带着表格结构和示例的说明时,比看一句“请整理成 Markdown 报告”要容易理解得多。格式约束越清晰,输出偏差越小。
3.3 写脚本做模型做不好的事
parse_feedback.py的作用不是做 NLP 层面的高深分析,而是替模型完成“完整读取输入、按预设规则分类”这种精确操作。脚本可以把结果输出成 JSON,让模型直接拿来做下一步推理。下面是个简化版本:
import json import sys import re CATEGORY_KEYWORDS = { "功能问题": ["闪退", "崩溃", "报错", "无法打开", "卡死", "白屏"], "体验问题": ["太难用", "不直观", "找不到", "反人类", "不方便"], "建议": ["希望", "建议", "增加", "能不能", "考虑"], "咨询": ["怎么用", "在哪", "如何", "请问", "操作"], "投诉": ["投诉", "赔偿", "垃圾", "差评", "再也不"], } NEGATIVE_WORDS = ["差", "慢", "闪退", "崩溃", "太难", "垃圾", "投诉"] POSITIVE_WORDS = ["好用", "流畅", "喜欢", "满意", "不错", "赞"] def classify(text: str) -> str: scores = {} for category, words in CATEGORY_KEYWORDS.items(): scores[category] = sum(1 for w in words if w in text) if not scores or max(scores.values()) == 0: return "未分类" return max(scores, key=scores.get) def detect_sentiment(text: str) -> str: neg_score = sum(1 for w in NEGATIVE_WORDS if w in text) pos_score = sum(1 for w in POSITIVE_WORDS if w in text) if neg_score > pos_score: return "负面" if pos_score > neg_score: return "正面" return "中性" def parse_input(raw: str) -> list: lines = [l.strip() for l in raw.splitlines() if l.strip()] records = [] for idx, line in enumerate(lines[:50], start=1): records.append({ "id": idx, "text": line, "category": classify(line), "sentiment": detect_sentiment(line), }) return records if __name__ == "__main__": raw_text = sys.stdin.read() print(json.dumps(parse_input(raw_text), ensure_ascii=False, indent=2))脚本接受标准输入,逐条扫描反馈文本,输出结构化的 JSON。虽然关键词判定的方式比较粗糙,但它足够稳定,也不会像大模型那样“发挥过度”,适合作为技能内部的确定性处理器。实际项目中如果要做更精准的语义分类,可以在这个位置换成调用分类 API,但整体架构保持不变。
写完之后别忘了补上requirements.txt。这个示例里只用了 Python 标准库,所以文件里可以留空,但我倾向于写一行注释说明用途。真实项目中只要有用到第三方库,就一定要固定版本,否则技能换一台机器就跑不起来,这是很多新手会踩的坑。
3.4 把技能接入主智能体
每个智能体框架对技能目录的加载方式略有不同,但基本思路一致:在主配置里声明技能目录位置,框架启动时扫描所以 SKILL.md,提取 YAML 头生成“技能索引”。模型在运行时看到的是技能名称和 description。当用户的请求与 description 匹配时,框架才会把完整的 SKILL.md 正文注入上下文。
我通常在接入时做三件事:
- 检查“技能索引”里的 name 是否唯一,没有与其他技能重名。
- 用一句测试问题验证技能是否能被自动触发。比如问“帮我把这几条客户反馈整理成报告”,观察模型是否读取到 SKILL.md 内容。
- 在测试阶段暂时关闭其他技能,避免模型因为“选择太多”而调错。等验证通过,再逐步放开其他技能。
3.5 测试技能时的两段式验证法
技能接入之后,验证工作不能只靠“跑一次u200b看效果”。我习惯把验证拆成两层:
- 调用层验证:确认模型在什么场景下会触发该技能。这一步不需要真的执行脚本,只需要观察模型是否读取了技能的完整说明。用不同的提问方式反复测,比如直接说“整理反馈”、含蓄地说“这些抱怨你帮我汇总一下”、拿来一段与技能无关的技术问题,看模型是否正确触发或正确拒绝。如果该触发时没触发,大概率是 description 写得不到位。
- 输出层验证:确认技能生成的报告结构是否规范、字段是否完整、脚本是否稳定执行。可以先准备好几组标准测试数据,跑脚本对比输出;再让模型生成报告,人工检查格式。输出层的问题通常出在 SKILL.md 里的“注意事项”没有写清楚,或者脚本本身存在边界 bug。
这个两段式验证法,帮我省去了大量“模型跑起来效果不对,但分不清是逻辑问题还是框架问题”的排查时间。
4. 我踩过的坑:Agent Skill 常见问题与排查实录
4.1 技能写了但模型就是不调用
这是我在最初做技能时遇到最多的问题。你辛辛苦苦写好了 SKILL.md,目录也挂载好了,模型却像看不见一样,自己凭记忆硬答。排查思路其实有几条,我按优先级排一下:
- 先看 description 是否足够“诱人”。如果描述是“一个用于处理客户反馈的技能”,模型很难理解什么情况下该用它。改成“当用户提供客户原始反馈并要求整理分析时使用”,触发率会明显上升。
- 再看 when_to_use 是否写了反向条件。我强烈建议每份 SKILL.md 都写明“什么时候不要用”。模型一旦拥有了这个技能的“排除条件”,误触率会大幅下降,但很多人会忽略这一点。
- 最后确认主提示里没有“技能无用”之类的指令。如果系统提示里写了“不用调用任何工具,直接回答”,那模型再聪明也不会去读技能。
定位问题最快的方法是开启智能体框架的调试模式,观察模型在收到用户请求后到底看到了哪些消息、有没有注入技能内容。这一步能看到所有技能触发判断过程。
4.2 技能被乱用:模型把不相干任务也套进技能里
技能不被调用是问题,技能被滥用又是另一个极端。有时候用户只是想问一句“你们产品怎么收费”,模型却直接调用了反馈总结技能,把一句话硬生生整理成报告。这种情况的根源,往往是 SKILL.md 里的边界条件写得不够清晰。
解决思路是在 when_to_use 中写得更具体,同时在正文“注意事项”里增加一句“如果在执行过程中发现输入内容与客户反馈无关,立即中止并回复用户:这不在我的处理范围内”。给模型一个“退出机制”,它就不会一条路走到黑。
还有一个我后来养成的习惯:技能内部的关键操作,比如生成报告、发送邮件、写入数据库,要求模型在执行前必须先用一句话向用户说明“即将做什么、为什么这么做”。这个简单的确认步骤,能挡住一半以上的误操作。
4.3 技能说明太长,反而挤爆上下文
有些开发者恨不得把项目管理规范、公司制度全部写进 SKILL.md,导致技能文件几万字。结果就是每次调用这个技能,上下文立刻被灌满,模型的推理能力下降还容易“丢三落四”。
这里有一个基本的上下文预算意识。技能文件被加载时,会占据相当一部分上下文窗口,而窗口总量有限。如果一个大技能动不动就占三万字,那留给当前对话数据的空间就所剩无几了。解决办法是“分拆”:把大技能拆成多个子技能,SKILL.md 里只写精简版步骤,更长的参考材料和模板放进assets/目录;模型需要详细了解模板时,可以再让脚本读取对应文件内容,而不是全文加载进上下文。
比如前面那个反馈总结技能,完整的公司话术模板就不该写进 SKILL.md,而应放在assets/templates/目录,由脚本按需读取。这个做法能很好地控制上下文开销。
4.4 技能之间互相打架
有段时间我同时挂了十几个技能,模型经常出现“左右互搏”:用户要一份周报,模型先调了周报技能,又觉得其中一步需要调用数据分析技能,两个技能互相交叉,最终输出结构混乱。
技能多了之后,一定要做“边界梳理”。我会做一张表格,把每个技能的 description、适用场景、关键词列出来,查看是否有重叠。发现两个技能描述相近时,要么调整边界,要么给技能增加“前置条件”,例如在周报技能里明确说明“本技能不包含数据分析,如数据量较大,请在报告中标注数据来源字段,而不是直接调用其他技能”。
另一个方法是在主提示中加入技能选择策略,比如“当任务可以被多个技能完成时,优先选择包含完整流程的那一个,并且不允许在一个回答中组合多个技能的输出”。这句话虽然简单,但能避免绝大多数混乱调用。
4.5 依赖环境引起的“时好时坏”
技能本身写对了,但运行时提示缺包、版本不匹配,或者模型读不到脚本生成的文件,这种环境类的坑往往最耗时间,因为它的表现不像逻辑错误那么直观,经常是一次能跑、一次不能跑。
我现在所有技能都按这三条规则来管理:
- 每个技能必须带独立的
requirements.txt,并锁定版本号,不用“>=”或“latest”。 - 部署时给每个技能准备独立的运行环境或容器,避免不同技能依赖冲突。
- 在任何一次重大改动后,先跑一遍技能的测试脚本,再接入智能体做整体测试,而不是“改了就直接上”。
另外,脚本路径尽量不要用相对路径,而是在 SKILL.md 里约定一个固定工作目录,脚本内部使用绝对路径定位输入输出文件。否则模型在运行时可能目录搞错,导致文件找不到,报错信息又不直观。
4.6 输出格式不稳定,模型偶尔“自由发挥”
即使 SKILL.md 里已经写了严格的输出格式,模型在极端情况下还是可能输出错误结构。面对这种情况,我会做两件事:
- 把“输出格式”部分写成可解析的模板,并且在“注意事项”里强调“不得更改字段名,不得改变表格列数”。模型对“不能动”这种约束的遵守程度,比对“建议如何”要高得多。
- 增加后置校验脚本。让智能体在输出报告后,调用一段校验代码检查必填字段和格式;如果不通过,自动要求模型重新生成。这个“人工后置校验换成程序校验”的思路,是把最后一道防线交给确定性代码,而不是模型自觉。
我在实际项目里,通过加一个几十行的格式校验脚本,就把输出格式的稳定性从“经常出错”提升到“连续跑几百次都不出错”。这笔投入非常值得。
最后想说的一点经验
Agent Skill 这个概念刚出来的时候,我也觉得“这不就是把 Prompt 整理一下吗”,直到自己维护的智能体数量变多、业务场景变复杂,才意识到它真正的价值不是“让模型多干活”,而是“让模型少想”。
每次设计新技能时,我的思考顺序其实很简单:先拆流程,把模型擅长的部分(理解、归纳、表达)和模型不擅长的部分(精确计算、稳定解析、格式校验)分开;然后把后者交脚本,前者以结构化说明的方式写进 SKILL.md;最后用测试数据把调用边界确定下来。整个过程像是给模型搭了一条有护栏的跑道,它跑得快不快另说,至少不会动不动冲出跑道。
后续如果你们团队的智能体开始跨项目复用,可以往“技能仓库”的方向走:把技能按领域分类、版本化、加上作者信息和变更记录,做成团队内部共享的能力中心。到那个阶段,Agent Skill 就真正从一个开发便利工具升级成了组织级的资产沉淀方式。
如果你想从零开始试,我的建议是先挑一个你自己反复做过的重复性任务,哪怕只是“把一段聊天记录整理成待办清单”这种小功能,严格按上面提到的结构把它做成技能,跑上两周。体验过“一次编写、处处复用”之后,你就再也不会回到把所有逻辑塞进一个巨型系统提示的老路上去了。