从零开始认识 Agent Skills:为什么它比“喂提示词”更优雅
如果你最近在关注大模型应用开发,应该会发现一个明显趋势:单个提示词能约束的复杂度已经触顶了。过去我们习惯把长篇大段的指令塞进 System Prompt,让大模型扮演某个角色、按某种流程做事。但一旦任务变复杂,比如“读取一份代码仓库 → 定位潜在BUG → 按团队规范输出审查报告 → 自动生成修复建议”,提示词就会膨胀到几千字,而且效果不稳定。
Agent Skills(智能体技能)的出现,正是为了解决这个问题。它不追求把所有指令写在提示词里,而是把特定领域的操作能力封装成一套结构化技能包:既包含给模型的指导说明,也包含可执行的脚本、依赖清单、示例数据。你可以把它理解成“给大模型装上专用工具箱”,而不是每次都往它的脑子里灌长篇说明书。
本文将从 Agent Skills 的底层概念讲起,逐步拆解其目录结构、核心文件语法、运行机制,然后通过一个完整的“Python 代码审查助手”实战项目,带你从零搭建并运行一套可复用的 Agent Skill。无论你是刚开始接触 AI 应用开发的新手,还是已经在用 Prompt Engineering 做项目的开发者,这篇教程都能帮你少踩很多弯路。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
辨析 Agent、Skill、Tool 与 Function Call 的区别
在进入实操前,有一组概念必须先理清,否则后面看官方文档很容易懵。
Agent 是“智能体”,指的是能够感知环境、做出决策并执行动作的完整系统。它通常由大模型作为“大脑”,负责理解任务、拆解步骤;由工具或技能作为“手脚”,负责具体执行。
Skill 是“技能”,是一种结构化的能力模块,可以被 Agent 动态加载。它不像 Tool 那样只是一个独立的函数接口,而是由说明文档、脚本、依赖、校验数据共同组成,更接近“教给模型的一套完整方法”。
Tool 是“工具”,通常指一个可以被模型调用的函数或 API 接口,例如 search_web()、run_sql()。它非常轻量,适合单一操作。
Function Call(函数调用)是一种模型能力的实现方式,指模型在对话中产出一个结构化的函数调用指令,由宿主程序执行后再把结果回传给模型。Tool 和 Skill 都依赖这种底层机制。
它们之间的关系,用一个简单的比方来解释:Function Call 是“手”,Tool 是“手边的一个螺丝刀”,Skill 是“包含了螺丝刀、说明书、安全规范在内的整套工具箱”。Agent 决定何时使用工具箱,Skill 负责让这个工具箱对模型“友好可用”。
接下来我们进入正题,开始搭建 Agent Skills 的开发和运行环境。
环境准备:搭建 Agent Skills 开发与运行环境
Agent Skills 本身是一个基于 Markdown 和文件目录结构的规范,因此环境准备并不复杂。但为了顺利运行后面的实战项目,建议按以下清单准备。
语言与核心依赖
Agent Skills 的脚本部分支持 Python、JavaScript、Shell 等多种语言,本文实战以 Python 为例。需要确认本机已安装:
- Python 3.9 及以上版本(推荐 3.10+)
- pip 包管理工具
- 一个代码编辑器(VS Code、Cursor 或任意你习惯的编辑器)
版本需要根据你的项目实际情况调整,本文示例以常见环境为例,重点演示配置思路。
运行引擎:Claude 客户端或 API
Agent Skills 由 Anthropic 提出,需要在支持动态技能加载的客户端或 API 环境中运行。目前常见方式有两种:
- Claude 桌面端或支持 Agent Skills 的客户端
- Anthropic API 配合官方 SDK
对于学习阶段,建议使用支持 Agent Skills 的客户端,因为它会自动读取 SKILL.md、动态加载技能文件,无需自己实现调度逻辑。
验证环境是否就绪
在开始之前,可以用一段极简的 Python 脚本验证环境:
python复制# 文件路径:check_env.py
import sys
import markdown
print(f"Python 版本: {sys.version}")
print(f"markdown 库已安装: {markdown.__version__}")
运行命令:
bash复制python check_env.py
如果输出了 Python 版本和 markdown 库版本,说明环境基本可用。如果提示缺少 markdown 库,执行:
bash复制pip install markdown
Agent Skills 的核心机制与文件结构拆解
在写代码之前,先花一点时间理解 Agent Skills 的目录规范和核心文件语法。这部分直接决定你的技能包能否被正确加载和运行。
标准目录结构
一个最小可用的 Agent Skill 遵循以下目录结构:
code复制my-skill/
├── SKILL.md
├── scripts/
│ └── main.py
├── assets/
│ └── example.md
└── references/
└── docs.md
SKILL.md:技能定义文件,是整个技能包的核心。必须放在技能根目录。它包含 Frontmatter(元数据)和 Markdown 正文两部分。scripts/:存放可执行的脚本,通常是 Python 或 JavaScript 文件。assets/:存放示例数据、模板文件,供模型在生成答案时参考。references/:存放参考文档,帮助模型理解特定领域知识。dependencies/:可以存放 Python 依赖清单或 Node.js 的package.json。
SKILL.md 的 Frontmatter 详解
SKILL.md 的开头部分使用 YAML Frontmatter 来声明技能的基本信息。下面是一个例子:
yaml复制---
name: python_code_reviewer
description: 用于对 Python 代码进行系统性审查,发现潜在错误、性能问题与安全隐患,并输出格式化审查报告。
---
name 是技能的唯一标识,建议使用小写字母和下划线组合。description 是最关键的部分,模型会通过它来判断“当前任务是否应该加载这个技能”。描述应当具体、不含糊,可以包含触发场景,例如:
yaml复制---
description: 当用户要求审查 Python 代码、查找 BUG、评估代码质量、生成重构建议时使用该技能。不适用于 Java 或 JavaScript 代码审查。
---
这样写的好处是:模型不会在讨论 Java 时错误加载 Python 审查技能。
严格的描述能显著提高技能触发的准确性。你在实践时也可以多测试几组描述,观察模型的行为差异,找到最适合你技能包的表述方式。
SKILL.md 的正文写法
Frontmatter 之后是正文。正文通常包含以下几个部分:
- 技能概述:用 200 字以内说明技能的目标和使用边界。
- 使用说明:告诉模型如何按步骤调用脚本、是否需要参数、如何解析输出。
- 规则与约束:明确哪些操作禁止执行,例如“不要删除用户文件”“不要在没有授权时执行修改操作”。
- 示例调用:给出 1 到 2 个完整的调用示例,方便模型模仿。
看过一些社区里共享的 Agent Skills 后,会发现一个关于“渐进式披露”的设计原则很值得学习:在 SKILL.md 里只放精简的“如何开始”信息,把更长的详细说明放到 references/ 里的附加文档,并在正文中用链接指向它们。这样既能避免一次涌入过多 Token 挤占上下文窗口,又能保证需要时随时查得到细节,是构建复杂能力时很实用的做法。
拿代码审查技能来说,SKILL.md 可能只写“调用 scripts/review.py,传入待审查文件路径,解析 JSON 输出”,而把“如何判断代码坏味道”“团队规范细则”放到 references/ 里。
脚本与数据文件的设计
scripts/ 中的脚本承担实际执行任务。它们应该遵循几条原则:
- 输入参数尽量简单明确,优先使用命令行参数而不是交互式输入。
- 输出使用结构化格式,例如 JSON,方便模型解析。
- 错误处理必须完善,任何异常都要有明确的退出码和错误信息。
- 尽量不依赖第三方大型库,减少环境安装成本。
assets/ 目录通常存放示例数据。例如对于审查技能,可以放一个“示范审查报告.md”和一个小型 Python 示例文件,让模型知道“合格的输出长什么样”以及“典型输入长什么样”。
实战项目:构建一个 Python 代码审查 Agent Skill
理论部分已经说得够多了,现在进入实战环节。我们将从零构建一个名为 python_code_reviewer 的 Agent Skill,它能够:
- 接收一个 Python 文件路径作为输入。
- 使用
ast模块解析代码结构。 - 检查函数过长、全局变量滥用、重复代码、异常吞噬等常见问题。
- 输出 JSON 格式的审查结论。
为了贴合实际场景,更完整的版本可以用 AST + 静态分析检测常见问题(未使用的导入、过于宽泛的异常捕获、危险的内置函数调用),本示例代码会控制在一个可读的规模内。
创建项目结构
在你的工作目录下创建如下结构:
code复制agent-skills-lab/
└── skills/
└── python_code_reviewer/
├── SKILL.md
├── scripts/
│ └── review.py
└── assets/
└── sample_report.md
使用命令逐层创建:
bash复制mkdir -p agent-skills-lab/skills/python_code_reviewer/scripts
mkdir -p agent-skills-lab/skills/python_code_reviewer/assets
编写核心审查脚本
创建 scripts/review.py,完整代码如下:
python复制#!/usr/bin/env python3
"""
Python 代码审查脚本
用法:python review.py <python_file_path>
输出:JSON 格式的审查报告
"""
import ast
import json
import sys
from pathlib import Path
def analyze_code(source: str) -> dict:
"""分析 Python 源码,返回问题列表。"""
tree = ast.parse(source)
issues = []
imports = []
for node in ast.walk(tree):
# 检查 Import 语句
if isinstance(node, ast.Import):
for alias in node.names:
imports.append(alias.asname or alias.name)
elif isinstance(node, ast.ImportFrom):
for alias in node.names:
imports.append(alias.asname or alias.name)
# 检查异常吞噬
if isinstance(node, ast.ExceptHandler):
if node.type is None:
issues.append({
"line": node.lineno,
"type": "WARNING",
"message": "裸 except 会吞掉所有异常,建议指定具体异常类型"
})
elif isinstance(node.type, ast.Tuple):
pass
elif isinstance(node.type, ast.Name) and node.type.id == "Exception":
issues.append({
"line": node.lineno,
"type": "WARNING",
"message": "捕获 Exception 过于宽泛,建议改为更具体的异常类型"
})
# 检查危险函数调用
if isinstance(node, ast.Call):
if isinstance(node.func, ast.Name) and node.func.id in {"eval", "exec"}:
issues.append({
"line": node.lineno,
"type": "CRITICAL",
"message": f"检测到危险函数 {node.func.id},可能导致代码注入"
})
# 检查全局变量滥用
if isinstance(node, ast.Global):
for name in node.names:
issues.append({
"line": node.lineno,
"type": "INFO",
"message": f"函数内使用了全局变量 '{name}',建议通过参数传递替代"
})
# 检查函数长度
for node in ast.walk(tree):
if isinstance(node, ast.FunctionDef):
func_line_count = (node.end_lineno or node.lineno) - node.lineno + 1
if func_line_count > 50:
issues.append({
"line": node.lineno,
"type": "WARNING",
"message": f"函数 '{node.name}' 长度达到 {func_line_count} 行,建议拆分为小函数"
})
# 检查未使用的导入(简化版:出现在源码中但未在 AST Name 节点中出现)
used_names = set()
for node in ast.walk(tree):
if isinstance(node, ast.Name):
used_names.add(node.id)
for imp in imports:
if imp not in used_names:
issues.append({
"line": 0,
"type": "INFO",
"message": f"导入 '{imp}' 似乎未被使用",
})
return {"imports": imports, "issues": issues}
def main():
if len(sys.argv) != 2:
print(json.dumps({"error": "请指定要审查的 Python 文件路径"}, ensure_ascii=False))
sys.exit(1)
file_path = Path(sys.argv[1])
if not file_path.exists():
print(json.dumps({"error": f"文件不存在: {file_path}"}, ensure_ascii=False))
sys.exit(1)
source = file_path.read_text(encoding="utf-8")
try:
result = analyze_code(source)
except SyntaxError as e:
result = {"error": f"语法错误: {e.msg} (行 {e.lineno})", "issues": []}
# 输出 JSON
print(json.dumps(result, ensure_ascii=False, indent=2))
if __name__ == "__main__":
main()
这段代码使用 Python 内置的 ast 模块(Abstract Syntax Tree,抽象语法树)来解析源码。它把源代码解析成树状结构,然后遍历这棵树查找特定模式。这样比正则表达式匹配更准确,因为它理解代码的语法结构,而不仅仅是文本。
编写 SKILL.md 定义文件
现在创建技能的核心描述文件 SKILL.md:
markdown复制---
name: python_code_reviewer
description: 用于审查 Python 代码质量的技能。当用户要求检查 Python 代码、寻找潜在 BUG、评估代码规范、检查安全风险时使用。支持从命令行接收 Python 文件路径并输出 JSON 格式审查报告。
---
# Python 代码审查技能
本技能用于对 Python 源码进行静态分析,定位常见问题并生成结构化审查报告。
## 使用步骤
1. 获取待审查文件的绝对路径。
2. 运行 `python3 scripts/review.py <file_path>`。
3. 解析脚本输出的 JSON。
4. 将 JSON 结果转换为人话,用中文向用户解释问题清单,严重级别从高到低排序。
## 规则与约束
- 本技能只负责读取和分析文件,不修改任何代码。
- 如果脚本输出 `error` 字段,直接向用户报告错误信息。
- 不要在审查报告中添加脚本未发现的问题。
- 对 `CRITICAL` 级别的问题,务必给出安全风险说明和修复建议。
## 输出模板参考
可以在 `assets/sample_report.md` 中查看一份标准审查报告示例。输出给用户时,建议包含:
- 问题摘要(共发现多少个问题,不同级别各多少个)
- 问题清单(行号、级别、描述、修复建议)
- 总体评价与后续建议
## 示例
用户:检查一下 app.py 这个文件。
助手(执行流程):
1. 运行 `python3 scripts/review.py /path/to/app.py`
2. 脚本返回 JSON
3. 将 JSON 转换为中文报告并解释
注意看,SKILL.md 的核心是“三步走”:如何调用、如何解析输出、如何呈现给用户。不要试图把审查规则全部写在 SKILL.md 里,那会导致模型被长文本干扰。
编写示例报告
创建 assets/sample_report.md,给模型一个输出范例:
markdown复制# Python 代码审查报告:demo.py
## 问题摘要
- 严重问题:1 个
- 警告:2 个
- 提示:3 个
## 问题清单
| 行号 | 级别 | 描述 | 修复建议 |
| --- | --- | --- | --- |
| 12 | CRITICAL | 使用 eval 处理用户输入,存在代码注入风险 | 改用 ast.literal_eval 或 JSON 解析 |
| 30 | WARNING | 裸 except 吞掉所有异常 | 改为 except ValueError |
| 55 | INFO | 导入 os 但未使用 | 移除该导入 |
## 总体评价
代码结构清晰,但存在安全风险,建议优先修复 CRITICAL 级别问题后再合并。
有了这个示例,模型输出报告时就会参考这种格式和风格,而不会每次生成截然不同的结构。
运行与验证
为了验证整个技能包能正常工作,创建一个待审查的示例文件:
python复制# 文件路径:demo_target.py
import os
import sys
def process_data(data):
global counter
counter = 0
for item in data:
try:
result = eval(item)
print(result)
except:
pass
def another_function():
print("hello")
if __name__ == "__main__":
process_data(["1", "2", "3"])
运行审查脚本:
bash复制cd skills/python_code_reviewer
python3 scripts/review.py ../../../demo_target.py
预期输出如下(节选):
json复制{
"imports": ["os", "sys"],
"issues": [
{
"line": 7,
"type": "WARNING",
"message": "函数内使用了全局变量 'counter',建议通过参数传递替代"
},
{
"line": 10,
"type": "CRITICAL",
"message": "检测到危险函数 eval,可能导致代码注入"
},
{
"line": 12,
"type": "WARNING",
"message": "裸 except 会吞掉所有异常,建议指定具体异常类型"
}
]
}
将技能接入客户端并验证加载
如果你使用支持 Agent Skills 的客户端,只需在设置中指定 agent-skills-lab/skills 作为技能目录,然后新建对话并输入:
code复制请审查 demo_target.py 文件,并给出优化建议。
客户端会自动判断该任务匹配 python_code_reviewer 技能,然后按 SKILL.md 中的步骤执行脚本、解析 JSON、生成报告。
进阶:多技能协同与依赖管理
学会了单个技能,接下来看两个更贴近真实工程场景的问题:多个技能如何协同工作,以及技能依赖的第三方包如何管理。
多技能协同示例
在真实项目中,一个 Agent 可能需要同时具备“代码审查”和“生成测试用例”两个技能。当用户说“审查代码并写两个单元测试”时,Agent 会先加载 python_code_reviewer,再加载 test_generator。两个技能各自独立,但通过 Agent 的调度上下文共享信息。
这要求每个技能的设计保持职责单一。如果一个技能既审查代码又生成测试,那么当用户只要求“写测试”时,技能也会被触发,浪费 Token 还可能干扰模型行为。
良好的多技能体系通常遵循单一职责原则:每个技能只做一件事,但做好、做专。遇到需要链路化任务时,再把技能“串联”起来。
依赖管理
如果 scripts/ 中的脚本需要第三方库,有两种常见方案:
- 在技能根目录放置
requirements.txt,然后建议模型先执行pip install -r requirements.txt。 - 使用轻量的标准库实现功能,避免外部依赖。
对于方案一,SKILL.md 的使用步骤里需要增加一段:
markdown复制## 首次使用准备
运行以下命令安装依赖:
```bash
pip install -r requirements.txt
某些客户端支持自动识别依赖并提前安装。对于公共发布的技能包,建议在说明文档中明确标注依赖环境和安装方式。
常见问题排查:让技能稳定运行的五个关键点
在实际使用和开发 Agent Skills 时,有几个高频问题非常影响体验。下面以排查清单的形式给出解决思路。
| 问题现象 | 可能原因 | 排查思路 |
|---|---|---|
| 模型不触发技能 | description 描述不准确,模型无法判断适用场景 |
重写描述,加入触发场景和关键词;开启调试模式观察技能加载日志 |
| 脚本运行报错 | 缺少第三方依赖,或 Python 环境不一致 | 检查 requirements.txt,确认运行路径正确;在脚本开头打印调试信息 |
| 输出结果不稳定 | SKILL.md 正文指令不够明确 |
增加示例调用步骤;给模型提供输出模板;使用结构化 JSON 约束脚本输出 |
| 技能被错误触发 | 描述过于宽泛 | 在描述中明确“不适用”的场景,缩小触发边界 |
| 消耗 Token 过多 | 技能文件过大,每个示例都重复加载 | 遵循“渐进式披露”原则,把详细文档放到 references/,在 SKILL.md 中只放核心步骤 |
另一个常见问题是脚本输出非 JSON 格式,导致模型解析困难。解决方案是:脚本内部用 json.dumps 确保输出合法 JSON;错误信息也包在 JSON 的 error 字段中;避免在 JSON 前打印任何日志内容。
最佳实践与工程建议
Agent Skills 看起来只是文件目录结构,但要把它们用好在生产环境中,需要掌握几个工程层面的要点。
命名规范与版本管理
技能包名称使用小写字母和下划线,例如 code_reviewer、sql_analyzer。不要使用空格或连字符。建议每个技能包内包含一个 CHANGELOG.md,记录版本变更,方便回滚。
如果你在团队内共享技能包,可以把它们单独放在一个 Git 仓库中。每次变更都走 MR 评审,保证技能包的“行为可预期”。
安全边界控制
这是整个技能体系中最重要的一环。Agent 一旦获得调用脚本的能力,就等同于获得了在当前环境中执行代码的权限。必须严格控制安全边界:
- 脚本默认以只读模式运行,禁止修改、删除文件。
- 涉及网络请求或数据库操作时,必须提示用户确认风险。
- 不要在技能中存储任何敏感凭据;密钥通过环境变量传入,避免写入文件。
- 对于
eval、exec、subprocess等高危操作,在脚本中直接拦截并生成警告。
日志与可观测性
技能脚本应该输出足够清晰的错误信息和执行时间,方便判断问题出在哪个环节。建议在脚本中记录:
- 输入参数摘要(避免打印完整敏感内容)。
- 关键步骤标记。
- 消耗的时长。
- 结束状态(成功或失败编号)。
这些信息不仅方便排错,还能帮助你了解模型是如何使用技能的——例如发现模型反复调用脚本但没有正确解析输出,就可以改进 SKILL.md 中的示例部分。
萌新最容易踩的 6 个坑
实战中我见过不少刚接触 Agent Skills 的人反复踩同一个坑,这里集中列一下:
坑 1:SKILL.md 中写满详细规则。
如果每个技能都有几千字的内容,每次触发技能都会消耗大量 Token。而且模型很容易“迷失”在长篇文本中,忽略关键指令。正确做法是只写技能的精简介绍和使用步骤,把详细的“原理”留给外部文档,让模型需要时再查。
坑 2:脚本输出是纯文本而不是 JSON。
模型解析纯文本的稳定性远低于解析 JSON。保持脚本输出为 JSON,是所有技能设计的前提。
坑 3:描述写得像营销文案而不是使用说明。
正确的 description 应该回答“这个技能何时被使用”,而不是“这个技能多厉害”。参考格式:“当用户需要 X 时,使用该技能;当用户请求 Y 时,不要使用。”
坑 4:技能目录放错位置。
技能根目录必须包含 SKILL.md 文件,如果没有,客户端无法识别该技能包。另外技能包之间不要互相嵌套,每个技能保持独立。
坑 5:不知道如何测试技能。
可以在自己的项目里创建一个待处理的测试文件,然后用最基础的指令触发技能,观察模型与技能包的交互过程。建立一个小型测试集,每次修改后跑一遍,保证技能的行为不会意外“漂移”。
坑 6:要求技能做超出能力边界的事。
一个技能只是一个静态分析器,不要指望它能接管整个 CI/CD 流程。构建“小且专”的技能,再通过 Agent 将它们组合成复杂工作流,才是成熟工程的思路。
下一步学习建议
Agent Skills 是个相对新的概念,相关的实践案例和社区的技能包分享也越来越多。完成本文的实战项目后,建议按以下顺序继续深入:
带着更强的设计感去浏览社区里开源的 Agent Skills 项目,学习别人的 SKILL.md 写法是提升最快的方式。可以重点观察它们如何处理依赖、如何设计描述、如何组织 references。
当你能熟练编写并运行单个技能后,开始尝试构建一套“工作流”:例如“数据文件解析 → 数据清洗 → 生成分析报告 → 自动绘制图表”。
如果顺利的话,接下来可以考虑让技能包支持多语言调用,比如 Python 审查技能加上 JavaScript 审查技能,并让 Agent 根据文件扩展名自动选择合适的技能。
在动手过程中始终保持三个意识:
- 让技能可测试,每个技能必须有一个最小验证用例。
- 让技能可更新,模块化设计使你每次只改一个文件。
- 让技能可审计,任何技能的行为都应该是透明的、可解释的。
这部分实践才是 Agent 应用开发中最值得沉淀的工程资产。
