新增 Skill

对应源码

扩展环节源码
Skill 发现和加载nanobot/agent/skills.py::SkillsLoader
Skill 写入模型上下文nanobot/agent/context.py::ContextBuilder.build_system_prompt
Skill 文件契约nanobot/skills/*/SKILL.md、nanobot/skills/README.md
Skill 命令和工具发现nanobot/command/builtin.py、nanobot/agent/tools/search.py
验证tests/agent/test_skills_loader.py、tests/command/test_skill_command.py

学习目标

读完后,你应当能够:

  • 解释 Skill 与 Tool 的权限边界;
  • 创建能被 SkillsLoader 发现的目录;
  • 理解 workspace override、requirements、always 和渐进加载;
  • 用行为实验验证 Skill,而不是只检查 Markdown 存在。

前置知识

  • 知道 system prompt 会影响模型决策,但不能提供新的执行权限;
  • 理解 ContextBuilder 的组成;
  • 能写清楚步骤、停止条件和验证标准。

1. Skill 是可加载的操作说明

Skill 的核心文件是:

<workspace>/skills/<skill-name>/SKILL.md

内置 Skill 位于:

nanobot/skills/<skill-name>/SKILL.md

它教 Agent 如何组合已有 Tool、何时询问用户、如何验证结果。它本身不执行 Python,也不会绕过 Tool 安全边界。

2. 发现与覆盖

SkillsLoader.list_skills():

  1. 扫描 workspace skills;
  2. 扫描 builtin skills;
  3. 同名时 workspace 版本覆盖 builtin;
  4. 移除 disabledSkills 中的名称;
  5. 默认过滤 requirements 未满足的 Skill。

Skill 的运行身份来自目录名。frontmatter 的 name 适合说明和兼容,但当前加载路径按 <目录名>/SKILL.md 查找。

flowchart TD
    A["workspace/skills"] --> C["按目录名合并"]
    B["nanobot/skills"] --> C
    C --> D["workspace 同名优先"]
    D --> E["disabledSkills 过滤"]
    E --> F["requirements 过滤"]
    F --> G["ContextBuilder"]

3. 一个最小 frontmatter

---
name: explain-session
description: Guide the agent to inspect and explain one nanobot Session safely.
metadata:
  nanobot:
    requires:
      bins:
        - rg
      env: []
---

正文至少应说明:

  • 何时使用;
  • 需要哪些输入;
  • 操作顺序;
  • 哪些步骤只读;
  • 什么时候必须询问用户;
  • 完成条件;
  • 失败时如何收敛。

4. requirements 的当前实现边界

SkillsLoader._check_requirements() 当前实际检查:

  • requires.bins:shutil.which();
  • requires.env:环境变量是否存在。

内置 Skill metadata 中还可能出现 os 等字段,但从当前 _check_requirements() 无法证明它会按 OS 过滤。文档若需要声称 OS gating,应先补实现和测试,不能只凭 frontmatter 推断。

get_skill_requirements() 会返回:

  • bins/env;
  • missing_bins/missing_env。

5. always 与渐进加载

ContextBuilder 分两类处理:

always Skill

frontmatter 顶层 always: true,或 metadata.nanobot.always 为真且 requirements 满足时,get_always_skills() 会把完整正文直接加入 system prompt。

普通 Skill

build_skills_summary() 只加入:

  • 名称;
  • description;
  • 路径;
  • availability。

模型需要时再用文件读取能力加载完整 SKILL.md。这就是渐进加载。

always 会持续占用上下文

只有每次 Turn 都必须遵守的短规则才适合 always。领域工作流、罕见平台操作和长教程应保持按需加载。

6. 写作结构

一份适合 Agent 的 Skill 通常比给人看的长教程更明确:

# Skill 名称
 
## Use when
 
## Do not use when
 
## Inputs
 
## Procedure
 
## Safety boundaries
 
## Verification
 
## Failure handling

重要原则:

  • 步骤要可观察,不写“妥善处理”;
  • 明确只读与写入;
  • 明确需要批准的动作;
  • 不鼓励绕过 Tool 错误;
  • 不把凭据示例写成真实值;
  • 说明结果验证,而不只说明如何执行。

7. 什么时候需要附带脚本

Skill 目录可以包含 scripts/、references/ 等资源。适合附脚本的情况:

  • 机械操作复杂且容易拼错;
  • 多次执行应保持一致;
  • 可写成小而可测试的 helper。

但脚本仍需通过现有 Tool 执行,并受 workspace、shell、权限和依赖约束。Skill 不能把脚本变成可信代码。

8. 验证一个 Skill

结构验证

  • SKILL.md 可被读取;
  • YAML 可解析;
  • description 非空且能区分触发场景;
  • requirements 能正确显示 unavailable;
  • workspace 同名覆盖符合预期。

行为验证

用一个固定任务观察:

  1. system prompt 的 skills summary 是否出现它;
  2. 模型是否在适用场景读取完整 Skill;
  3. 是否按步骤使用正确 Tool;
  4. 是否在写入前停下询问;
  5. 是否执行验证;
  6. 不适用场景是否没有误触发。

模型行为存在随机性,测试应把 loader/context 的确定性部分与真实模型 smoke test 分开。

9. 源码与测试

源码:

  • nanobot/agent/skills.py::SkillsLoader
  • nanobot/agent/context.py::ContextBuilder.build_system_prompt
  • nanobot/skills/README.md
  • nanobot/skills/*/SKILL.md

测试:

  • tests/agent/test_skills_loader.py
  • tests/command/test_skill_command.py
  • tests/tools/test_search_tools.py 中 disabled Skill 相关用例

10. 最小修改范围

workspace Skill 通常只需:

workspace/
└── skills/
    └── <name>/
        └── SKILL.md

只有要随 nanobot 发布时,才放入 nanobot/skills 并检查 wheel include、内置文档和相邻测试。

自检

  1. Skill 为什么不能新增文件读取权限?
  2. workspace 与 builtin 同名时谁优先?
  3. metadata.os 是否已被当前 loader 强制检查?
  4. 哪类 Skill 不适合 always: true?

下一篇:接入 MCP