目录

v4: Skills 机制 - 知识外部化

问题:领域知识

工具 vs 技能

范式转变:知识外部化

SKILL.md 标准

渐进式披露

SkillLoader 实现

Skill 工具定义

缓存保留注入

实际工作流程

深层洞察

知识即代码

层次化知识

创建自定义 Skill

步骤 1:创建目录

步骤 2:编写 SKILL.md

步骤 3:添加资源(可选)

步骤 4:重启 Agent

运行示例

总结

系列总结



上一篇

v4: Skills 机制 - 知识外部化

本文是 Learn Claude Code (C# 版) 系列的第五篇,对应代码文件 v4_skills_agent.cs

问题:领域知识

v3 给了我们子代理来分解任务。但有个更深的问题:

模型如何知道怎样处理特定领域的任务?

You: 处理这个 PDF 文件,提取文本

模型需要知道:

  • pdftotext 快速但格式简单

  • PyMuPDF 保留更多结构

  • Ghostscript 用于复杂转换

You: 构建一个 MCP 服务器

模型需要知道:

  • MCP 协议规范

  • JSON-RPC 消息格式

  • 必须实现的方法

这些不是"能力"(工具),是"知识"。

工具 vs 技能

概念

是什么

示例

工具 (Tool)

模型能做什么

bash, read_file, write_file

技能 (Skill)

模型知道怎么做

PDF 处理, MCP 开发, 代码审查

工具是能力,技能是知识

范式转变:知识外部化

传统 AI:

  • 知识锁在模型参数里

  • 教新技能:收集数据 → 训练 → 部署

  • 成本:1M+

  • 时间:数周

  • 需要:ML 专家、GPU 集群

Skills 方式:

  • 知识存在可编辑文件中

  • 教新技能:写一个 SKILL.md

  • 成本:免费

  • 时间:几分钟

  • 需要:任何人都行

就像热插拔的 LoRA 适配器,不需要训练!

SKILL.md 标准

skills/
├── pdf/
│   └── SKILL.md          # 必需:YAML frontmatter + Markdown
├── mcp-builder/
│   ├── SKILL.md
│   └── references/       # 可选:参考文档
└── code-review/
    ├── SKILL.md
    └── scripts/          # 可选:辅助脚本

YAML frontmatter 提供元数据,Markdown 正文提供详细指令。

渐进式披露

层 1: 元数据(始终加载)        ~100 tokens/skill
      只有 name + description
      → 系统提示中列出

层 2: SKILL.md 正文(触发时)   ~2000 tokens
      详细指令和模式
      → Skill 工具调用时注入

层 3: 资源(按需)             无限
      scripts/, references/
      → 模型可以 read_file 访问

这保持了上下文精简,同时允许任意深度的知识。

SkillLoader 实现

class SkillLoader
{
    privatereadonly Dictionary<string, Skill> _skills = new();

    public SkillLoader(string skillsDir)
    {
        // 扫描 skills/ 目录,加载所有 SKILL.md
        foreach (var dir in Directory.GetDirectories(skillsDir))
        {
            var skillMd = Path.Combine(dir, "SKILL.md");
            if (File.Exists(skillMd))
            {
                var skill = ParseSkillMd(skillMd);
                if (skill != null)
                    _skills[skill.Name] = skill;
            }
        }
    }

    private Skill? ParseSkillMd(string path)
    {
        var content = File.ReadAllText(path);
        
        // 解析 YAML frontmatter
        var match = Regex.Match(content, @"^---\s*\n(.*?)\n---\s*\n(.*)$", 
            RegexOptions.Singleline);
        if (!match.Success) returnnull;
        
        // 提取 name, description, body
        // ...
    }

    public string GetDescriptions()
    {
        // 返回所有技能的简短描述(层 1)
        returnstring.Join("\n", 
            _skills.Select(kv => $"- {kv.Key}: {kv.Value.Description}"));
    }

    publicstring? GetSkillContent(string name)
    {
        // 返回完整内容 + 资源列表(层 2 + 层 3 提示)
        // ...
    }
}

Skill 工具定义

var skillTool = new Tool
{
    Name = "Skill",
    Description = $"""
        加载技能以获取任务的专业知识。

        可用技能:
        {skills.GetDescriptions()}

        何时使用:
        - 用户任务匹配技能描述时立即使用
        - 在尝试特定领域工作之前

        技能内容将注入对话,给你详细指令和资源。
        """,
    InputSchema = new InputSchema
    {
        Type = "object",
        Properties = new Dictionary<string, JsonElement>
        {
            ["skill"] = JsonDocument.Parse("""{"type": "string"}""").RootElement
        },
        Required = ["skill"]
    }
};

缓存保留注入

关键洞察: Skill 内容放入 tool_result(用户消息),不是系统提示!

string RunSkill(string skillName)
{
    var content = skills.GetSkillContent(skillName);
    
    return $"""
        <skill-loaded name="{skillName}">
        {content}
        </skill-loaded>

        按照上面技能中的指令完成用户的任务。
        """;
}

为什么这很重要?

错误方式:
  修改系统提示 → cache miss → 重新计算整个前缀 → 20-50x 成本

正确方式:
  作为 tool_result 追加 → 前缀不变 → cache hit → 只计算新内容

这是成本优化的关键技巧。

实际工作流程

You: 帮我处理 report.pdf,提取其中的表格数据

我注意到这是 PDF 处理任务,让我加载相关技能:

> Skill: {"skill": "pdf"}
  Skill loaded (1523 chars)

<skill-loaded name="pdf">
# PDF 处理技能

## 读取 PDF
使用 pdftotext 快速提取文本...

## 提取表格
对于表格数据,推荐使用 tabula-py:

pip install tabula-py
import tabula
tables = tabula.read_pdf("input.pdf", pages='all')

...
</skill-loaded>

根据技能指导,我将使用 tabula-py 提取表格:

> bash: {"command": "pip install tabula-py"}
  Successfully installed tabula-py-2.7.0

> write_file: {"path": "extract_tables.py", "content": "..."}
  Wrote 312 bytes to extract_tables.py

> bash: {"command": "python extract_tables.py report.pdf"}
  Found 3 tables in report.pdf
  Saved to: tables_page1.csv, tables_page2.csv, tables_page3.csv

成功提取了 3 个表格!

深层洞察

知识即代码

SKILL.md 是"可执行的文档":

  • 对人类:可读的说明

  • 对模型:可执行的指令

这模糊了"文档"和"程序"的边界。

层次化知识

模型基础知识(参数中)
    ↓
系统提示(全局配置)
    ↓
Skills(按需领域知识)
    ↓
文件内容(即时上下文)

每一层有不同的持久性和加载成本。

创建自定义 Skill

步骤 1:创建目录

mkdir -p skills/my-skill

步骤 2:编写 SKILL.md

---
name: my-skill
description: 简短描述,用于触发判断。
---

# My Skill

## 什么时候使用

当用户需要...

## 如何使用

### 模式 1: ...

### 模式 2: ...

## 常见问题

...

步骤 3:添加资源(可选)

skills/my-skill/
├── SKILL.md
├── scripts/
│   └── helper.py
└── references/
    └── spec.md

步骤 4:重启 Agent

Agent 启动时会扫描 skills/ 目录。

运行示例

Mini Claude Code v4 (with Skills) - /path/to/project
Skills: pdf, dotnet, code-review
Agent 类型: explore, code, plan
输入 'exit' 退出。

You: 帮我审查 src/Services/AuthService.cs

我注意到这是代码审查任务,让我加载相关技能:

> Skill: {"skill": "code-review"}
  Skill loaded (2105 chars)

首先阅读代码:

> read_file: {"path": "src/Services/AuthService.cs"}
  ...

根据代码审查技能的检查清单,我发现以下问题:

**安全问题:**
1. 第 45 行:密码比较使用 == 而非常量时间比较
2. 第 78 行:缺少输入验证

**代码质量:**
1. Login 方法过长(120 行),建议拆分
2. 缺少日志记录

**建议修复:**

> edit_file: {"path": "src/Services/AuthService.cs", ...}
  Edited src/Services/AuthService.cs

已修复密码比较问题,其他问题需要更大重构,建议创建单独任务。

总结

v4 的哲学:

知识外部化。专业无需重训。

Skills 机制让任何人都能在几分钟内"教会"模型新的领域知识。这是 AI Agent 从"通用工具"到"领域专家"的关键一步。


系列总结

从 v0 到 v4,我们构建了一个完整的 AI Agent:

版本

核心概念

关键洞察

v0

Bash Agent

一个工具就够了

v1

Basic Agent

模型即代理

v2

Todo Agent

约束赋能复杂性

v3

Subagent

干净上下文 = 更好结果

v4

Skills

知识可以外部化

核心模式始终不变:

while (response.StopReason == StopReason.ToolUse)
{
    var results = await ExecuteToolsAsync(response.ToolCalls);
    messages.Add(results);
}

模型是 80%,代码是 20%。

希望这个系列帮助你理解了 Claude Code 这类 AI Agent 的工作原理。现在,去构建你自己的 Agent 吧!

引入地址

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐