本文深入分析如何在 Agent 开发中集成 Skill 系统,包括概念介绍、规范设计、框架实现、执行流程细节,以及如何将外部能力封装为 LLM 可调用的能力。
在 LLM Agent 的发展过程中,「Tool(工具)」 是最早被广泛采用的能力扩展方式。但随着任务复杂度提升,单一 Tool 难以满足需求——开发者往往需要将多个工具、文档、脚本组合使用才能完成一个任务。
「Skill 是更高层级的能力封装」,它不仅仅是一个工具,而是一个可以整合多种资源的"技能包":
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 一个 Skill 可以包含 │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ 📄 文档(SKILL.md) │
│ │ • 能力说明、使用方法、示例 │
│ │ • LLM 通过阅读文档学习使用方式 │
│ │ │
│ 🔧 Tool(函数调用) │
│ │ • 封装好的 API 调用 │
│ │ • 数据处理函数 │
│ │ │
│ 🔌 MCP Server(外部服务) │
│ │ • 数据库连接 │
│ │ • 第三方 API 集成 │
│ │ │
│ 📜 脚本(可执行代码) │
│ │ • Python 脚本 │
│ │ • Bash 命令 │
│ │ │
│ 📋 规则和约束 │
│ • 编码规范 │
│ • 安全约束 │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Skill vs Tool 的本质区别 │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ Tool:单一能力 │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ 一个 Tool = 一个函数 │ │
│ │ db_query() / http_request() / file_read() │ │
│ │ │ │
│ │ 特点: │ │
│ │ • 需要编写代码实现特定接口 │ │
│ │ • 与 Agent 同进程执行 │ │
│ │ • 难以跨项目、跨语言复用 │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
│ Skill:能力集合 + 知识 │
│ ┌───────────────────────────────────────────────────────────────────────┐ │
│ │ 一个 Skill = 多个 Tool + MCP + 文档 + 脚本 + 规则 │ │
│ │ │ │
│ │ 例如:data-analysis Skill 可能包含 │ │
│ │ • 文档:如何分析数据的指南 │ │
│ │ • Tool:read_csv, write_report │ │
│ │ • MCP:连接数据库获取源数据 │ │
│ │ • 脚本:analyze.py(复杂分析逻辑) │ │
│ │ • 规则:输出格式要求、安全约束 │ │
│ │ │ │
│ │ 特点: │ │
│ │ • 通过 Markdown 文档定义,无需编写代码 │ │
│ │ • 可在沙箱/容器中隔离执行 │ │
│ │ • 易于分享和跨项目复用 │ │
│ └───────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
「Skill 的核心思想是:让 LLM 像阅读文档一样学习如何使用一整套能力。」
┌─────────────────────────────────────────────────────────────────────────────────┐
│ │
│ SKILL.md 本质上是给 LLM 看的"使用手册" │
│ │
│ • 如果你能教会一个人使用某个工具集,那也能教会 LLM │
│ • Skill 文档就是那份"教学材料" │
│ • 文档中可以引用 Tool、MCP、脚本等各种资源 │
│ │
│ 这种"文档即接口"的模式,大大降低了能力扩展的门槛 │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
业界逐渐形成了相对统一的 Skill 定义规范,采用 「YAML Front Matter + Markdown Body」 格式:
「SKILL.md 示例文件:」
# === YAML Front Matter(文件开头)===
---
name: ocr
description: Extract text from images using Tesseract OCR
---
# OCR Skill
## Capabilities
This skill extracts text from images using Tesseract OCR engine.
## Usage
python3 scripts/ocr.py <input_image> <output_file> [--lang <language>]
## Parameters
| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| input_image | Yes | - | Path to the input image file |
| output_file | Yes | - | Path to save the extracted text |
| --lang | No | eng | OCR language (eng, chi_sim, etc.) |
## Examples
# Basic usage
python3 scripts/ocr.py image.png output.txt
# Chinese text recognition
python3 scripts/ocr.py chinese_doc.png result.txt --lang chi_sim
┌─────────────────────────────────────────────────────────────────────────────────┐
│ SKILL.md 结构解析 │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ Part 1: YAML Front Matter(元数据) │ │
│ ├─────────────────────────────────────────────────────────────────────────┤ │
│ │ --- │ │
│ │ name: ocr ← 唯一标识符(必须) │ │
│ │ description: ... ← 简短描述(必须) │ │
│ │ version: 1.0.0 ← 版本号(可选) │ │
│ │ author: xxx ← 作者(可选) │ │
│ │ --- │ │
│ │ │ │
│ │ ✅ 框架解析并索引 │ │
│ │ ✅ 用于 Skill 发现和列表展示 │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ Part 2: Markdown Body(正文) │ │
│ ├─────────────────────────────────────────────────────────────────────────┤ │
│ │ # 任意标题 │ │
│ │ ## Capabilities / Usage / Parameters / Examples ... │ │
│ │ │ │
│ │ ❌ 框架不解析结构 │ │
│ │ ✅ 原封不动注入 LLM 上下文 │ │
│ │ ✅ LLM 自主阅读理解 │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
│ 关键理解: │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ • "Capabilities"、"Usage"、"Parameters" 等只是约定俗成的章节名 │ │
│ │ • 框架不强制要求这些章节 │ │
│ │ • 写得越清晰、示例越丰富,LLM 理解得越好 │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
skills/
├── ocr/ ← Skill 目录(目录名即 Skill 名)
│ ├── SKILL.md ← 必须:Skill 定义文件
│ ├── scripts/ ← 可选:可执行脚本
│ │ ├── ocr.py
│ │ └── utils.py
│ ├── configs/ ← 可选:配置文件
│ │ └── models.yaml
│ └── templates/ ← 可选:模板文件
│
├── whisper/ ← 另一个 Skill
│ ├── SKILL.md
│ └── scripts/
│ └── transcribe.py
│
└── data-analysis/ ← 又一个 Skill
├── SKILL.md
└── notebooks/
└── analyze.ipynb
trpc-agent-go 框架实现了完整的 Skill 系统,架构如下:
┌─────────────────────────────────────────────────────────────────────────────────┐
│ trpc-agent-go Skill 系统架构 │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ LLM Agent │ │
│ ├─────────────────────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ Tool 层(4 个 Skill 相关工具) │ │
│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │
│ │ │ skill_load │ │ skill_run │ │skill_list │ │skill_select │ │ │
│ │ │(加载 Skill) │ │(执行命令) │ │ _docs │ │ _docs │ │ │
│ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │ │
│ │ └─────────────────┴────────────────┴─────────────────┘ │ │
│ └─────────────────────────────────────┬───────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────▼───────────────────────────────────┐ │
│ │ SkillsRequestProcessor (请求预处理器) │ │
│ │ • 每次 LLM 请求时动态注入 Skill 概览和已加载内容 │ │
│ └─────────────────────────────────────┬───────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────▼───────────────────────────────────┐ │
│ │ Skill Repository (FSRepository) │ │
│ │ • 启动时扫描目录,构建 name → path 索引 │ │
│ │ • 延迟加载:按需读取 SKILL.md 完整内容 │ │
│ └─────────────────────────────────────┬───────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────▼───────────────────────────────────┐ │
│ │ Code Executor │ │
│ │ ┌────────────────────────┐ ┌────────────────────────────────┐ │ │
│ │ │ Local Executor │ │ Container Executor │ │ │
│ │ │ (本地执行) │ │ (Docker 沙箱执行) │ │ │
│ │ └────────────────────────┘ └────────────────────────────────┘ │ │
│ └─────────────────────────────────────┬───────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────▼───────────────────────────────────┐ │
│ │ Workspace Manager │ │
│ │ • 创建隔离的工作目录 • 管理输入/输出文件 • 支持会话级复用 │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
在 trpc-agent-go 中集成 Skill 只需 3 步:
import (
"trpc.group/trpc-go/trpc-agent-go/llmagent"
"trpc.group/trpc-go/trpc-agent-go/skill"
"trpc.group/trpc-go/trpc-agent-go/codeexecutor/container"
)
// 1. 创建 Skill 仓库(扫描 skills/ 目录)
repo, _ := skill.NewFSRepository("./skills", "./user-skills")
// 2. 创建代码执行器(生产环境用容器沙箱)
exec, _ := container.New(container.WithHost("unix:///var/run/docker.sock"))
// 3. 创建 Agent 并启用 Skill
agent := llmagent.New(
"my-agent",
llmagent.WithSkills(repo), // 自动注册 4 个 Skill 工具
llmagent.WithCodeExecutor(exec), // 配置执行器
)
「Skill 目录结构示例:」
skills/
├── ocr/
│ ├── SKILL.md ← 必须:定义文件
│ └── scripts/ocr.py ← 可选:执行脚本
├── data-analysis/
│ ├── SKILL.md
│ └── scripts/analyze.py
这是理解整个 Skill 系统最关键的部分:「Skill 是如何从磁盘文件变成 LLM 可用能力的?」
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Skill 加载全流程 │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ 阶段 1: 系统启动时 - Repository 初始化 │ │
│ ├─────────────────────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ NewFSRepository(roots...) │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ 扫描所有 roots 目录 │ │ │
│ │ │ ├── skills/ │ │ │
│ │ │ │ ├── ocr/SKILL.md ──────▶ 解析 front matter │ │ │
│ │ │ │ ├── data-analysis/SKILL.md ──────▶ 提取 name, description│ │ │
│ │ │ │ └── whisper/SKILL.md ──────▶ 构建索引 │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ index: map[string]string │ │ │
│ │ │ { │ │ │
│ │ │ "ocr": "/path/skills/ocr", │ │ │
│ │ │ "data-analysis": "/path/skills/data-analysis", │ │ │
│ │ │ "whisper": "/path/skills/whisper", │ │ │
│ │ │ } │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ⚠️ 此时只解析了 front matter,Skill 正文内容尚未加载! │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ 阶段 2: Agent 初始化时 - 注册 Skill 工具 │ │
│ ├─────────────────────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ llmagent.New( │ │
│ │ llmagent.WithSkills(repo), ◄── 传入 Repository │ │
│ │ ) │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 注册 4 个 Skill 相关工具到 Agent: │ │
│ │ ┌──────────────────────────────────────────────────────────────┐ │ │
│ │ │ • skill_load - 加载 Skill 到上下文 │ │ │
│ │ │ • skill_list_docs - 列出 Skill 文档 │ │ │
│ │ │ • skill_select_docs - 选择要加载的文档 │ │ │
│ │ │ • skill_run - 执行 Skill 命令 │ │ │
│ │ └──────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ 同时创建 SkillsRequestProcessor(请求预处理器) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ 阶段 3: 每次 LLM 请求时 - 动态注入 │ │
│ ├─────────────────────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ SkillsRequestProcessor.ProcessRequest() │ │
│ │ │ │ │
│ │ ├──────▶ 1) 始终注入 Skill 概览(名称+描述) │ │
│ │ │ │ │
│ │ ├──────▶ 2) 检查 Session 状态,找出已加载的 Skills │ │
│ │ │ │ │
│ │ └──────▶ 3) 注入已加载 Skill 的完整内容到系统消息 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ 阶段 4: LLM 调用 skill_load 时 - 状态更新 │ │
│ ├─────────────────────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ LLM: skill_load({skill: "ocr", include_all_docs: true}) │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ LoadTool.StateDelta() 返回状态变更: │ │
│ │ { │ │
│ │ "temp:skill:loaded:ocr": "1", // 标记为已加载 │ │
│ │ "temp:skill:docs:ocr": "*" // 加载所有文档 │ │
│ │ } │ │
│ │ │ │ │
│ │ ▼ │ │
│ │ 状态写入 Session.State,下次请求时 Processor 可读取 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
// skill/repository.go
// Summary 是轻量级的 Skill 摘要,启动时加载
type Summary struct {
Name string// Skill 名称,来自 front matter
Description string// Skill 描述
}
// Doc 表示 Skill 目录下的附加文档
type Doc struct {
Path string// 相对路径,如 "docs/api.md"
Content string// 文档内容
}
// Skill 是完整的 Skill 定义,按需加载
type Skill struct {
Summary // 嵌入摘要信息
Body string // SKILL.md 的 Markdown 正文(front matter 之后的部分)
Docs []Doc // 目录下所有 .md/.txt 附加文档
}
// FSRepository 是基于文件系统的 Skill 仓库
type FSRepository struct {
roots []string // 多个根目录(支持 http/https/file URL)
index map[string]string// name → 目录路径的映射(启动时构建)
}
// 状态键常量
const (
StateKeyLoadedPrefix = "temp:skill:loaded:"// 标记 skill 是否已加载
StateKeyDocsPrefix = "temp:skill:docs:" // 选中的文档列表
)
// skill/repository.go
// SKILL.md 格式示例:
// ---
// name: ocr
// description: Extract text from images using Tesseract OCR
// ---
// # OCR Skill
//
// ## Usage
// python3 scripts/ocr.py <input_image> <output_file>
// ...
func splitFrontMatter(text string) (map[string]string, string) {
// 1. 检查是否以 "---\n" 开头
if !strings.HasPrefix(text, "---\n") {
returnmap[string]string{}, text // 无 front matter,整个都是 body
}
// 2. 找到结束的 "\n---\n"
idx := strings.Index(text[4:], "\n---\n")
if idx < 0 {
returnmap[string]string{}, text
}
// 3. 提取 front matter(YAML 部分)和 body(Markdown 正文)
fm := text[4 : 4+idx] // "name: ocr\ndescription: ..."
body := text[4+idx+5:] // "# OCR Skill\n..."
// 4. 简单解析 YAML key: value
m := make(map[string]string)
for _, line := range strings.Split(fm, "\n") {
if i := strings.Index(line, ":"); i >= 0 {
k := strings.TrimSpace(line[:i])
v := strings.TrimSpace(line[i+1:])
m[k] = strings.Trim(v, " \"'") // 去除引号
}
}
return m, body
}
// skill/repository.go
func NewFSRepository(roots ...string) (*FSRepository, error) {
// 1. 解析每个根路径(支持 http/https/file URL)
resolved := make([]string, 0, len(roots))
for _, root := range roots {
p, err := resolveSkillsRoot(root)
if err != nil {
returnnil, err
}
resolved = append(resolved, p)
}
// 2. 创建仓库并扫描构建索引
r := &FSRepository{
roots: resolved,
index: make(map[string]string),
}
if err := r.scan(); err != nil {
returnnil, err
}
return r, nil
}
func (r *FSRepository) scan() error {
for _, root := range r.roots {
filepath.WalkDir(root, func(p string, d fs.DirEntry, err error) error {
if err != nil || !d.IsDir() {
returnnil
}
// 查找 SKILL.md 文件
skillFile := filepath.Join(p, "SKILL.md")
if _, err := os.Stat(skillFile); err != nil {
returnnil// 不是 Skill 目录
}
// 解析 front matter 获取摘要
sum, err := parseSummary(skillFile)
if err != nil {
returnnil
}
// ⭐ 关键:只记录第一次出现的同名 Skill(后续忽略)
// 这允许用户通过 roots 顺序覆盖系统 Skill
if _, exists := r.index[sum.Name]; !exists {
r.index[sum.Name] = p
}
return filepath.SkipDir // 不递归扫描子目录
})
}
returnnil
}
这是最关键的部分 —— 「Skill 内容是如何被注入到 LLM 上下文的」:
// internal/flow/processor/skills.go
type SkillsRequestProcessor struct {
repo skill.Repository // Skill 仓库
}
func (p *SkillsRequestProcessor) ProcessRequest(
ctx context.Context,
inv *agent.Invocation,
req *model.Request,
ch chan<- *event.Event,
) {
var contentBuilder strings.Builder
// ═══════════════════════════════════════════════════════════════
// 第 1 步:始终注入 Skill 概览(名称 + 描述列表)
// ═══════════════════════════════════════════════════════════════
p.injectOverview(req, &contentBuilder)
// ═══════════════════════════════════════════════════════════════
// 第 2 步:获取已加载的 Skills(从 Session 状态读取)
// ═══════════════════════════════════════════════════════════════
loadedSkills := p.getLoadedSkills(inv)
// ═══════════════════════════════════════════════════════════════
// 第 3 步:注入已加载 Skill 的完整内容
// ═══════════════════════════════════════════════════════════════
for _, name := range loadedSkills {
sk, err := p.repo.Get(name)
if err != nil {
continue
}
// 注入 SKILL.md 的 body 部分
if sk.Body != "" {
contentBuilder.WriteString("\n[Loaded] ")
contentBuilder.WriteString(name)
contentBuilder.WriteString("\n\n")
contentBuilder.WriteString(sk.Body)
}
// 注入选中的文档
selectedDocs := p.getDocsSelection(inv, name)
iflen(selectedDocs) > 0 {
docText := p.buildDocsText(sk, selectedDocs)
contentBuilder.WriteString(docText)
}
}
// ═══════════════════════════════════════════════════════════════
// 第 4 步:合并到系统消息
// ═══════════════════════════════════════════════════════════════
p.mergeIntoSystem(req, contentBuilder.String())
}
// tool/skill/load.go
type loadInput struct {
Skill string `json:"skill"` // Skill 名称
Docs []string`json:"docs,omitempty"` // 指定加载的文档
IncludeAllDocs bool `json:"include_all_docs"`// 是否加载所有文档
}
// StateDelta 返回状态变更,框架会自动应用到 Session
func (t *LoadTool) StateDelta(args []byte, _ []byte) map[string][]byte {
var in loadInput
if err := json.Unmarshal(args, &in); err != nil {
returnnil
}
delta := make(map[string][]byte)
// 1. 标记 Skill 为已加载
loadedKey := skill.StateKeyLoadedPrefix + in.Skill // "temp:skill:loaded:ocr"
delta[loadedKey] = []byte("1")
// 2. 设置文档选择
docsKey := skill.StateKeyDocsPrefix + in.Skill // "temp:skill:docs:ocr"
if in.IncludeAllDocs {
delta[docsKey] = []byte("*") // "*" 表示加载所有文档
} elseiflen(in.Docs) > 0 {
b, _ := json.Marshal(in.Docs)
delta[docsKey] = b // JSON 数组形式
}
return delta
}
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Skill 内容加载到哪里? │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ LLM Request 结构: │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ │ │
│ │ messages: [ │ │
│ │ { │ │
│ │ role: "system", │ │
│ │ content: """ │ │
│ │ [原始系统提示词] │ │
│ │ │ │
│ │ ───────────────────────────────────────────── │ │
│ │ Available skills: ◄── Skill 概览 │ │
│ │ - ocr: Extract text from images... │ │
│ │ - whisper: Transcribe audio files... │ │
│ │ │ │
│ │ Use skill_load to load a skill before using it. │ │
│ │ ───────────────────────────────────────────── │ │
│ │ │ │
│ │ [Loaded] ocr ◄── 已加载的 Skill │ │
│ │ │ │
│ │ # OCR Skill │ │
│ │ ## Usage │ │
│ │ python3 scripts/ocr.py <input> <output> │ │
│ │ """ │ │
│ │ }, │ │
│ │ {role: "user", content: "帮我识别这张图片"}, │ │
│ │ ... │ │
│ │ ], │ │
│ │ │ │
│ │ tools: [ ◄── Skill 工具定义 │ │
│ │ {name: "skill_load", ...}, │ │
│ │ {name: "skill_run", ...}, │ │
│ │ ... │ │
│ │ ] │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
设计决策 | 原因 |
|---|---|
「延迟加载」 | 启动时只解析 front matter,按需读取完整 body,节省内存和启动时间 |
「状态驱动」 | 通过 Session.State 管理加载状态,支持多轮对话中的状态持久化 |
「概览优先」 | 每次请求都注入所有 Skill 概览,让 LLM 始终知道有哪些能力可用 |
「系统消息注入」 | Skill 内容合并到 System Prompt,符合 LLM 的角色设计 |
「优先级覆盖」 | 多个 roots 时先扫描到的优先,支持用户自定义覆盖系统 Skill |
当用户发起一次对话请求后,Skill 的执行并非单次调用完成,而是通过 「LLM 多轮自动对话」 逐步完成的。这一过程对用户透明——用户只需发送一条消息,框架和 LLM 会自动协作完成 Skill 的发现、加载、学习和执行。
trpc-agent-go 的核心设计是一个 「自动循环」:每当 LLM 返回 Tool 调用请求,框架会自动执行 Tool 并将结果反馈给 LLM,触发下一轮对话,直到 LLM 生成最终回复。
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Skill 自动执行的核心循环 │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ 用户: "帮我识别这张图片中的文字" │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ flow.Run() 自动循环 │ │
│ │ ┌─────────────────────────────────────────────────────────────────┐ │ │
│ │ │ │ │ │
│ │ │ for { │ │ │
│ │ │ // 1. 预处理:注入 Skill 信息、历史消息、Tools 定义 │ │ │
│ │ │ request := preprocess(invocation) │ │ │
│ │ │ │ │ │
│ │ │ // 2. 调用 LLM │ │ │
│ │ │ response := callLLM(request) │ │ │
│ │ │ │ │ │
│ │ │ // 3. 后处理:执行 Tool(如 skill_run) │ │ │
│ │ │ postprocess(response) │ │ │
│ │ │ │ │ │
│ │ │ // 4. 判断是否结束 │ │ │
│ │ │ if response.IsFinalResponse() { │ │ │
│ │ │ break // LLM 返回文本回复,循环结束 │ │ │
│ │ │ } │ │ │
│ │ │ // 否则继续循环:LLM 返回了 tool_calls,需要执行后再问 LLM │ │ │
│ │ │ } │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 返回给用户: "图片中的文字内容如下:..." │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
「关键点」:用户只发送了一条消息,但框架内部可能执行了 3-5 轮 LLM 对话来完成整个 Skill 调用流程。
以用户请求 "帮我识别这张图片中的文字" 为例,展示 Skill 如何自动完成执行:
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 一次用户请求触发的 Skill 自动执行全过程 │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ 用户输入: "帮我识别这张图片中的文字" + [图片附件] │
│ │
│ ════════════════════════════════════════════════════════════════════════════ │
│ 【自动】第 1 轮:LLM 判断需要 OCR 能力,选择加载 Skill │
│ ════════════════════════════════════════════════════════════════════════════ │
│ │
│ 框架 → LLM: │
│ ┌───────────────────────────────────────────────────────────────────────────┐ │
│ │ System: "...Available skills: │ │
│ │ - ocr: 图片文字识别 │ │
│ │ - pdf: PDF 文档解析 │ │
│ │ ..." │ │
│ │ User: "帮我识别这张图片中的文字" │ │
│ │ Tools: [skill_load, skill_select_docs, skill_run, ...] │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ LLM 判断: 需要 ocr Skill → 返回 tool_call │
│ ┌───────────────────────────────────────────────────────────────────────────┐ │
│ │ tool_calls: [{name: "skill_load", args: {skill: "ocr"}}] │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 框架自动执行: skill_load("ocr") → 返回 "loaded: ocr" │
│ │
│ ════════════════════════════════════════════════════════════════════════════ │
│ 【自动】第 2 轮:LLM 获取 Skill 使用文档 │
│ ════════════════════════════════════════════════════════════════════════════ │
│ │
│ 框架 → LLM: (带上第 1 轮的 tool 执行结果) │
│ ┌───────────────────────────────────────────────────────────────────────────┐ │
│ │ ...之前的消息... │ │
│ │ Assistant: tool_calls: [skill_load] │ │
│ │ Tool: "loaded: ocr" ← 第 1 轮结果 │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ LLM 判断: 需要了解如何使用 → 返回 tool_call │
│ ┌───────────────────────────────────────────────────────────────────────────┐ │
│ │ tool_calls: [{name: "skill_select_docs", args: {skill: "ocr", │ │
│ │ docs: ["SKILL.md"]}}] │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 框架自动执行: skill_select_docs() → 返回 Skill 文档内容 │
│ ┌───────────────────────────────────────────────────────────────────────────┐ │
│ │ "# OCR Skill │ │
│ │ ## Usage │ │
│ │ python3 scripts/ocr.py <input_image> <output_file> │ │
│ │ ## Parameters │ │
│ │ - input_image: 输入图片路径 │ │
│ │ - output_file: 输出文本路径" │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ════════════════════════════════════════════════════════════════════════════ │
│ 【自动】第 3 轮:LLM 学习文档后,构造执行命令 │
│ ════════════════════════════════════════════════════════════════════════════ │
│ │
│ 框架 → LLM: (带上第 2 轮的文档内容) │
│ ┌───────────────────────────────────────────────────────────────────────────┐ │
│ │ ...之前的消息... │ │
│ │ Tool: "# OCR Skill\n## Usage\npython3 scripts/ocr.py..." ← LLM 学习文档 │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ LLM 基于文档构造正确的调用参数: │
│ ┌───────────────────────────────────────────────────────────────────────────┐ │
│ │ tool_calls: [{ │ │
│ │ name: "skill_run", │ │
│ │ args: { │ │
│ │ skill: "ocr", │ │
│ │ command: "python3 scripts/ocr.py inputs/image.png output.txt", │ │
│ │ input_files: [{name: "image.png", content: "base64..."}], │ │
│ │ output_files: ["output.txt"] │ │
│ │ } │ │
│ │ }] │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 框架自动执行 skill_run: │
│ ┌───────────────────────────────────────────────────────────────────────────┐ │
│ │ 1. 创建隔离 Workspace │ │
│ │ 2. 写入输入文件 (base64 解码 → workspace/inputs/image.png) │ │
│ │ 3. 复制 Skill 脚本到 Workspace │ │
│ │ 4. 在沙箱中执行: python3 scripts/ocr.py inputs/image.png output.txt │ │
│ │ 5. 读取输出文件内容 │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 返回执行结果: "识别结果:\n这是图片中的文字内容..." │
│ │
│ ════════════════════════════════════════════════════════════════════════════ │
│ 【自动】第 4 轮:LLM 整理结果,生成最终回复 │
│ ════════════════════════════════════════════════════════════════════════════ │
│ │
│ 框架 → LLM: (带上执行结果) │
│ ┌───────────────────────────────────────────────────────────────────────────┐ │
│ │ ...之前的消息... │ │
│ │ Tool: "识别结果:\n这是图片中的文字内容..." ← Skill 执行结果 │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ LLM 生成最终回复 (finish_reason: "stop"): │
│ ┌───────────────────────────────────────────────────────────────────────────┐ │
│ │ "根据图片识别结果,图片中的文字内容如下: │ │
│ │ │ │
│ │ 这是图片中的文字内容... │ │
│ │ │ │
│ │ 如果需要进一步处理,请告诉我。" │ │
│ └───────────────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ IsFinalResponse() = true → 循环结束,返回给用户 │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
「1. Skill 信息的自动注入」
在每轮 LLM 调用前,SkillsRequestProcessor 会自动将 Skill 概览注入 System Prompt:
// 框架启动时从 SKILL.md 解析 name 和 description
// 每次 LLM 调用前自动注入
func (p *SkillsRequestProcessor) injectOverview(req *model.Request) {
overview := "Available skills:\n"
for _, skill := range p.repo.Summaries() {
overview += fmt.Sprintf("- %s: %s\n", skill.Name, skill.Description)
}
req.SystemPrompt += overview // LLM 能"看到"有哪些 Skill
}
「2. Tool 调用的自动执行」
当 LLM 返回 tool_calls 时,FunctionCallResponseProcessor 自动执行并将结果添加到消息历史:
func (p *FunctionCallResponseProcessor) ProcessResponse(...) {
for _, call := range response.GetToolCalls() {
// 自动执行 Tool(如 skill_load, skill_run)
result := tool.Run(ctx, call.Arguments)
// 将结果作为 Tool Message 添加到历史
// 下一轮 LLM 调用时会看到这个结果
invocation.AddMessage(model.NewToolMessage(call.ID, result))
}
}
「3. 循环终止条件」
当 LLM 不再返回 tool_calls,而是返回纯文本回复(finish_reason: "stop")时,循环自动终止:
for {
response := callLLM(request)
postprocess(response) // 执行可能的 tool_calls
if response.IsFinalResponse() { // finish_reason == "stop"
break // 循环结束
}
// 否则继续:LLM 还需要更多信息
}
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Skill 自动执行的核心要点 │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 用户感知:一问一答 │
│ 用户发送一条消息 → 收到一条完整回复 │
│ 中间的多轮 LLM 对话对用户完全透明 │
│ │
│ 2. 框架内部:自动多轮对话 │
│ 框架维护一个循环,自动处理 LLM 的 tool_calls │
│ 每次 tool 执行结果会追加到消息历史,触发下一轮 │
│ │
│ 3. LLM 行为:自主决策 │
│ LLM 基于 Skill 概览判断使用哪个 Skill │
│ LLM 阅读 Skill 文档后自主构造调用参数 │
│ LLM 收到执行结果后自主决定是继续调用还是生成最终回复 │
│ │
│ 4. 典型流程:4 轮自动对话 │
│ 第 1 轮:LLM 选择 Skill → skill_load │
│ 第 2 轮:LLM 获取文档 → skill_select_docs │
│ 第 3 轮:LLM 执行命令 → skill_run │
│ 第 4 轮:LLM 整理结果 → 最终回复 │
│ │
│ 5. 优化场景:减少轮次 │
│ • Skill 已加载:跳过第 1-2 轮,直接 skill_run │
│ • 简单 Skill:skill_load 时一次性获取所有文档 │
│ • 会话复用:同一会话内 Skill 状态持久化 │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
❝「核心价值」:这种自动多轮对话机制使得 Skill 的使用对用户完全透明。用户无需了解 Skill 的存在,只需自然地描述需求,框架和 LLM 会自动协作完成能力的发现、学习和执行。 ❞
代码执行是 Skill 最典型的应用场景之一,trpc-agent-go 提供了完善的执行器机制。
// codeexecutor/codeexecutor.go
type Executor interface {
// ExecuteCode 执行代码块
ExecuteCode(ctx context.Context, input CodeExecutionInput) (CodeExecutionResult, error)
// Engine 返回执行引擎(用于 Skill)
Engine() Engine
}
// CodeExecutionInput 执行输入
type CodeExecutionInput struct {
ExecutionID string // 执行标识
CodeBlocks []CodeBlock // 代码块列表
}
// CodeBlock 单个代码块
type CodeBlock struct {
Language string// 语言:python, bash, sh
Code string// 代码内容
}
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 代码执行器对比 │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────┐ ┌──────────────────────────────────┐ │
│ │ Local Executor │ │ Container Executor │ │
│ │ (本地执行) │ │ (容器沙箱执行) │ │
│ ├──────────────────────────────────┤ ├──────────────────────────────────┤ │
│ │ │ │ │ │
│ │ 执行方式: │ │ 执行方式: │ │
│ │ os/exec 直接调用系统命令 │ │ Docker 容器内执行 │ │
│ │ │ │ │ │
│ │ 安全性: ❌ 不安全 │ │ 安全性: ✅ 沙箱隔离 │ │
│ │ • 可访问宿主机文件系统 │ │ • 网络隔离 (NetworkMode: none) │ │
│ │ • 可访问网络 │ │ • 非特权模式 │ │
│ │ • 无资源限制 │ │ • 文件系统隔离 │ │
│ │ │ │ • 自动清理 │ │
│ │ 性能: ✅ 快 │ │ 性能: ⚠️ 首次启动慢 │ │
│ │ │ │ │ │
│ │ 适用场景: │ │ 适用场景: │ │
│ │ • 开发测试 │ │ • 生产环境 │ │
│ │ • 受信任环境 │ │ • 多租户场景 │ │
│ │ │ │ • 执行不受信任代码 │ │
│ └──────────────────────────────────┘ └──────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
Container Executor 采用「长期运行容器 + docker exec」 的模式:
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 容器生命周期(一次创建,多次复用) │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ 应用程序 Container Executor Docker 容器 │
│ │ │ │ │
│ │ New() │ │ │
│ │─────────────────────▶│ │ │
│ │ │ ContainerCreate() │ │
│ │ │──────────────────────────▶│ 🐳 容器创建 │
│ │ │ ContainerStart() │ │
│ │ │──────────────────────────▶│ 🏃 容器启动 │
│ │ │ │ (tail -f /dev/null) │
│ │◀─────────────────────│ │ ✅ 持续运行... │
│ │ │ │ │
│ │ ExecuteCode(code1) │ │ │
│ │─────────────────────▶│ ContainerExecCreate() │ │
│ │ │──────────────────────────▶│ ▶️ docker exec │
│ │◀─────────────────────│◀──────────────────────────│ 🏃 继续运行... │
│ │ │ │ │
│ │ ExecuteCode(code2) │ │ │
│ │─────────────────────▶│ ContainerExecCreate() │ │
│ │ │──────────────────────────▶│ ▶️ docker exec │
│ │◀─────────────────────│◀──────────────────────────│ 🏃 继续运行... │
│ │ │ │ │
│ │ ... 多次调用 ... │ │ 同一个容器复用! │
│ │ │ │ │
│ │ Close() │ ContainerStop() │ │
│ │─────────────────────▶│──────────────────────────▶│ 🛑 停止 │
│ │ │ ContainerRemove() │ │
│ │ │──────────────────────────▶│ 🗑️ 删除 │
│ │ │ │ │
└─────────────────────────────────────────────────────────────────────────────────┘
「设计考量」:
方案 | 启动耗时 | 资源消耗 | 隔离性 |
|---|---|---|---|
每次新建容器 | 秒级 | 高 | 完全隔离 |
「复用长期容器」 ✅ | 毫秒级 | 低 | exec 间共享状态 |
「核心源码:」
// codeexecutor/container/container.go
func New(opts ...Option) (*CodeExecutor, error) {
c := &CodeExecutor{
hostConfig: container.HostConfig{
AutoRemove: true, // 停止时自动删除
Privileged: false, // 非特权模式
NetworkMode: "none", // 禁止网络访问
},
containerConfig: container.Config{
Image: "python:3.9-slim",
WorkingDir: "/",
Cmd: []string{"tail", "-f", "/dev/null"}, // 保持容器运行
Tty: true,
},
}
// ...
}
┌─────────────────────────────────────────────────────────────────────────────────┐
│ Workspace 目录结构 │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ /workspace-{execution-id}/ │
│ │ │
│ ├── skills/ ← Skill 文件被 Stage 到这里 │
│ │ └── ocr/ │
│ │ ├── scripts/ ← 可执行脚本(只读) │
│ │ │ └── ocr.py │
│ │ ├── out/ ← 符号链接 → ../../out │
│ │ ├── work/ ← 符号链接 → ../../work │
│ │ └── inputs/ ← 符号链接 → ../../work/inputs │
│ │ │
│ ├── out/ ← 输出目录(可写) │
│ │ └── result.txt ← Skill 的输出文件 │
│ │ │
│ ├── work/ ← 工作目录(可写) │
│ │ └── inputs/ ← 输入文件目录 │
│ │ └── image.png ← 用户上传的文件 │
│ │ │
│ └── .metadata.json ← 工作区元数据 │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
通过对 Skill 机制的分析,我们可以总结出一套为 LLM 封装外部能力的通用方法论。
理解 Tool、MCP、Skill 的层级关系是正确封装能力的关键:
┌─────────────────────────────────────────────────────────────────────────────────┐
│ LLM 外部能力封装的三个层级 │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ 层级 3: Skill(高层抽象 - 能力集合) │ │
│ ├─────────────────────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ 定位:将完成某类任务所需的全部能力打包成一个"技能包" │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────────────┐ │ │
│ │ │ Skill: data-analysis │ │ │
│ │ │ ├── 文档:如何分析数据的完整指南 │ │ │
│ │ │ ├── Tool: read_csv(), write_report() │ │ │
│ │ │ ├── MCP: PostgreSQL Server (获取源数据) │ │ │
│ │ │ ├── 脚本: analyze.py, visualize.py │ │ │
│ │ │ └── 规则: 输出格式要求、安全约束 │ │ │
│ │ └─────────────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ 适用场景: │ │
│ │ • 复杂任务需要多种能力协作 │ │
│ │ • 需要完整的上下文知识(不仅是工具,还有"怎么用") │ │
│ │ • 希望以文档形式传递领域知识 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ 层级 2: MCP(中间层 - 标准化协议) │ │
│ ├─────────────────────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ 定位:为外部服务提供标准化的访问协议 │ │
│ │ │ │
│ │ LLM ──MCP 协议──▶ MCP Server ──RPC──▶ 外部服务 │ │
│ │ │ │
│ │ 适用场景: │ │
│ │ • 跨语言、跨进程的服务集成 │ │
│ │ • 需要标准化接口的企业级系统 │ │
│ │ • 复杂的状态管理(如数据库会话) │ │
│ │ │ │
│ │ 特点:可以被 Skill 引用和包含 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ 层级 1: Tool(基础层 - 单一函数) │ │
│ ├─────────────────────────────────────────────────────────────────────────┤ │
│ │ │ │
│ │ 定位:封装单一的、原子化的操作 │ │
│ │ │ │
│ │ LLM ──Function Call──▶ Agent ──调用──▶ 函数 │ │
│ │ │ │
│ │ 适用场景: │ │
│ │ • 简单的 API 调用 │ │
│ │ • 参数结构固定、可用 JSON Schema 描述 │ │
│ │ • 需要类型安全和编译时检查 │ │
│ │ │ │
│ │ 特点:可以被 Skill 和 MCP 包含 │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
根据任务复杂度和需求,选择合适的封装层级:
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 能力封装层级选择决策树 │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ 开始 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ 任务需要多种能力协作吗? │ │
│ │ (多个 Tool + 文档 + 规则) │ │
│ └───────────────┬─────────────────────┘ │
│ │ │
│ ┌───Yes──┴──No───┐ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌─────────────────────────────────┐ │
│ │ Skill │ │ 需要跨语言/跨进程集成吗? │ │
│ │ (高层封装) │ │ (独立服务、标准化接口) │ │
│ │ │ └───────────────┬─────────────────┘ │
│ │ 可包含: │ │ │
│ │ • Tool │ ┌───Yes──┴──No───┐ │
│ │ • MCP │ ▼ ▼ │
│ │ • 脚本 │ ┌──────────────┐ ┌─────────────────────────┐ │
│ │ • 文档 │ │ MCP │ │ 参数结构固定且简单吗? │ │
│ └──────────────┘ │ (协议层) │ └───────────┬─────────────┘ │
│ │ │ │ │
│ │ 可被 Skill │ ┌───Yes──┴──No───┐ │
│ │ 引用包含 │ ▼ ▼ │
│ └──────────────┘ ┌──────────────┐ ┌──────────────┐ │
│ │ Tool │ │ Skill │ │
│ │ (基础层) │ │ (文档驱动) │ │
│ │ │ │ │ │
│ │ 可被 Skill │ │ 复杂参数通过 │ │
│ │ 和 MCP 包含 │ │ 文档描述 │ │
│ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
「简化决策规则:」
场景 | 推荐封装层级 |
|---|---|
单一 API 调用 | Tool |
外部独立服务 | MCP |
复杂任务 + 需要领域知识 | 「Skill(整合 Tool + MCP + 文档)」 |
执行不受信任代码 | Skill + 沙箱执行器 |
已有命令行工具 | Skill(文档化 CLI 用法) |
┌─────────────────────────────────────────────────────────────────────────────────┐
│ 外部能力封装最佳实践 │
├─────────────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. 描述要清晰 │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ 无论是 Tool 的 description 还是 Skill 的 SKILL.md │ │
│ │ • 用自然语言说明"这个能力是做什么的" │ │
│ │ • 明确输入输出的含义和格式 │ │
│ │ • 提供具体的使用示例 │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
│ 2. 错误处理要完善 │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ • 返回清晰的错误信息,帮助 LLM 理解问题 │ │
│ │ • 区分可重试错误和不可重试错误 │ │
│ │ • 提供错误恢复建议 │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
│ 3. 安全边界要明确 │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ • 不受信任的代码 → 沙箱执行 │ │
│ │ • 敏感操作 → 权限控制 │ │
│ │ • 外部调用 → 超时控制 │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
│ 4. 粒度要适中 │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ • 太细:LLM 需要多次调用才能完成任务 │ │
│ │ • 太粗:功能过于复杂,LLM 难以正确使用 │ │
│ │ • 建议:一个能力解决一个明确的问题 │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
│ 5. 幂等性设计 │
│ ┌─────────────────────────────────────────────────────────────────────────┐ │
│ │ • LLM 可能多次调用同一能力(重试、误解等) │ │
│ │ • 尽量设计为幂等操作,避免副作用累积 │ │
│ │ • 写操作提供去重机制 │ │
│ └─────────────────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
本文以 trpc-agent-go 框架为实例,深入分析了如何在 Agent 开发中设计和集成 Skill 系统。
「核心要点」:
skill_list 发现能力,skill_run 执行命令「Skill 的核心价值」:
┌─────────────────────────────────────────────────────────────────────────────────┐
│ │
│ Skill = 工具 + 知识 + 工作流 │
│ │
│ • Tool 只告诉 LLM "有什么工具" │
│ • MCP 只告诉 LLM "如何调用外部服务" │
│ • Skill 告诉 LLM "如何使用这些工具和服务完成任务" │
│ │
│ 这种"知识传递"是 Skill 相比 Tool 和 MCP 的核心优势 │
│ │
└─────────────────────────────────────────────────────────────────────────────────┘
Skill 机制代表了 LLM 能力扩展的一个重要方向:「从单一工具到能力集合,从接口调用到知识传递」。随着 LLM 理解能力的提升,这种"文档即接口"的模式将会被更广泛采用。