Appearance
Agent Skills
AgentScope 2.x 的 Toolkit 统一装配三类来源:Python 函数、MCP 服务与 Skills。前两类都是"Agent 可以直接调用的工具",而 Skills 是第三类--它不是工具,而是一组指令。Agent 需要先通过内置的 Skill 查看器元工具阅读 Skill 说明,再按说明使用已有工具去执行。本页讲清 Skill 的概念、目录结构、 SKILL.md 格式、LocalSkillLoader 的扫描行为,以及它与 tools / MCP / Workspace 的关系。
版本基线:本文以 Python
agentscope 2.0.4(PyPI,2026-07-07)为准,要求 Python>=3.11。 Skill 是 2.x 新增概念(changelog 标记为 "Skill New"),1.x 没有对应能力。导入路径已与main分支源码核对:from agentscope.skill import Skill, SkillLoaderBase, LocalSkillLoader(源:src/agentscope/skill/__init__.py,其中Skill来自_base.py、LocalSkillLoader来自_local_loader.py)。2.x 文档站存在版本化陷阱:非版本化的/en/URL 会 404,本页所有官方链接 均使用versions/2.0.4/en/形式。
为什么需要 Skills
工具回答的是"Agent 能调用什么函数",Skill 回答的是"Agent 应该如何完成某类任务"。当一类任务的 最佳实践涉及多步操作、特定顺序、固定约束时,把它写成一段指令塞进系统提示既不优雅也难复用。Skill 把"如何完成某类任务"的指令打包成一个独立目录,可以跨 Agent 加载、按需查看、独立维护,而执行指令 所需的工具仍然由 Toolkit 里已有的函数与 MCP 提供。
前端类比
如果你来自前端,可以把 Skill 类比为三种东西的结合:
- 可复用的 npm 脚本:
package.json里的"scripts": { "deploy": "...", "release": "..." }本身不是代码,而是一段"调用哪些已有工具、按什么顺序"的指令。Skill 同样不定义新工具,只编排已有 工具。 - Claude Code 的 Skill:把一段带
SKILL.md的目录作为可加载单元,运行时按需读取其说明再执行。 AgentScope 的 Skill 在机制上与之同源(同样以SKILL.md为入口)。 - VS Code 插件里的指令集:插件不是新 API,而是告诉编辑器"在某个场景下用这些已有命令完成目标"。
AgentScope 原生语义:Skill 是一个数据类(@dataclass class Skill),字段为 name、 description、dir、markdown、updated_at。它不是 ToolBase 的子类,不会出现在 Agent 的工具 Schema 列表里。Toolkit 在构造时扫描所有 Skill 来源,把每个 Skill 的 name + description(仅这两 项)拼成一段系统提示片段,并自动注册一个名为 Skill 的内置查看器元工具。Agent 想用某个 Skill 时, 先调用 Skill 查看器传入名字,查看器读取对应 SKILL.md 的完整 Markdown 返回给 Agent,Agent 再 按这段指令调用已有工具执行。换言之,Skill 改变的是 Agent 的"行为模式",而不是"能力集合"。
Skill 的结构
一个 Skill 就是一个目录,目录里必须有一个 SKILL.md 文件,其余都是可选资源(脚本、模板、参考 文档等,Agent 可以通过 Read 等内置工具读取它们)。SKILL.md 是唯一被框架解析的入口文件。
加载 Skill 涉及三个类,全部来自 agentscope.skill:
| 类 | 作用 |
|---|---|
Skill | 数据类,承载一个已加载 Skill 的 name / description / dir / markdown / updated_at。不继承 ToolBase,不可被 Agent 直接调用。 |
SkillLoaderBase | 加载器抽象基类,唯一抽象方法 async list_skills() -> list[Skill]。自定义来源(远程仓库、数据库、Workspace)时继承它。 |
LocalSkillLoader | 本地目录加载器,SkillLoaderBase 的官方实现。扫描指定目录下的 SKILL.md,按文件修改时间缓存。 |
Skill 的字段含义(源:src/agentscope/skill/_base.py):
| 字段 | 类型 | 说明 |
|---|---|---|
name | str | Skill 名,来自 SKILL.md frontmatter,用于 Agent 调用查看器时传参 |
description | str | Skill 描述,来自 frontmatter,被拼进系统提示的 Skills 索引 |
dir | str | Skill 所在目录的绝对路径,Agent 可据此读取目录内其他资源 |
markdown | str | SKILL.md 去掉 frontmatter 后的正文,查看器返回的就是它 |
updated_at | float | SKILL.md 的文件修改时间(mtime),用于缓存失效判断 |
LocalSkillLoader 示例
LocalSkillLoader 是最常用的加载器。它扫描一个目录下的 SKILL.md,把它解析成 Skill 对象。
python
import os
from agentscope.skill import LocalSkillLoader
from agentscope.tool import Toolkit
from agentscope.agent import Agent
from agentscope.model import DashScopeChatModel
from agentscope.credential import DashScopeCredential
from agentscope.message import UserMsg
# 准备一个加载器:扫描 ./my_skills 目录及其子目录
loader = LocalSkillLoader(
directory="./my_skills",
scan_subdir=True,
)
# Skills 与 tools / mcps 一样,统一经 Toolkit 装配
toolkit = Toolkit(
skills_or_loaders=[loader],
)
model = DashScopeChatModel(
credential=DashScopeCredential(api_key=os.environ["DASHSCOPE_API_KEY"]),
model="qwen-plus",
)
agent = Agent(
system_prompt="你是一个能按 Skill 指令完成任务的助手。",
model=model,
toolkit=toolkit,
)
# Agent 运行时会先在系统提示里看到 Skills 索引(仅 name + description),
# 需要时调用内置 Skill 查看器读取完整指令,再用已有工具执行。
reply = await agent.reply(UserMsg("帮我按照 research-paper 技能调研一个主题。"))LocalSkillLoader 的构造参数(源:src/agentscope/skill/_local_loader.py):
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
directory | str | 必填 | 要扫描的本地目录,内部会 os.path.abspath 转绝对路径 |
scan_subdir | bool | False | 是否扫描子目录。False 只看 directory 自身的 SKILL.md;True 会 os.walk 所有子目录 |
扫描行为细节
list_skills() 的扫描逻辑(源码已核对):
- 先检查
directory自身是否含SKILL.md,若有则视为一个 Skill。 - 仅当
scan_subdir=True时,才os.walk遍历所有子目录(跳过顶层目录自身,避免重复),收集每个 含SKILL.md的子目录。 - 所有
SKILL.md并发解析(asyncio.gather),单个解析失败只记 warning 并跳过,不影响其他 Skill。 - 每个 Skill 按
skill_root目录做缓存,若updated_at(mtime)未变则直接复用缓存,避免重复读盘。
字符串路径简写
如果不需要调 scan_subdir,直接传一个目录路径字符串给 skills_or_loaders 即可,Toolkit 内部会 用 LocalSkillLoader 包装它(scan_subdir 取默认值 False):
python
from agentscope.tool import Toolkit
toolkit = Toolkit(
skills_or_loaders=["/path/to/skills"],
)skills_or_loaders 列表里的每一项可以是三种形态(源:官方文档 Tool#Skill):
| 形态 | 说明 |
|---|---|
str 目录路径 | 简写,Toolkit 内部包成 LocalSkillLoader(scan_subdir=False) |
Skill 对象 | 已构造好的 Skill 实例,跳过加载阶段直接注册 |
SkillLoaderBase 实例 | 自定义加载器(如 LocalSkillLoader(scan_subdir=True) 或你自己的子类),Toolkit 会 await loader.list_skills() |
SKILL.md 格式
SKILL.md 是一个带 YAML frontmatter 的 Markdown 文件,由 python-frontmatter 库解析(源: _local_loader.py 中 import frontmatter)。frontmatter 必须包含 name 和 description 两个 字段,两者缺一不可--缺任一字段时,加载器会记 warning 并跳过该 Skill(源码: if not name or not description: logger.warning(...); return None)。frontmatter 之后的正文就是 Agent 通过查看器读到的完整指令。
markdown
---
name: research-paper
description: 调研一个学术主题并产出结构化报告,需先搜索再总结。
---
# Research Paper Skill
当用户要求调研一个主题时,按以下步骤执行:
1. 用 web_search 工具搜索 3-5 篇相关资料。
2. 用 scrape_url 工具读取每篇资料正文。
3. 交叉核对关键事实,标注来源。
4. 产出一份带"结论 / 依据 / 不确定项"三段的结构化报告。
约束:
- 不得把搜索摘要直接当作最终事实。
- 每条结论必须附至少一个来源链接。frontmatter 字段说明(源码已核对):
| 字段 | 必填 | 类型 | 说明 |
|---|---|---|---|
name | 是 | str | Skill 名,Agent 调用 Skill 查看器时按它传参;会进入系统提示索引 |
description | 是 | str | Skill 描述,会进入系统提示索引,决定 Agent 是否选用这个 Skill |
关于正文格式:源码只把 frontmatter 之后的全部内容作为
markdown字段原样存储并返回,不对正文 做结构化解析。正文写法是普通 Markdown,没有强制 schema。社区实践中常见"步骤 + 约束 + 输出格式"的 写法,但具体惯例(是否分固定小节、是否引用目录内其他文件)以官方示例为准,本文不擅自规定更细的 模板(需核实:2.0.4 文档 Tool#Skill 未给出正文 schema 约束)。
与 tools / MCP 的关系
Skills 与 tools / MCP 都由 Toolkit 统一装配,但职责不同(详见 工具与 MCP):
| 来源 | 是否可被 Agent 直接调用 | 是否定义新工具 | 装配参数 |
|---|---|---|---|
| Python 工具 | 是 | 是 | tools=[...] |
| MCP 服务 | 是 | 是(服务端发现) | mcps=[...] |
| Skill | 否 | 否 | skills_or_loaders=[...] |
关键区别:Skill 不定义新工具,而是指导 Agent 如何使用现有工具。一个 Skill 里描述的"用 web_search 搜索、用 scrape_url 读取"所指的工具,必须已经在同一个 Toolkit(同一个 "basic" 工具组或可激活的工具组)里注册,否则 Agent 读到指令也无工具可用。因此装配 Skill 时通常需要同时装配 它所依赖的 tools / MCPs:
python
from agentscope.tool import Toolkit, Bash, Read, FunctionTool
from agentscope.skill import LocalSkillLoader
toolkit = Toolkit(
tools=[Bash(), Read(), FunctionTool(func=web_search), FunctionTool(func=scrape_url)],
skills_or_loaders=[
LocalSkillLoader(directory="./my_skills", scan_subdir=True),
],
)Toolkit 检测到 skills_or_loaders 非空后会自动做两件事(源:官方文档 Tool#Skill "How Skill Works"):
- 注册
Skill查看器元工具:Agent 可按名字调用它读取某个 Skill 的完整 Markdown。 - 注入系统提示片段:列出所有可用 Skill 的
name+description(仅这两项,不含正文),并指示 Agent 需要时调用查看器读取完整内容。
与 Workspace 的关系
Workspace 不仅能执行工具,还能"供应" Skills。三类 Workspace(LocalWorkspace / DockerWorkspace / E2BWorkspace)都有 skills/ 子目录,并提供统一的 Skill 管理接口(详见 工作区与沙箱):
| 接口 | 作用 |
|---|---|
list_skills() | 返回 skills/ 目录下解析出的 list[Skill],可直接喂给 Toolkit 的 skills_or_loaders |
add_skill(path) | 把本地 Skill 目录打包复制进工作区 skills/,运行时动态新增 |
remove_skill(name) | 按 Agent 可见的 Skill 名移除 |
Workspace 构造时还可传 skill_paths=["./my_skills"],在首次 initialize() 时把这些本地 Skill 播种 到沙箱的 skills/ 目录。典型用法是把 Workspace 作为 Skill 来源:
python
import asyncio
from agentscope.workspace import LocalWorkspace
from agentscope.tool import Toolkit
ws = LocalWorkspace(workdir="./my_workspace")
asyncio.run(ws.initialize())
# Workspace 自己就是 Skill 加载来源
toolkit = Toolkit(
skills_or_loaders=await ws.list_skills(),
)这样 Skill 与工具执行共用同一个沙箱边界:Skill 描述的指令在沙箱内执行,其引用的资源文件也位于沙箱 文件系统中。
常见错误
SKILL.md缺name或description:源码中if not name or not description会记 warning 并 跳过该 Skill,list_skills()不返回它。表现是"目录在、文件在,但 Agent 系统提示里看不到这个 Skill"。解决:确认 frontmatter 两个字段都填了非空值,且 frontmatter 用---正确分隔。SKILL.md文件名拼错:加载器只认严格大小写的SKILL.md(源码os.path.join(skill_root, "SKILL.md"))。写成skill.md、Skill.md、SKILL.MD都不会被扫描到。scan_subdir未开导致子目录 Skill 未加载:LocalSkillLoader的scan_subdir默认是False, 只看directory自身的SKILL.md。如果你的多个 Skill 分别放在directory/research/、directory/writing/等子目录里,必须显式传scan_subdir=True,否则一个都加载不到。字符串路径 简写skills_or_loaders=["./my_skills"]同样只扫描顶层,子目录 Skill 需要改用LocalSkillLoader(directory="./my_skills", scan_subdir=True)。- Skill 指令依赖了未注册的工具:Skill 正文里写"用
web_search搜索",但Toolkit里没有注册 这个工具,Agent 读取指令后会找不到工具可调用。解决:装配 Skill 的同时把它依赖的 tools / MCPs 一起 放进同一个Toolkit。 - 期望
add_skill/remove_skill对已运行 Agent 实时生效:Workspace 的这两个方法只改工作区状态, 已构造的Toolkit不会自动重新扫描。需要重建 Toolkit 或重启 Agent 才能让新增 / 移除的 Skill 反映 到系统提示索引里。 - 把 Skill 当工具直接调用:Skill 不是
ToolBase,不会出现在工具 Schema 里。Agent 只能先调用内置Skill查看器读取指令,再用已有工具执行。如果你希望"直接调用",那应该写FunctionTool或自定义ToolBase,而不是 Skill。
先修
- 长期记忆 - Skill 与长期记忆都属于"扩展 Agent 行为"的 能力,理解中间件后再看 Skill 的定位更清晰
- 工具与 MCP - Skill 与 tools / MCP 统一由 Toolkit 装配,需先 掌握 Toolkit 的统一装配模型
下一步
- 状态与会话 - Skill 索引与查看器返回的 Markdown 如何随 AgentState 持久化、跨会话复用