首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >Skill 在实际 Agent 业务中的实现方案

Skill 在实际 Agent 业务中的实现方案

作者头像
tunsuy
发布2026-04-09 11:34:22
发布2026-04-09 11:34:22
1380
举报

本文深入分析如何在 Agent 开发中集成 Skill 系统,包括概念介绍、规范设计、框架实现、执行流程细节,以及如何将外部能力封装为 LLM 可调用的能力。


目录

  1. Skill 简介
  2. Skill 规范详解
  3. trpc-agent-go 中的 Skill 实现
    • 3.1 整体架构
    • 3.2 快速集成指南
    • 3.3 Skill 加载机制详解(核心)
    • 3.4 一次对话中 Skill 如何自动完成执行
  4. 代码执行:Skill 的典型场景
  5. 扩展思考:如何为 LLM 封装外部能力

1. Skill 简介

1.1 什么是 Skill?

在 LLM Agent 的发展过程中,「Tool(工具)」 是最早被广泛采用的能力扩展方式。但随着任务复杂度提升,单一 Tool 难以满足需求——开发者往往需要将多个工具、文档、脚本组合使用才能完成一个任务。

「Skill 是更高层级的能力封装」,它不仅仅是一个工具,而是一个可以整合多种资源的"技能包":

代码语言:javascript
复制
┌─────────────────────────────────────────────────────────────────────────────────┐
│                         一个 Skill 可以包含                                       │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   📄 文档(SKILL.md)                                                            │
│   │  • 能力说明、使用方法、示例                                                   │
│   │  • LLM 通过阅读文档学习使用方式                                               │
│   │                                                                             │
│   🔧 Tool(函数调用)                                                            │
│   │  • 封装好的 API 调用                                                         │
│   │  • 数据处理函数                                                              │
│   │                                                                             │
│   🔌 MCP Server(外部服务)                                                      │
│   │  • 数据库连接                                                                │
│   │  • 第三方 API 集成                                                           │
│   │                                                                             │
│   📜 脚本(可执行代码)                                                           │
│   │  • Python 脚本                                                               │
│   │  • Bash 命令                                                                 │
│   │                                                                             │
│   📋 规则和约束                                                                   │
│      • 编码规范                                                                  │
│      • 安全约束                                                                  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

1.2 Skill 与 Tool 的区别

代码语言:javascript
复制
┌─────────────────────────────────────────────────────────────────────────────────┐
│                         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 文档定义,无需编写代码                                  │    │
│   │  • 可在沙箱/容器中隔离执行                                               │    │
│   │  • 易于分享和跨项目复用                                                  │    │
│   └───────────────────────────────────────────────────────────────────────┘    │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

1.3 Skill 的核心理念

「Skill 的核心思想是:让 LLM 像阅读文档一样学习如何使用一整套能力。」

代码语言:javascript
复制
┌─────────────────────────────────────────────────────────────────────────────────┐
│                                                                                 │
│   SKILL.md 本质上是给 LLM 看的"使用手册"                                          │
│                                                                                 │
│   • 如果你能教会一个人使用某个工具集,那也能教会 LLM                                │
│   • Skill 文档就是那份"教学材料"                                                  │
│   • 文档中可以引用 Tool、MCP、脚本等各种资源                                       │
│                                                                                 │
│   这种"文档即接口"的模式,大大降低了能力扩展的门槛                                   │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

2. Skill 规范详解

2.1 SKILL.md 标准格式

业界逐渐形成了相对统一的 Skill 定义规范,采用 「YAML Front Matter + Markdown Body」 格式:

「SKILL.md 示例文件:」

代码语言:javascript
复制
# === YAML Front Matter(文件开头)===
---
name: ocr
description: Extract text from images using Tesseract OCR
---
代码语言:javascript
复制
# 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

2.2 规范解析

代码语言:javascript
复制
┌─────────────────────────────────────────────────────────────────────────────────┐
│                         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 理解得越好                                 │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

2.3 Skill 目录结构

代码语言:javascript
复制
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

3. trpc-agent-go 中的 Skill 实现

3.1 整体架构

trpc-agent-go 框架实现了完整的 Skill 系统,架构如下:

代码语言:javascript
复制
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    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                                │  │
│   │   • 创建隔离的工作目录   • 管理输入/输出文件   • 支持会话级复用          │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

3.2 快速集成指南

在 trpc-agent-go 中集成 Skill 只需 3 步:

代码语言:javascript
复制
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 目录结构示例:」

代码语言:javascript
复制
skills/
├── ocr/
│   ├── SKILL.md              ← 必须:定义文件
│   └── scripts/ocr.py        ← 可选:执行脚本
├── data-analysis/
│   ├── SKILL.md
│   └── scripts/analyze.py

3.3 Skill 加载机制详解(核心)

这是理解整个 Skill 系统最关键的部分:「Skill 是如何从磁盘文件变成 LLM 可用能力的?」

4.3.1 加载过程全景图
代码语言:javascript
复制
┌─────────────────────────────────────────────────────────────────────────────────┐
│                         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 可读取                    │  │
│   │                                                                         │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
4.3.2 核心数据结构
代码语言:javascript
复制
// 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:"    // 选中的文档列表
)
4.3.3 SKILL.md 文件解析
代码语言:javascript
复制
// 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
}
4.3.4 Repository 初始化(系统启动时)
代码语言:javascript
复制
// 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
}
4.3.5 每次请求时的内容注入(核心!)

这是最关键的部分 —— 「Skill 内容是如何被注入到 LLM 上下文的」

代码语言:javascript
复制
// 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())
}
4.3.6 skill_load 工具的状态更新
代码语言:javascript
复制
// 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
}
4.3.7 加载位置总结
代码语言:javascript
复制
┌─────────────────────────────────────────────────────────────────────────────────┐
│                         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", ...},                                           │  │
│   │     ...                                                                 │  │
│   │   ]                                                                     │  │
│   │                                                                         │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
4.3.8 为什么这样设计?

设计决策

原因

「延迟加载」

启动时只解析 front matter,按需读取完整 body,节省内存和启动时间

「状态驱动」

通过 Session.State 管理加载状态,支持多轮对话中的状态持久化

「概览优先」

每次请求都注入所有 Skill 概览,让 LLM 始终知道有哪些能力可用

「系统消息注入」

Skill 内容合并到 System Prompt,符合 LLM 的角色设计

「优先级覆盖」

多个 roots 时先扫描到的优先,支持用户自定义覆盖系统 Skill

3.4 一次对话中 Skill 如何自动完成执行

当用户发起一次对话请求后,Skill 的执行并非单次调用完成,而是通过 「LLM 多轮自动对话」 逐步完成的。这一过程对用户透明——用户只需发送一条消息,框架和 LLM 会自动协作完成 Skill 的发现、加载、学习和执行。

3.4.1 核心机制:事件驱动的自动循环

trpc-agent-go 的核心设计是一个 「自动循环」:每当 LLM 返回 Tool 调用请求,框架会自动执行 Tool 并将结果反馈给 LLM,触发下一轮对话,直到 LLM 生成最终回复。

代码语言:javascript
复制
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    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 调用流程。

3.4.2 完整示例:一次 OCR 请求的自动执行过程

以用户请求 "帮我识别这张图片中的文字" 为例,展示 Skill 如何自动完成执行:

代码语言:javascript
复制
┌─────────────────────────────────────────────────────────────────────────────────┐
│          一次用户请求触发的 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 → 循环结束,返回给用户                                 │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘
3.4.3 自动执行的三个关键环节

「1. Skill 信息的自动注入」

在每轮 LLM 调用前,SkillsRequestProcessor 会自动将 Skill 概览注入 System Prompt:

代码语言:javascript
复制
// 框架启动时从 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 自动执行并将结果添加到消息历史:

代码语言:javascript
复制
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")时,循环自动终止:

代码语言:javascript
复制
for {
    response := callLLM(request)
    postprocess(response)  // 执行可能的 tool_calls
    
    if response.IsFinalResponse() {  // finish_reason == "stop"
        break  // 循环结束
    }
    // 否则继续:LLM 还需要更多信息
}
3.4.4 关键理解
代码语言:javascript
复制
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    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 会自动协作完成能力的发现、学习和执行。 ❞


4. 代码执行:Skill 的典型场景

代码执行是 Skill 最典型的应用场景之一,trpc-agent-go 提供了完善的执行器机制。

4.1 执行器架构

代码语言:javascript
复制
// 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// 代码内容
}

4.2 两种执行器对比

代码语言:javascript
复制
┌─────────────────────────────────────────────────────────────────────────────────┐
│                         代码执行器对比                                           │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   ┌──────────────────────────────────┐  ┌──────────────────────────────────┐   │
│   │       Local Executor             │  │     Container Executor           │   │
│   │       (本地执行)                  │  │     (容器沙箱执行)                │   │
│   ├──────────────────────────────────┤  ├──────────────────────────────────┤   │
│   │                                  │  │                                  │   │
│   │  执行方式:                        │  │  执行方式:                        │   │
│   │  os/exec 直接调用系统命令         │  │  Docker 容器内执行                │   │
│   │                                  │  │                                  │   │
│   │  安全性: ❌ 不安全                │  │  安全性: ✅ 沙箱隔离              │   │
│   │  • 可访问宿主机文件系统           │  │  • 网络隔离 (NetworkMode: none)  │   │
│   │  • 可访问网络                     │  │  • 非特权模式                     │   │
│   │  • 无资源限制                     │  │  • 文件系统隔离                   │   │
│   │                                  │  │  • 自动清理                       │   │
│   │  性能: ✅ 快                      │  │  性能: ⚠️ 首次启动慢             │   │
│   │                                  │  │                                  │   │
│   │  适用场景:                        │  │  适用场景:                        │   │
│   │  • 开发测试                       │  │  • 生产环境                       │   │
│   │  • 受信任环境                     │  │  • 多租户场景                     │   │
│   │                                  │  │  • 执行不受信任代码               │   │
│   └──────────────────────────────────┘  └──────────────────────────────────┘   │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

4.3 容器执行器的生命周期

Container Executor 采用「长期运行容器 + docker exec」 的模式:

代码语言:javascript
复制
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    容器生命周期(一次创建,多次复用)                              │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   应用程序           Container Executor              Docker 容器                 │
│      │                      │                           │                       │
│      │  New()               │                           │                       │
│      │─────────────────────▶│                           │                       │
│      │                      │  ContainerCreate()        │                       │
│      │                      │──────────────────────────▶│  🐳 容器创建          │
│      │                      │  ContainerStart()         │                       │
│      │                      │──────────────────────────▶│  🏃 容器启动          │
│      │                      │                           │  (tail -f /dev/null) │
│      │◀─────────────────────│                           │  ✅ 持续运行...       │
│      │                      │                           │                       │
│      │  ExecuteCode(code1)  │                           │                       │
│      │─────────────────────▶│  ContainerExecCreate()    │                       │
│      │                      │──────────────────────────▶│  ▶️ docker exec       │
│      │◀─────────────────────│◀──────────────────────────│  🏃 继续运行...       │
│      │                      │                           │                       │
│      │  ExecuteCode(code2)  │                           │                       │
│      │─────────────────────▶│  ContainerExecCreate()    │                       │
│      │                      │──────────────────────────▶│  ▶️ docker exec       │
│      │◀─────────────────────│◀──────────────────────────│  🏃 继续运行...       │
│      │                      │                           │                       │
│      │  ... 多次调用 ...    │                           │  同一个容器复用!      │
│      │                      │                           │                       │
│      │  Close()             │  ContainerStop()          │                       │
│      │─────────────────────▶│──────────────────────────▶│  🛑 停止              │
│      │                      │  ContainerRemove()        │                       │
│      │                      │──────────────────────────▶│  🗑️ 删除              │
│      │                      │                           │                       │
└─────────────────────────────────────────────────────────────────────────────────┘

「设计考量」

方案

启动耗时

资源消耗

隔离性

每次新建容器

秒级

完全隔离

「复用长期容器」 ✅

毫秒级

exec 间共享状态

「核心源码:」

代码语言:javascript
复制
// 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,
        },
    }
    // ...
}

4.4 Workspace 工作区管理

代码语言:javascript
复制
┌─────────────────────────────────────────────────────────────────────────────────┐
│                         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               ← 工作区元数据                               │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

5. 扩展思考:如何为 LLM 封装外部能力

通过对 Skill 机制的分析,我们可以总结出一套为 LLM 封装外部能力的通用方法论。

5.1 能力封装的三个层级

理解 Tool、MCP、Skill 的层级关系是正确封装能力的关键:

代码语言:javascript
复制
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    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 包含                                         │  │
│   │                                                                         │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

5.2 选择决策树

根据任务复杂度和需求,选择合适的封装层级:

代码语言:javascript
复制
┌─────────────────────────────────────────────────────────────────────────────────┐
│                         能力封装层级选择决策树                                    │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   开始                                                                          │
│     │                                                                           │
│     ▼                                                                           │
│   ┌─────────────────────────────────────┐                                      │
│   │ 任务需要多种能力协作吗?              │                                      │
│   │ (多个 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 用法)

5.3 封装外部能力的最佳实践

代码语言:javascript
复制
┌─────────────────────────────────────────────────────────────────────────────────┐
│                    外部能力封装最佳实践                                           │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│   1. 描述要清晰                                                                  │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  无论是 Tool 的 description 还是 Skill 的 SKILL.md                       │  │
│   │  • 用自然语言说明"这个能力是做什么的"                                      │  │
│   │  • 明确输入输出的含义和格式                                               │  │
│   │  • 提供具体的使用示例                                                     │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   2. 错误处理要完善                                                              │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  • 返回清晰的错误信息,帮助 LLM 理解问题                                   │  │
│   │  • 区分可重试错误和不可重试错误                                            │  │
│   │  • 提供错误恢复建议                                                       │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   3. 安全边界要明确                                                              │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  • 不受信任的代码 → 沙箱执行                                              │  │
│   │  • 敏感操作 → 权限控制                                                    │  │
│   │  • 外部调用 → 超时控制                                                    │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   4. 粒度要适中                                                                  │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  • 太细:LLM 需要多次调用才能完成任务                                      │  │
│   │  • 太粗:功能过于复杂,LLM 难以正确使用                                    │  │
│   │  • 建议:一个能力解决一个明确的问题                                        │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
│   5. 幂等性设计                                                                  │
│   ┌─────────────────────────────────────────────────────────────────────────┐  │
│   │  • LLM 可能多次调用同一能力(重试、误解等)                                 │  │
│   │  • 尽量设计为幂等操作,避免副作用累积                                       │  │
│   │  • 写操作提供去重机制                                                      │  │
│   └─────────────────────────────────────────────────────────────────────────┘  │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

总结

本文以 trpc-agent-go 框架为实例,深入分析了如何在 Agent 开发中设计和集成 Skill 系统。

「核心要点」

  1. 「Skill 规范」:采用 YAML Front Matter(元数据)+ Markdown Body(使用文档)格式,框架解析元数据,LLM 理解文档
  2. 「框架设计」:四大核心组件(Repository、Executor、Workspace Manager、Skill Tools)及其职责划分
  3. 「两阶段调用模型」skill_list 发现能力,skill_run 执行命令
  4. 「flow.Run 执行流程」:事件驱动循环 + 多轮 LLM 对话,通过 RequestProcessor 和 ResponseProcessor 链处理请求响应
  5. 「代码执行安全」:通过容器沙箱实现隔离执行,容器采用"一次创建,多次复用"模式
  6. 「层级关系」:Tool(基础层)→ MCP(协议层)→ 「Skill(高层抽象,可包含前两者)」

「Skill 的核心价值」

代码语言:javascript
复制
┌─────────────────────────────────────────────────────────────────────────────────┐
│                                                                                 │
│   Skill = 工具 + 知识 + 工作流                                                   │
│                                                                                 │
│   • Tool 只告诉 LLM "有什么工具"                                                 │
│   • MCP 只告诉 LLM "如何调用外部服务"                                            │
│   • Skill 告诉 LLM "如何使用这些工具和服务完成任务"                               │
│                                                                                 │
│   这种"知识传递"是 Skill 相比 Tool 和 MCP 的核心优势                             │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

Skill 机制代表了 LLM 能力扩展的一个重要方向:「从单一工具到能力集合,从接口调用到知识传递」。随着 LLM 理解能力的提升,这种"文档即接口"的模式将会被更广泛采用。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-03-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 有文化的技术人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 目录
  • 1. Skill 简介
    • 1.1 什么是 Skill?
    • 1.2 Skill 与 Tool 的区别
    • 1.3 Skill 的核心理念
  • 2. Skill 规范详解
    • 2.1 SKILL.md 标准格式
    • 2.2 规范解析
    • 2.3 Skill 目录结构
  • 3. trpc-agent-go 中的 Skill 实现
    • 3.1 整体架构
    • 3.2 快速集成指南
    • 3.3 Skill 加载机制详解(核心)
      • 4.3.1 加载过程全景图
      • 4.3.2 核心数据结构
      • 4.3.3 SKILL.md 文件解析
      • 4.3.4 Repository 初始化(系统启动时)
      • 4.3.5 每次请求时的内容注入(核心!)
      • 4.3.6 skill_load 工具的状态更新
      • 4.3.7 加载位置总结
      • 4.3.8 为什么这样设计?
    • 3.4 一次对话中 Skill 如何自动完成执行
      • 3.4.1 核心机制:事件驱动的自动循环
      • 3.4.2 完整示例:一次 OCR 请求的自动执行过程
      • 3.4.3 自动执行的三个关键环节
      • 3.4.4 关键理解
  • 4. 代码执行:Skill 的典型场景
    • 4.1 执行器架构
    • 4.2 两种执行器对比
    • 4.3 容器执行器的生命周期
    • 4.4 Workspace 工作区管理
  • 5. 扩展思考:如何为 LLM 封装外部能力
    • 5.1 能力封装的三个层级
    • 5.2 选择决策树
    • 5.3 封装外部能力的最佳实践
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档