Skip to content

Skill manifest

外部技能是子进程可执行文件,由放在 manager 一个白名单目录下的 skill.json 描述。加载器启动时走这些目录,解析每份 skill.json,在全局技能 registry 里注册一个 SubprocessSkill。LLM 然后看到这个技能跟内置工具并列。

真理之源:internal/skill/loader.go

磁盘布局

text
/etc/ongrid/skills/                  ← one entry in $ONGRID_SKILLS_EXTERNAL_DIRS
└── disk-cleaner/
    ├── skill.json
    └── run.sh                       ← the executable

每份 skill.json 在自己的子目录顶部。目录树递归走;任何叫 skill.json 的 文件被当 manifest 处理。多个技能可以住在同一个白名单根下。

manifest schema

json
{
  "name": "disk_cleaner",
  "description": "Free up disk by clearing stale build caches and journals.",
  "schema": {
    "type": "object",
    "properties": {
      "path":   { "type": "string", "description": "Root path to clean." },
      "dry_run": { "type": "boolean", "default": true }
    },
    "required": ["path"]
  },
  "entry": "run.sh",
  "env_allow": ["PATH", "HOME"],
  "timeout_seconds": 60,
  "class": "mutating",
  "category": "ops"
}
字段类型必填描述
namestring技能 key。必须是匹配 [a-z0-9_]+lower_snake。成为 LLM 面的函数名。
descriptionstring显示给人类(UI)和给 LLM(函数描述)。
schemaJSON Schema参数对象的裸 JSON Schema。缺失 = 空对象 schema。
entrystring可执行文件路径。相对路径相对于含 skill.json 的目录解析。绝对路径必须在白名单根下。
env_allowstring[]转给子进程的 env 变量名显式列表。空列表 = 没有 env(连 PATH 都没)。加 "PATH" opt in PATH。
timeout_secondsint子进程超时。零回退到 DefaultSubprocessTimeout(30s)。
classenumsafe(默认)、mutatingdangerous。见 class 分类
categorystring自由分组 label。默认 external。UI 按这个对子进程技能分组。

协议

子进程被调用时stdin = JSON 参数对象stdout = JSON 结果对象stderr = 给 manager 的日志行

text
$ cat /tmp/args.json
{"path": "/var/cache", "dry_run": true}

$ run.sh < /tmp/args.json
{"freed_bytes": 1048576, "files_deleted": 17}

非零退出码被当失败处理。stderr 被捕获到 tool 调用事件 timeline 里,LLM 能 读到部分进展。

结果对象逐字返给 LLM。agent kernel 把它格式化成 tool 调用的响应;LLM 被 期望对 JSON 形状推理。

class 分类

同样的 {safe, mutating, dangerous} 分类适用于原生技能和子进程技能。

Class能做什么需要的 persona 权限
safe只读 —— 不影响 host 或任何系统。read-only(任何 persona)
mutating创建 / 更新状态。可逆。mutating-with-confirmdual-sign-required
dangerous不可逆(删、重启、执行任意命令)。dual-sign-required(SOP)

persona 的 permission_mode 字段卡哪些 class 能不确认就跑。见 Agent persona 格式

白名单规则

运维通过 ONGRID_SKILLS_EXTERNAL_DIRS(冒号或逗号分隔的绝对路径)配白名 单目录。加载器严格执行:

  • 每个目录必须是绝对路径。相对路径跳过并打日志。
  • 不存在的路径跳过(新装没 /etc/ongrid/skills 也能干净启动)。
  • 每个 manifest 的 entryfilepath.EvalSymlinks 规范化,并检查住在 白名单根下。指出根的符号链接被拒(entry %s escapes allowlist root %s)。

超出这个的沙箱化是子进程自己的责任。技能需要严格封闭就用 wrapper(bwrapfirejailnsenter)作为 entry 引用。

加载器行为

text
LoadDirs(cfg)
  for each dir in cfg.Dirs:
    filepath.Walk(dir)
      for each skill.json found:
        parseManifest(path)
        buildSubprocessSkill(manifest, path, root)
        if skill already registered: skip (log line)
        else Register(skill)
  • 按 manifest 的校验失败记日志并跳过。一个坏包不该阻止启动。
  • 重名跳过而不是报错(让你在删旧 manifest 前放新 manifest 的重部署不会让 manager 崩)。
  • loader 返回成功注册的技能数。manager 启动对技能 loader 非阻塞:空的外部 目录 no-op。

启动时日志

manager 每个 manifest 打一行:

text
skill loader: registered subprocess skill "disk_cleaner" from /etc/ongrid/skills/disk-cleaner/skill.json
skill loader: skill "broken_one" already registered, skipping /etc/ongrid/skills/broken-one/skill.json
skill loader: parse /etc/ongrid/skills/typo/skill.json: invalid character ',' looking for beginning of object key
skill loader: build "bad_path": entry /tmp/escape.sh escapes allowlist root /etc/ongrid/skills

journalctl -u docker-compose@ongriddocker compose logs ongrid 确 认捡到了什么。

另见

原生技能 metadata(SKILL.md frontmatter)

./skills/ 下发的内置技能用更丰富的 YAML frontmatter SKILL.md 格式, 跟 openclaw / claude-code 技能生态互操作。schema 在 internal/manager/biz/aiops/chatruntime/types.go 定义:

yaml
---
name: query_promql
description: Run a PromQL query and return the result matrix.
when_to_use: When the user asks for current or recent metric values.
activation:
  mode: always
metadata:
  os: [linux, darwin]
  requires:
    bins: []
    config: []
  ongrid:
    scope: manager
    activation:
      mode: always
tools:
  - name: query_promql
    impl: builtin:prom.QueryPromQL
    class: read
    description: Execute a PromQL query and return the matrix.
---

# query_promql

PromQL query tool ...

第三方写的子进程技能优先用上面更简单的 skill.json 格式。SKILL.md 给编进 manager 二进制的技能用。