ByteNoteByteNote
渐进式披露:Skills 怎么组织 Agent 能力
字

字节笔记本

2026年10月24日 · 约 11 分钟读完

渐进式披露:Skills 怎么组织 Agent 能力

API中转
¥120

Skills:扩展 Claude 的能力

本文是《从 0 写 Agent》系列第 30 章。这个系列基于开源项目 agents-go(OpenAI Agents SDK 的 Go 移植版),用 Go 从零实现一个生产级 Agent 运行时;本章讲「Skills:渐进式披露的 Agent 能力」。

你给 Agent 写了很多"工作方法",怎么写 PR、怎么做 code review、怎么生成 PPT。这些方法如果全塞进 prompt,上下文会爆炸。

Skills 用"渐进式披露"解决,只把索引放进 prompt,让模型按需读全文。这一章拆 agents-go 的 skills 包,以及它怎么用 Go 1.24 的 os.Root 做安全的文件读取。


引子:上下文窗口的预算管理

Agent 的 prompt 有预算,上下文窗口有限(即使百万 token 也很贵)。如果把所有"工作方法"全塞进 instructions:

text
你的工作方法:
  写 PR 的方法:...(500 字)
  Code review 的方法:...(800 字)
  生成 PPT 的方法:...(600 字)
  生成视频的方法:...(700 字)
  ...还有 20 个方法

几万字塞进去,token 爆炸,而且大部分用不上,用户这次只让你写 PR,其他 20 个方法是浪费。

渐进式披露的思路,只放索引,按需读取:

text
你可以使用以下技能(按需调用 read_skill_file 读取详细说明):
  - write_pr: 写 Pull Request
  - code_review: 代码审查
  - make_ppt: 生成演示文稿
  - make_video: 生成视频
  ...

用户:"帮我写个 PR"
模型:调用 read_skill_file("write_pr/SKILL.md") → 读取详细方法 → 按方法执行

只在实际需要时,才把那个 skill 的全文读进上下文。 这就是"渐进式披露",索引常驻,详情按需。


MCP:外部工具标准

SKILL.md 格式

agents-go 实现的是开放格式 agentskills/agentskills。每个 skill 是一个目录,里面有 SKILL.md:

text
skills/
├── write_pr/
│   ├── SKILL.md          # 技能说明(yaml frontmatter + markdown)
│   ├── template.md       # 参考资料
│   └── checklist.md      # 检查清单
├── code_review/
│   └── SKILL.md
└── make_ppt/
    └── SKILL.md

SKILL.md 的格式(skills.go 的 frontmatter):

yaml
---
name: write_pr
description: 写一个规范的 Pull Request
compatibility: 兼容所有支持 function calling 的模型
---

# 写 PR 的方法

## 步骤
1. 理解改动...
2. 写描述...

YAML frontmatter(元数据)+ Markdown 正文(详细方法)。 frontmatter 用于生成索引,正文按需读取。


源码对照:Load 和 RenderIndex

Load,扫描 skill 目录

Load(skills.go:68)扫描目录下的 SKILL.md:

go
// 概念示意
func Load(dir string) ([]Skill, error) {
    entries, _ := os.ReadDir(dir)
    for _, entry := range entries {
        skillFile := filepath.Join(dir, entry.Name(), "SKILL.md")
        if !exists(skillFile) { continue }  // 没有 SKILL.md 跳过
        skill := parseFrontmatter(skillFile)  // 解析 yaml
        validate(skill)  // 名字必须匹配目录名,长度限制
    }
}

命名规则,skill 的 name 必须匹配目录名(skills.go:126 的 validate)。而且 name 只能是小写字母+数字+单连字符(nameRe,skills.go:38)。严格的命名规则防止路径问题。

RenderIndex,生成发现段

RenderIndex(skills.go:152)生成放进 prompt 的索引:

text
你可以使用以下技能。需要时调用 read_skill_file 读取详细说明:

- write_pr: 写一个规范的 Pull Request
- code_review: 代码审查
- make_ppt: 生成演示文稿

注意:只在需要时读取相关技能文件,不要一次读取所有,不跨轮携带。

这个索引很短(每个 skill 一行),常驻 prompt。详细方法通过 read_skill_file 工具按需读取。


源码对照:ReadFileTool 与 os.Root

ReadFileTool(skills.go:179)是暴露给模型的工具,读 skill 文件:

go
// skills.go:179(骨架)
func ReadFileTool(root *os.Root) agents.Tool {
    return agents.NewFunctionTool("read_skill_file", "读取技能文件", func(ctx, tc, args) (any, error) {
        data, err := root.ReadFile(args.Path)  // os.Root 限制的读取
        if len(data) > maxSkillFileBytes {  // 256KiB 封顶
            data = data[:maxSkillFileBytes]
        }
        return string(data), nil
    })
}

os.Root,目录囚禁

注意 root *os.Root,这是 Go 1.24 的新特性,"rooted I/O"。它把文件操作限制在一个目录内:

go
root, _ := os.OpenRoot("skills/")  // root 只能访问 skills/ 下
root.ReadFile("write_pr/SKILL.md")  // ✅ 可以
root.ReadFile("../../etc/passwd")   // ❌ 被拒!
root.ReadFile("write_pr/../../../etc/passwd")  // ❌ 路径逃逸被防

为什么用 os.Root 而不是手动检查路径? 因为手动检查(strings.Contains(path, ".."))容易漏,symlink、URL 编码、Windows 路径分隔符……各种绕过方式。os.Root 在内核层面限制,无法逃逸。

第 31 章详讲 os.Root 的安全性。

256KiB 封顶

go
const maxSkillFileBytes = 256 * 1024  // 256KiB

每个 skill 文件最多读 256KiB。 防止模型读到超大文件(比如有人往 skill 目录放了 100MB 的东西)撑爆上下文。这是防御性编程。


设计权衡:渐进式披露 vs 全塞进 prompt

方式prompt 占用准确性适合
全塞 prompt大(几万字)高(模型一直看得到)skill 少(1-3 个)
渐进式披露小(索引几百字)中(按需读,可能忘)skill 多(10+)

渐进式披露的代价,模型可能"忘记"读 skill。它看到索引,但没主动调用 read_skill_file,就直接凭记忆干活。解法,在 instructions 里强调"执行 X 任务前,必须先 read_skill_file"。

经验法则,skill 少(<5)全塞 prompt;skill 多(>5)用渐进式披露。


小结

读完这一章,你应该理解:

  1. 渐进式披露,索引常驻 prompt,详情按需读取,省上下文
  2. SKILL.md 格式,yaml frontmatter + markdown,开放标准
  3. Load + RenderIndex,扫描目录生成索引
  4. ReadFileTool,模型调它读 skill 全文
  5. os.Root 目录囚禁,Go 1.24 新特性,内核级路径限制
  6. 256KiB 封顶,防超大文件撑爆上下文

Skills 是 Agent 能力的模块化方式,像给 Agent 装"插件"。 下一章深入 os.Root 的安全性。


下一篇

第 31 章,os.Root:Go 1.24 的目录囚禁。为什么传统 path 清洗不够?os.OpenRoot 怎么防 ../ 和 symlink 逃逸?这是 Go 安全编程的新工具。


本文是《从 0 写 Agent》系列第 30 章,案例代码基于开源项目 agents-go(OpenAI Agents SDK 的 Go 移植版)。全系列目录见总览。

相关文章

分享: