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
快速开始
1) 要求
- Python3.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):
- hello from skillrun
- 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
编写建议:
- 保持简洁:
name和description字段应简洁明了,用于概览展示 - 详细说明:在主体中,包含使用时机、操作步骤/命令、输出文件路径等信息
- 脚本组织:将脚本放在
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.md的Tools:部分定义的工具名称数组 - 如果技能未定义工具,返回空数组
功能行为:
- 返回指定技能在
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.md中Tools:部分定义的所有工具
提示词指导:
这个工具由 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_list和skill_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:收集的输出文件数组(每个文件包含name、content、mime_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")
关键点说明:
概览注入(第一次请求)
- 框架自动调用
_inject_overview()将所有技能的简短描述注入到系统消息 - LLM 根据描述判断是否需要加载某个技能
- 此时成本极低,仅包含
name和description字段
- 框架自动调用
技能加载(
skill_load调用)- LLM 主动调用
skill_load(skill_name="weather-tools") - 工具更新会话状态:
temp:skill:loaded:weather-tools = "1" - 状态增量通过
state_delta传递给框架
- LLM 主动调用
内容注入(第二次请求)
- 框架检测到技能已加载(通过
_get_loaded_skills()) - 从仓库获取完整的
SKILL.md内容 - 将 Markdown body 部分注入到系统消息
- LLM 此时能看到详细的使用说明和示例
- 框架检测到技能已加载(通过
工具调用
- LLM 根据注入的详细内容,调用相应的工具函数
- 工具执行完成后,返回结果给 LLM
状态注入机制
工具通过 InvocationContext.actions.state_delta 写入临时状态键,框架根据这些状态增量动态构建系统消息,实现技能内容的按需注入。
主要状态键:
temp:skill:loaded:<name>:标记技能已加载temp:skill:docs:<name>:存储已选择的文档列表temp:skill:tools:<name>:存储已选择的工具列表
最佳实践:
- 使用约定的前缀:保持状态键命名一致(如
temp:skill:loaded:) - JSON 序列化:复杂数据结构使用 JSON 序列化存储
- 合并状态:读取时合并
session_state和state_delta - 最小化状态:只存储必要的状态信息
- 文档化状态键:在代码中明确注释状态键的含义和格式
执行隔离
脚本在工作空间边界内运行,只将选定的输出文件带回,而不是脚本源代码本身,确保了安全性和可控性。
故障排除
常见问题及解决方案:
未知技能错误:
- 检查技能名称是否正确
- 验证仓库路径是否正确
- 确保在调用
skill_load之前,技能概览中已列出该技能
缺少执行器:
- 创建仓库时显式配置
workspace_runtime - 或依赖本地默认执行器(开发环境)
- 创建仓库时显式配置
超时或非零退出代码:
- 检查命令语法和依赖项是否正确
- 调整
timeout参数 - 注意:容器模式下默认禁用网络访问
缺少输出文件:
- 检查 glob 模式是否正确匹配文件
- 验证输出文件的实际位置
- 确认文件是否在预期的工作空间目录中生成
Dynamic Tool Selection(动态工具选择)
概述
动态工具选择是一种高级的 token 优化策略,它允许根据 skill 的定义动态选择和暴露工具给 LLM,而不是一次性加载所有工具。
核心问题
当 Agent 有大量预定义工具时(例如 50+ 个工具),如果全部注入到 LLM 上下文中:
- ❌ Token 消耗巨大(每个工具 ~150 tokens)
- ❌ LLM 推理变慢(上下文过大)
- ❌ 成本高昂
- ❌ LLM 可能选择错误的工具(选项太多)
解决方案
通过 SKILL.md 中的 Tools 部分来声明该 skill 需要哪些工具,系统会:
- 解析 SKILL.md 中的
Tools:部分 - 只加载声明的工具
- LLM 只看到相关的工具定义
- 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, # 提供工具池 │
│ # 只加载激活的 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 autom
…(truncated)