ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

learn-claude-code 技能加载机制详解:用 SKILL.md 与双层注入实现 Agent 知识的按需加载

2026/9/7 14:47:21 拓冰建站 浏览量
learn-claude-code 技能加载机制详解:用 SKILL.md 与双层注入实现 Agent 知识的按需加载 learn-claude-code 技能加载机制详解用 SKILL.md 与双层注入实现 Agent 知识的按需加载【免费下载链接】learn-claude-codeBash is all you need - A nano claude code–like 「agent harness」, built from 0 to 1项目地址: https://gitcode.com/GitHub_Trending/an/learn-claude-code本文围绕 learn-claude-code 课程第 s05 节「Skills技能加载」展开讲解如何用skills/*/SKILL.md目录结构 load_skill工具把领域知识从系统提示词中剥离出来、由模型按需注入到tool_result中。读完后你将能够独立搭建一套「目录即注册、元数据进系统提示、正文按需加载」的技能体系并理解 agents/s05_skill_loading.py 中SkillLoader的完整实现、边界条件与测试验证方式。一、要解决的问题系统提示词不是知识库在编码 Agent 中我们经常希望模型遵守一套领域工作流git 操作规范、测试编写模式、代码评审检查清单、PDF 处理流程等等。最直觉的做法是把它们全部写进系统提示词system prompt但 docs/ja/s05-skill-loading.md 指出的核心矛盾在于系统提示词是每一轮对话都会随请求发送的常驻上下文把所有技能全文放进去等于为大量当前任务用不到的知识持续支付 token 成本——原文给出的估算相当直接10 个技能 × 每技能约 2000 token ≈ 20000 token其中绝大部分对任意给定任务都是无关的。s05 节给出的设计原则是一句话不要把所有东西放进系统提示词而是在需要时加载Load on demand。这一原则在整个 learn-claude-code 的 harness 分层中处于「知识与观察层」的位置模型负责决策harness 负责在正确的时机供给正确的上下文。二、方案双层注入架构Two-layer Injections05 的核心方案是把每个技能拆成「目录信息」和「技能正文」两层分别放在两个成本等级不同的位置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 namegit | || Full git workflow instructions... | ~2000 tokens || Step 1: ... | || /skill | --------------------------------------层位置内容成本生命周期第 1 层系统提示词技能名称 一句话描述每技能约 100 token全程常驻第 2 层tool_result技能正文完整工作流指令按需约 2000 token/次仅当模型调用load_skill后进入消息历史这样模型在每一轮都「知道有哪些技能」低成本只在判断相关时才「读取全文」高成本。第 1 层的描述文本因此承担了类似「路由」的职责——它写得越准确模型越少误触发或漏触发load_skill。三、机制每个技能就是一个含 SKILL.md 的目录3.1 技能目录布局约定每个技能是一个目录目录内必须有SKILL.md目录名即技能标识符skills/ pdf/ SKILL.md # ---\n name: pdf\n description: Process PDF files\n ---\n ... code-review/ SKILL.md # ---\n name: code-review\n description: Review code\n ---\n ...SKILL.md由两部分组成YAML frontmatter位于首尾两条---分隔线之间声明name技能名与description第 1 层使用的描述正文 body分隔线之后的 Markdown 全文即load_skill时注入的完整领域知识。3.2 仓库内置的 4 个真实技能当前仓库 skills/ 目录下有 4 个可直接运行的示例技能它们就是 s05 实验的数据源技能目录frontmatterdescription节选正文要点skills/pdf/SKILL.mdProcess PDF files - extract text, create PDFs, merge documentspdftotext/PyMuPDF 读取、pandoc/ReportLab/wkhtmltopdf 生成、PyMuPDF 合并与拆分以及一张「任务 → 库 → 安装命令」对照表skills/code-review/SKILL.mdPerform thorough code reviews with security, performance, and maintainability analysis五维检查清单Security/Correctness/Performance/Maintainability/Testing、固定输出格式模板、Python/JS 反例模式、npm audit、radon cc等评审命令与 7 步评审工作流skills/mcp-builder/SKILL.mdBuild MCP (Model Context Protocol) servers that give Claude new capabilitiesMCP 概念Tools/Resources/Prompts、Python MCP server 模板、stdio 接入方式skills/agent-builder/SKILL.mdDesign and build AI agents for any domain多行 YAML 块标量Agent 三要素Capabilities/Knowledge/Context、agent loop 心法并附带references/与scripts/子目录两个值得注意的实现细节agent-builder是多行 description 的实例。它的 frontmatter 使用 YAML 块标量description: |后跟多行缩进文本说明第 1 层描述并不限于单行短语测试 tests/test_skill_loading.py 也专门覆盖了块标量内含---行、以及 CRLF 换行的解析正确性。子目录不产生独立技能。agent-builder/references/如 agent-philosophy.md、minimal-agent.py和agent-builder/scripts/init_agent.py不会被注册成技能——技能名以顶层目录名/frontmattername为准这些附属文件的设计意图是模型加载正文后再用read_file/bash自行深入读取相当于「正文里引用的二级知识库」。3.3 SkillLoader扫描、解析、双接口s05 源码 agents/s05_skill_loading.py 中SkillLoader在启动时一次性完成「扫描 解析」之后对外只提供两个廉价接口class SkillLoader: def __init__(self, skills_dir: Path): self.skills_dir skills_dir self.skills {} self._load_all() def _load_all(self): 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) self.skills[name] {meta: meta, body: body, path: str(f)} def _parse_frontmatter(self, text: str) - tuple: Parse YAML frontmatter between --- delimiters. match re.match(r^---\n(.*?)\n---\n(.*), text, re.DOTALL) if not match: return {}, text try: meta yaml.safe_load(match.group(1)) or {} except yaml.YAMLError: meta {} return meta, match.group(2).strip() def get_descriptions(self) - str: Layer 1: short descriptions for the system prompt. 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: full skill body returned in tool_result. skill self.skills.get(name) if not skill: return fError: Unknown skill {name}. Available: {, .join(self.skills.keys())} return fskill name\{name}\\n{skill[body]}\n/skill实现要点逐条拆解rglob(SKILL.md)递归扫描L68只要目录名叫SKILL.md就会被发现因此技能目录可以嵌套sorted(...)保证加载顺序稳定。技能名优先取 frontmatter 的name字段缺省时回退为父目录名f.parent.name这就是「目录名即技能标识」约定的实现落点。frontmatter 解析的容错L74-L83正则要求首行恰好是---、用非贪婪(.*?)匹配到下一条独立---行没有匹配则返回({}, 原文)——即「无 frontmatter 的文件整体当作正文」yaml.safe_load抛YAMLError时静默降级为空元数据。这套降级路径不是臆测测试 tests/test_skill_loading.py 显式断言了---not frontmatter这类不合法开头不触发解析、块标量中夹带的---行不会被误认为结束符。get_descriptions()就是第 1 层L85-L97每技能输出一行- {name}: {desc}源码还支持可选的tags字段追加[tags]后缀。注意 s05 版与 s07 重构版的行为差异s07 的SkillLoaders07_skill_loading/code.py把元数据解析改成了逐行扫描的parse_frontmatter静态方法并增加了「非 dict 的 YAML如列表回退为{}、description 为空时回退取正文首行」等防御逻辑——从源码结构看这是 s05 之后针对更恶劣输入补强的版本。get_content()就是第 2 层L99-L104命中时把正文包进skill name...XML 风格标签再返回——标签给模型一个明确的知识边界标记告诉它「从这里开始是某个技能的全部指令结束于/skill」。未命中时返回的错误信息会附带全部可用技能名这让模型可以在一次纠错循环内自修复拼写错误而不是盲目重试。3.4 注入点系统提示词 工具注册两个注入点各只有一行胶水代码。第 1 层在模块加载时拼进SYSTEM常量agents/s05_skill_loading.py#L107-L114SKILL_LOADER SkillLoader(SKILLS_DIR) # Layer 1: skill metadata injected into system prompt SYSTEM fYou are a coding agent at {WORKDIR}. Use load_skill to access specialized knowledge before tackling unfamiliar topics. Skills available: {SKILL_LOADER.get_descriptions()}第 2 层则是一个普通工具处理器与bash/read_file等基础工具并列注册L166-L184TOOL_HANDLERS { bash: lambda **kw: run_bash(kw[command]), read_file: lambda **kw: run_read(kw[path], kw.get(limit)), write_file: lambda **kw: run_write(kw[path], kw[content]), edit_file: lambda **kw: run_edit(kw[path], kw[old_text], kw[new_text]), load_skill: lambda **kw: SKILL_LOADER.get_content(kw[name]), } TOOLS [ # ...base 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的input_schema只有一个必填的name字符串参数——接口刻意做到极简。agent_loopL188-L208中当模型返回stop_reason tool_use且块为load_skill时get_content的返回值被原样塞进tool_result并回传——至此完整技能正文已作为一条消息进入历史模型随后基于它行动而系统提示词始终只有那一两行目录。四、相对 s04 的变化s05 在 s04hooks基础上只做了三处增量原变更对照表如下ComponentBefore (s04)After (s05)Tools5 (base task)5 (base load_skill)System promptStatic string skill descriptionsKnowledgeNoneskills/*/SKILL.mdfilesInjectionNoneTwo-layer (system result)也就是说s05 的改造面非常克制工具数量不变基础工具集 一个知识工具变化集中在「系统提示词从静态字符串变为目录驱动的模板」和「新增了skills/文件作为外部知识源」两点。五、动手实验5.1 环境准备s05 脚本的运行依赖从 agents/s05_skill_loading.py 的导入与环境变量读取确认Python 包anthropic、python-dotenvload_dotenv、pyyamlyaml.safe_load仓库根目录 requirements.txt 提供项目统一依赖环境变量MODEL_ID必填代码中os.environ[MODEL_ID]缺省即KeyError、ANTHROPIC_BASE_URL可选指向自定义网关注意代码在设置了ANTHROPIC_BASE_URL时会主动pop掉ANTHROPIC_AUTH_TOKEN避免认证头冲突见 L49-L50.env文件会被load_dotenv(overrideTrue)优先加载。5.2 运行cd learn-claude-code python agents/s05_skill_loading.py启动后出现青色s05 交互提示符。仓库文档docs/ja/s05-skill-loading.md「試してみる」小节给出的 4 条验证提示正好覆盖双层机制的不同路径What skills are available?—— 验证第 1 层模型不加载任何正文仅凭系统提示词中的目录作答Load the agent-builder skill and follow its instructions—— 直接指令式触发load_skill(agent-builder)观察终端打印的 load_skill:输出I need to do a code review -- load the relevant skill first—— 验证隐式路由模型需要自行把「code review」意图映射到code-review技能名Build an MCP server using the mcp-builder skill—— 技能正文驱动后续多轮工具调用写文件、执行命令验证第 2 层知识能否真正改变模型行为。观察要点每条工具调用都会打印 {tool}:前缀和输出的前 200 字符L205-L206据此可以确认load_skill恰好在预期轮次发生、且全文仅进入消息历史一次。5.3 测试用例给出的行为边界tests/test_skill_loading.py 针对技能加载课程代码s07 与 s15 集成版断言了 s05 同款语义可作为自建技能体系时的验收清单目录小、正文大test_catalog_stays_small...SYSTEM中只出现- code-review: Review code for bugs, regressions, and missing tests.一行断言完整指令UNIQUE_FULL_INSTRUCTION不在SYSTEM里而load(code-review)返回的是整个SKILL.md原文工具面收敛L96-L107TOOLS只暴露bash / read_file / write_file / edit_file / glob / load_skill六个工具技能不引入额外工具frontmatter 边界L110-L128非独立行的---开头不解析块标量|中含---内容不被截断CRLF 换行同样成立回退与安全性L131-L164name/description为空时回退取正文首行作描述YAML 解析成非 mapping如列表时回退空元数据SKILL.md是符号链接且指向skills/外部文件时拒绝注册——这是 s07 版在scan()中manifest.resolve().is_relative_to(skills_root)检查s07_skill_loading/code.py#L87-L91对应的防御防止目录投毒把任意文件注册为技能。自建技能时的三条实用建议均由上述源码直接导出description 要写「何时该用」对照仓库 4 个内置技能pdf与code-review的 description 都带触发条件Use when user asks to...agent-builder甚至列出了 5 类触发场景 关键词这正是第 1 层路由质量的来源正文开头给结论、中间给命令内置技能普遍是「工作流 → 可复制命令/代码 → 对照表 → 最佳实践」的顺序方便模型加载后立即执行不要让正文依赖系统提示词load_skill注入的正文是一段tool_result技能之间不共享上下文每个SKILL.md必须自包含。六、小结这套机制为什么值得抄进自己的 Agents05 的完整实现不超过 250 行agents/s05_skill_loading.py其可迁移的设计是知识外置skills/*/SKILL.md让领域知识成为可版本化、可 diff、可增量添加的文件而非散落的提示词成本分层系统提示词只付「目录税」每技能约 100 token全文只在命中时付一次失败自愈未知技能名返回错误时附带可用清单一次纠错即可收敛接口最小化load_skill(name)一个参数正文用skill name...标签包裹边界清晰。与后续章节的关系s07s07_skill_loading/code.py把SkillLoader重构得更健壮并接入权限 hookss15s15_integrated_harness/code.py将其并入完整 harness而 agents/s_full.py 是全部机制的合体版。理解 s05 的「双层注入」后再看这些版本只会是同一思想在边界条件上的加固。【免费下载链接】learn-claude-codeBash is all you need - A nano claude code–like 「agent harness」, built from 0 to 1项目地址: https://gitcode.com/GitHub_Trending/an/learn-claude-code创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考