| 你需要先具备 | 说明 |
|---|---|
| 读完第 1—6 章 | 会看工具注册、load 类副作用、task 委派即可 |
| YAML/README 常识 | 知道 frontmatter 大概是什么 |
本章导读(你在这)
↓
① 先看一眼一次真实的加载 ← 目录与正文各在哪一轮出现
↓
② 知识管理和知识加载是两件事 ← 构建期快照 vs 每次现读
↓
③ 目录和清单契约 ← frontmatter 五字段
↓
④ 扫描只读 frontmatter ← 预算/启动成本
↓
⑤ load_skill:按名加载 ← name 不是 path;双重校验
↓
⑥ 接入注册表 + 与第 6 章对比 ← 子 Agent 没有目录
↓
⑦ 离线证明 + 运行 + 实验 ← 七~十一
↓
⑧ 小结 ← 三句话 + 八条 + 自测--- 之间的元数据。扫描只解析到第二个 ---。「管理」:哪些技能存在、它们的描述目录——这是启动时生成的构建期快照,改了要重启目录才刷新。「加载」:模型真正要用时把正文读进来——这是每次现读,改了就立刻生效。两级解耦,避免「不该提前带的不要带」。
每个 Skill 目录含一个 SKILL.md,frontmatter 有必需字段:name(唯一)、description(含 Use when / Don't use for 正反例)、version、author、license。目录条目只有 name + description——目录里一个字正文都没有(离线断言 not.toContain(...))。
-----
name: typescript-style
description: Use when writing/checking TypeScript or JS ...
version: 1.0.0
-----
正文:项目约定的完整指南
扫描只解码到第二个 ---,启动成本与正文总量无关——一份 5000 行的 SKILL.md,扫描也只读那十几行元数据。预算按 UTF-8 byte 算,超限丢整条目录条目;被丢弃的 Skill 仍可被 load_skill 直接加载(只是不在目录里)。
模型只能给 name,不能给 path——名字到文件的映射由 Harness 决定。加 name 后的解析:先查目录索引拿相对路径,再 realpath 二次校验,最后读取正文作为 tool result 回到消息流。`../secret`、多余字段、`nul` 三种输入都在碰磁盘前被 schema 拒掉。五种 skill_* 错误码都不含内部文件路径。
load_skill 就是一个普通五工具之外的只读工具,effect=read,不弹审批框但仍进审计(任何最终决定都进审计)。第 6 章的子 Agent 也有 load_skill 但没有目录——所以 task 的 description 要写清 Skill 名,子 Agent 才能找到并加载它。
第 7 章不新增 Agent Loop / Hook / 权限改动,只往注册表加一个只读工具 + bootstrap 提供技能目录。知识技能化是一种「面向组合根的扩展」,副作用路径全被第 3 章权限与硬边界覆盖。
| 测试文件 | 验证内容 |
|---|---|
skills.test.ts | 目录契约、frontmatter 扫描、预算、重名拒绝、load_skill 双重校验、错误码 |
ch07-skills.test.ts | 端到端:目录进 system、正文只在 load 后出现、子 Agent 加载 |
刻意不做:不把正文塞进 system(破坏前缀);不把 Skill 正文自动带进每次请求;不提供 path 参数;重名时直接报错而非静默覆盖。恶意 Skill 即使写「删掉 C:\Windows」,也只是正文文本,加载它不自动执行——执行仍走审批。
请求1 system 里有目录(所有 SKILL 的 name+description,无正文)
→ 模型看到目录,决定调用 load_skill
请求2 model.load(request): 用 name 查出路径,现读正文
工具结果 = 完整正文(SKILL.md 内容回到消息流)
→ 模型基于正文回答
# 注意:第一次请求时,SKILL.md 正文不在上下文里;frontmatter 也不在(只有目录条目)
# 加载正文需要人点"同意"吗?不需要——load_skill 是只读 read 类,不弹审批框
Set-Location code + npm ci。
npm run test:ch07 预期 18 个文件 143 个测试。
check skills 目录。
请求1 只有目录,请求2 才有正文。
description 写清 Skill 名。
npm run test:ch07 # 期望 18 files / 143 tests
# 中间的"配置错误: Missing required settings: ..."是 config.test.ts 断言输出,不是报错
npm run ch07 -- --prompt "先调用 load_skill 加载 typescript-style,再总结其中两条约定"
# 让子 Agent 去加载
npm run agent-tutorial -- run --chapter 7 --prompt "调用 task,让子 Agent 加载 typescript-style 并检查当前项目是否遵守它"
| 现象 | 原因 / 处理 |
|---|---|
| 配置错误出现后直接退出 | code 下 .env 没配。复制 .env.example 并填三个值 |
| 模型不知道有哪些 Skill | 目录在 system;如果没看到,检查 skills 目录是否有合法 SKILL.md |
| load_skill 报 skill_not_found | name 拼错或该技能被预算丢弃;被丢弃的仍可直接按名加载 |
| 重名报错 | 两个目录同 name 直接报错,优于静默覆盖 |
npm run test:ch07 执行 18 个测试文件、143 个测试。四个只读不写的实验:
| 实验 | 预期 | 学到什么 |
|---|---|---|
| 一 · 证明目录里没有正文 检查 catalog 的字节数 | 目录只有 name+description,无正文 | 两级加载:常驻提示 vs 按需正文 |
| 二 · 预算压到 10 bytes 设 maxCatalogBytes=10 | names 里有?catalogEntries 为空;loadSkill 仍可用 | 预算按 UTF-8 byte,超限丢整条 |
| 三 · 制造五种坏 frontmatter 缺字段/坏 YAML/超预算… | 扫描时按不同错误码失败 | 失败时机在构建期,不在模型运行时 |
| 四 · 制造重名 | 直接报错,不静默覆盖 | 让配置错误在组合根就暴露 |
| # | 结论 | 出现在哪一节 |
|---|---|---|
| 1 | 目录里一个字正文都没有,not.toContain 是断言过的 | 先看一眼一次真实的加载 |
| 2 | 目录是构建期快照(改了要重启),正文每次现读(改了立刻生效) | 知识管理和知识加载是两件事 |
| 3 | description 写 Use when / Don't use for,正例反例都要给 | Skill 的目录和清单契约 |
| 4 | 扫描只解码到第二个 ---,启动成本与正文总量无关 | 扫描阶段只读 frontmatter |
| 5 | 预算按 UTF-8 byte,超限丢整条;被丢的 Skill 仍可加载 | 扫描阶段只读 frontmatter |
| 6 | ../secret、多余字段、nul 三种输入都在碰磁盘前被 schema 拒掉 | load_skill:按名称加载正文 |
| 7 | 五种错误码都不含内部文件路径 | load_skill:按名称加载正文 |
| 8 | 子 Agent 有 load_skill 但没有目录——task 的 description 要写清 Skill 名 | 接入现有工具注册表 |