# Zh

> Skill (Agent Skills)

- Skill: `trpc-group/zh` (Agent Skill, multi-file: 39 files)
- Install (CLI): `npx skillmds@latest add trpc-group/zh`
- Raw SKILL.md: https://api.skillmd.com/api/skills/trpc-group/zh/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: trpc-group (https://skillmd.com/u/trpc-group)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/trpc-group/zh

---

# 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` 中的 `name` 和 `description` 注入到系统消息中
   - 让模型了解存在哪些可用技能，无需加载完整内容

**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](../../../trpc_agent_sdk/skills/_repository.py)

## 快速开始

### 1) 要求

- Python3.12
- 模型提供商的 API 密钥（兼容 OpenAI）
- 可选 Docker（用于容器执行器）

常用环境变量：

```bash
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` 加载）：

```bash
# .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

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

```python
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` 中应包含完整的技能使用工作流指导：

```python
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](../../../trpc_agent_sdk/skills/tools/__init__.py)
  - `skill_run` 实现：[trpc_agent_sdk/skills/tools/_skill_run.py](../../../trpc_agent_sdk/skills/tools/_skill_run.py)（其余工具见下文各节「声明位置」）
### 3) 运行示例

完整示例交互式演示：[examples/skills/run_agent.py](../../../examples/skills/run_agent.py)

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

```bash
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` 文件：

```bash
# 创建 .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](../../../examples/skills/skills/python-math/SKILL.md)

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

#### 运行结果

以 user-file-ops 为例
```txt
🆔 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>/`, 目录下的文件
```txt
├── 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：代码中指定

```python
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}, 例如：

```txt
/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：在提示词中编写

```python
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](../../../examples/skills_with_dynamic_tools/run_agent.py)

### URL 类型的 Skills Root

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

相关实现：[trpc_agent_sdk/skills/_url_root.py](../../../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`）。

#### 使用方法

**通过环境变量配置**：

```bash
# 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"
```

**代码中直接使用**：

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

#### 下载缓存机制

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

```txt
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/` |

可通过环境变量覆盖：

```bash
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 主体格式：

```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
```

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

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

## SKILL 工具详解

### `skill_list`

**声明位置**：[trpc_agent_sdk/skills/tools/_skill_list.py](../../../trpc_agent_sdk/skills/tools/_skill_list.py)

**输入参数**：无

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

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

**提示词指导**：

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

```python
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](../../../trpc_agent_sdk/skills/tools/_skill_list_tool.py)

**输入参数**：
- `skill_name`（必需）：技能名称

**返回值**：
- 该技能在 `SKILL.md` 的 `Tools:` 部分定义的工具名称数组
- 如果技能未定义工具，返回空数组

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

**提示词指导**：

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

```python
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:` 部分声明：

```markdown
---
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](../../../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 使用。提示词应包含：

```python
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](../../../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.md` 中 `Tools:` 部分定义的所有工具

**提示词指导**：

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

```python
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_list` 和 `skill_list_tools` 确认需求后加载技能
- 需要查看技能的详细使用说明和示例
- 准备使用技能的工具或执行命令

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

### `skill_select_docs`

**声明位置**：[trpc_agent_sdk/skills/tools/_skill_select_docs.py](../../../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 在需要更多文档时调用。提示词应包含：

```python
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](../../../trpc_agent_sdk/skills/tools/_skill_list_docs.py)

**输入参数**：
- `skill_name`（必需）：技能名称

**返回值**：
- 可用文档文件名的数组（如 `["API_REFERENCE.md", "CONFIGURATION.md", "TROUBLESHOOTING.md"]`）

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

**提示词指导**：

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

```python
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](../../../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`：收集的输出文件数组（每个文件包含 `name`、`content`、`mime_type`）
  - `artifact_files`：工件文件信息

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

**提示词指导**：

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

```python
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
"""
```

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

**执行流程**

```txt
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/_base_workspace_runtime.py)

**实现方式**：
- **本地执行器**：[trpc_agent_sdk/code_executors/local/_local_ws_runtime.py](../../../trpc_agent_sdk/code_executors/local/_local_ws_runtime.py)
  - 直接在本地系统执行命令，适合开发和测试
- **容器执行器**（Docker）：[trpc_agent_sdk/code_executors/container/_container_ws_runtime.py](../../../trpc_agent_sdk/code_executors/container/_container_ws_runtime.py)
  - 在 Docker 容器中执行，提供更好的隔离性
- **Cube 执行器**（远端 E2B 沙箱）：[trpc_agent_sdk/code_executors/cube/_runtime.py](../../../trpc_agent_sdk/code_executors/cube/_runtime.py)
  - 在远端 Cube/E2B 沙箱中执行；适合宿主上没有 Docker、或者需要强远端隔离的场景
  - 通过 `create_cube_workspace_runtime(executor, workspace_cfg=...)` 构造；详见 [code_executor.md](code_executor.md#cubeworkspaceruntime)
  - 需要安装可选 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 调用工具的完整流程：

```txt
用户查询: "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. **概览注入**（第一次请求）
   - 框架自动调用 `_inject_overview()` 将所有技能的简短描述注入到系统消息
   - LLM 根据描述判断是否需要加载某个技能
   - 此时成本极低，仅包含 `name` 和 `description` 字段

2. **技能加载**（`skill_load` 调用）
   - LLM 主动调用 `skill_load(skill_name="weather-tools")`
   - 工具更新会话状态：`temp:skill:loaded:weather-tools = "1"`
   - 状态增量通过 `state_delta` 传递给框架

3. **内容注入**（第二次请求）
   - 框架检测到技能已加载（通过 `_get_loaded_skills()`）
   - 从仓库获取完整的 `SKILL.md` 内容
   - 将 Markdown body 部分注入到系统消息
   - LLM 此时能看到详细的使用说明和示例

4. **工具调用**
   - LLM 根据注入的详细内容，调用相应的工具函数
   - 工具执行完成后，返回结果给 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_state` 和 `state_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`

```python
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`

````markdown
---
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 autom

…(truncated)
