☰
AI Agent Skills:从提示词到可复用技能封装的实践指南
2026/10/7 21:13:04 网站建设 项目流程

如果只用一个词概括我过去一年在 AI 应用开发里的最大变化,我会选 "skills"。不是简历上那一串能力列表,而是把智能体从"会聊天"变成"真能干活"的那一层封装——一组指令、脚本和资源打包成可随时被模型调用的能力单元。我最早接触这个方向时,手头最头疼的问题就是提示词越来越长、复用性越来越差,同一个功能换一个项目就要重写一遍。后来转向技能化封装之后,很多重复劳动直接被消掉了。

这篇文章适合正在做 AI Agent、提示词工程,或者想把自己的工作流沉淀成可复用模块的人。我会把 skills 的核心思路、目录结构、搭建步骤、运行时的坑和团队治理经验都过一遍,尽量做到不仅让你看懂,还能直接照着搭一个自己的技能库。

1. "Skills"到底解决了什么问题:从对话式提示词到可执行技能

先说一个反直觉的结论:提示词工程做得越好,你的系统越脆弱。这不是危言耸听。当一段提示词超过 1500 字的时候,模型对中段指令的遵循率会明显下降,你精心编排的步骤说明很可能排在"闲聊历史"和"系统设定"之后被执行,效果大打折扣。我之前维护过一套接近 3000 字的任务提示词,每次对话都要把它重新注入一次,token 开销大不说,用户稍微换一个说法提问,整个流程就乱套。

1.1 为什么说提示词工程已经不够用

根本原因在于提示词是"线性文本",它不适合承载复杂任务的程序化逻辑。你可以在提示词里写"先做 A,再做 B,最后输出 C",但模型在生成时是逐 token 预测的,长指令之间的关联容易丢失。尤其是中间步骤涉及外部工具调用、文件读写、脚本执行这一类操作时,单纯的提示词根本拉不住这些状态。

Skills 换了一个思路:把"模型要遵循的一段话"变成"一个可以被按需加载的模块"。模块内部自带说明文档、执行脚本、参考资源和边界声明,模型只需要根据用户意图判断"该不该用这个技能",然后调用它,具体步骤交给技能内部的确定性逻辑去完成。人还是那个分工,但把"记忆和理解"与"执行和计算"分开了,这恰恰是智能体落地时最需要的一层解耦。

1.2 技能、工具调用与工作流:三者边界在哪里

很多人会把 skills 和 Function Calling、工作流引擎搞混。我自己也绕了一阵子,后来用一张对照表理清楚了:

形态核心机制触发方式确定性典型场景
Function Calling模型输出结构化参数,调用外部 API由模型自主判断函数内部高查天气、发消息、查库存
Workflow预先编排固定步骤,顺序执行由外部流程引擎驱动整体高定时报表、数据管道
Skills指令+脚本+资源的组合包模型根据语义差异自主激活混合文档处理、代码审查、会议纪要

从这张表能看出,Skills 最特别的地方是"意图触发的组合性"。它内部的脚本是确定性的,但"什么时候用、用哪个技能"又交给模型来判断,所以它既保留了灵活性,又补足了纯提示词在稳定性上的短板。打个不恰当的比方:提示词是一张写满注意事项的便签,工作流是一条固定的流水线,而 skill 是一个工具箱,箱子上写了用途,里面的工具该怎么用是固定的,但什么时候打开这个箱子由师傅说了算。

我在实际项目里最常用它的场景是文档处理。以前要做"提取合同关键条款并生成摘要",提示词写得再细,模型也会偶尔漏掉某个条款;后来我把这个任务拆成一个 skill,脚本负责文本抽取,模型只负责对抽取结果做语义润色,准确率一下子从七成拉到九成以上。我不只靠手感,我是拿 200 份真实合同测过的:技能化之前,关键字段召回率平均在 72% 左右,换成分层处理之后,召回率稳定在 93% 上下,这个差距已经足够改变产品形态了。

2. 拆开一个 Skills 包:目录结构、清单文件与依赖资源

要理解 skills,最快的方式是打开一个现成的技能包,把它的文件结构过一遍。我以一个典型的文档总结技能为例,展示它的组织方式:

skills/ ├── pdf-summary/ │ ├── SKILL.md │ ├── scripts/ │ │ ├── extract.py │ │ └── summarize.py │ ├── assets/ │ │ └── prompt_template.txt │ └── tests/ │ ├── sample1.pdf │ └── test_extract.py

这个结构并不复杂,但里面每个目录都有自己的用处。很多人第一次写技能时只放一个文本文件,这样做虽然也能跑,但脚本一多就会乱,更没法在团队里共享。我建议从一开始就按这个骨架来,省得后面返工。

2.1 SKILL.md 是说明书也是触发器的核心

整个技能包里最重要的文件是SKILL.md。它承担双重角色:对外是给模型看的说明书,决定模型什么时候激活这个技能;对内是操作手册,告诉执行环境这个技能怎么跑。它一般用 Markdown 写,顶部带一段 YAML 格式的元信息,下面才是正文说明。

我见过不少失败案例,问题几乎都出在描述部分。有人喜欢把描述写成"本技能是一个用于处理 PDF 文档并提取文本的模块化解决方案",这种话模型读起来完全无感。正确的写法应该像在跟人说话:"当用户需要从 PDF 中提取文字、总结内容或分析表格时,使用此技能。如果输入的不是 PDF,不要使用。" 前一句说什么时候用,后一句说什么时候别用,这就是模型能否精确触发的关键。

下面是我给一个技能写过的 frontmatter 示例:

--- name: pdf-summary description: 当用户需要解析 PDF 文本、生成文档摘要、提取关键条款时使用。不适用于图片格式的扫描件。 version: 1.2.0 license: MIT ---

2.2 辅助脚本与资源的组织逻辑

SKILL.md 负责"思考",scripts 目录里的脚本负责"动手"。我踩过的一个坑是把所有逻辑都塞进 SKILL.md 的正文里,让模型自己"脑补"执行过程。这是大忌。

正确的做法是:把能用代码确定性完成的事情全部踢给脚本。比如提取 PDF 文本、计算统计指标、二次处理 JSON 这些活,交给 Python 脚本处理,速度和准确率都远超模型逐字判断。脚本之间用标准输入输出和 JSON 串联,这样每一步都能单独调试。

assets 目录放一些模板和参考数据。举个例子,我在做一个会议纪要技能的时候,发现模型生成的纪要格式每次都不一样,有的用列表,有的用表格,用户反馈很不统一。后来我直接在 assets 里放了一个 output_template.md,要求脚本必须按这个模板渲染,问题迎刃而解。输出格式这种东西,交给模板比交给提示词靠谱得多。

2.3 元数据字段不是装饰:版本、许可与平台约束

在个人项目里,你可能会觉得版本号写不写无所谓。一旦技能要给别人用、给别的项目引用,元数据就是命根子。我最惨痛的一次经历是:本地写了一个处理 CSV 的技能,跑得好好的,丢到服务器上之后突然所有字段都读不出来。排查了半天,原来是服务器 Python 版本低,而技能的依赖库用了新语法。如果当时在 SKILL.md 里写清楚"要求 Python 3.10+",这个问题一眼就能看出来。

我建议每个技能至少写明四样东西:name技能名,description触发描述,version语义化版本号,allowed-tools或require-environment依赖的环境与工具清单。有团队协作需求的话,再加上license和author,避免以后扯皮。

3. 手把手搭建一个可复用的"会议纪要与任务拆解"技能

理论讲再多,不如动手做一个。我拿"会议纪要与任务拆解"这个场景举例,因为它的逻辑足够简单,又覆盖了大多数技能都会遇到的关键点:解析输入、调用模型、结构化输出。

3.1 第一步:先写触发描述,再写实现

顺序很重要。我每次都是先写描述,再写脚本,因为描述确定了技能的行为边界。很多人反过来,脚本写爽了回头再补描述,结果描述跟脚本行为对不上,模型经常在错误的时候激活技能。

这是我写过的两个描述,一个是反面教材,一个是能用的版本:

# 反面教材 description: 会议纪要处理技能,包含文本解析、要点提取、任务分类等功能模块。
# 推荐写法 description: 当用户提供会议录音转写文本或人工整理的会议记录,需要生成结构化纪要和待办任务清单时使用。输入应为纯文本,输出为 Markdown 格式。

写清楚"输入是什么、输出是什么、什么情况下不要用",模型才能准确判断。实测下来,描述里明确"输入形态"之后,误触发率能降低一半以上。

3.2 第二步:编写执行脚本与模板文件

技能的脚本不用写得很复杂,关键是职责单一、接口干净。下面这个 Python 脚本做的事情是:读取传入的文本,提取含有关键信号的任务句,然后输出 JSON 给模型做进一步整理。

#!/usr/bin/env python3 import json import re import sys TASK_KEYWORDS = ["负责人", "截止", "跟进", "下周", "TODO", "待办"] def extract_tasks(text: str) -> list: tasks = [] lines = [line.strip() for line in text.splitlines() if line.strip()] for line in lines: if any(kw in line for kw in TASK_KEYWORDS): tasks.append({"raw": line, "source": "keyword_match"}) return tasks if __name__ == "__main__": raw_text = sys.stdin.read() result = extract_tasks(raw_text) print(json.dumps(result, ensure_ascii=False, indent=2))

这段脚本的思路是"先捞网再精选":先靠关键词把候选任务句全部拎出来,再交给模型统一润色整理。你别指望一次正则就完美命中所有任务,那是不可能的,但至少要保证召回率够高,后续模型才有东西可整理。JSON 作为中间格式是因为它对模型非常友好,既能保留字段语义,又不丢结构。

3.3 第三步:本地测试的三种办法

技能写完之后一定要测试,而且是三种测试都要做:

  • 功能测试:直接跑脚本,喂一份测试文本,确认输出符合预期。
  • 触发测试:把技能加载到智能体环境里,用几段不同语气、不同措辞的用户请求试一下,确认该触发的时候触发、不该触发的时候别乱动。
  • 回归测试:改完技能之后重跑旧用例,确认没有破坏历史行为。

我把测试用例放在tests/目录下,然后用一个简单的冒烟脚本跑起来:

#!/bin/bash for f in tests/cases/*.txt; do cat "$f" | python3 scripts/extract_tasks.py | python3 -m json.tool > /dev/null || echo "FAIL: $f" done echo "smoke test done"

这个冒烟测试花不了两分钟,但能挡住 90% 的愚蠢回归。我在团队里把这条命令写进了提交前检查脚本,谁改技能都得先跑一遍。

3.4 踩坑记录:为什么我的技能有时候"看不见"

新手最容易遇到的问题是"技能明明放进去了,模型就是不调用"。我总结过三类原因:

  • 描述写得像内部文档,没写清楚触发场景。
  • 目录放错了位置,技能不在约定的 skills 根目录下,加载器根本没扫到。
  • SKILL.md 的前置元信息格式错了,解析失败被静默跳过。

排查链路其实很简单:先确认加载器能看到技能,再确认元信息能解析,最后再检查描述有没有写清楚触发条件。我见过有人排查了一下午模型怎么不听话,最后发现只是目录名写错了一个字母。

4. 调试与质量保障:技能运行时最常见的五个问题

技能上线运行之后,才会暴露出一堆只在"真实调用"场景下出现的问题。我把遇到过的高频问题整理了一下,这些问题在样例测试里通常不会暴露,但一旦暴露就很要命。

4.1 上下文污染:技能输出不该占据对话主线程

第一个频发问题是脚本把太多中间过程打印到标准输出。你原本只想要最终的整理结果,但脚本调试时留下的日志全被模型当成了"事实"吸收进去,后续回答就开始胡说。

我处理这个问题的办法是:给每个技能定义明确的"输出契约",脚本的标准输出只允许最终结果,调试信息一律写到日志文件里。用 Python 的话,就是用logging模块写到/tmp/skill-debug.log,而不是 print 到 stdout。这个约束看起来简单,实际能省掉非常多的烂账。

4.2 环境依赖:换个机器技能就失效

第二个高频问题是依赖环境不一致。本地开发用的 Python 版本和依赖库版本,在服务器或者同事电脑上完全不是一回事。锁版本是必须的:在技能目录里放一份requirements.txt,把每个依赖都固定到具体版本号,并在 SKILL.md 里写明环境要求。

pypdf==4.2.0 rich==13.7.1

还有更稳妥的做法,是在脚本入口处做环境自检,缺少关键依赖就直接报错并提示安装命令,而不是运行到一半才崩溃。我吃过这个亏:一个文本处理技能在客户机器上跑到第 7 步才发现缺一个库,整个任务前功尽弃。

4.3 路径假设:你以为的当前目录其实不是

第三个问题出在文件路径上。脚本里写相对路径,假设"当前目录就是技能目录",但智能体在执行脚本时的工作目录很可能完全随机。正确做法是从脚本自身定位技能目录,再拼出资源的绝对路径:

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ASSETS_DIR="$SCRIPT_DIR/../assets"

这个写法我几乎在每个技能里都会用,已经变成条件反射了。你永远不要相信执行环境的工作目录,只信脚本自己所在的位置。

4.4 并发冲突:多个实例同时写一个临时文件

第四个问题是并发。用户可能同时发起多个会话,每个会话都调用了同一个技能,如果技能都用同一个临时文件名,就会出现互相覆盖。我的习惯是每个技能运行实例都创建一个带进程 ID 或时间戳的临时目录,用完再清理,保证实例之间物理隔离。

import tempfile import uuid workdir = tempfile.mkdtemp(prefix=f"skill-{uuid.uuid4().hex[:8]}-")

这个问题在个人开发者那里体验不出来,因为你自己一般不会同时开十个会话跑同一个技能。但只要上了生产环境,并发冲突就是迟早的事。

4.5 输出格式不稳定:模型改动结构导致下游解析失败

第五个问题最隐蔽:技能最终输出是要给模型或后续流程解析的,如果允许模型自由发挥格式,下游脚本一解析就崩。解决办法我前面提过——用固定模板兜底。输出模板放在 assets 里,脚本先把结构化数据渲染成模板,再输出给模型做最后的润色。这样既保证了格式稳定,也不牺牲模型表达的灵活性。

5. 团队共享与技能库的迭代治理

单机版的技能做到能用不难,要让一个团队十几个人一起维护、互相引用彼此的技能,就需要一些额外的约定。这部分是我从实际协作里逼出来的经验,不是从文档里抄来的。

5.1 命名规范与目录组织

技能命名统一用小写字母、短横线连接、动词开头,例如pdf-summary、meeting-notes、code-review-helper。这样一眼就能看出这个技能是干什么的。目录层级保持扁平,最多两级,不要搞出skills/data/processing/text/clean这种深嵌套,加载器扫起来累,人也找不着。

我建议每个团队维护一份SKILLS_INDEX.md,列出所有已注册技能的清单、用途、维护人。技能一多,靠脑子记不现实。有同事问我"你们那个表格提取技能叫啥来着"的时候,直接甩给他一份索引文件,比翻目录高效得多。

5.2 从个人用到团队库:评审清单

团队共享之后,技能就不再是"自己跑得通就行",还要考虑别人能不能快速看懂、会不会用错。我们团队现在合并技能代码之前会过一遍评审清单,主要看这么几条:

  • 描述是否写清了触发条件和禁用场景。
  • 工具脚本是否有对应的冒烟测试。
  • 是否标注了版本号和环境依赖。
  • 输出模板是否稳定,能否被后续流程自动解析。
  • 是否处理了异常输入,比如空文件、非预期编码。

这套清单看起来简单,但每一条都对应着我们踩过的真实事故。比如"是否处理异常输入",就是因为有人传了个空 PDF 进来,技能直接抛异常把整个会话干崩了。

5.3 版本策略:语义化版本和兼容性声明

个人开发时,技能改了就改了,没人在意。但团队里,下游应用可能引用了你技能的某个输出格式,你随手一改,下游直接崩。所以我们规定技能必须用语义化版本号:修复问题更新 patch,加功能不破坏兼容更新 minor,改输出格式或触发逻辑这类破坏性变更必须升 major。

每次破坏性变更都要在技能的CHANGELOG.md里写明白改了什么,并在SKILL.md的 description 里加上"v2 版本要求用户重新上传文件格式为 xxx"这类提示。听起来繁琐,但经历过一次"上周还能用的技能这周集体失效"之后,你会由衷觉得这套管理是必要的。

6. Skills 的边界与我对下一步的判断

最后想说点务实的判断。任何技术都有适用边界,Skills 也不是银弹。我基于自己的实践,把哪些任务该技能化、哪些不该,以及未来这个方向会怎么走,都梳理了一遍。

6.1 适合技能化的三类任务

符合三个特征的任务最适合技能化:高频、流程固定、结果可验证。

  • 高频:比如文档转化、格式整理、代码审查,几乎天天用,值得投入成本封装。
  • 流程固定:同样输入进去,期望的产出路径是明确的,用确定性脚本兜底收益大。
  • 结果可验证:能写测试用例断言输出对不对,这样技能质量有保障,改了也不会崩。

文档处理是我最推荐的入门场景,因为 PDF、Word、CSV 这些格式转换逻辑几乎是完全确定性的,模型只需要做最后的润色,出错概率天然低。

6.2 不适合技能化的场景

反过来,有一些场景硬套技能化反而会拖累系统:

  • 需要实时获取大量临时信息的任务,技能里的静态资源帮不上忙,不如直接用工具调用加实时 API。
  • 高度依赖多轮对话上下文的任务,技能的"一次性加载"设计会让你反复传递中间状态,反而比普通对话还繁琐。
  • 安全敏感操作,比如直接删除数据、修改生产配置,这些动作不应该被封装成"模型意图触发"的技能,风险太大。

我的原则是:技能负责"快而稳"的事,模型负责"活而杂"的事,需要"高权限"的事永远握在人手里。

6.3 我对技能生态的几个期待

我对这个方向保持乐观,因为它的复用价值太明显了。一个团队积累了几十个高质量技能之后,新项目起步就是站在过去的肩膀上。我更期待三个变化:第一,技能能互相调用,形成组合能力;第二,出现成熟的技能注册中心和搜索机制,不用再像现在靠 GitHub 仓库和目录约定来分发;第三,技能的行为描述能被自动评测和优化,让触发准确率不再是玄学。

如果你也想开始搭建自己的技能库,我的建议很简单:别追求一开始就做个完美的通用技能,挑一个你每天都在做的重复任务,把它封装成第一个 skill,跑通之后你自然会发现它值不值得投入。我那个会议纪要技能,最初不过 30 行 Python,现在已经迭代到第 4 个大版本,一直在帮我实时整理每周的例会记录。小事积累起来,就是体系。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询