跳到主要内容

创建自定义技能

本指南将教你如何从零开始创建一个 CountBot 技能,包括文件结构、配置管理和最佳实践。

致谢 anthropics/skills

CountBot 的技能系统设计深受 anthropics/skills 的启发。感谢 Anthropic 团队开创性的 Agent Skills 机制,为整个行业提供了规范与示例。CountBot 在此基础上,增加了图形化配置界面和中文友好的开发体验。

目录


什么是技能

技能(Skills) 是 CountBot 的扩展机制,用于教会 AI 如何完成特定任务。技能本质上是:

  • 文档 - 包含使用说明和示例的 Markdown 文件
  • 🐍 脚本 - 可选的 Python 脚本实现具体功能
  • ⚙️ 配置 - 可选的配置文件(API Key、参数等)

技能让 AI 能够:

  • 调用外部 API(搜索、地图、天气等)
  • 执行复杂的数据处理
  • 生成特定格式的内容
  • 与第三方服务集成

技能 vs 工具

理解技能和工具的区别很重要:

维度工具(Tools)技能(Skills)
定义内置的系统能力外部扩展的功能
调用方式LLM 直接调用通过 exec 工具执行
实现语言Python(内置)任意语言(通常 Python)
配置系统配置独立配置文件
示例read_file, exec, web_fetch天气查询、图片生成、邮件管理

工作流程

用户: "查询北京天气"

AI: 使用 read_file 读取 weather 技能文档

AI: 阅读文档中的命令示例

AI: 使用 exec 工具执行命令

返回结果给用户

快速开始

1. 创建技能目录

技能存放在 workspace/skills/ 目录下:

workspace/skills/
└── my-skill/ # 技能名称(小写,连字符分隔)
├── SKILL.md # 必需:技能文档
├── scripts/ # 可选:Python 脚本
│ ├── main.py
│ └── config.json # 可选:配置文件
└── README.md # 可选:开发文档

2. 创建 SKILL.md

最简单的技能只需要一个 SKILL.md 文件:

---
name: my-skill
description: 我的第一个技能
metadata: {"CountBot": {"always": false}}
---

# 我的技能

这个技能用于演示如何创建自定义技能。

## 使用方法

直接在对话中提到相关功能,AI 会自动读取并使用这个技能。

## 示例

用户: "使用我的技能"
AI: 我会读取这个文档并按照说明操作

3. 重载技能

创建完成后,在 CountBot Web UI 中:

  1. 进入「设置」→「技能管理」
  2. 点击「重载技能」按钮
  3. 新技能会出现在列表中

4. 使用图形化配置(可选)

CountBot 提供了图形化配置界面,无需手动编辑 JSON 文件:

  1. 打开技能管理

    • 点击右上角的「技能」图标
    • 或进入「设置」→「技能管理」
  2. 查看技能列表

    • 显示所有已加载的技能
    • 可以启用/禁用技能
    • 查看技能详情
  3. 配置技能

    • 点击技能卡片的「配置」按钮
    • 在图形化界面中填写配置项
    • 支持的字段类型:
      • 文本输入(API Key、URL 等)
      • 密码输入(自动隐藏)
      • 数字输入(带范围验证)
      • 布尔开关
      • 下拉选择
      • 嵌套对象
  4. 保存配置

    • 点击「保存」按钮
    • 配置自动写入 scripts/config.json
    • 自动验证配置格式

图形化配置的优势

  • 无需手动编辑 JSON 文件
  • 自动验证配置格式
  • 敏感信息自动隐藏
  • 提供字段说明和帮助文档
  • 支持配置自动修复

文件结构

标准技能结构

my-skill/
├── SKILL.md # 技能文档(必需)
├── scripts/ # 脚本目录(可选)
│ ├── main.py # 主脚本
│ ├── utils.py # 工具函数
│ ├── config.json # 配置文件
│ └── requirements.txt # Python 依赖
├── tests/ # 测试目录(可选)
│ └── test_main.py
├── examples/ # 示例目录(可选)
│ └── example.md
└── README.md # 开发文档(可选)

文件说明

文件必需说明
SKILL.md技能文档,AI 会读取这个文件
scripts/main.pyPython 脚本实现
scripts/config.json配置文件(API Key 等)
scripts/requirements.txtPython 依赖列表
README.md开发者文档

SKILL.md 格式

YAML Frontmatter

SKILL.md 文件以 YAML frontmatter 开头:

---
name: skill-name
description: 技能的简短描述
metadata:
CountBot:
always: false
requires:
bins: ["python", "curl"]
env: ["API_KEY"]
---

Frontmatter 字段

字段类型必需说明
namestring技能名称(小写,连字符分隔)
descriptionstring技能描述(一句话说明功能)
metadata.CountBot.alwaysboolean是否自动加载(默认 false)
metadata.CountBot.requires.binsarray依赖的二进制程序
metadata.CountBot.requires.envarray依赖的环境变量

always 字段说明

  • always: true - 技能内容会自动注入到系统提示词,适合频繁使用的技能
  • always: false - 技能只在需要时加载(推荐),节省 Token

Markdown 内容

Frontmatter 之后是 Markdown 格式的技能文档:

# 技能标题

技能的详细说明。

## 功能介绍

- 功能 1
- 功能 2
- 功能 3

## 使用方法

### 命令行调用

\`\`\`bash
python scripts/main.py --arg1 value1 --arg2 value2
\`\`\`

### 参数说明

- `--arg1`: 参数 1 的说明
- `--arg2`: 参数 2 的说明

## 示例

### 示例 1:基本用法

\`\`\`bash
python scripts/main.py --city "北京"
\`\`\`

输出:
\`\`\`
北京天气:晴,温度 25°C
\`\`\`

### 示例 2:高级用法

\`\`\`bash
python scripts/main.py --city "上海" --format json
\`\`\`

## 注意事项

- 注意事项 1
- 注意事项 2

## 错误处理

常见错误及解决方法...

编写技巧

  1. 清晰的命令示例 - AI 会直接复制命令,确保示例可以直接运行
  2. 完整的参数说明 - 说明每个参数的含义和可选值
  3. 实际的输出示例 - 帮助 AI 理解预期结果
  4. 错误处理说明 - 告诉 AI 如何处理常见错误

配置管理

CountBot 提供两种配置方式:图形化配置(推荐)和手动编辑 JSON

图形化配置(推荐)

CountBot 的 Web UI 提供了完整的图形化配置界面:

访问配置界面

  1. 方式一:右上角快捷入口

    • 点击右上角的「技能」图标
    • 快速访问技能管理
  2. 方式二:设置页面

    • 进入「设置」→「技能管理」
    • 查看所有技能

配置流程

  1. 选择技能

    • 在技能列表中找到要配置的技能
    • 点击技能卡片
  2. 查看配置项

    • 系统自动加载配置 Schema
    • 显示所有可配置的字段
    • 每个字段都有说明和帮助文档
  3. 填写配置

    • 根据字段类型填写配置
    • 必填项会有标记
    • 敏感信息(如 API Key)自动隐藏
  4. 验证和保存

    • 点击「保存」按钮
    • 系统自动验证配置格式
    • 配置写入 scripts/config.json

支持的字段类型

类型界面展示示例
string文本输入框名称、URL
password密码输入框(隐藏)API Key
email邮箱输入框(验证)邮箱地址
number数字输入框(带范围)超时时间、最大值
boolean开关按钮启用/禁用
select下拉选择框选项列表
object可折叠的嵌套表单复杂配置

配置验证

图形化界面提供实时验证:

  • 必填项检查
  • 数据类型验证
  • 范围验证(min/max)
  • 格式验证(email、URL)
  • 自动修复缺失字段

配置文件位置

配置文件位于 scripts/config.json

{
"api_key": "your_api_key_here",
"api_base": "https://api.example.com",
"timeout": 30,
"max_retries": 3
}

配置 Schema

为了支持图形化配置,需要在 backend/modules/agent/skills_schema.py 中定义配置 Schema:

SKILL_SCHEMAS = {
"my-skill": {
"skill_name": "my-skill",
"version": "1.0.0",
"description": "我的技能配置",
"config_file": "scripts/config.json",
"help_text": "配置说明:\n1. 获取 API Key\n2. 填写配置\n3. 保存",
"fields": [
{
"key": "api_key",
"type": "password",
"label": "API Key",
"description": "API 密钥",
"required": True,
"sensitive": True,
"placeholder": "请输入 API Key",
"help_text": "在服务商网站获取"
},
{
"key": "timeout",
"type": "number",
"label": "超时时间",
"description": "请求超时时间(秒)",
"default": 30,
"min": 1,
"max": 300,
"help_text": "建议设置为 30-60 秒"
},
{
"key": "enabled",
"type": "boolean",
"label": "启用功能",
"description": "是否启用此功能",
"default": True
}
]
}
}

Schema 字段说明

顶层字段

  • skill_name: 技能名称(必需)
  • version: Schema 版本(必需)
  • description: 配置说明(必需)
  • config_file: 配置文件路径(必需)
  • help_text: 帮助文档(可选)

字段定义

  • key: 配置键名(必需)
  • type: 字段类型(必需)
  • label: 显示标签(必需)
  • description: 字段说明(必需)
  • required: 是否必填(可选,默认 false)
  • sensitive: 是否敏感信息(可选,默认 false)
  • default: 默认值(可选)
  • placeholder: 占位符文本(可选)
  • help_text: 帮助文本(可选)
  • min/max: 数字范围(number 类型)
  • options: 选项列表(select 类型)

嵌套对象示例

{
"key": "email_config",
"type": "object",
"label": "邮箱配置",
"description": "邮箱服务器配置",
"collapsible": True, # 可折叠
"fields": [
{
"key": "server",
"type": "string",
"label": "服务器地址",
"required": True
},
{
"key": "port",
"type": "number",
"label": "端口",
"default": 587
}
]
}

下拉选择示例

{
"key": "mode",
"type": "select",
"label": "运行模式",
"description": "选择运行模式",
"default": "auto",
"options": [
{"value": "auto", "label": "自动"},
{"value": "manual", "label": "手动"},
{"value": "disabled", "label": "禁用"}
]
}

手动编辑配置(高级)

如果不定义 Schema,也可以直接编辑 scripts/config.json

# 编辑配置文件
vim workspace/skills/my-skill/scripts/config.json

# 验证 JSON 格式
python -m json.tool workspace/skills/my-skill/scripts/config.json

注意:手动编辑时需要确保 JSON 格式正确,否则技能可能无法加载。

字段类型

类型说明示例
string文本名称、URL
password密码API Key、密码
email邮箱邮箱地址
number数字超时时间、最大值
boolean布尔启用/禁用
select下拉选择选项列表
object嵌套对象复杂配置

在脚本中读取配置

import json
from pathlib import Path

# 读取配置
config_path = Path(__file__).parent / "config.json"
config = json.loads(config_path.read_text())

api_key = config.get("api_key")
timeout = config.get("timeout", 30)

Python 脚本

基本结构

#!/usr/bin/env python3
"""
技能脚本:my-skill
描述:我的技能实现
"""

import argparse
import json
import sys
from pathlib import Path


def load_config():
"""加载配置文件"""
config_path = Path(__file__).parent / "config.json"
if not config_path.exists():
return {}
return json.loads(config_path.read_text())


def main():
"""主函数"""
parser = argparse.ArgumentParser(description="我的技能")
parser.add_argument("--arg1", required=True, help="参数 1")
parser.add_argument("--arg2", default="default", help="参数 2")

args = parser.parse_args()
config = load_config()

# 实现功能
result = do_something(args.arg1, args.arg2, config)

# 输出结果(JSON 格式)
print(json.dumps(result, ensure_ascii=False, indent=2))


def do_something(arg1, arg2, config):
"""实现具体功能"""
# 你的代码
return {"status": "success", "data": "..."}


if __name__ == "__main__":
try:
main()
except Exception as e:
print(json.dumps({"error": str(e)}), file=sys.stderr)
sys.exit(1)

最佳实践

  1. 使用 argparse - 标准的命令行参数解析
  2. JSON 输出 - 结构化输出,便于 AI 解析
  3. 错误处理 - 捕获异常,输出到 stderr
  4. 配置分离 - 敏感信息放在 config.json
  5. 添加 shebang - #!/usr/bin/env python3

依赖管理

requirements.txt

scripts/requirements.txt 中列出 Python 依赖:

requests>=2.28.0
beautifulsoup4>=4.11.0
python-dotenv>=0.20.0

安装依赖

cd workspace/skills/my-skill/scripts
pip install -r requirements.txt

在 SKILL.md 中声明依赖

---
name: my-skill
description: 我的技能
metadata:
CountBot:
requires:
bins: ["python"]
env: ["API_KEY"]
---

CountBot 会在加载技能时检查依赖是否满足。


完整示例:天气查询

让我们创建一个完整的天气查询技能。

1. 创建目录结构

mkdir -p workspace/skills/weather/scripts
cd workspace/skills/weather

2. 创建 SKILL.md

---
name: weather
description: 查询全球城市天气信息
metadata:
CountBot:
always: false
requires:
bins: ["python"]
---

# 天气查询技能

使用 wttr.in 服务查询全球城市的天气信息。

## 功能特性

- 支持全球城市查询
- 中文天气描述
- 包含温度、湿度、风速等信息
- 支持多日天气预报

## 使用方法

### 基本查询

\`\`\`bash
python workspace/skills/weather/scripts/main.py --city "北京"
\`\`\`

### 指定语言

\`\`\`bash
python workspace/skills/weather/scripts/main.py --city "Beijing" --lang zh
\`\`\`

### 多日预报

\`\`\`bash
python workspace/skills/weather/scripts/main.py --city "上海" --days 3
\`\`\`

## 参数说明

- `--city`: 城市名称(中文或英文)
- `--lang`: 语言(zh/en,默认 zh)
- `--days`: 预报天数(1-3,默认 1)

## 输出示例

\`\`\`json
{
"city": "北京",
"current": {
"temperature": "25°C",
"condition": "晴",
"humidity": "45%",
"wind": "东北风 3级"
},
"forecast": [
{
"date": "2026-03-05",
"high": "28°C",
"low": "15°C",
"condition": "多云"
}
]
}
\`\`\`

## 注意事项

- 使用免费的 wttr.in 服务,无需 API Key
- 城市名称支持中文和英文
- 网络请求可能需要几秒钟

## 错误处理

- 城市不存在:返回错误信息
- 网络超时:自动重试 3 次
- 服务不可用:提示用户稍后重试

3. 创建 Python 脚本

scripts/main.py:

#!/usr/bin/env python3
"""
天气查询技能
使用 wttr.in 服务查询天气信息
"""

import argparse
import json
import sys
import requests


def get_weather(city, lang="zh", days=1):
"""
查询天气信息

Args:
city: 城市名称
lang: 语言(zh/en)
days: 预报天数

Returns:
dict: 天气信息
"""
url = f"https://wttr.in/{city}"
params = {
"format": "j1", # JSON 格式
"lang": lang,
}

try:
response = requests.get(url, params=params, timeout=10)
response.raise_for_status()
data = response.json()

# 解析数据
current = data["current_condition"][0]
forecast = data["weather"][:days]

result = {
"city": city,
"current": {
"temperature": f"{current['temp_C']}°C",
"condition": current["lang_zh"][0]["value"] if lang == "zh" else current["weatherDesc"][0]["value"],
"humidity": f"{current['humidity']}%",
"wind": f"{current['winddir16Point']} {current['windspeedKmph']}km/h"
},
"forecast": [
{
"date": day["date"],
"high": f"{day['maxtempC']}°C",
"low": f"{day['mintempC']}°C",
"condition": day["hourly"][0]["lang_zh"][0]["value"] if lang == "zh" else day["hourly"][0]["weatherDesc"][0]["value"]
}
for day in forecast
]
}

return result

except requests.exceptions.RequestException as e:
raise Exception(f"网络请求失败: {str(e)}")
except (KeyError, IndexError) as e:
raise Exception(f"数据解析失败: {str(e)}")


def main():
"""主函数"""
parser = argparse.ArgumentParser(description="查询天气信息")
parser.add_argument("--city", required=True, help="城市名称")
parser.add_argument("--lang", default="zh", choices=["zh", "en"], help="语言")
parser.add_argument("--days", type=int, default=1, choices=[1, 2, 3], help="预报天数")

args = parser.parse_args()

# 查询天气
result = get_weather(args.city, args.lang, args.days)

# 输出结果
print(json.dumps(result, ensure_ascii=False, indent=2))


if __name__ == "__main__":
try:
main()
except Exception as e:
print(json.dumps({"error": str(e)}, ensure_ascii=False), file=sys.stderr)
sys.exit(1)

4. 创建依赖文件

scripts/requirements.txt:

requests>=2.28.0

5. 测试技能

# 安装依赖
cd workspace/skills/weather/scripts
pip install -r requirements.txt

# 测试脚本
python main.py --city "北京"
python main.py --city "Shanghai" --lang en --days 3

6. 在 CountBot 中使用

  1. 重载技能(Web UI → 设置 → 技能管理 → 重载)
  2. 在对话中使用:
用户: 查询北京天气

AI: 我来查询北京的天气信息...
[使用 read_file 读取 weather 技能文档]
[使用 exec 执行命令]

北京当前天气:
- 温度:25°C
- 天气:晴
- 湿度:45%
- 风向:东北风 3级

最佳实践

1. 文档优先

  • 先写 SKILL.md,确保 AI 能理解如何使用
  • 提供清晰的命令示例和输出示例
  • 说明常见错误和解决方法

2. 命令行友好

  • 使用标准的命令行参数(argparse)
  • 支持 --help 查看帮助
  • 输出结构化数据(JSON)

3. 错误处理

  • 捕获所有异常
  • 输出友好的错误信息
  • 使用非零退出码表示失败

4. 配置分离

  • 敏感信息放在 config.json
  • 不要在代码中硬编码 API Key
  • 提供配置示例文件

5. 依赖管理

  • 在 requirements.txt 中列出所有依赖
  • 在 SKILL.md 中声明二进制依赖
  • 检查依赖是否满足

6. 测试

  • 编写单元测试
  • 测试各种输入情况
  • 测试错误处理

7. 文档完善

  • 添加 README.md 说明开发细节
  • 提供使用示例
  • 说明配置方法

调试技巧

1. 查看技能是否加载

Web UI → 设置 → 技能管理,检查技能是否出现在列表中。

2. 检查依赖

# 检查 Python
which python

# 检查依赖包
pip list | grep requests

3. 手动测试脚本

cd workspace/skills/my-skill/scripts
python main.py --help
python main.py --arg1 test

4. 查看日志

CountBot 日志会显示技能加载和执行信息:

[INFO] Loaded 10 skills
[DEBUG] Skill 'weather' loaded from workspace
[DEBUG] Skill 'weather' dependencies satisfied

5. 使用 always: true 测试

临时将技能设置为自动加载,检查是否正确注入到系统提示词:

metadata:
CountBot:
always: true # 测试时使用,正式使用改为 false

6. 检查配置

# 查看配置文件
cat workspace/skills/my-skill/scripts/config.json

# 验证 JSON 格式
python -m json.tool workspace/skills/my-skill/scripts/config.json

常见问题

Q: 技能没有出现在列表中?

A: 检查:

  1. 目录名称是否正确(小写,连字符分隔)
  2. SKILL.md 文件是否存在
  3. YAML frontmatter 格式是否正确
  4. 是否点击了「重载技能」按钮

Q: AI 不使用我的技能?

A: 检查:

  1. 技能描述是否清晰
  2. 是否在对话中提到了相关功能
  3. 技能文档是否包含清晰的使用示例
  4. 考虑设置 always: true 自动加载

Q: 脚本执行失败?

A: 检查:

  1. Python 路径是否正确
  2. 依赖是否已安装
  3. 配置文件是否存在
  4. 手动执行脚本是否成功

Q: 如何调试脚本?

A: 方法:

  1. 在脚本中添加 print 语句
  2. 查看 CountBot 日志
  3. 手动执行脚本测试
  4. 使用 Python 调试器(pdb)

下一步


参考资源