OpenHuman Skills 子系统完全指南:SKILL.md 发现、作用域解析、信任标记与安全执行
【免费下载链接】openhumanOpenHuman is an open source personal AI for Mac, Windows and Linux — local-first memory, agent orchestration, and deep research.项目地址: https://gitcode.com/GitHub_Trending/op/openhuman
导读
本文以 OpenHuman 仓库中 src/openhuman/skills/README.md 为核心,系统拆解 Skills 子系统的完整能力边界:如何发现并解析 agentskills.io 风格的技能包(一个含 YAML frontmatter 与 Markdown 指令的SKILL.md目录)、User / Project / Legacy 三种作用域如何解析与冲突裁决、信任标记(trust marker)如何门控项目级技能加载、资源读取的安全边界,以及技能如何通过run_skill在隔离 worker 中执行。读完本文,你将掌握技能包目录布局、frontmatter 字段语义、安装/卸载/资源读取的 RPC 面、事件总线触发机制,以及仓库源码中对应的实现与测试位置。
注:当前仓库已把技能领域内部术语从 "skill" 迁移为 "workflow"(如
ops_types.rs中Workflow/WorkflowScope/WORKFLOW.md),但SKILL.md仍是向后兼容的主定义文件,RPC 面也保留skills.*命名,全文按既有公开 API 名称展开。
Skills 子系统的职责边界
根据 README 与 mod.rs 模块文档,Skills 子系统只负责技能的发现、解析、作用域管理、信任标记执行、资源读取与安装/卸载,明确不拥有运行时执行内部机制与通用工具执行(后者属于tools//javascript/)。
- 发现与解析:扫描目录、读取
SKILL.md的 YAML frontmatter 与 Markdown 指令体,见 ops_discover.rs 与 ops_parse.rs。 - 作用域解析:User vs Project vs Legacy(以及后续新增的 Profile 作用域)的优先级裁决。
- 信任标记执行:项目级技能仅在
<workspace>/.openhuman/trust存在时加载。 - 资源读取:读取技能捆绑的子资源(
scripts/、references/、assets/等),并做路径穿越/符号链接/大小/UTF-8 四重防护。 - 安装 / 卸载:基于 HTTPS 的 URL 安装与卸载,见 ops_install_part_01.rs。
技能最终以紧凑的## Installed Skills目录形式暴露给 Agent,并通过run_skill在隔离 worker 中执行——技能正文不再被拼接进聊天轮次。README 明确:"bodies are no longer spliced into chat turns",这是与早期"提示词拼接"式技能实现的关键区别。
从 mod.rs 还可以看到两个重要架构事实:
- 编译期特性门控:
pub mod skills;始终编译(作为 facade),而ops、bus、schemas、registry等行为性子模块由默认开启的skillsCargo feature 门控;关闭时由 stub.rs 提供同签名空实现。 - 类型不参与门控:
types与ops_types在两种 feature 方向下都编译,因为ToolResult/ToolContent被tools::traits再导出为整个 crate 的统一工具结果类型(约 236 个文件消费),Workflow/WorkflowFrontmatter/WorkflowScope也出现在常开的 agent-harness 与提示词签名中。
公开 API 面(Public surface)
README 列出的公开 API 全部可在源码中逐项确认:
| 公开项 | 位置 | 说明 |
|---|---|---|
pub enum SkillScope(现为WorkflowScope) | ops_types.rs | 发现作用域User/Project/Legacy(另有Profile),决定同名冲突时的优先级 |
MAX_SKILL_RESOURCE_BYTES(现为MAX_WORKFLOW_RESOURCE_BYTES = 128 * 1024) | ops_types.rs | 单资源 RPC 载荷上限(128 KB) |
pub use ops::* | mod.rs | 再导出发现、解析、安装、卸载、资源读取与 frontmatter 类型 |
pub struct ToolResult/pub enum ToolContent | types.rs | 技能/工具执行返回的内容块 |
pub mod bus | bus.rs | 在全局事件总线上发出技能事件 |
RPCskills.{skills_list, skills_read_resource, skills_create, skills_install_from_url, skills_uninstall} | schemas/mod.rs 与 controller_schemas.rs | 通过all_skills_controller_schemas/all_skills_registered_controllers注册 |
RPC 控制器全部通过持久化配置层(config::load_config_with_timeout)解析活动工作区,因此 CLI 与 UI 看到的是同一份技能目录,调用方无需手动传入工作区路径。
技能包格式:SKILL.md + frontmatter + 捆绑资源
目录布局与定义文件
一个技能就是一个目录,核心定义文件是SKILL.md:YAML frontmatter(含name、description等必填字段)+ Markdown 指令体。可选捆绑资源位于兄弟子目录中。从 ops_types.rs 的RESOURCE_DIRS可以看到当前支持的资源目录:
scripts/、references/、assets/(传统三个)templates/、examples/、prompts/(Hermes 风格技能常见目录,按可浏览资源处理,不作为可执行运行时入口)
此外 ops_types.rs 定义了向后兼容的文件名常量:
| 常量 | 文件名 | 状态 |
|---|---|---|
WORKFLOW_MD | WORKFLOW.md | 当前主定义文件(create/update 写入) |
WORKFLOW_TOML | workflow.toml | 当前 sidecar 清单(inputs / when_to_use / [github]) |
SKILL_MD | SKILL.md | 旧定义文件,skills→workflows 重命名前编写的技能仍会读取 |
SKILL_TOML | skill.toml | 旧 sidecar 清单,向后兼容读取 |
SKILL_JSON | skill.json | 更早的清单格式,向后兼容读取 |
Frontmatter 字段语义
WorkflowFrontmatter(ops_types.rs)严格对齐 agentskills.io SKILL.md 规范:
| 字段 | 必填 | 说明 |
|---|---|---|
name | 是 | 技能名,也是同名冲突的裁决键 |
description | 是 | 短描述,用于目录摘要 |
license | 否 | 许可证 |
compatibility | 否 | 兼容性说明 |
platforms | 否 | 平台兼容提示(Hermes 风格),缺省表示全平台 |
metadata | 否 | 规范兼容的元数据 map,version、author、tags等非必填字段应放这里 |
allowed-tools | 否 | 技能作者声明的依赖工具(非强制提示,宿主决定暴露什么) |
triggers | 否 | 激活该技能的事件触发模式(见下文事件总线章节) |
extra | 否 | 规范扩展的落点(#[serde(flatten)]);旧式顶层version/author/tags会触发迁移警告 |
解析时对顶层遗留字段的兼容处理可参看 ops_types.rs 的extract_version/extract_author/extract_tags:优先读metadata.*,否则回退到顶层并在warnings中记录弃用提示。
frontmatter 解析实现
ops_parse.rs 的parse_workflow_md/parse_workflow_md_str展示了解析细节:
- 以首行
---开启 frontmatter 块,以第二个---终止;未终止的 frontmatter 块返回None(解析失败)。 - 无 frontmatter 时整文件视为指令体(返回默认
WorkflowFrontmatter)。 - YAML 反序列化失败不致命:记录
frontmatter parse error警告并回退到默认 frontmatter,保证技能仍可被发现。
load_from_workflow_md(同文件后半部分)将SKILL.md组装为完整的Workflow结构,inventory_resources(ops_parse.rs)浅扫技能目录枚举资源——注意它用symlink_metadata拒绝符号链接资源根,且walk_files递归时同样跳过符号链接,防止无限递归与目录外泄漏。
作用域(Scope)解析与冲突裁决
三种作用域 + Profile
README 描述的是三作用域模型,当前源码已扩展为四作用域(ops_types.rs):
| 作用域 | 磁盘位置 | 优先级 |
|---|---|---|
User(默认) | ~/.openhuman/skills/<name>/或~/.agents/skills/<name>/ | 1 |
Project | <workspace>/.openhuman/skills/<name>/或<workspace>/.agents/skills/<name>/,需信任标记 | 2 |
Legacy | <workspace>/skills/<name>/(扁平旧布局) | 0 |
Profile | <workspace>/personalities/<id>/skills/,仅活动 profile 的轮次可见 | 3 |
从源码结构看,Profile 作用域是后来为"按 Agent profile 私有技能"新增的:discover_workflows_with_profile扫描 profile 私有根目录,无需信任标记(目录由 core 管理),且对所属 profile 在同名冲突中拥有最高优先级,会遮蔽同名的全局技能。默认会话与其他 profile 完全不受影响(None与discover_workflows逐字节等价)。
信任标记(Trust Marker)
项目级技能只有在<workspace>/.openhuman/trust存在时才加载。is_workspace_trusted(ops_discover.rs)实现极简:文件内容被忽略,存在即信任。这是关键安全边界——克隆一个陌生仓库后,skills/目录中的项目级技能默认不会进入 Agent 上下文,用户必须显式放置信任标记才会加载。
发现顺序与冲突裁决
discover_filtered(ops_discover.rs)的实现要点:
- 扫描顺序:User 根 → Project 根(仅 trusted 时)→ Legacy
<ws>/skills/→ Profile 根。HashMap以 name 为键,后注册者覆盖先注册者。 - 优先级函数
precedence:Legacy=0 < User=1 < Project=2 < Profile=3。同名冲突时高优先级保留,低优先级技能被丢弃并在保留者的warnings中记录遮蔽原因(如"shadowed Project-scope skill ... at <path>"),这些警告会浮现在目录摘要中供用户调试。 - 稳定性保证:
read_dir顺序未定义,为避免同名兄弟目录跨运行产生非确定性胜者,scan_root_inner先按磁盘目录名排序再处理。 - 排除规则:
EXCLUDED_SKILL_DIRS跳过.git、.github、node_modules、__pycache__、.venv等常见无关目录;点开头的目录名也跳过。 - 符号链接防护:用
file_type()而非path.is_dir()判断——is_dir()会解引用符号链接,可能重新打开目录外加载漏洞;manifest 文件也要求是真实(非符号链接)常规文件。
发现结果按name排序输出。README 提到的 "project-scope wins" 在代码中得到印证,并且冲突警告中同时给出dir_name、name、作用域与磁盘位置,方便定位。
资源读取的安全边界
read_workflow_resource/read_workflow_resource_with_profile(ops_discover.rs)是 README 所述"resource reading"的实现核心,防护层层递进:
- 参数校验:
skill_id与relative_path非空;拒绝绝对路径;拒绝任何..、.、空组件与 Windows 前缀(Component::ParentDir | CurDir | RootDir | Prefix)。 - 技能解析:复用标准发现管线(含信任标记与 profile 根),将读取范围限定在已安装技能集合内。
- 根目录校验:先
canonicalize技能根(要求真实目录,非符号链接)。 - 叶子检查:用
symlink_metadata预先拒绝符号链接与非常规文件(socket/fifo/目录)。 - 大小门禁:
leaf_meta.len() > MAX_WORKFLOW_RESOURCE_BYTES (128 KB)直接拒绝——先看元数据再读文件,避免为超大文件分配缓冲区。 - 穿越校验:
canonicalize完整路径并断言其仍在技能根之内。 - 严格 UTF-8:非 UTF-8 内容直接拒绝(不做 lossy 替换,宁可拒绝二进制文件也不静默损坏)。
resolve_workflow_for_resource(同文件 ops_discover.rs)支持用dir_name(磁盘目录 id)或name(frontmatter 显示名)定位技能,二者歧义时报错,明确要求使用目录 id。
安装 / 卸载:HTTPS URL 安装器
install_workflow_from_url(ops_install_part_01.rs 起)实现了 README 所述的 URL 安装:
核心常量
| 常量 | 值 | 含义 |
|---|---|---|
DEFAULT_INSTALL_TIMEOUT_SECS | 60s | 默认拉取超时 |
MAX_INSTALL_TIMEOUT_SECS | 600s | 调用方可请求的超时上限 |
MAX_INSTALL_URL_LEN | 2048 | URL 长度上限 |
MAX_WORKFLOW_MD_BYTES | 1 MiB | SKILL.md 正文大小上限(防御恶意/错误配置主机流式返回无限响应) |
网络 I/O 前的验证
- URL 必须为
https://;拒绝回环、私网、link-local、组播、共享地址段、localhost及.local/.localhostmDNS 主机名。 github.com/<o>/<r>/blob/<b>/<p>自动改写为raw.githubusercontent.com/<o>/<r>/<b>/<p>,方便用户直接粘贴浏览器地址。- 路径必须以
.md结尾(大小写不敏感);repo/tree URL 与 tarball 被拒绝(unsupported url form:)。 timeout_secs被钳制到MAX_INSTALL_TIMEOUT_SECS。
运行时行为
- 先查
Content-Length头,下载后再校验缓冲长度(双保险,防谎报头)。 - frontmatter 校验:按 agentskills.io 规范要求
name与description必填。 - slug 派生:优先
metadata.id,否则用清洗后的name。若目标目录已有SKILL.md,视为幂等成功(报告"已安装");其他目录冲突保持致命,已有文件绝不静默覆盖。 - 写入原子化:目标目录写
SKILL.md.tmp,成功后rename。 - 安装完成后重新发现完整目录,返回自调用开始新出现的 skill slug 列表(
new_skills)。
安装目的地为<workspace>/.openhuman/skills/<slug>/SKILL.md。README 所在模块的注释解释了设计动因:vercel-labsskillsCLI 写入的 per-agent 目录(./claude-code/skills/、./cursor/skills/等)与 OpenHuman 的技能布局不兼容,因此直接拉取 SKILL.md 并写入发现管线可见的布局。
一个值得注意的运维细节:HTTP localhost 安装默认被拒,需显式设置OPENHUMAN_SKILL_INSTALL_ALLOW_LOCAL_HTTP=1(仅用于本地 fixture)。
事件总线与触发器(bus.rs)
bus.rs 实现了 README 中 "emits skill events on the global event bus" 的承诺,并进一步支撑了WorkflowFrontmatter::triggers字段:
TriggerPattern::parse:将"composio"、"composio/trigger_received"、"cron"、"channel/inbound_message"等原始字符串解析为domain+ 可选event_slug。裸 domain(无/)匹配该域内任何事件;slug 为*也视为匹配整个域。TriggerPattern::matches:先比对event.domain(),slug 限定模式在DomainEvent暴露稳定slug()之前暂不能精确匹配(源码留有TODO(#skills-triggers),实现上保守地避免静默匹配整个域)。TriggeredWorkflowIndex+TriggeredSkillSubscriber:启动时为声明triggers:的技能建立索引并注册订阅者;匹配事件到达时记录应激活的技能。实际 agent-session 启动有意不在此处——它需要完整 harness 上下文(provider、memory、config),由 channel runtime 在总线初始化后接线,本模块只提供类型管道与观察者。
运行时执行:catalog 与 runtime 两个子模块
虽然 README 声明"Does NOT own runtime execution internals",当前仓库已将技能领域进一步组织为两个子模块,理解它们有助于定位源码:
catalog(技能注册表)
catalog/README.md 说明skill_registry负责远程技能目录与已装技能生命周期:
- 拉取并缓存远程目录(默认
https://hermes-agent.nousresearch.com/docs/api/skills.json)。 - 核心启动时异步强制刷新远程目录(
OPENHUMAN_SKILL_REGISTRY_REFRESH_ON_BOOT=0可关闭),不阻塞核心就绪。 - 提供浏览/搜索、派生 Hermes 内置与可选技能的安装 URL、安装到用户技能目录、卸载用户作用域技能,并托管内置
skill_setupagent。
可用环境变量覆盖(用于生产脚本与确定性测试):
OPENHUMAN_SKILL_REGISTRY_CATALOG_URL=https://example.com/skills.json OPENHUMAN_SKILL_REGISTRY_DOWNLOAD_BASE_URL=https://example.com/skills OPENHUMAN_SKILL_REGISTRY_REFRESH_ON_BOOT=0生产冒烟示例:
openhuman skill_registry schemas openhuman skill_registry browse --force_refresh true openhuman skill_registry search --query git openhuman skill_registry sources openhuman skill_registry install --entry_id git-helper openhuman skill_registry uninstall --name git-helperruntime(技能执行)
runtime/README.md 说明skill_runtime拥有已装SKILL.md工作流的执行:
- 启动与取消技能运行、读取近期运行元数据与运行日志。
- 脚本型技能运行前解析可复用的语言运行时:复用
runtime_node(Node.js、npm、npx 与 PATH 注入)与runtime_python(Python 解释器解析与进程启动),复用workflows的发现、元数据、资源与运行日志。 - 托管内置
skill_executoragent。
生产冒烟示例:
openhuman skill_runtime schemas openhuman skill_runtime resolve_runtimes --runtime all openhuman skill_runtime run --skill_id git-helper --inputs '{}' openhuman skill_runtime recent_runs --limit 10兼容性承诺:既有openhuman workflows run、workflows cancel与运行日志 RPC 保持可用;新脚本应优先使用skill_runtime命名空间。
技能即带输入的 Agent
registry.rs 揭示了技能与 Agent 的关系:一个技能就是一个AgentDefinition加上声明的[[inputs]]。agent 字段(id、system_prompt、tools、max_iterations、sandbox_mode等)从同一skill.toml扁平化注入,因此技能本质是"同时广告自己所需输入的、可运行的 Agent"。WorkflowInput(name/description/required/type)在skill_run时取值并渲染进提示词(render_inputs_block)。[github]块(含IdentityMatch枚举:Strict/Any/None)是可选预检门——存在且required = true时,orchestrator 启动前会运行 GitHub 身份一致性预检。
与 Agent 层的交互点
README 的 "Calls into / Called by" 章节说明了技能目录如何进入 Agent 上下文:
## Installed Skills目录渲染:prompt.rs 渲染该章节,数据源是PromptContext上的技能列表(context.rs)。- 每轮注入:turn.rs 是 per-turn 注入点;fork_context.rs 在 fork 上下文时传播注入的技能。
- 提示词章节:mod.rs 与 types.rs 渲染
## Available Skills目录章节。 - 工具结果类型共享:traits.rs 消费
ToolResult/ToolContent。 - 工作区引导:ops.rs 在工作区启动时触碰技能目录布局。
- 集成 agent:integrations_agent/prompt.rs 读取技能目录。
- 控制器注册:all.rs 完成
all_skills_registered_controllers的接线。
测试布局
README 指出本领域没有独立*_tests.rs文件,单测与实现同文件共存(#[cfg(test)] mod tests)。当前仓库结构有所演进,实测测试文件分布为:
- 顶层:ops_tests.rs、ops_types.rs(
types_tests.rs等)、bus_tests.rs、registry_tests.rs、preflight_tests.rs、run_log_tests.rs、tools_tests.rs、schemas_tests.rs。 - 作用域与解析专项:ops_discover_include_skills_tests_tests.rs、ops_discover_profile_scope_tests_tests.rs、ops_create_render_skill_toml_tests_tests.rs、ops_install_install_fetch_tests_tests.rs。
- 端到端:e2e_plumbing_tests.rs 与 e2e_run_tests.rs。
- 跨切面行为:README 指出由 turn_tests.rs 与 runtime_tests.rs 间接覆盖(agent + skill 的组合行为)。
小结:一张图看懂技能生命周期
一个技能从落地到执行,完整经过以下阶段:
- 落地:
skills.install_from_url经 HTTPS 拉取SKILL.md(校验 URL 安全、大小与超时、frontmatter 必填项、slug 冲突),原子写入<workspace>/.openhuman/skills/<slug>/;或由skills.create脚手架新技能,或用户手动放置到~/.openhuman/skills/。 - 发现:启动/轮次时扫描 User / Project(需信任标记)/ Legacy / Profile 根,按优先级吸收同名冲突,产出按 name 排序的目录。
- 暴露:技能以
## Installed Skills紧凑目录进入 Agent 提示词;skills.list/skills.describe(含[[inputs]])供 UI 渲染动态表单。 - 资源:
skills.read_resource以 128 KB 上限 + 路径穿越/符号链接/UTF-8 防护读取捆绑资源。 - 触发:声明
triggers:的技能由事件总线订阅者匹配DomainEvent记录激活。 - 执行:
skill_runtime run/run_skill在隔离 worker 中运行(复用 Node/Python 语言运行时),skills.cancel取消,运行日志经run_log查询。
至此,你已掌握 OpenHuman Skills 子系统的目录规范、frontmatter 语义、四作用域裁决、信任标记门控、资源读取安全边界、URL 安装器的验证链、事件触发机制与运行时架构——这些都可以直接对照 src/openhuman/skills/ 目录下的源码与测试继续深挖。
【免费下载链接】openhumanOpenHuman is an open source personal AI for Mac, Windows and Linux — local-first memory, agent orchestration, and deep research.项目地址: https://gitcode.com/GitHub_Trending/op/openhuman
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考