05 — 技能加载:按需注入领域知识
"用到什么知识,临时加载什么知识" — 通过 tool_result 注入,不塞 system prompt。
Harness 层:按需知识 — 模型开口要时才给的领域专长。
课程路径:[[01-agent-loop|s01 智能体循环]] → [[02-tool-use|s02 工具使用]] → [[03-todo-write|s03 待办写入]] → [[04-subagent|s04 子智能体]] → ⭐ s05 技能加载
为什么需要技能加载?
[[01-agent-loop|s01]] 中我们建立了最小 agent loop,[[02-tool-use|s02]] 添加了工具系统,[[03-todo-write|s03]] 引入了待办清单,[[04-subagent|s04]] 实现了子智能体调度。但还有一个根本问题没解决:模型怎么知道特定领域的最佳实践?
如果希望模型遵循 git 提交规范、走代码审查清单、按测试驱动开发的流程工作,最简单的做法是把所有规则塞进系统提示:
"Git 规范:……测试规范:……代码审查规范:……MCP 规范:……"假设 10 个技能,每个 2000 token,就是 20,000 token。大部分技能跟当前任务毫无关系。更糟的是,系统提示的每个 token 都会占用模型的有效上下文窗口 — 模型真正用来reasoning 的空间被压缩了。
这就是技能加载解决的问题。
核心思想:两层知识注入
System prompt (Layer 1 — always present):
+--------------------------------------+
| You are a coding agent. |
| Skills available: |
| - git: Git workflow helpers | ~100 tokens/skill
| - test: Testing best practices |
+--------------------------------------+
When model calls load_skill("git"):
+--------------------------------------+
| tool_result (Layer 2 — on demand): |
| <skill name="git"> |
| Full git workflow instructions... | ~2000 tokens
| Step 1: ... |
| </skill> |
+--------------------------------------+第一层(系统提示):只放技能名称和一句话描述,成本极低(~100 tokens/技能)。第二层(tool_result):模型主动请求时,才注入完整的技能内容(~2000 tokens/技能)。
关键洞察:由模型决定什么时候加载什么知识。Harness 只需提供"技能目录"和"技能加载工具"。模型会自主判断是否需要特定领域的指导。
技能文件结构
每个技能是一个目录,包含 SKILL.md 文件:
skills/
git/
SKILL.md
code-review/
SKILL.md
agent-builder/
SKILL.md
mcp-builder/
SKILL.mdSKILL.md 使用 YAML frontmatter 定义元数据:
---
name: git
description: Git workflow helpers — commit conventions, branch management, rebase workflow
---
# Git Workflow
## Commit Convention
- Use `type(scope): description` format
- Types: feat, fix, refactor, chore, docs, test
## Branch Management
1. Create feature branch from main
2. Rebase onto main before merge
...SkillLoader 实现
扫描文件系统、解析 frontmatter、提供两个关键方法:
class SkillLoader:
def __init__(self, skills_dir: Path):
self.skills = {}
for f in sorted(skills_dir.rglob("SKILL.md")):
text = f.read_text()
meta, body = self._parse_frontmatter(text)
name = meta.get("name", f.parent.name)
self.skills[name] = {"meta": meta, "body": body}
def get_descriptions(self) -> str:
"""Layer 1: 返回精简短描述(放入系统提示)"""
lines = []
for name, skill in self.skills.items():
desc = skill["meta"].get("description", "")
lines.append(f" - {name}: {desc}")
return "\n".join(lines)
def get_content(self, name: str) -> str:
"""Layer 2: 返回完整技能内容(通过 load_skill 工具注入)"""
skill = self.skills.get(name)
if not skill:
return f"Error: Unknown skill '{name}'."
return f"<skill name=\"{name}\">\n{skill['body']}\n</skill>"集成到 Harness
与 [[02-tool-use|s02 的工具系统]] 一样,load_skill 不过是 dispatch map 中的又一个工具:
SYSTEM = f"""You are a coding agent at {WORKDIR}.
Skills available:
{SKILL_LOADER.get_descriptions()}"""
TOOL_HANDLERS = {
"read": handle_read,
"write": handle_write,
"bash": handle_bash,
"load_skill": lambda **kw: SKILL_LOADER.get_content(kw["name"]),
}模型在 agent loop 中读到"Skills available"列表,需要某个技能时就调用 load_skill(name)。
完整的 Agent Loop(含技能加载)
User --> messages[] --> LLM
|
stop_reason == "tool_use"?
/ \
yes no
| |
dispatch tools return text
- read ↑
- write |
- bash 交互中模型可能:
- load_skill 1. 浏览技能目录
↓ 2. 按需加载技能
append results 3. 遵循技能指令
loop back ----→ 执行任务相对 s04 的变更
| 组件 | 之前 (s04) | 之后 (s05) |
|---|---|---|
| Tools | 5 (基础 + task) | 5 (基础 + load_skill) |
| 系统提示 | 静态字符串 | + 技能描述列表 |
| 知识库 | 无 | skills/*/SKILL.md 文件 |
| 注入方式 | 无 | 两层(系统提示 + result) |
技能 vs 系统提示:为什么两层更好?
| 方面 | 全量系统提示 | 按需技能加载 |
|---|---|---|
| 初始 token | 20,000+(10 技能) | ~1,000(技能目录) |
| 峰值 token | 20,000+ | 3,000 (加载 1-2 技能) |
| 灵活性 | 静态,更新换版本 | 动态,随时添加技能 |
| 模型自主性 | 被动接收 | 主动选择加载时机 |
| 扩展性 | 每加一个技能都增加成本 | 加再多也只消耗目录 token |
⭐ 核心要点
- 模型决定加载什么 — Harness 不预判模型需要什么知识,而是提供"技能目录"让模型自己选择
- 低成本目录,高成本内容 — 技能名称和描述便宜,完整指令仅在需要时加载
- 技能即文件 — 每个技能是一个包含 frontmatter + 内容的
SKILL.md,易于版本管理和协作 - load_skill 就是工具 — 没有任何特殊机制,和 read、write 一样是 agent loop 中的普通工具
- 技能扩展与模型无关 — 添加新技能不需要修改模型或系统提示的核心逻辑
试一试
cd learn-claude-code
python3 agents/s05_skill_loading.py试试这些 prompt(英文 prompt 对 LLM 效果更好,也可以用中文):
What skills are available?Load the agent-builder skill and follow its instructionsI need to do a code review -- load the relevant skill firstBuild an MCP server using the mcp-builder skill
思考题
- 技能描述的粒度应该多细?一个"git"技能够用,还是应该拆成"git-commit"、"git-rebase"、"git-branch"?
- 如果有两个技能的内容互相矛盾,模型会怎么处理?Harness 应该做什么?
- 什么时候应该把知识放进系统提示(总是存在),什么时候应该做成技能(按需加载)?
[[04-subagent|← 上一课:子智能体]] | [[06-context-compact|下一课:上下文压缩 →]] | [[术语表|术语表]]