Dawn-AI Skill 系统架构

基于 Progressive Disclosure 的 Agent Skill 加载与管理体系

01 · Skill 来源
● Builtin

内置 Skill(Classpath)

classpath:skills-builtin/{name}/SKILL.md

打包进 JAR 的兜底示例,随应用发布。如 code-review-zh。Builtin 先扫描,作为默认填充。

● External

外挂 Skill(Filesystem)

${app.skills.path}/{name}/SKILL.md

运维外挂目录,默认 ./skills。External 后扫描,同名覆盖 Builtin,实现无需重新发布的定制。

扫描顺序:BUILTIN 先扫 → EXTERNAL 后扫 → 同名覆盖
SkillLoader 解析
02 · 注册中心(核心)
CORE

SkillRegistry

发现与注册
@PostConstruct 初始化扫描 Builtin + External 两条路径
线程安全
内部 Map 为不可变引用,refresh() 整体替换,无锁读取
错误隔离
单个 SKILL.md 解析失败仅 ERROR 跳过,不阻塞启动
list() get(name) readResource(name, path) listResources(name) refresh()

SkillProperties

Spring Boot 配置属性
app.skills.path — 外挂目录
app.skills.enabled — 总开关

SkillsActuatorEndpoint

热加载入口。仅新 Session 见到最新清单,老 Session 的 System Prompt 已固化。
POST /actuator/skills
AgentOrchestrator.formatSkills()
03 · Progressive Disclosure(渐进式加载)
LAYER 0 System Prompt 注入清单

AgentOrchestrator 构建 System Prompt 时,仅将所有 Skill 的 name + description 注入。模型看到清单后自主判断是否需要深入。

## 可用 Skills
- code-review-zh: 中文 Java 代码 review...
- grill-me: Interview the user relentlessly...
LAYER 1 按需加载完整指令

模型判断某 Skill 匹配当前任务时,调用工具获取 SKILL.md 正文(body)+ 可用子文件列表(availableResources)。

load_skill(name="code-review-zh")
→ { content: "# Code Review...", availableResources: ["references/checklist.md"] }
🔧 LoadSkillTool
LAYER 2 深入读取子资源

模型需要更细节的参考资料时,调用工具读取 Skill 目录下的子文件。含路径穿越防御 + source-aware 读取策略。

read_skill_resource(skill="code-review-zh", path="references/checklist.md")
→ { content: "# Review Checklist\n1. 空指针检查..." }
🔧 ReadSkillResourceTool
04 · 数据模型
Skill RECORD
manifest frontmatter 元信息 SkillManifest
body SKILL.md 正文(去 frontmatter) String
resourceDir 资源根目录(EXTERNAL 有值) Path?
source BUILTIN | EXTERNAL Source
SkillManifest RECORD
name kebab-case, 1-64 字符 String
description 用途描述, ≤1024 字符 String
extras 保留字段(未消费) Map<K,V>

目录结构

skills/ ← app.skills.path
  code-review-zh/
    SKILL.md ← frontmatter(name/desc) + 正文指令
    references/
      checklist.md ← Layer 2 按需加载
  grill-me/
    SKILL.md
05 · 校验规则(SkillLoader)
Frontmatter
必须含完整 --- ... --- 包裹的 YAML 块
Name
必填,正则 ^[a-z][a-z0-9-]{0,63}$(kebab-case)
Description
必填,超 1024 字符截断 + WARN,不视为致命错误
安全
readResource 含路径穿越防御:toRealPath() + startsWith 校验
隔离
单个 Skill 解析失败仅 ERROR 日志 + 跳过,不阻塞应用启动
热加载
POST /actuator/skills 触发全量 refresh,仅新 Session 生效