Skip to content

技能(Skills)

技能(skill)是 agent 可以按需查阅的一份「怎么把某件事做好」的知识:一段有名字、有描述的操作说明(拟一份周计划、总结一段对话记录、跑一次代码评审)。与工具(tool,一个供模型调用的动作)不同,技能是供模型阅读的知识。是否用某个技能由模型自己判断,因此技能是模型自选的(model-invoked)。

技能采用渐进式披露(progressive disclosure,先只给概要、用到了再给全文):启动时 agent 只看到每个技能的名字和一行描述(一份塞进系统提示词的廉价目录),只有当模型真的要用某个技能时,它的完整正文才会被载入上下文。因此一个庞大的技能库在被用到之前几乎不产生任何开销。

快速上手

下面是 examples/16_skills.py 的原样内容。它是自洽的(写入一个临时目录,无需 API key、无需联网):

import tempfile
from pathlib import Path

from agentmaker import SkillLoader

# Create a couple of skills on disk; each is a folder containing a SKILL.md.
root = Path(tempfile.mkdtemp())
(root / "greet").mkdir()
(root / "greet" / "SKILL.md").write_text(
    "---\nname: greet\ndescription: greet the user warmly by name\n---\n"
    "Step 1: address the user by name.\nStep 2: ask how you can help.\n")
(root / "summarize").mkdir()
(root / "summarize" / "SKILL.md").write_text(
    "---\nname: summarize\ndescription: condense a long text into bullet points\n---\n"
    "Keep only the key facts, decisions, and open questions.\n")

loader = SkillLoader(str(root))

# The catalog is cheap (name + description only): show it to the model so it can pick a skill.
print("catalog:")
print(loader.catalog())

# Load the full body only when the chosen skill is actually needed.
print("\nloaded 'greet' body:")
print(loader.load("greet"))

技能是什么

每个技能是一个目录,里面有一个 SKILL.md 文件。该文件有一段 YAML frontmatter(文件头部由 --- 括起的元数据块),含 namedescription,其后是正文:

---
name: greet
description: greet the user warmly by name
---
Step 1: address the user by name.
Step 2: ask how you can help.

name 是唯一标识(约定用 kebab-case,即小写加连字符)。description 说明这个技能做什么、何时该用;由于模型仅凭目录里的这一行来挑选技能,描述正是让技能可被发现的关键。正文承载真正的步骤或知识。

frontmatter 的解析使用 pyyaml(一个核心依赖),因此支持多行与折叠标量(例如 description: >-)。

加载器

SkillLoader(skills_dir) 扫描一个目录,其中每个含有 SKILL.md 的子目录即为一个技能。路径由你的应用提供,框架不写死任何位置。它暴露三个方法:

  • discover() 返回一个 Skill 对象列表,只读取每个文件的 frontmatter(name 与 description),按目录名排序。此阶段正文留空。技能名必须唯一,重名会抛出 ValueError
  • catalog() 把每个技能的名字与描述拼成一段目录字符串,可直接放进系统提示词供模型挑选。每一行形如 - greet: greet the user warmly by name
  • load(name) 读取并返回某个技能的完整正文,若不存在该名字的技能则返回 None

Skill 是一个数据类,含 namedescriptionpathbody(在 load() 读取之前为空)。

渐进式披露

catalog()load() 的分工正是这套设计的用意所在,它分两层发生:

  1. 目录(廉价、始终在场)。 启动时 discover() / catalog() 只读取每个技能的 frontmatter,因此只有名字和描述进入系统提示词。正文无论多长,此阶段都不加载。
  2. 加载(按需)。 当模型从目录判断出它需要某个技能,你才调用 load(name),并把返回的正文放入上下文。

这样一来,无论你有多少技能,提示词都能保持精简:始终付出的成本是每个技能一行,而完整正文只在被用到时才付费。

技能与工具的取舍

当 agent 需要「做」某件有副作用的事(调用 API、跑一次计算、读取文件)时,用工具(见 工具)。当 agent 需要「知道」怎么做某件事(一套流程、一份清单、一种风格约定)时,用技能。工具被调用并返回结果;技能被阅读并影响模型接下来怎么走。二者可以组合:一个技能的正文可以指示模型去使用特定的工具。