Learn Claude Code Agent 开发 | 5、按需技能加载:领域知识不用全塞系统提示
整体概述
技能加载核心实现引入了按需技能加载机制,核心思想是:
"用到什么知识, 临时加载什么知识" -- 通过 tool_result 注入, 不塞 system prompt。
Harness 层: 按需知识 -- 模型开口要时才给的领域专长。
这解决了领域知识注入的核心痛点:避免把所有领域规则都塞进系统提示导致token浪费和上下文臃肿,实现了"需要什么知识再加载什么知识"的轻量化知识管理。
解决的核心问题
之前的知识注入方式的缺陷:
- 把所有领域规则(git约定、测试规范、代码审查清单等)全塞进系统提示太浪费token
- 10个技能每个2000token就是20000token,大部分和当前任务毫无关系
- 技能越多系统提示越臃肿,反而会稀释核心指令的权重
核心架构设计
两层注入机制:
┌─────────────────────────────────────────────────┐
│ Layer 1 (低成本,常驻系统提示): │
│ System prompt │
│ +--------------------------------------+ │
│ | You are a coding agent. | │
│ | Skills available: | │
│ | - git: Git workflow helpers | ~100 token/skill
│ | - test: Testing best practices | │
│ +--------------------------------------+ │
└─────────────────────────────────────────────────┘
当模型调用 load_skill("git") 时触发第二层注入:
┌─────────────────────────────────────────────────┐
│ Layer 2 (按需加载,仅在需要时注入): │
│ tool_result │
│ +--------------------------------------+ │
│ | <skill name="git"> | │
│ | Full git workflow instructions... | ~2000 token
│ | Step 1: ... | │
│ | Step 2: ... | │
│ | </skill> | │
│ +--------------------------------------+ │
└─────────────────────────────────────────────────┘
核心创新:
- 分层注入:第一层只在系统提示放技能的名称和描述(非常轻量),第二层需要时才加载完整技能内容
- 技能文件化 :所有技能都以
skills/<技能名>/SKILL.md的形式存储,支持YAML frontmatter元数据 - 完全兼容现有架构:技能加载只是新增一个普通工具,核心循环不需要做任何修改
逐段解析
1. SkillLoader 类 ⭐ 核心组件
python
class SkillLoader:
def __init__(self, skills_dir: Path):
self.skills_dir = skills_dir
self.skills = {}
self._load_all() # 启动时扫描所有技能文件
def _load_all(self):
"""递归扫描skills目录下所有SKILL.md文件"""
if not self.skills_dir.exists():
return
for f in sorted(self.skills_dir.rglob("SKILL.md")):
text = f.read_text()
meta, body = self._parse_frontmatter(text)
name = meta.get("name", f.parent.name) # 优先用frontmatter里的name,没有就用目录名
self.skills[name] = {"meta": meta, "body": body, "path": str(f)}
def _parse_frontmatter(self, text: str) -> tuple:
"""解析YAML frontmatter(---分隔的元数据部分)"""
match = re.match(r"^---\n(.*?)\n---\n(.*)", text, re.DOTALL)
if not match:
return {}, text
meta = {}
for line in match.group(1).strip().splitlines():
if ":" in line:
key, val = line.split(":", 1)
meta[key.strip()] = val.strip()
return meta, match.group(2).strip()
def get_descriptions(self) -> str:
"""Layer 1:生成系统提示里的技能描述列表,非常轻量"""
if not self.skills:
return "(no skills available)"
lines = []
for name, skill in self.skills.items():
desc = skill["meta"].get("description", "No description")
tags = skill["meta"].get("tags", "")
line = f" - {name}: {desc}"
if tags:
line += f" [{tags}]"
lines.append(line)
return "\n".join(lines)
def get_content(self, name: str) -> str:
"""Layer 2:返回完整的技能内容,通过tool_result注入"""
skill = self.skills.get(name)
if not skill:
return f"Error: Unknown skill '{name}'. Available: {', '.join(self.skills.keys())}"
return f"<skill name=\"{name}\">\n{skill['body']}\n</skill>"
设计亮点:
- 零依赖解析:用正则手动解析frontmatter,不需要引入PyYAML等额外依赖
- 自动扫描 :启动时自动递归扫描
skills目录下所有SKILL.md,新增技能只需要加文件,不需要改代码 - 灵活命名:技能名优先用文件里的元数据,没有就用目录名,使用方便
- 标签支持:支持给技能打标签,方便模型理解技能的适用场景
2. 系统提示动态生成
python
# Layer 1: skill metadata injected into system prompt
SYSTEM = f"""You are a coding agent at {WORKDIR}.
Use load_skill to access specialized knowledge before tackling unfamiliar topics.
Skills available:
{SKILL_LOADER.get_descriptions()}"""
- 系统提示里动态注入可用技能的列表,每次启动自动更新
- 明确告诉模型遇到不熟悉的领域时先调用
load_skill加载相关知识 - 这部分非常轻量,每个技能只占几十token,10个技能也才几百token
3. 工具注册
和之前的版本相比,只新增了一个 load_skill工具,完全符合"加工具只加handler"的设计原则:
python
TOOL_HANDLERS = {
# ...其他基础工具不变...
"load_skill": lambda **kw: SKILL_LOADER.get_content(kw["name"]),
}
TOOLS = [
# ...其他基础工具不变...
{"name": "load_skill", "description": "Load specialized knowledge by name.",
"input_schema": {"type": "object", "properties": {"name": {"type": "string", "description": "Skill name to load"}}, "required": ["name"]}},
]
load_skill和其他工具完全平等,通过分发字典注册- 模型可以像调用bash、read_file一样调用这个工具
- 返回的技能内容作为普通的tool_result进入对话上下文
4. 核心智能体循环
和之前版本的循环完全一致,没有做任何修改,完美体现了开闭原则:扩展功能不需要修改核心逻辑。
5. 交互式主循环
和之前版本完全一致,仅提示符做了修改。
版本对比
| 组件 | 子智能体版(s04) | 技能加载版(s05) |
|---|---|---|
| 工具数量 | 5个(基础+task) | 5个(基础+load_skill) |
| 系统提示 | 静态固定字符串 | 动态注入技能描述列表 |
| 知识库 | 无 | skills/*/SKILL.md 文件系统存储 |
| 知识注入方式 | 无 | 两层注入:系统提示(轻量)+ 按需加载(完整) |
| 扩展性 | 新增知识需要改代码 | 新增技能只需要加SKILL.md文件 |
使用示例
bash
python agents/s05_skill_loading.py
s05 >> What skills are available?
> load_skill: [显示所有可用技能列表]
s05 >> I need to do a code review -- load the relevant skill first
> load_skill (code-review): <skill name="code-review">
# 代码审查规范
1. 检查是否有类型标注
2. 检查是否有单元测试
3. 检查是否符合PEP8规范
...
</skill>
s05 >> Load the agent-builder skill and follow its instructions
> load_skill (agent-builder): [加载智能体构建技能的完整内容]
核心设计思想和收获
技能加载机制建立了智能体领域知识管理的核心范式:
1. 按需加载,token效率最大化
不需要的知识永远不加载,10个技能每个2000token,之前需要20000token常驻系统提示,现在只需要几百token的技能列表,节省95%以上的token开销。
2. 知识和代码完全解耦
新增技能不需要修改任何代码,只需要在 skills目录下加一个 SKILL.md文件,运营人员也可以维护技能库,不需要开发参与。
3. 版本化管理方便
技能是纯markdown文件,可以用git做版本管理,支持回滚、diff、审核等工作流。
4. 可扩展性极强
可以支持任意领域的技能:代码审查、git规范、设计规范、安全检查、项目业务规则等等,完全不限制使用场景。
这种设计完美解决了"让智能体遵守特定领域规则"的需求,同时保持了架构的简洁性和扩展性,是生产级智能体系统的必备功能。
本内容参考开源项目 learn-claude-code