C# 实现简版 Claude Code | Skills机制与知识热插拔(5)
目录
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 吧!
更多推荐

所有评论(0)