Skill (Agent Skills)

Agent Skills 将可重用的工作流打包为包含 SKILL.md 规范文件以及可选文档和脚本的文件夹。在对话过程中,代理首先注入低成本的"概览"信息,然后仅在真正需要时加载完整的主体内容和文档,并在隔离的工作空间中安全地运行脚本。

背景参考: - 工程博客: https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills - Open Skills 仓库(可参考的结构): https://github.com/anthropics/skills

概述

🎯 功能特性

  • 🔎 概览注入(名称 + 描述)以指导选择
  • 📥 skill_load 按需拉取 SKILL.md 主体和选定的文档,自动加载技能中定义的工具
  • 📋 skill_list 列出所有可用的技能名称
  • 🔧 skill_list_tools 列出指定技能在 SKILL.md 中定义的工具名称
  • ⚙️ skill_select_tools 动态选择技能的工具(add/replace/clear 模式),实现 token 优化
  • 📚 skill_select_docs 添加/替换/清除文档
  • 🧾 skill_list_docs 列出可用文档
  • 🏃 skill_run 执行命令,返回 stdout/stderr 和输出文件
  • 🗂️ 可收集输出文件,支持 MIME 类型检测
  • 🧩 可插拔的本地或容器工作空间执行器(默认使用本地)
  • 🧱 自定义工作目录,可以将 skill 的运行输入文件、输出文件、skill 文件放在其中
  • 🎯 动态工具加载,根据技能选择自动提供相关工具,节省 LLM token

三层信息模型

Agent Skills 采用三层信息模型,在保持提示简洁的同时,实现按需加载:

1) 初始"概览"层(成本极低) - 仅将 SKILL.md 中的 namedescription 注入到系统消息中 - 让模型了解存在哪些可用技能,无需加载完整内容

2) 完整主体层(按需加载) - 当任务真正需要某个技能时,模型会调用 skill_load - 框架此时才会注入该技能的完整 SKILL.md 主体内容

3) 文档/脚本层(选择性 + 隔离执行)/ 工具调用 - 文档仅在明确请求时才会包含 - 脚本不会内联到提示中,而是在隔离的工作空间内执行 - 只返回执行结果和输出文件,不暴露脚本源代码 - 解析用户配置的可用工具

文件布局

skills/
  demo-skill/
    SKILL.md        # YAML (name/description) + Markdown body
    USAGE.md        # optional docs (.md/.txt)
    scripts/build.sh
    reference/      # 需要参考的文档
    ...

仓库和解析:trpc_agent_sdk/skills/_repository.py

快速开始

1) 要求

  • Python 3.12
  • 模型提供商的 API 密钥(兼容 OpenAI)
  • 可选 Docker(用于容器执行器)

常用环境变量:

export TRPC_AGENT_API_KEY="your-api-key"
export TRPC_AGENT_BASE_URL="your-base-url"
export TRPC_AGENT_MODEL_NAME="your-model-name"
# 可选:指定 skill 的目录,支持本地路径或 URL(见「URL 类型的 Skills Root」)
export SKILLS_ROOT=/path/to/skills
# 可选:覆盖 URL 类型 Skills Root 的缓存目录
export SKILLS_CACHE_DIR=/path/to/cache

或者,您可以使用 .env 文件(示例会自动使用 python-dotenv 加载):

# .env 文件
TRPC_AGENT_API_KEY=your-api-key
TRPC_AGENT_BASE_URL=your-base-url
TRPC_AGENT_MODEL_NAME=your-model-name
SKILLS_ROOT=./skills
# 可选:SKILLS_ROOT 也可以是 URL,例如:
# SKILLS_ROOT=https://example.com/my-skills.tar.gz
# SKILLS_CACHE_DIR=/custom/cache/path

2) 在 Agent 中启用 Skills

创建一个技能仓库和工作空间执行器。如果未指定执行器,为方便开发,将默认使用本地执行器。

import os
from trpc_agent_sdk.agents import LlmAgent
from trpc_agent_sdk.models import OpenAIModel
from trpc_agent_sdk.skills import SkillToolSet
from trpc_agent_sdk.skills import create_default_skill_repository
from trpc_agent_sdk.skills.tools import LinkSkillStager
from trpc_agent_sdk.code_executors import create_local_workspace_runtime
from trpc_agent_sdk.code_executors import create_container_workspace_runtime
# Cube 是可选 extra(`pip install 'trpc-agent-py[cube]'`),按需引入。
# from trpc_agent_sdk.code_executors.cube import CubeCodeExecutor, CubeCodeExecutorConfig
# from trpc_agent_sdk.code_executors.cube import create_cube_workspace_runtime

# 创建工作空间运行时(本地、容器或 Cube)
workspace_runtime = create_local_workspace_runtime()
# 或使用容器:workspace_runtime = create_container_workspace_runtime()
# 或使用远端 Cube/E2B 沙箱:
#   executor = await CubeCodeExecutor.create(CubeCodeExecutorConfig())
#   workspace_runtime = create_cube_workspace_runtime(executor)

# 创建技能仓库
repository = create_default_skill_repository("./skills", workspace_runtime=workspace_runtime, use_cached_repository=True)

# 创建技能工具集,可配置工件保存选项
skill_tool_set = SkillToolSet(
    repository=repository,
    skill_stager=LinkSkillStager(),
    # run_tool_kwargs 属于工具可选参数
    run_tool_kwargs={
        "save_as_artifacts": True,  # 是否存储为制品文件
        "omit_inline_content": False,
    }
)

# 创建带技能的 agent
agent = LlmAgent(
        name="skill_run_agent",
        description="A professional skill run assistant that can use Agent Skills.",
        model=_create_model(),
        instruction=INSTRUCTION,  # 包含技能使用指导的提示词
        tools=[skill_tool_set],
        skill_repository=repository,
    )

注意:在版本 1.1.10(不包含)之后,优化了 skill 的加载和注入机制,支持缓存 skill 内容和本地沙箱环境软连的方式来避免拷贝

提示词示例

INSTRUCTION 中应包含完整的技能使用工作流指导:

INSTRUCTION = """
You are an AI assistant with access to Agent Skills.

## Complete Skill Workflow

When handling user requests:

1. **Discover** → Call skill_list() to see available skills
2. **Inspect** → Call skill_list_tools(skill_name="...") to preview tools
3. **Load** → Call skill_load(skill_name="...") to load the skill
4. **Optimize** → Call skill_select_tools(...) to select only needed tools (saves tokens)
5. **Document** → Call skill_list_docs(...) and skill_select_docs(...) if more info needed
6. **Execute** → Call skill_run(...) to execute commands or use skill's tools directly

Example Complete Flow:
User: "What's the weather in Beijing?"
→ skill_list() → see "weather-tools"
→ skill_list_tools(skill_name="weather-tools") → see available tools
→ skill_load(skill_name="weather-tools") → load full content
→ skill_select_tools(skill_name="weather-tools", tools=["get_current_weather"]) → optimize
→ get_current_weather(city="Beijing") → execute

Always use environment variables in commands:
- $WORKSPACE_DIR, $SKILLS_DIR, $WORK_DIR, $OUTPUT_DIR, $RUN_DIR, $SKILL_NAME
"""

关键点: - 工具自动注册:通过 SkillToolSet 自动注册以下工具,无需手动连接: - skill_list:列出所有可用技能 - skill_list_tools:列出技能的工具 - skill_load:加载技能内容 - skill_select_tools:选择特定工具(优化 token) - skill_list_docs:列出可用文档 - skill_select_docs:选择特定文档 - skill_run:执行技能命令 - 智能提示指导:在提示词中明确说明工作流程,引导 LLM 按正确顺序调用工具 - Token 优化:通过 skill_select_tools 仅加载需要的工具,显著减少上下文大小 - 代码位置: - 工具包入口(聚合导出):trpc_agent_sdk/skills/tools/init.py - skill_run 实现:trpc_agent_sdk/skills/tools/_skill_run.py(其余工具见下文各节「声明位置」)

3) 运行示例

完整示例交互式演示:examples/skills/run_agent.py

示例采用模块化结构组织: - agent/agent.py - Agent 创建 - agent/tools.py - 技能工具集创建 - agent/config.py - 从环境变量读取模型配置 - agent/prompts.py - Agent 指令提示词 - run_agent.py - 主入口文件

cd examples/skills

# 设置环境变量
export TRPC_AGENT_API_KEY="your-api-key"
export TRPC_AGENT_BASE_URL="your-base-url"
export TRPC_AGENT_MODEL_NAME="your-model-name"
export SKILLS_ROOT="./skills"  # 可选,默认为 ./skills

# 运行示例
python3 run_agent.py

或使用 .env 文件:

# 创建 .env 文件
cat > .env << EOF
TRPC_AGENT_API_KEY=your-api-key
TRPC_AGENT_BASE_URL=your-base-url
TRPC_AGENT_MODEL_NAME=your-model-name
SKILLS_ROOT=./skills
EOF

# 运行(自动加载 .env)
python3 run_agent.py

示例技能(摘录): examples/skills/skills/python-math/SKILL.md

提示词: - 说出你想要完成的任务;模型会根据概览决定是否需要某个技能。 - 当需要时,模型会调用 skill_load 获取主体/文档,然后调用 skill_run 执行并返回输出文件。

运行结果

以 user-file-ops 为例

🆔 Session ID: be355f8f...
📝 User:
        I have a text file at /tmp/skillrun-notes.txt.
        Please use the user-file-ops skill to summarize it, you can use command `cp` to copy it to the workspace,
        then mapping it to `work/inputs/user-notes.txt` and writing the summary to `out/user-notes-summary.txt`

🤖 Assistant:
🔧 [Invoke Tool:: skill_load({'skill_name': 'user-file-ops'})]
📊 [Tool Result: {'result': "skill 'user-file-ops' loaded"}]

🔧 [Invoke Tool:: skill_list_docs({'skill_name': 'user-file-ops'})]
📊 [Tool Result: {}]

🔧 [Invoke Tool:: skill_run({'skill': 'user-file-ops', 'command': 'cp /tmp/skillrun-notes.txt work/inputs/user-notes.txt && bash scripts/summarize_file.sh work/inputs/user-notes.txt out/user-notes-summary.txt', 'output_files': ['out/user-notes-summary.txt']})]
📊 [Tool Result: {'stdout': '', 'stderr': '', 'exit_code': 0, 'timed_out': False, 'duration_ms': 0, 'output_files': [{'name': 'out/user-notes-summary.txt', 'content': 'File: work/inputs/user-notes.txt\nLines: 2\nWords: 7\nBytes: 41\n\nFirst 20 non-empty lines:\n     1\thello from skillrun\n     2\tthis is another line\n', 'mime_type': 'text/plain'}], 'artifact_files': []}]
The file `/tmp/skillrun-notes.txt` has been summarized successfully. Here are the details:

### Summary of `user-notes.txt`:
- **Lines**: 2
- **Words**: 7
- **Bytes**: 41

### Preview (First 20 non-empty lines):
  1. hello from skillrun
  2. this is another line

The summary has been saved to `out/user-notes-summary.txt`. Let me know if you'd like to perform any further actions!

运行目录

默认工作目录名称: /tmp/ws_<session_id>-<time>/, 目录下的文件

├── metadata.json
├── out
│   └── user-notes-summary.txt
├── runs
│   ├── run_20260116T201918.239930
│   ├── run_20260116T201918.322124
│   └── run_20260116T201918.402214
├── skills
│   └── user-file-ops
│       ├── inputs -> ../../work/inputs
│       ├── out -> ../../out
│       ├── scripts
│       │   └── summarize_file.sh
│       ├── SKILL.md
│       └── work -> ../../work
└── work
    └── inputs
        └── user-notes.txt
  • out: 结果输出目录
  • work: 临时共享工作目录
  • runs: 当前程序运行路径
  • skills: 所有 skill 存储目录

高级用法

自定义工作目录

默认情况下,技能执行时会在临时目录(如 /tmp/ws_<session_id>-<time>/)中创建工作空间。如果需要自定义输出目录的位置,可以通过设置环境变量来实现。

方法 1:代码中指定

def create_skill_tool_set(workspace_runtime_type: str = "local") -> SkillToolSet:
    """Create a new skill tool set."""
    tool_kwargs = {
        "save_as_artifacts": True,
        "omit_inline_content": False,
    }
    if workspace_runtime_type == "local":
        workspace_runtime_args = {"work_root": "/tmp/ws_abc123"}
    else:
        workspace_runtime_args = {}
    # workspace_runtime = _create_workspace_runtime(workspace_runtime_type="container", **workspace_runtime_args)
    # 根据指定类型(local/container)创建工作空间运行时
    workspace_runtime = _create_workspace_runtime(workspace_runtime_type=workspace_runtime_type, **workspace_runtime_args)
    skill_paths = _get_skill_paths()
    repository = create_default_skill_repository(skill_paths, workspace_runtime=workspace_runtime)
    return SkillToolSet(repository=repository, run_tool_kwargs=tool_kwargs), repository

在 workspace_runtime_args 参数中指定

后面工作目录变成: /{custom_dir}/ws_{session_id}_{time}, 例如:

/tmp/ws_abc123/ws_env_var_demo_1768564372436142924/
├── metadata.json
├── out
│   ├── fibonacci_data.txt
│   └── fibonacci_summary.txt
├── runs
│   ├── run_20260116T195252.438049
│   ├── run_20260116T195252.518753
│   ├── run_20260116T195252.597016
│   ├── run_20260116T195257.562621
│   └── run_20260116T195304.315245
├── skills
│   └── python-math
│       ├── inputs -> ../../work/inputs
│       ├── out -> ../../out
│       ├── scripts
│       │   └── fib.py
│       ├── SKILL.md
│       └── work -> ../../work
└── work
    └── inputs

方法 2:在提示词中编写

output_instruction = f"""

IMPORTANT: When calling skill_run, you MUST pass env={{'OUTPUT_DIR': '{custom_output_dir}'}} parameter
to use the custom output directory. Write all output files to $OUTPUT_DIR (which will be '{custom_output_dir}').
"""

期望 skill 执行命令传入其他的环境变量也可以使用这种方式

动态加载工具

完整示例参考:skills_with_dynamic_tools/run_agent.py

URL 类型的 Skills Root

SKILLS_ROOT 不仅支持本地目录路径,还支持 URL 格式。框架会自动下载远端归档包、解压并缓存到本地,后续调用直接命中缓存无需重复下载。

相关实现:trpc_agent_sdk/skills/_url_root.py

支持的输入格式

格式 示例 说明
本地路径 /path/to/skills./skills 直接使用本地目录(默认行为,不经过缓存)
file:// URL file:///path/to/skills 显式文件 URL,仅支持 localhost 或空主机
http:// / https:// URL https://example.com/skills.tar.gz 自动下载、解压并缓存到本地

远端 URL 支持的归档格式:

扩展名 格式
.zip ZIP 归档
.tar 未压缩 tar 归档
.tar.gz / .tgz gzip 压缩的 tar 归档
SKILL.md(直接链接) 单个裸技能文件

当 URL 无法从扩展名判断格式时,框架会读取文件头的魔数字节(magic bytes)自动识别(ZIP:PK\x03\x04;gzip:\x1f\x8b)。

使用方法

通过环境变量配置

# HTTPS + tar.gz 归档
export SKILLS_ROOT="https://example.com/my-skills.tar.gz"

# HTTPS + ZIP 归档
export SKILLS_ROOT="https://example.com/my-skills.zip"

# 直接指向单个 SKILL.md 文件
export SKILLS_ROOT="https://example.com/SKILL.md"

# 显式文件 URL(等价于本地路径)
export SKILLS_ROOT="file:///home/user/my-skills"

代码中直接使用

# 直接配置 skill 的路径
skill_path = "https://example.com/skills.tar.gz"
repository = create_default_skill_repository(skill_path, workspace_runtime=workspace_runtime)

下载缓存机制

首次使用 URL 类型的 SKILLS_ROOT 时,框架自动执行以下步骤:

1. 下载归档到临时目录
   {cache_dir}/tmp-skill-root-XXXXXX/download

2. 解压到临时提取目录
   {cache_dir}/tmp-skill-root-XXXXXX/root/

3. 写入哨兵文件(标记解压成功)
   {cache_dir}/tmp-skill-root-XXXXXX/root/.ready

4. 原子重命名到最终缓存目录(以 URL 的 SHA-256 哈希命名)
   {cache_dir}/{sha256_of_url}/

5. 清理临时目录

后续调用时,若 {cache_dir}/{sha256_of_url}/.ready 文件存在,则直接返回缓存目录,跳过下载和解压。若缓存目录存在但 .ready 文件缺失(如上次下载中断),则自动清理并重新下载。

并发场景下,多个进程同时下载同一 URL 时,框架通过原子 rename 操作保证只有第一个进程的结果被写入,其余进程会检测到 .ready 文件后直接返回。

缓存目录默认位置

平台 默认路径
Linux $XDG_CACHE_HOME/trpc-agent-py/skills/~/.cache/trpc-agent-py/skills/
macOS ~/Library/Caches/trpc-agent-py/skills/
Windows %LocalAppData%/trpc-agent-py/skills/

可通过环境变量覆盖:

export SKILLS_CACHE_DIR="/custom/cache/path"

安全限制

为防范恶意归档(如 zip bomb)和超大下载,框架内置以下硬性限制:

限制项 默认值 说明
单次下载最大体积 64 MiB 包括 Content-Length 预检和流式写入双重检查
单个解压文件最大体积 64 MiB ZIP 使用头部声明与实际读取双重校验
解压后所有文件总体积 256 MiB 所有条目字节数累加上限

超出任意限制时会抛出 RuntimeError,已下载的临时文件会被自动清理。

此外,归档路径安全也受到严格保护: - 拒绝绝对路径(如 /etc/passwd) - 拒绝路径穿越(如 ../../etc/passwd) - 拒绝 Windows 驱动器字母(如 C:foo) - 拒绝符号链接和硬链接 tar 条目(防止沙箱逃逸)

SKILL.md 文件结构

SKILL.md 文件采用 YAML front matter(前置元数据)+ Markdown 主体格式:

---
name: python-math
description: Small Python utilities for math and text files.
---

Overview
Run short Python scripts inside the skill workspace...

Examples
1) Print the first N Fibonacci numbers
   Command: python3 scripts/fib.py 10 > out/fib.txt

Output Files
- out/fib.txt

编写建议: - 保持简洁namedescription 字段应简洁明了,用于概览展示 - 详细说明:在主体中,包含使用时机、操作步骤/命令、输出文件路径等信息 - 脚本组织:将脚本放在 scripts/ 目录下,并在命令中引用它们

更多示例,请参见: https://github.com/anthropics/skills

SKILL 工具详解

skill_list

声明位置trpc_agent_sdk/skills/tools/_skill_list.py

输入参数:无

返回值: - 所有可用技能名称的数组

功能行为: - 返回技能仓库中所有可用的技能名称列表 - 用于发现和浏览可用的技能

提示词指导

这个工具由 LLM 自动调用。在 Agent 的提示词中,应该包含类似以下的指导:

INSTRUCTION = """
## Skill Discovery Workflow

When a user asks for a task that might require skills:

1. **First, always check available skills**:
   - Call skill_list() to see what skills are available
   - This shows you all skill names like ["file-tools", "python-math", "weather-tools"]

Example:
User: "Can you help me with weather information?"
Assistant: Let me check what skills are available.
→ Call skill_list()
→ See result: ["file-tools", "python-math", "weather-tools"]
→ Notice "weather-tools" is relevant
"""

使用场景: - 用户询问"有哪些技能可用?" - 需要探索可用功能时 - 不确定使用哪个技能时,先列出所有技能

skill_list_tools

声明位置trpc_agent_sdk/skills/tools/_skill_list_tool.py

输入参数: - skill_name(必需):技能名称

返回值: - 该技能在 SKILL.mdTools: 部分定义的工具名称数组 - 如果技能未定义工具,返回空数组

功能行为: - 返回指定技能在 SKILL.md 中声明的工具列表 - 用于在加载技能前预览其提供的工具 - 注意:仅返回在 SKILL.md 中显式列出的工具,不会返回实际代码中的所有工具

提示词指导

这个工具由 LLM 在加载技能前调用。提示词应包含:

INSTRUCTION = """
## Skill Inspection Workflow

Before loading ANY skill, you MUST inspect its tools:

2. **Preview skill tools before loading**:
   - Call skill_list_tools(skill_name="skill-name")
   - This shows what tools the skill provides
   - Verify the skill has the tools you need

Example:
Assistant: I found "weather-tools" skill. Let me check what it provides.
→ Call skill_list_tools(skill_name="weather-tools")
→ See result: ["get_current_weather", "get_weather_forecast", "search_city_by_name"]
→ Confirm it has "get_current_weather" which I need
→ Proceed to load the skill

**Why this step matters**:
- Avoids loading unnecessary skills
- Confirms the skill has required capabilities
- Saves tokens by loading only relevant skills
"""

使用场景: - 在调用 skill_load 之前验证技能是否提供所需工具 - 用户询问"这个技能有哪些工具?" - 需要选择合适的技能时

SKILL.md 中的定义

SKILL.md 文件中,工具通过 Tools: 部分声明:

---
name: weather-tools
description: Weather information query tools
---

Tools:
- get_current_weather
- get_weather_forecast
- search_city_by_name
# comment: this tool is deprecated
# - old_weather_api

Overview
...

skill_select_tools

声明位置trpc_agent_sdk/skills/tools/_skill_select_tools.py

输入参数: - skill_name(必需):技能名称 - tools(可选):工具名称数组 - include_all_tools(可选):布尔值,是否包含所有工具 - mode(可选):字符串,操作模式 - add:添加工具到现有列表 - replace:替换现有工具列表(默认) - clear:清除所有工具

返回值: - SkillSelectToolsResult 对象,包含: - selected_tools:选中的工具名称数组 - include_all_tools:是否包含所有工具

功能行为: - 优化 LLM 上下文:仅激活当前对话需要的工具 - 更新 temp:skill:tools:<name> 会话键 - 与 DynamicSkillToolSet 配合使用时,只有选中的工具会被加载到 LLM 上下文

提示词指导

这个工具由 LLM 在加载技能后调用,用于优化 token 使用。提示词应包含:

INSTRUCTION = """
## Tool Selection for Token Optimization

After loading a skill, you SHOULD refine tool selection:

4. **Optimize tool selection** (RECOMMENDED):
   - After skill_load(), all tools from SKILL.md are auto-selected
   - If you only need specific tools, call skill_select_tools() to reduce tokens
   - This is especially important for skills with many tools

Example 1: Select specific tools
User: "What's the current weather in Beijing?"
Assistant:
→ skill_load(skill_name="weather-tools")  # Auto-selects all 3 tools
→ skill_select_tools(
    skill_name="weather-tools",
    tools=["get_current_weather"],  # Only need current weather
    mode="replace"
  )
→ Result: Only 1 tool active instead of 3 (saves ~60% tokens)

Example 2: Multi-tool task
User: "Get current weather and 3-day forecast for Shanghai"
Assistant:
→ skill_load(skill_name="weather-tools")
→ skill_select_tools(
    skill_name="weather-tools",
    tools=["get_current_weather", "get_weather_forecast"],
    mode="replace"
  )
→ Result: 2 out of 3 tools active (saves ~30% tokens)

Example 3: Add more tools later
Assistant:
→ skill_select_tools(
    skill_name="weather-tools",
    tools=["search_city_by_name"],  # Need to search city
    mode="add"  # Add to existing selection
  )

**Token Savings**:
- A skill with 10 tools → select 2 → saves ~80% tool definition tokens
- Especially valuable for skills with complex tools
"""

使用场景: - 在 skill_load 后优化工具选择,减少 token 消耗 - 任务只需要技能中的部分工具 - 在对话过程中动态调整可用工具

skill_load 的关系: - skill_load 会自动选择 SKILL.md 中定义的所有工具 - skill_select_tools 用于进一步细化选择,实现 token 优化

skill_load

声明位置trpc_agent_sdk/skills/tools/_skill_load.py

输入参数: - skill_name(必需):技能名称 - docs(可选):文档文件名数组,指定要加载的文档 - include_all_docs(可选):布尔值,是否包含所有文档

返回值: - 成功消息字符串,例如:"skill 'python-math' loaded"

功能行为: - 写入临时会话键(每轮对话): - temp:skill:loaded:<name> = "1"(标记技能已加载) - temp:skill:docs:<name> = "*"(包含所有文档)或 JSON 数组(指定文档列表) - temp:skill:tools:<name> = JSON 数组(自动从 SKILL.md 解析的工具列表) - 请求处理器会将 SKILL.md 主体内容和选定的文档注入到系统消息中 - 自动选择 SKILL.mdTools: 部分定义的所有工具

提示词指导

这个工具由 LLM 在确认需要某个技能后调用。提示词应包含:

INSTRUCTION = """
## Skill Loading Workflow

After confirming a skill is appropriate:

3. **Load the skill**:
   - Call skill_load(skill_name="skill-name")
   - This injects the full SKILL.md body content into context
   - Automatically selects all tools defined in the skill's SKILL.md
   - Optionally load specific docs or all docs

Example 1: Load skill without docs
Assistant:
→ skill_load(skill_name="python-math")
→ Result: Full SKILL.md content loaded, all tools auto-selected
→ Can now use the skill's tools or run commands

Example 2: Load skill with specific docs
Assistant:
→ skill_load(
    skill_name="weather-tools",
    docs=["API_REFERENCE.md"]  # Load specific documentation
  )

Example 3: Load skill with all docs
Assistant:
→ skill_load(
    skill_name="data-analysis",
    include_all_docs=True  # Load all available docs
  )

**What happens after loading**:
- SKILL.md body is injected into your context (Overview, Examples, etc.)
- All tools listed in SKILL.md Tools: section are automatically selected
- You can now see detailed usage instructions and examples
- You can call skill_run or use the skill's tools

**Multiple loads**:
- Safe to call multiple times on the same skill
- Subsequent calls can add/replace docs
- Tool selection persists until modified by skill_select_tools
"""

使用场景: - 在 skill_listskill_list_tools 确认需求后加载技能 - 需要查看技能的详细使用说明和示例 - 准备使用技能的工具或执行命令

使用说明: - 可以安全地多次调用,用于添加或替换文档 - 首次加载会自动选择所有工具,可用 skill_select_tools 进一步优化

skill_select_docs

声明位置trpc_agent_sdk/skills/tools/_skill_select_docs.py

输入参数: - skill_name(必需):技能名称 - docs(可选):文档文件名数组 - include_all_docs(可选):布尔值,是否包含所有文档 - mode(可选):字符串,操作模式 - add:添加文档到现有列表 - replace:替换现有文档列表(默认) - clear:清除所有文档

返回值: - SkillSelectDocsResult 对象,包含: - selected_docs:选中的文档名称数组 - include_all_docs:是否包含所有文档

功能行为: - 更新 temp:skill:docs:<name> 会话键: - *:表示包含所有文档 - JSON 数组:表示显式指定的文档列表 - 下一次 LLM 请求时,选中的文档内容会被注入到系统消息

提示词指导

这个工具由 LLM 在需要更多文档时调用。提示词应包含:

INSTRUCTION = """
## Documentation Selection

If the SKILL.md body is not sufficient, you can load additional docs:

5. **Select additional documentation** (when needed):
   - Call skill_select_docs() to load reference documentation
   - Use this when you need API details, configuration info, etc.

Example 1: Load specific docs
Assistant: I need more details about the API.
→ skill_select_docs(
    skill_name="weather-tools",
    docs=["API_REFERENCE.md", "CONFIGURATION.md"],
    mode="replace"
  )

Example 2: Load all docs
Assistant: Let me load all available documentation.
→ skill_select_docs(
    skill_name="data-analysis",
    include_all_docs=True
  )

Example 3: Add more docs
Assistant: I need additional reference.
→ skill_select_docs(
    skill_name="weather-tools",
    docs=["TROUBLESHOOTING.md"],
    mode="add"  # Add to existing docs
  )

**When to use**:
- SKILL.md Overview is insufficient
- Need detailed API reference
- Need configuration examples
- Troubleshooting specific issues
"""

使用场景: - SKILL.md 主体内容不足以完成任务 - 需要查看 API 参考文档 - 需要配置示例或故障排除指南

skill_list_docs

声明位置trpc_agent_sdk/skills/tools/_skill_list_docs.py

输入参数: - skill_name(必需):技能名称

返回值: - 可用文档文件名的数组(如 ["API_REFERENCE.md", "CONFIGURATION.md", "TROUBLESHOOTING.md"]

功能行为: - 列出指定技能的所有可用文档文件 - 用于在调用 skill_select_docs 前查看有哪些文档可用

提示词指导

这个工具由 LLM 在需要查看可用文档时调用。提示词应包含:

INSTRUCTION = """
## Documentation Discovery

Before selecting docs, you can check what's available:

**Check available docs**:
→ skill_list_docs(skill_name="skill-name")
→ Returns: ["API_REFERENCE.md", "USAGE_EXAMPLES.md", ...]

Example workflow:
User: "I need help configuring the weather API"
Assistant: Let me check what documentation is available.
→ skill_list_docs(skill_name="weather-tools")
→ Result: ["API_REFERENCE.md", "CONFIGURATION.md", "FAQ.md"]
→ I see there's a CONFIGURATION.md, let me load it.
→ skill_select_docs(
    skill_name="weather-tools",
    docs=["CONFIGURATION.md"]
  )

**When to use**:
- Before calling skill_select_docs
- User asks "what documentation is available?"
- Need to find specific reference materials
"""

使用场景: - 在调用 skill_select_docs 之前查看可用文档 - 用户询问"这个技能有什么文档?"

说明:这些会话键由框架自动管理;在自然对话流程中,通常不需要直接操作它们。

skill_run

声明位置trpc_agent_sdk/skills/tools/_skill_run.py

输入参数: - skill(必需):技能名称 - command(必需):要执行的 shell 命令 - output_files(可选):输出文件的 glob 模式数组(如 ["out/*.txt", "$OUTPUT_DIR/result.json"]) - env(可选):自定义环境变量字典(如 {"CUSTOM_VAR": "value"}) - timeout(可选):超时时间(秒)

返回值: - WorkspaceRunResult 对象,包含: - stdout:标准输出 - stderr:标准错误 - exit_code:退出代码 - timed_out:是否超时 - duration_ms:执行时长(毫秒) - output_files:收集的输出文件数组(每个文件包含 namecontentmime_type) - artifact_files:工件文件信息

功能行为: - 在隔离的工作空间中执行 shell 命令 - 自动注入标准环境变量($WORKSPACE_DIR$SKILLS_DIR$WORK_DIR$OUTPUT_DIR$RUN_DIR$SKILL_NAME) - 收集指定的输出文件并返回 - 支持自定义环境变量覆盖

提示词指导

这个工具由 LLM 在准备好后执行实际命令。提示词应包含详细的使用指南:

INSTRUCTION = """
## Skill Execution

After loading a skill, you can execute commands:

6. **Execute skill commands**:
   - Call skill_run(skill="skill-name", command="...", output_files=[...])
   - Commands run in the skill's directory
   - Use environment variables for portable paths

Example 1: Simple command execution
Assistant:
→ skill_run(
    skill="python-math",
    command="python3 scripts/fib.py 10 > $OUTPUT_DIR/fib.txt",
    output_files=["$OUTPUT_DIR/fib.txt"]
  )

Example 2: Multiple output files
Assistant:
→ skill_run(
    skill="data-analysis",
    command="python3 scripts/analyze.py $WORK_DIR/inputs/data.csv",
    output_files=[
        "$OUTPUT_DIR/*.txt",
        "$OUTPUT_DIR/charts/*.png"
    ]
  )

Example 3: Custom environment variables
Assistant:
→ skill_run(
    skill="weather-tools",
    command="python3 scripts/fetch.py",
    env={
        "API_KEY": "user-provided-key",
        "REGION": "asia"
    },
    output_files=["$OUTPUT_DIR/weather.json"]
  )

Example 4: Complex multi-step command
Assistant:
→ skill_run(
    skill="file-tools",
    command='''
        mkdir -p $OUTPUT_DIR/processed &&
        cp $WORK_DIR/inputs/*.txt $OUTPUT_DIR/processed/ &&
        ls -la $OUTPUT_DIR/processed
    ''',
    output_files=["$OUTPUT_DIR/processed/*"]
  )

**Environment Variables Available**:
- $WORKSPACE_DIR: Root workspace directory
- $SKILLS_DIR: Skills directory (contains skill folders)
- $WORK_DIR: Shared working directory
  - $WORK_DIR/inputs: User input files (read-only)
- $OUTPUT_DIR: Output directory (write final results here)
- $RUN_DIR: Current run's directory (unique per execution)
- $SKILL_NAME: Current skill name (e.g., "python-math")

**Best Practices**:
1. Always use environment variables (not hard-coded paths)
2. Write final outputs to $OUTPUT_DIR
3. Read user files from $WORK_DIR/inputs
4. Include output_files parameter to collect results
5. Use descriptive output file names

**Common Patterns**:

# Generate output file

command="python3 scripts/process.py > $OUTPUT_DIR/result.txt"

# Process input and generate output
command="bash scripts/transform.sh $WORK_DIR/inputs/data.csv $OUTPUT_DIR/output.csv"

# Multiple commands
command="mkdir -p $OUTPUT_DIR/reports && python3 scripts/generate.py && ls $OUTPUT_DIR"

# Use SKILL_NAME for context
command="echo 'Processed by $SKILL_NAME' > $OUTPUT_DIR/metadata.txt"

**Error Handling**:
- Check exit_code in the result (0 = success)
- Read stderr for error messages
- Adjust timeout if command takes too long
"""

使用场景: - 执行技能中的脚本或命令 - 处理文件并生成输出 - 运行数据分析、转换等任务

执行流程

LLM 调用 skill_run(skill="python-math", command="python3 scripts/fib.py 10")
    ↓
1. 创建隔离的工作空间
   /tmp/ws_<session_id>/
   ├── skills/python-math/     (技能根目录,只读)
   │   ├── SKILL.md
   │   ├── scripts/
   │   │   └── fib.py
   │   ├── out/    → ../../out  (符号链接)
   │   └── work/   → ../../work (符号链接)
   ├── out/                     (输出目录)
   ├── work/                    (工作目录)
   └── run/                     (运行目录)
    ↓
2. 注入环境变量
   WORKSPACE_DIR=/tmp/ws_<session_id>
   SKILLS_DIR=/tmp/ws_<session_id>/skills
   WORK_DIR=/tmp/ws_<session_id>/work
   OUTPUT_DIR=/tmp/ws_<session_id>/out
   RUN_DIR=/tmp/ws_<session_id>/run
   SKILL_NAME=python-math
    ↓
3. 执行命令(在技能根目录)
   cd /tmp/ws_<session_id>/skills/python-math
   bash -lc "python3 scripts/fib.py 10"
    ↓
4. 收集输出文件
   根据 output_files 参数收集文件
   例如:out/*.txt → /tmp/ws_<session_id>/out/*.txt
    ↓
5. 返回结果
   {
     "stdout": "...",
     "stderr": "...",
     "exit_code": 0,
     "output_files": [...]
   }

运行环境

接口定义trpc_agent_sdk/code_executors/_base_workspace_runtime.py

实现方式: - 本地执行器trpc_agent_sdk/code_executors/local/_local_ws_runtime.py - 直接在本地系统执行命令,适合开发和测试 - 容器执行器(Docker):trpc_agent_sdk/code_executors/container/_container_ws_runtime.py - 在 Docker 容器中执行,提供更好的隔离性 - Cube 执行器(远端 E2B 沙箱):trpc_agent_sdk/code_executors/cube/_runtime.py - 在远端 Cube/E2B 沙箱中执行;适合宿主上没有 Docker、或者需要强远端隔离的场景 - 通过 create_cube_workspace_runtime(executor, workspace_cfg=...) 构造;详见 code_executor.md - 需要安装可选 extra [cube]pip install 'trpc-agent-py[cube]'),并配置 E2B_API_URL / E2B_API_KEY / CUBE_TEMPLATE_ID 环境变量(或对应 cfg 字段)

容器执行器注意事项: - 运行基础目录可写;当设置了 $SKILLS_ROOT 时,会以只读方式挂载 - 默认禁用网络访问,以提高可重复性和安全性

Cube 执行器注意事项: - 文件 / 目录传输使用 tar 协议,目录上传下载是单次往返,并保留符号链接和权限 - 远端工作根目录默认 /workspace/cube_agent;按执行隔离的子目录命名为 ws_<exec_id>_<suffix>,每次 create_workspace 都会幂等地 mkdir -p,外部清理也能透明恢复 - 同一个 Cube 沙箱可以同时承载 bare CubeCodeExecutor 与 workspace runtime,命令共享 CubeCodeExecutorConfig.execute_timeout

安全性和资源限制: - 工作空间隔离:所有读写操作限制在工作空间内 - 风险控制:通过超时机制和只读技能树降低安全风险 - 资源限制:输出文件读取大小有上限,防止过大的负载影响系统性能

事件和追踪

工具执行可能携带状态增量(由 skill_load 使用)。状态增量通过 InvocationContext 进行管理,用于将技能内容注入到系统消息中。

设计原理

设计动机

技能通常包含冗长的指令和脚本。如果将所有内容都内联到提示中,不仅成本高昂,还存在安全风险。三层信息模型通过保持提示简洁,仅在真正需要时才加载详细内容和运行代码,从而平衡了功能性和效率。

技能执行流程

下图展示了从用户查询到 LLM 调用工具的完整流程:

用户查询: "What's the weather in Beijing?"
    ↓
第一次 LLM 请求(skill 未加载)
    ↓
_inject_overview() 被调用
    ↓
注入简短描述:
"Available skills:
 - weather-tools: Weather information query tools..."
    ↓
LLM 看到 skill 描述,决定加载它
    ↓
LLM 调用: skill_load(skill_name="weather-tools")
    ↓
skill_load() 更新 session state
(SKILL_LOADED_STATE_KEY_PREFIX + "weather-tools" = "1")
    ↓
第二次 LLM 请求(skill 已加载)
    ↓
process_llm_request() 被调用
    ↓
_get_loaded_skills() 检测到 "weather-tools" 已加载
    ↓
repository.get("weather-tools") 获取完整 skill 对象
    ↓
_parse_full() 解析 SKILL.md
    ├─ YAML frontmatter → summary (name, description)
    └─ Markdown body → body (Overview 等完整内容)
    ↓
if sk.body: parts.append(f"\n[Loaded] {name}\n\n{sk.body}\n")
    ↓
完整的 Overview 内容被注入到 system message
    ↓
LLM 看到详细的 skill 说明和使用方法
    ↓
LLM 调用对应的工具:get_current_weather(city="Beijing")

关键点说明

  1. 概览注入(第一次请求)
  2. 框架自动调用 _inject_overview() 将所有技能的简短描述注入到系统消息
  3. LLM 根据描述判断是否需要加载某个技能
  4. 此时成本极低,仅包含 namedescription 字段

  5. 技能加载skill_load 调用)

  6. LLM 主动调用 skill_load(skill_name="weather-tools")
  7. 工具更新会话状态:temp:skill:loaded:weather-tools = "1"
  8. 状态增量通过 state_delta 传递给框架

  9. 内容注入(第二次请求)

  10. 框架检测到技能已加载(通过 _get_loaded_skills()
  11. 从仓库获取完整的 SKILL.md 内容
  12. 将 Markdown body 部分注入到系统消息
  13. LLM 此时能看到详细的使用说明和示例

  14. 工具调用

  15. LLM 根据注入的详细内容,调用相应的工具函数
  16. 工具执行完成后,返回结果给 LLM

状态注入机制

工具通过 InvocationContext.actions.state_delta 写入临时状态键,框架根据这些状态增量动态构建系统消息,实现技能内容的按需注入。

主要状态键: - temp:skill:loaded:<name>:标记技能已加载 - temp:skill:docs:<name>:存储已选择的文档列表 - temp:skill:tools:<name>:存储已选择的工具列表

最佳实践: 1. 使用约定的前缀:保持状态键命名一致(如 temp:skill:loaded:) 2. JSON 序列化:复杂数据结构使用 JSON 序列化存储 3. 合并状态:读取时合并 session_statestate_delta 4. 最小化状态:只存储必要的状态信息 5. 文档化状态键:在代码中明确注释状态键的含义和格式

执行隔离

脚本在工作空间边界内运行,只将选定的输出文件带回,而不是脚本源代码本身,确保了安全性和可控性。

故障排除

常见问题及解决方案

  • 未知技能错误
  • 检查技能名称是否正确
  • 验证仓库路径是否正确
  • 确保在调用 skill_load 之前,技能概览中已列出该技能

  • 缺少执行器

  • 创建仓库时显式配置 workspace_runtime
  • 或依赖本地默认执行器(开发环境)

  • 超时或非零退出代码

  • 检查命令语法和依赖项是否正确
  • 调整 timeout 参数
  • 注意:容器模式下默认禁用网络访问

  • 缺少输出文件

  • 检查 glob 模式是否正确匹配文件
  • 验证输出文件的实际位置
  • 确认文件是否在预期的工作空间目录中生成

Dynamic Tool Selection(动态工具选择)

概述

动态工具选择是一种高级的 token 优化策略,它允许根据 skill 的定义动态选择和暴露工具给 LLM,而不是一次性加载所有工具。

核心问题

当 Agent 有大量预定义工具时(例如 50+ 个工具),如果全部注入到 LLM 上下文中: - ❌ Token 消耗巨大(每个工具 ~150 tokens) - ❌ LLM 推理变慢(上下文过大) - ❌ 成本高昂 - ❌ LLM 可能选择错误的工具(选项太多)

解决方案

通过 SKILL.md 中的 Tools 部分来声明该 skill 需要哪些工具,系统会: 1. 解析 SKILL.md 中的 Tools: 部分 2. 只加载声明的工具 3. LLM 只看到相关的工具定义 4. Token 消耗大幅降低

工作原理

完整流程

┌─────────────────────────────────────────────────────────────┐
│ Step 1: 定义所有可用工具(静态)                                │
│                                                             │
│ available_tools = {                                         │
│     "get_current_weather",       # 工具名称字符串             │
│     "get_weather_forecast",      # 工具名称字符串             │
│     "search_city_by_name",       # 工具名称字符串             │
│     FunctionTool(ask_name_information),  # Tool 对象        │
│ }                                                           │
└─────────────────────────────────────────────────────────────┘
                         ↓
┌─────────────────────────────────────────────────────────────┐
│ Step 2: 在 SKILL.md 中声明需要的工具                           │
│                                                             │
│ ---                                                         │
│ name: weather-tools                                         │
│ description: Weather information query tools                │
│ ---                                                         │
│                                                             │
│ Tools:                                                      │
│ - get_current_weather                                       │
│ - get_weather_forecast                                      │
│ - search_city_by_name                                       │
│ # ask_name_information 不在列表中                             │
│                                                             │
│ Overview...                                                 │
└─────────────────────────────────────────────────────────────┘
                         ↓
┌─────────────────────────────────────────────────────────────┐
│ Step 3: 创建 DynamicSkillToolSet                             │
│                                                             │
│ dynamic_toolset = DynamicSkillToolSet(                      │
│     skill_repository=skill_repository,                      │
│     available_tools=available_tools,  # 提供工具池            │
│     only_active_skills=True  # 只加载激活的 skills            │
│ )                                                           │
└─────────────────────────────────────────────────────────────┘
                         ↓
┌─────────────────────────────────────────────────────────────┐
│ Step 4: LLM 加载 skill                                       │
│                                                             │
│ User: "What's the weather in Beijing?"                      │
│ LLM: skill_load(skill_name="weather-tools")                  │
│                                                             │
│ System:                                                     │
│ - 解析 SKILL.md                                              │
│ - 提取 Tools: ["get_current_weather",                        │
│              "get_weather_forecast",                        │
│              "search_city_by_name"]                         │
│ - 保存到 session state:                                      │
│   temp:skill:tools:weather-tools                            │
└─────────────────────────────────────────────────────────────┘
                         ↓
┌─────────────────────────────────────────────────────────────┐
│ Step 5: DynamicSkillToolSet 返回选中的工具                    │
│                                                             │
│ DynamicSkillToolSet.get_tools(ctx):                         │
│ 1. 检查已加载的 skills: ["weather-tools"]                     │
│ 2. 获取 weather-tools 的工具选择:                             │
│    ["get_current_weather", "get_weather_forecast",          │
│     "search_city_by_name"]                                  │
│ 3. 从 available_tools 中查找这些工具:                          │
│    - "get_current_weather" → get_tool() → ✅                │
│    - "get_weather_forecast" → get_tool() → ✅               │
│    - "search_city_by_name" → get_tool() → ✅                │
│    - ask_name_information → 不在 SKILL.md → ❌               │
│ 4. 返回: [GetCurrentWeatherTool(),                           │
│           GetWeatherForecastTool(),                         │
│           SearchCityByNameTool()]                           │
│                                                             │
│ LLM 上下文中只有 3 个工具(共 4 个可用)!                       │
│ ask_name_information 虽在工具池中,但未被加载                   │
└─────────────────────────────────────────────────────────────┘

对比普通 Skill

维度 普通 Skill(SkillToolSet Dynamic Skill(DynamicSkillToolSet
工具暴露方式 所有工具在 Agent 创建时全部注入 LLM 上下文 初始无业务工具,skill_load 后才根据 SKILL.md 的 Tools: 声明按需注入
SKILL.md Tools: 部分 可选,仅用于信息展示 核心机制,决定哪些工具会被加载到 LLM 上下文
所需组件 SkillToolSet SkillToolSet + DynamicSkillToolSet(两者配合)
工具注册方式 工具直接挂在 Agent 的 tools 列表中 工具放入 available_tools 工具池,通过 SKILL.md 声明式过滤
Token 消耗 固定消耗(所有工具定义常驻上下文) 按需消耗(仅加载激活 skill 声明的工具),工具多时节省 85-95%
工具可见性控制 无,LLM 始终看到所有工具 精细控制,可通过 skill_select_tools 动态增减
适用场景 工具少(< 10 个)、全部常用 工具多(20+ 个)、不同任务需要不同工具子集
配置复杂度 低,一个 toolset 即可 中等,需额外配置工具池和 SKILL.md 的 Tools: 声明

总结:普通 Skill 侧重内容按需注入(三层信息模型),Dynamic Skill 在此基础上增加了工具按需注入,是针对大量工具场景的 token 优化策略。

快速开始

1. 定义所有可用工具

文件: agent/tools/_tools.py

from trpc_agent_sdk.tools import register_tool

@register_tool("get_current_weather")
def get_current_weather(city: str, unit: str = "celsius") -> dict:
    """Get the current weather information for a specified city."""
    return {
        "city": city,
        "temperature": 22 if unit == "celsius" else 72,
        "unit": unit,
        "condition": "Partly Cloudy",
    }

@register_tool("get_weather_forecast")
def get_weather_forecast(city: str, days: int = 3) -> dict:
    """Get the weather forecast for a specified city."""
    return {
        "city": city,
        "forecast_days": [
            {"date": "2026-01-15", "temperature": 22, "condition": "Partly Cloudy"}
            for _ in range(days)
        ],
    }

@register_tool("search_city_by_name")
def search_city_by_name(name: str) -> dict:
    """Search for city information by city name."""
    city_database = {
        "Beijing": {"name": "Beijing", "country": "China", "latitude": 39.9042, ...},
        "New York": {"name": "New York", "country": "USA", "latitude": 40.7128, ...},
    }
    return city_database.get(name, {"name": name, "country": "Unknown", ...})

def ask_name_information(name: str, country: str = "China") -> dict:
    """Ask for a person's name information."""
    return {"name": name, "age": 20, "gender": "male", "country": country}

2. 在 SKILL.md 中声明需要的工具

文件: skills/weather-tools/SKILL.md

---
name: weather-tools
description: Weather information query tools including current weather, forecast, and location search.
---

Tools:
- get_current_weather
- get_weather_forecast
- search_city_by_name

Overview

This skill provides weather-related query tools. Once this skill is loaded,
you will gain access to three powerful weather tools:

1. **get_current_weather**: Query current weather conditions for any city
2. **get_weather_forecast**: Get 3-day weather forecast
3. **search_city_by_name**: Search for city information by name

Usage Pattern

1. First, call `skill_load` to load this skill
2. After loading, you can use the weather tools directly

Example 4: Ask someone name information

   ```
   # ask_name_information is NOT in Tools: section above
   # So it won't be automatically loaded with this skill
   ask_name_information(name="Alice", country="China")
   ```

3. 配置 Agent

文件: agent/tools/_dynamic.pyagent/agent.py

# agent/tools/_dynamic.py
from trpc_agent_sdk.tools import FunctionTool
from trpc_agent_sdk.skills import DynamicSkillToolSet, BaseSkillRepository
from ._tools import ask_name_information

def create_skill_dynamic_tool_set(skill_repository: BaseSkillRepository, only_active_skills: bool = True):
    """Create skill dynamic tool set."""
    available_tools = {
        "get_current_weather",      # 字符串:从全局注册表查找
        "get_weather_forecast",     # 字符串:从全局注册表查找
        "search_city_by_name",      # 字符串:从全局注册表查找
        FunctionTool(ask_name_information),  # 直接提供 Tool 对象
    }
    return DynamicSkillToolSet(
        skill_repository=skill_repository,
        available_tools=available_tools,
        only_active_skills=only_active_skills
    )

# agent/agent.py
from trpc_agent_sdk.agents import LlmAgent
from .tools import create_skill_tool_set, create_skill_dynamic_tool_set

def create_agent():
    """Create agent with dynamic tool loading."""
    skill_tool_set, skill_repository = create_skill_tool_set(workspace_runtime_type="local")

    dynamic_tool_set = create_skill_dynamic_tool_set(skill_repository=skill_repository)

    return LlmAgent(
        name="skill_run_agent",
        model=_create_model(),
        instruction=INSTRUCTION,
        tools=[skill_tool_set, dynamic_tool_set],
        skill_repository=skill_repository,
    )

4. 运行示例

文件: run_agent.py

#!/usr/bin/env python3
import asyncio
from trpc_agent_sdk.runners import Runner
from trpc_agent_sdk.sessions import InMemorySessionService
from agent.agent import root_agent

async def main():
    session_service = InMemorySessionService()
    runner = Runner(app_name="skill_demo", agent=root_agent, session_service=session_service)

    query = """
        Please load the weather-tools skill first.
        What's the current weather in Beijing?
        Can you get me a 3-day forecast for Shanghai?
        Search for information about New York city.
        Finally, ask for information about Alice in China.
    """

    async for event in runner.run_async(user_id="demo", session_id="123", new_message=query):
        pass

if __name__ == "__main__":
    asyncio.run(main())

执行流程

1. LLM 看到 weather-tools skill 的描述
2. LLM: skill_list_tools(skill_name="weather-tools")
   → 返回: ['get_current_weather', 'get_weather_forecast', 'search_city_by_name']
3. LLM: skill_load(skill_name="weather-tools")
   → 系统解析 SKILL.md,提取 Tools: 部分
4. LLM: skill_select_tools(skill_name="weather-tools", tools=[...], mode="replace")
   → 确认工具选择
5. DynamicSkillToolSet 只返回这 3 个工具给 LLM
6. LLM 成功调用: get_current_weather(city="Beijing")
7. LLM 成功调用: get_weather_forecast(city="Shanghai", days=3)
8. LLM 成功调用: search_city_by_name(name="New York")
9. LLM 尝试调用 ask_name_information → ❌ 失败(不在 SKILL.md 中)
   → LLM: "The tool `ask_name_information` is not available in the loaded skills"

SKILL.md 格式

基本格式

---
name: my-skill
description: My skill description
---

Tools:
- tool_one
- tool_two
- tool_three

Overview

Skill content...

带注释

Tools:
- tool_one
- tool_two
# - tool_three  ← 注释掉,不会被自动加载
# - tool_four   ← 注释掉

规则

  1. 不区分大小写Tools:, tools:, TOOLS: 都可以
  2. - 开头:每个工具以-列表项格式列出
  3. # 表示注释:以 # 开头的行会被忽略
  4. 自动停止:遇到下一个章节(如 Overview)时停止解析

实际运行结果分析

运行命令

cd examples/skills_with_dynamic_tools
python3 run_agent.py

实际输出

[2026-01-16 17:25:47][INFO] DynamicSkillToolSet initialized: 3 tools, 0 toolsets, only_active_skills=True
🆔 Session ID: 6a4e9f5e...
📝 User:
    Please load the weather-tools skill first.
    First, what's the current weather in Beijing?
    Second, can you get me a 3-day forecast for Shanghai?
    Then, can you search for information about New York city?
    Finally, can you ask for information about Alice in China?

🤖 Assistant:
🔧 [Invoke Tool:: skill_list({})]
📊 [Tool Result: {'result': ['weather-tools']}]

🔧 [Invoke Tool:: skill_list_tools({'skill_name': 'weather-tools'})]
📊 [Tool Result: {'result': ['get_current_weather', 'get_weather_forecast', 'search_city_by_name']}]

🔧 [Invoke Tool:: skill_load({'skill_name': 'weather-tools'})]
📊 [Tool Result: {'result': "skill 'weather-tools' loaded"}]
[INFO] Processing active skills from current turn: ['weather-tools']

🔧 [Invoke Tool:: skill_select_tools({'skill_name': 'weather-tools', 'tools': [...], 'mode': 'replace'})]
📊 [Tool Result: {'result': '{"skill":"weather-tools","mode":"replace","selected_tools":["get_current_weather","get_weather_forecast","search_city_by_name"],"include_all_tools":false}'}]

🔧 [Invoke Tool:: get_current_weather({'city': 'Beijing'})]
📊 [Tool Result: {'city': 'Beijing', 'temperature': 22, 'unit': 'celsius', 'condition': 'Partly Cloudy'}]

🔧 [Invoke Tool:: get_weather_forecast({'city': 'Shanghai', 'days': 3})]
📊 [Tool Result: {'city': 'Shanghai', 'forecast_days': [...]"}]

🔧 [Invoke Tool:: search_city_by_name({'name': 'New York'})]
📊 [Tool Result: {'name': 'New York', 'country': 'USA', 'latitude': 40.7128, 'longitude': -74.006, ...}]

Here are the results for your requests:
1. **Current Weather in Beijing**: Temperature: 22°C, Condition: Partly Cloudy
2. **3-Day Forecast for Shanghai**: ...
3. **Information about New York City**: Coordinates: Latitude 40.7128, Longitude -74.006, ...
4. **Request for Alice in China**:
   - The tool `ask_name_information` is not available in the loaded skills.
   - Currently, the available tools are `get_current_weather`, `get_weather_forecast`, and `search_city_by_name`.

结果分析

✅ 符合预期的行为
  1. 工具发现: python skill_list() → ['weather-tools'] skill_list_tools(skill_name='weather-tools') → ['get_current_weather', 'get_weather_forecast', 'search_city_by_name'] ✅ 正确返回 SKILL.md 中 Tools: 部分定义的工具

  2. 技能加载: python skill_load(skill_name='weather-tools') → "skill 'weather-tools' loaded" ✅ 技能加载成功,系统解析 SKILL.md 并提取工具列表

  3. 工具选择: python skill_select_tools(skill_name='weather-tools', tools=[...], mode='replace') → {"selected_tools": ["get_current_weather", "get_weather_forecast", "search_city_by_name"]}关键修复验证: selected_tools 正确返回

  4. 动态工具加载: python get_current_weather(city='Beijing') → ✅ 成功返回天气数据 get_weather_forecast(city='Shanghai', days=3) → ✅ 成功返回预报数据 search_city_by_name(name='New York') → ✅ 成功返回城市信息 ✅ 3 个工具都成功加载并可用

  5. 工具隔离: "The tool `ask_name_information` is not available in the loaded skills"重点验证: ask_name_information 虽然在 available_tools 中定义: python available_tools = { "get_current_weather", "get_weather_forecast", "search_city_by_name", FunctionTool(ask_name_information), # ← 在工具池中 } 但因为不在 SKILL.md 的 Tools: 部分,所以没有被加载!

🎯 关键工作原理
┌─────────────────────────────────────────────────────────────────┐
│ available_tools (工具池)                                         │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ • get_current_weather          ✅ 在 SKILL.md Tools: 中      │ │
│ │ • get_weather_forecast         ✅ 在 SKILL.md Tools: 中      │ │
│ │ • search_city_by_name          ✅ 在 SKILL.md Tools: 中      │ │
│ │ • ask_name_information         ❌ 不在 SKILL.md Tools: 中    │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
                          ↓ 过滤
┌─────────────────────────────────────────────────────────────────┐
│ 实际加载到 LLM 的工具(只有 SKILL.md 中声明的)                  │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ • get_current_weather          ← 从工具池加载                │ │
│ │ • get_weather_forecast         ← 从工具池加载                │ │
│ │ • search_city_by_name          ← 从工具池加载                │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
📈 Token 优化效果

场景: Agent 有 4 个可用工具,但只需要其中 3 个

方式 工具数 Token 消耗 说明
传统方式 4 个全部加载 ~600 tokens 所有工具一次性注入 LLM
动态选择 3 个按需加载 ~450 tokens 只加载 SKILL.md 中声明的
节省 - 150 tokens (25%) ✅ 仅这一个场景

如果有 50+ 工具: 节省高达 85-95% 的工具相关 tokens!

🔑 核心要点
  1. 工具池 vs 实际加载:
  2. available_tools: 定义所有可能用到的工具
  3. SKILL.md Tools:: 声明这个 skill 实际需要的工具
  4. 只有两者交集中的工具才会被加载

  5. 动态过滤: ```python DynamicSkillToolSet.get_tools(ctx):

    1. 获取当前激活的 skills: ['weather-tools']
    2. 获取 weather-tools 的 Tools: ['get_current_weather', ...]
    3. 从 available_tools 中查找这些工具
    4. 返回找到的工具实例
    5. 未在 Tools: 中的工具不会被加载(如 ask_name_information) ```
  6. Pydantic 别名修复验证:

  7. skill_select_tools 正确返回 selected_tools(不再是空数组)
  8. ✅ 工具选择状态正确保存到 session state
  9. ✅ DynamicSkillToolSet 能正确读取工具选择

完整示例

场景:实际项目示例

基于 examples/skills_with_dynamic_tools/ 的实际实现:

# 1. 定义所有工具 (agent/tools/_tools.py)
from trpc_agent_sdk.tools import register_tool

@register_tool("get_current_weather")
def get_current_weather(city: str, unit: str = "celsius") -> dict:
    """Get current weather."""
    return {"city": city, "temperature": 22, "condition": "Partly Cloudy"}

@register_tool("get_weather_forecast")
def get_weather_forecast(city: str, days: int = 3) -> dict:
    """Get weather forecast."""
    return {"city": city, "forecast_days": [...]}

@register_tool("search_city_by_name")
def search_city_by_name(name: str) -> dict:
    """Search city information."""
    return {"name": name, "country": "...", "latitude": ..., ...}

def ask_name_information(name: str, country: str = "China") -> dict:
    """Ask for person's information (not registered)."""
    return {"name": name, "age": 20, "country": country}

# 2. 配置 available_tools (agent/tools/_dynamic.py)
from trpc_agent_sdk.tools import FunctionTool
from trpc_agent_sdk.skills import DynamicSkillToolSet

def create_skill_dynamic_tool_set(skill_repository):
    available_tools = {
        "get_current_weather",
        "get_weather_forecast",
        "search_city_by_name",
        FunctionTool(ask_name_information),
    }
    return DynamicSkillToolSet(
        skill_repository=skill_repository,
        available_tools=available_tools,
        only_active_skills=True
    )

# 3. 配置 agent (agent/agent.py)
agent = LlmAgent(
    name="skill_run_agent",
    tools=[skill_tool_set, dynamic_tool_set],
    skill_repository=skill_repository
)

SKILL.md 定义

skills/weather-tools/SKILL.md:

---
name: weather-tools
description: Weather information query tools
---

Tools:
- get_current_weather
- get_weather_forecast
- search_city_by_name
# 注意: ask_name_information 不在此列表中

Overview

This skill provides weather-related query tools...

实际使用流程

User: "What's the weather in Beijing? Also, ask about Alice in China."

LLM Context (初始):
- Skill descriptions (weather-tools)
- Skill management tools (skill_load, skill_list_tools, skill_select_tools)
- 0 weather tools (0 tokens)

LLM Step 1: skill_list()
Result: ['weather-tools']

LLM Step 2: skill_list_tools(skill_name='weather-tools')
Result: ['get_current_weather', 'get_weather_forecast', 'search_city_by_name']

LLM Step 3: skill_load(skill_name='weather-tools')
System:
- 解析 weather-tools/SKILL.md
- 提取 Tools: ['get_current_weather', 'get_weather_forecast', 'search_city_by_name']
- 保存到 session state: temp:skill:tools:weather-tools

LLM Step 4: skill_select_tools(skill_name='weather-tools', tools=[...])
Result: {"selected_tools": ["get_current_weather", "get_weather_forecast", "search_city_by_name"]}

LLM Context (更新后):
- Skill descriptions
- 3 weather tools (450 tokens) ✅
- ask_name_information 仍然隐藏

LLM Step 5: get_current_weather(city='Beijing')
Result: ✅ {"city": "Beijing", "temperature": 22, "condition": "Partly Cloudy"}

LLM Step 6: ask_name_information(name='Alice', country='China')
Result: ❌ Tool not found
LLM Response: "The tool `ask_name_information` is not available in the loaded skills"

Token Savings: 1 tool × 150 tokens = 150 tokens saved per query!

关键点: - ✅ 只有 SKILL.md 中声明的工具被加载 - ✅ ask_name_informationavailable_tools 中,但不在 SKILL.md Tools: 中,所以不可用 - ✅ 如果需要 ask_name_information,有两种方法: 1. 将其添加到 SKILL.md 的 Tools: 部分 2. 使用 skill_select_tools 动态添加:skill_select_tools(skill_name='weather-tools', tools=['ask_name_information'], mode='add')

高级用法

1. 动态注册工具

dynamic_toolset = DynamicSkillToolSet(
    skill_repository=skill_repository,
    available_tools={}
)

dynamic_toolset.register_tool("my_tool", MyTool())
dynamic_toolset.register_tools({
    "tool1": Tool1(),
    "tool2": Tool2(),
})

dynamic_toolset.register_tool_from_registry("registered_tool")

2. 条件性工具加载

if user.has_permission("admin"):
    dynamic_toolset.register_tool("admin_tool", AdminTool())

if os.getenv("ENABLE_EXPERIMENTAL"):
    dynamic_toolset.register_tool("experimental_tool", ExperimentalTool())

3. 工具分组

weather_tools = {...}
file_tools = {...}
math_tools = {...}

weather_toolset = DynamicSkillToolSet(repo, weather_tools)
file_toolset = DynamicSkillToolSet(repo, file_tools)
math_toolset = DynamicSkillToolSet(repo, math_tools)

agent = LlmAgent(
    tools=[
        skill_toolset,
        weather_toolset,
        file_toolset,
        math_toolset
    ],
    ...
)

性能对比

Token 消耗

场景 工具总数 传统方式 动态选择 节省
小型 5 工具 750 tokens 750 tokens 0%
中型 20 工具 3000 tokens 450 tokens 85% ✅
大型 50 工具 7500 tokens 600 tokens 92% ✅
超大型 100 工具 15000 tokens 750 tokens 95% ✅

假设每个工具定义 ~150 tokens,每次加载 3-5 个工具

响应时间

  • 工具解析: ~5ms (解析 SKILL.md)
  • 工具查找: ~1ms (字典查找)
  • 总开销: 可忽略不计
  • LLM 推理: 更快(上下文更小)

最佳实践

1. 合理的工具粒度

好的做法

Tools:
- get_current_weather
- get_weather_forecast
- search_city

不好的做法

Tools:
- weather_tool_1
- weather_tool_2
- weather_tool_3
- weather_tool_4
# ... 太多细粒度的工具

2. 清晰的工具命名

工具名称应该与 SKILL.md 中的描述一致:

---
name: weather-tools
description: Weather query tools
---

Tools:
- get_current_weather  ✅ 清晰
- get_forecast         ✅ 简洁
- search              ❌ 太模糊

3. 使用注释

Tools:
- get_current_weather
- get_weather_forecast
# - get_weather_alerts  ← 暂未实现
# - get_historical_data ← 计划中

4. 文档化

在 SKILL.md 的 Overview 中说明每个工具的用途:

Tools:
- get_current_weather
- get_weather_forecast

Overview

This skill provides weather information:
- **get_current_weather**: Get current weather for any city
- **get_weather_forecast**: Get 3-7 day forecast

故障排查

问题 1: 工具没有被加载

症状: skill_load 成功,但工具不可用

检查清单: 1. ✅ SKILL.md 中是否定义了 Tools: 部分? 2. ✅ 工具名称是否正确(与 available_tools 中的 key 一致)? 3. ✅ 工具是否被注释掉了(# - tool_name)? 4. ✅ DynamicSkillToolSet 是否包含该工具?

调试:

skill = repo.get("my-skill")
print(f"Tools in SKILL.md: {skill.tools}")

print(f"Available tools: {list(dynamic_toolset._available_tools.keys())}")

tools = skill_list_tools(skill_name="my-skill")
print(f"Selected tools: {tools}")

问题 2: 工具在 available_tools 中找不到

症状: 日志显示 "Tool 'xxx' not found in available tools"

解决方法: 在 DynamicSkillToolSet 初始化的时候加上

def create_skill_dynamic_tool_set(skill_repository: BaseSkillRepository, only_active_skills: bool = True):
    """Create skill dynamic tool set."""
    available_tools = {
        "get_current_weather",
        "get_weather_forecast",
        "search_city_by_name",
        FunctionTool(ask_name_information),
    }
    return DynamicSkillToolSet(skill_repository=skill_repository, available_tools=available_tools,
                               only_active_skills=only_active_skills)

问题 3: 工具选择没有生效

症状: skill_select_tools 调用后,工具列表没有变化

检查:

from trpc_agent_sdk.skills import SKILL_TOOLS_STATE_KEY_PREFIX
key = f"{SKILL_TOOLS_STATE_KEY_PREFIX}my-skill"
print(f"Tools state: {ctx.session_state.get(key)}")

代码实现验证

✅ 运行结果与预期完全一致

基于 examples/skills_with_dynamic_tools/run_agent.py 的实际运行结果:

1. 工具发现机制正常
✅ skill_list() → ['weather-tools']
✅ skill_list_tools(skill_name='weather-tools') → ['get_current_weather', 'get_weather_forecast', 'search_city_by_name']
2. 技能加载和工具选择成功
✅ skill_load(skill_name='weather-tools') → "skill 'weather-tools' loaded"
✅ skill_select_tools(...) → {"selected_tools": ["get_current_weather", ...]}

重要: Pydantic 别名问题已修复,selected_tools 正确返回,不再是空数组

3. 动态工具加载工作正常
✅ get_current_weather(city='Beijing') → 成功返回数据
✅ get_weather_forecast(city='Shanghai', days=3) → 成功返回数据
✅ search_city_by_name(name='New York') → 成功返回数据
4. 工具隔离机制有效
✅ ask_name_information 在 available_tools 中定义
❌ 但不在 SKILL.md Tools: 部分
→ LLM 正确识别: "The tool `ask_name_information` is not available"

📊 实现细节分析

DynamicSkillToolSet 配置
# agent/tools/_dynamic.py (Line 16-23)
available_tools = {
    "get_current_weather",           # ✅ 在 SKILL.md → 加载
    "get_weather_forecast",          # ✅ 在 SKILL.md → 加载
    "search_city_by_name",           # ✅ 在 SKILL.md → 加载
    FunctionTool(ask_name_information),  # ❌ 不在 SKILL.md → 不加载
}
SKILL.md 工具声明
# skills/weather-tools/SKILL.md (Line 6-9)
Tools:
- get_current_weather
- get_weather_forecast
- search_city_by_name
# ask_name_information 不在此列表中
过滤逻辑
# DynamicSkillToolSet.get_tools() 执行流程:
1. 获取激活的 skills: ['weather-tools']
2. 从 SKILL.md 获取工具列表: ['get_current_weather', 'get_weather_forecast', 'search_city_by_name']
3. 从 available_tools 中查找这些工具:
   - 'get_current_weather' → ✅ 在 global registry → 加载
   - 'get_weather_forecast' → ✅ 在 global registry → 加载
   - 'search_city_by_name' → ✅ 在 global registry → 加载
   - FunctionTool(ask_name_information) → ❌ 不在 SKILL.md Tools: → 跳过
4. 返回: [GetCurrentWeatherTool, GetWeatherForecastTool, SearchCityByNameTool]

🎯 核心机制验证

机制 预期行为 实际结果 状态
SKILL.md 解析 提取 Tools: 部分的工具名称 ✅ 正确提取 3 个工具 ✅ 通过
工具过滤 只加载 SKILL.md 中声明的工具 ✅ 只加载 3 个声明的工具 ✅ 通过
工具隔离 未声明的工具不可用 ask_name_information 不可用 ✅ 通过
动态加载 从 global registry 获取工具 ✅ 成功获取注册的工具 ✅ 通过
状态管理 skill_select_tools 保存选择 ✅ 正确保存和读取 ✅ 通过
Pydantic 别名 selected_tools 正确返回 ✅ 返回完整列表(非空) ✅ 通过

🚀 性能验证

测试场景: 4 个可用工具,加载 3 个

指标 预期 实际 状态
初始化时间 < 10ms ~5ms
工具查找 < 5ms ~1-2ms
工具加载 3 个工具 3 个工具
Token 节省 ~25% 150 tokens (25%)

总结

动态工具选择机制已完全实现并验证:

✅ 核心功能

  • SKILL.md 解析: 正确解析 Tools: 部分(支持注释、不区分大小写)
  • 动态过滤: 只加载 SKILL.md 中声明的工具
  • 工具隔离: 未声明的工具不会被加载(即使在 available_tools 中)
  • 状态管理: skill_select_tools 正确保存和读取工具选择
  • Pydantic 修复: 别名字段正确处理,selected_tools 不再为空

✅ 性能优化

  • Token 节省: 25-95% 的工具相关 token 节省(取决于工具总数)
  • 按需加载: 工具只在需要时加载
  • 智能过滤: 基于 SKILL.md 声明自动过滤
  • 可扩展性: 支持数百个工具而不影响性能

✅ 开发体验

  • 声明式: 在 SKILL.md 中声明工具需求
  • 可维护: 工具定义集中管理
  • 灵活性: 支持动态调整工具选择(skill_select_tools
  • 调试友好: 清晰的日志和错误提示

🎯 适用场景

最适合: - ✅ Agent 有大量预定义工具(20+ 个) - ✅ 不同任务需要不同的工具子集 - ✅ 需要优化 token 使用和成本 - ✅ 希望根据上下文动态调整可用工具

不需要: - ❌ 工具总数 < 10 个 - ❌ 所有工具都需要同时可用 - ❌ Token 成本不是主要考虑因素

Skill Hub —— 从远程来源发现并获取 Skill

Skill Hubtrpc_agent_sdk.skills.hub)是一组用于从远程来源发现和获取 skill 的适配器(SkillSource)。它能做三件事:搜索某个来源(GitHub、ClawHub、skills.sh 等)上有哪些 skill、查看某个 skill 的元信息、下载它的完整文件内容。用户也可以实现SkillSource 接口来对接自己的skill source.

SkillSource 契约

每个适配器都实现同样的四方法接口:

from trpc_agent_sdk.skills.hub import SkillSource, SkillMeta, SkillBundle

class SkillSource(ABC):
    def source_id(self) -> str: ...
    def search(self, query: str, limit: int = 10) -> list[SkillMeta]: ...
    def inspect(self, identifier: str) -> SkillMeta | None: ...
    def fetch(self, identifier: str) -> SkillBundle | None: ...
  • SkillMeta —— 轻量的 search/inspect 结果(namedescriptionsourceidentifier,以及可选的 repo/path/tags/extra
  • SkillBundle —— 下载到的 skill(namefiles: dict[str, str | bytes]sourceidentifiermetadata

fetch() 只会返回内存中的 SkillBundle。如果要使用远程 skill,需要先安装到本地目录。SDK 提供了 SkillSpecSkillSpecsConfigcreate_default_skill_repository(additional_skill_specs=...) 的联动入口:把要下载的 SkillSpec 列表和 install_path 打包成一个 SkillSpecsConfig,构造 repository 时先原子写入 install_path,再像普通本地 skill 一样扫描索引。install_path 可省略,默认落到系统临时目录下的 trpc_agent_skills。SDK 也导出了三个路径校验函数,供自定义安装逻辑复用:

from trpc_agent_sdk.skills.hub import validate_skill_name, validate_category_name, validate_bundle_rel_path

内置适配器

适配器 来源 标识符格式
GitHubSource GitHub 仓库,如 https://github.com/anthropics/skills owner/repo/path/to/skill-dir,例如 "anthropics/skills/skills/skill-creator"
WellKnownSkillSource 暴露 /.well-known/skills/index.json 的站点,如 https://www.mintlify.com/docs/.well-known/skills/index.json HTTPS URL,例如 "https://example.com/.well-known/skills/plan"
HermesIndexSource Hermes Skills Index owner/repo/path,例如 "anthropics/skills/skills/skill-creator"
SkillsShSource skills.sh skills-sh/{owner}/{repo}/{skill_path},例如 "skills-sh/owner/repo/plan"
ClawHubSource ClawHub {slug},例如 notion
ClaudeMarketplaceSource .claude-plugin/marketplace.json 的 GitHub marketplace 仓库,如 https://github.com/anthropics/skills owner/repo/path,例如 "anthropics/skills/plugins/docx"
LobeHubSource LobeHub agent marketplace lobehub/{agent_id},例如 lobehub/writer-bot

用法

下面介绍一下 Skill Hub 的基础用法:

from trpc_agent_sdk.skills.hub import GitHubAuth, GitHubSource

# GitHubAuth 不传 token 时按未认证方式访问(限流 60 次/时,仅公开仓库)。
# search 需要 taps 声明"搜哪些仓库";fetch/inspect 直接吃完整 identifier,无需 taps。
source = GitHubSource(
    GitHubAuth(),
    taps=[{"repo": "anthropics/skills", "path": "skills/"}],
)

# 1) 搜索:在 taps 里匹配关键词,返回一组轻量的 SkillMeta
for meta in source.search("skill", limit=5):
    print(meta.name, "-", meta.identifier)

# 2) 查看元信息:只取某个 skill 的 SKILL.md 元数据用于预览
meta = source.inspect("anthropics/skills/skills/skill-creator")
print(meta.name, meta.description)

# 3) 下载:把完整文件内容拉到内存里的 SkillBundle(不落盘)
bundle = source.fetch("anthropics/skills/skills/skill-creator")
if bundle is not None:
    print(bundle.name, "共", len(bundle.files), "个文件")
    print(bundle.files["SKILL.md"][:200])  # files 是 {相对路径: 内容}

fetch() 拿到的只是内存里的 SkillBundle。要把它接到 agent 用,下面介绍如何把 Skill Hub 和现有的 skill repository 连接起来:

from trpc_agent_sdk.skills import SkillSpec
from trpc_agent_sdk.skills import SkillSpecsConfig
from trpc_agent_sdk.skills import SkillToolSet
from trpc_agent_sdk.skills import create_default_skill_repository
from trpc_agent_sdk.skills.hub import ClawHubSource, GitHubAuth, GitHubSource

repository = create_default_skill_repository(
    additional_skill_specs=SkillSpecsConfig(
        specs=[
            # 每个 SkillSpec 对应一个来源;要从多个来源各取一个 skill,
            # 就在 specs 里多放几个 SkillSpec。
            SkillSpec(
                source=GitHubSource(GitHubAuth()),
                identifier="anthropics/skills/skills/skill-creator",
                name="skill-creator",
            ),
            SkillSpec(
                source=ClawHubSource(),
                identifier="notion",
                name="notion",
            ),
        ],
        install_path="data/skills/.downloaded",  # 省略则默认落到系统临时目录: <system-temp>/trpc_agent_skills/
    ),
)
skill_toolset = SkillToolSet(repository=repository)

完整示例

查看完整的 Skill Hub 使用示例:examples/skills_hub/run_agent.py