先从几个核心判断聊起吧。近期集中调研和学习了 SKILLS 的相关内容,不少人对这个概念存在理解偏差。这篇文章算是把这段时间的思考做个系统梳理,希望能帮到正在探索 Agent 开发方向的同学。
SKILLS 是什么
简单来说,SKILLS 本质上就是在做一件事——组织和管理 Prompt 提示词。
无论是 SKILLS 还是那些包装得颇为复杂的 Agent 框架,拆开来看,核心工作都离不开“往 Prompt 里塞什么、怎么塞”这两个问题。这一点想明白了,很多困惑自然就解开了。
与传统 Prompt 相比,SKILLS 最关键的改进在于引入了“按需注入”机制。以往是一股脑儿把全部规则塞进 System Prompt,无论当前任务是否需要。按需注入的好处很直观:大幅减少 token 使用量,说白了就是省钱。
基于什么发展而来?
SKILLS 不是凭空冒出来的概念,背后是一条清晰的演进脉络。
1. Prompt Engineering 的沉淀
早期的开发者很快发现:同样的任务,仅仅是提示词的措辞不同,最终结果可能天差地别。经过大量试错之后,那些被证明有效的提示策略自然需要被复用、被共享——SKILLS 就是用来承载这些经过验证的工程经验的容器。
2. RAG 的思路迁移
RAG(检索增强生成)的核心思路是在运行时动态注入外部知识,SKILLS 借鉴的正是这个逻辑——只不过注入的不是事实性知识,而是过程性知识,也就是“怎么做”的指导。运行时注入知识,而不是提前写死在系统提示里。
3. Tool Use / Function Calling 的延伸
当大模型具备了调用工具的能力之后,下一个必然的问题是:什么场景该用什么工具、怎样用好?这正是 SKILLS 要解决的事情。工具本身是通用的,但使用工具的方法和最佳实践需要被专门定义。
4. ReAct / Chain-of-Thought 框架的落地需求
像 ReAct 这类框架赋予了 Agent 规划-行动-观察的循环能力,但每个领域的规划策略是不同的。举个例子,处理文档的推理流程和处理代码的推理流程完全是两回事。SKILLS 为不同领域提供了定制化的推理引导,相当于把人类专家的经验编码成 Agent 可读的操作手册。
一句话总结 Agent Skill 的本质:把人类专家积累的操作经验,编码成 Agent 在运行时能动态读取的规范文档,让 Agent 能够像领域专家一样执行特定任务。
单一任务 Agent 使用 SKILLS 的价值
这里需要面对一个现实:对于只做单一类型任务的 Agent 来说,SKILLS 按需选择知识的核心优势确实发挥不出来——因为任务种类单一,没什么好“选”的。
但问题没那么绝对。即使只处理一种任务,你仍然面对这几个现实困境:
- 这个任务的最佳实践怎么沉淀下来?
- Prompt 写在哪里、谁来维护、怎么迭代版本?
- 新同事来接手,怎么快速理解这个 Agent 的设计意图?
所以此处的判断需要分情况:对多任务 Agent 来说,SKILLS 的动态检索能力是整个架构的核心价值所在;而对单任务 Agent 来说,SKILLS 作为动态检索机制的价值确实有限,但如果把它看作 Prompt 知识库的组织形式,仍然有不可忽视的工程价值——至少在可维护性上比把几千行 Prompt 堆在一个文件里要强得多。
当然,如果单任务 Agent 的 Prompt 本身就很简短,那确实没必要刻意引入 SKILLS 机制——直接写死在 System Prompt 里反而最省事。
看几个 SKILLS 的例子
理论讲再多也不如看几个实际的例子来得直观。
第一个例子:Claude Desktop 内置了不少 SKILLS 示例,比如 MCP-builder 这个 Skill,可以直接引导模型按照规范步骤开发 MCP Server。
第二个例子:Anthropic 官方在 GitHub 上放了一个 Skills 仓库(github.com/anthropics/…),里面包含了各种实际可用的 Skill 范例。比如处理 Python 生成 PDF 文档的 Skill,规定了调用哪些库、按什么步骤处理文档、异常如何兜底。
第三个例子:Vercel 团队推出了 React 最佳实践的 Skill(github.com/vercel-labs…),专门用来引导 AI 编程工具按照正确的 React 规范来写代码。同样的思路,Vue、Next.js、Svelte 也都陆续有了各自的 SKILLS。
实际体验 SKILLS
在 Claude Desktop 里做了一个测试:既然内置了 MCP-builder 的 Skill,那就直接让它用这个 Skill 开发一个 MCP Server。
使用 nodejs 技术栈帮我开发一个 MCP server ,用于在 cursor 中连接 notion ,可以查看和搜索我在 notion 中的文档和内容。
可以看到模型会主动搜索已有的 Skills,识别出与当前任务匹配的 Skill,然后严格按照 Skill 中定义的步骤来执行操作。这就不是简单的 Prompt 模板背诵了——而是真正在执行一个经过验证的工作流。
不过这里有一个需要注意的地方:执行过程不是瞬间完成的。第一次测试时等了大概 10-15 分钟,一度以为卡死了——结果它自行完成了整个流程。
最终产出了完整的开发步骤、使用方法说明、所包含的 Tool 清单,以及可以直接下载的代码包。如果没有 SKILLS,仅靠一个混长的 System Prompt,很难想象 AI 能够如此精细地完成这类复杂任务的开发。
SKILLS 和 Tools
值得注意的一点:SKILLS 中会定义很多脚本和命令——Python 脚本、JS 脚本、Shell 命令行等等。但这些代码和 SKILLS 中其他文字一样,本质上只是文本,会作为 Prompt 一起被传递给大模型。
问题在于:大模型本身无法直接执行这些代码。代码的执行必须依赖 Agent 中定义的 Tool。
Skill.md 里的 Python 脚本↓ 只是文本/模板↓Agent 通过 Tool 执行↓ 才真正产生结果
这意味着什么?如果你要自己开发一个 Agent,必须先定义一个能够执行 Python 代码的 Tool,Skill 中的 Python 脚本才能被真正执行起来。
const executePythonTool = tool(async ({ code }) => {const tmpFile = `/tmp/script_${Date.now()}.py`;fs.writeFileSync(tmpFile, code);return new Promise((resolve) => {exec(`python3 ${tmpFile}`, (error, stdout, stderr) => {fs.unlinkSync(tmpFile);resolve(error ? `执行失败: ${stderr}` : stdout);});});},{name: 'execute_python',description: '执行 Python 脚本',schema: z.object({code: z.string().describe('要执行的 Python 代码'),}),});
SKILL 中的“中间数据”
举个例子,一个 SKILL 可能有这样的描述:先要把文档转换为 JSON 格式,再把 JSON 生成表格。这里的 JSON 数据就是一个中间数据,不是最终产出。
对于规模不大的中间数据,通常不需要额外管理——直接把处理步骤作为描述写入 Prompt,由大模型统一处理即可。
但如果中间数据量很大,就需要提供一个 write_file 这样的 Tool,让大模型把中间结果写入本地或服务器的临时文件。
Skill 说"写入 /tmp/intermediate.json"↓LLM 理解这个意图,生成如下代码:fs.writeFileSync('/tmp/intermediate.json', JSON.stringify(data))↓LLM 调用 execute_nodejs Tool 执行这段代码↓Tool 在服务器/沙箱环境里真正创建了这个文件
SKILLS 和 RAG
RAG 本质上也是一种运行时信息注入机制,和 SKILLS 非常相似,但两者的目的完全不同:
Skill → 注入"怎么做"(操作规范、流程)RAG → 注入"知识内容"(业务数据、文档库)
在架构中,它们各自的位置是这样的:
用户输入↓[RAG 检索] ──→ 从知识库找相关内容 ──→ 注入 Prompt[Skill 检索] ─→ 找对应操作规范 ────→ 注入 Prompt↓[LLM] 结合两者生成计划↓[Tool 执行]
RAG 和 SKILL 属于同一层级的东西,都在 LLM 推理之前注入 Prompt。区别在于:SKILL 给方法,RAG 给数据,Tool 负责执行。三者协同才是完整的 Agent 架构。
用一个实际场景来说清楚:用户提问“把我们公司 Q3 销售数据生成 Word 报告”。
RAG 检索→ 从公司文档库找到 Q3 销售数据.csv 的内容→ 注入 Prompt:"以下是相关数据:销售额 300万,同比+15%..."Skill 检索→ 匹配到 docx Skill→ 注入 Prompt:"生成 Word 文档请遵循以下规范:..."LLM 结合两者→ 知道数据内容(来自 RAG)→ 知道怎么生成文档(来自 Skill)→ 生成代码调用 Tool 执行
没有 RAG,LLM 不知道 Q3 数据是什么。没有 Skill,LLM 不知道怎么规范地生成 Word。两者缺一不可。
async function runAgent(userInput) {// Step 1: 并行检索(RAG + Skill)const [ragContext, skill] = await Promise.all([retrieveFromRAG(userInput), // 检索业务知识retrieveSkill(userInput), // 检索操作规范]);// Step 2: 组装 Promptconst systemPrompt = `你是一个智能助手。
如何在 Agent 中实现 SKILLS 功能
SKILLS 本质上就是一堆文本,除了 name 和 description 之外没有其他格式约定。如果你自己开发 Agent,需要自行实现 SKILLS 功能。
以 LangChain 框架为例,开发带 SKILLS 的 Agent 主要分五个步骤。
第一,Skills 存储。可以是本地文件,也可以是数据库,反正本质都是文本。
/skills/docx.md/text-parser.md/excel.md
第二,按需获取 Skill(这才是关键)。推荐使用向量检索,这样可以匹配多种不同的 Skill。
const { MemoryVectorStore } = require('langchain/vectorstores/memory');const { OpenAIEmbeddings } = require('@langchain/openai');// 启动时把所有 Skill 索引进向量库async function indexSkills() {const skills = [{ name: 'docx', content: loadSkill('docx'), desc: '生成Word文档' },{ name: 'excel', content: loadSkill('excel'), desc: '生成Excel表格' },];const vectorStore = await MemoryVectorStore.fromTexts(skills.map(s => s.desc),skills.map(s => ({ name: s.name })),new OpenAIEmbeddings());return vectorStore;}// 按需检索最相关的 Skillasync function retrieveSkill(userInput, vectorStore) {const results = await vectorStore.similaritySearch(userInput, 1);return loadSkill(results[0].metadata.name);}
第三,把检索出来的 Skill 注入到 Prompt。
const { ChatPromptTemplate } = require('@langchain/core/prompts');async function buildPromptWithSkill(userInput, skill) {const prompt = ChatPromptTemplate.fromMessages([['system', `你是一个文档生成助手。以下是你完成任务需要遵循的操作规范:
第四,定义必要的 Tool。前面已经讲过如何定义执行代码的 Tool。
第五,完成 Agent 串联,包括 LLM、Tools、Prompt的组合。
const { createReactAgent } = require('langchain/agents');const { ChatOpenAI } = require('@langchain/openai');async function runDocumentAgent(userInput) {const llm = new ChatOpenAI({ model: 'gpt-4o' });// 1. 按需加载 Skillconst skill = await retrieveSkill(userInput, vectorStore);// 2. 构建带 Skill 的 System Promptconst systemPrompt = `你是文档生成助手,严格遵循以下规范:
最终这个 Agent 的整体工作流程如下:
用户输入 ↓[Skill 检索] ──→ 读取 .md 文件 ──→ 注入 System Prompt ↓[LangChain Agent] ──→ LLM 规划步骤 ↓[Tool 调用循环] ├── install_npm_package(环境准备) ├── generate_docx(执行代码) └── 验证结果 / 自我修正 ↓返回文件路径 / 下载链接
