❝该系列文章基于
github.com/shareAI-lab/learn-claude-code写就,该仓库以大道至简的风格剖析了Claude Code的核心原理,值得大家学习。由于该仓库是基于Python语言,为方便.NET开发者学习,我已经将代码基于.NET 10的dotnet file重写,源码已上传至github,源码地址见文末。
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 吧!
点击阅读原文,获取仓库地址:👇👇👇