Agent Platform · Detail Design

Skill 版本治理设计

把 Skill 从「可调用的工具」升级为「可治理的资产」:manifest 锁定、semver 约束、Run 级 hash 绑定、灰度发布与 fallback 声明。

返回文档目录平台首页

Manifest 锁定

published 后 manifest 不可静默修改:要改就发新版本,published 即契约

Run 级 hash 绑定

Run 启动时计算 manifest_hash 写入 RunContext,整个 Run 不变;半路换版本不影响当前 Run

灰度 + Fallback

三态生命周期 + 比例灰度 + manifest 内声明 fallback_skill,主 Skill 熔断时切备

详细设计说明

DESIGN DOCUMENT · SKILL VERSIONING

让 Skill 升级不再「把跑着的 Run 搞挂」

这份文档不讨论 Skill SDK 的实现细节,只讨论 Skill 作为可治理资产的版本生命周期:manifest 怎么写、什么算破坏性升级、Run 启动时如何锁住 hash、灰度发布如何切回、主 Skill 熔断时如何 fallback。评审时请关注:每一次工具调用是否都能定位到具体 manifest_hash、具体灰度环、具体责任人。

设计主张

Skill 版本治理的核心不是「支持多版本号」,而是让企业知道:当前 Run 用的是哪个 Skill 的哪个 manifest_hash、为什么允许使用、出问题时能不能精准回滚到上一稳定版。

Manifest 不可变Semver 强约束Run 级 hash 锁定灰度三态Fallback 声明

评审关注点

  • published 后 manifest 是否真的不可静默修改
  • Run 中途版本变化是否会污染当前 Run
  • 什么样的 schema 改动算破坏性
  • 主 Skill 熔断时 fallback 链路是否兜底
D1版本即契约Run 绑定具体 manifest_hash
D2发布不可变修改必须发新版本
D3升级显式不吃 latest,需人工确认
D4灰度可回异常版本秒级切回
D5Fallback 兜底主 Skill 熔断切备
状态:v1 草案
日期:2026-05-07
定位:Skill Hub 在 Tier 1 提供 manifest 注册 + 灰度配置 UI;Tier 2 在派 Run 时选定具体版本;Tier 3 在 Run 启动时锁定 manifest_hash 并写入 Trace
关联:与 harness-kernel-design(Run 级 hash 绑定与 Trace 留痕)、governance-hitl-design(破坏性升级触发 HITL)、agent-teams-design(团队级 Skill 包)配套
目标:让 Skill 从 draft 到 deprecated 的全生命周期可灰度、可回滚、可追溯,破坏性升级不再悄无声息地把生产 Run 搞挂

1. 定位与概念

SECTION GOAL

一张图看清 Skill Hub 在三层架构里的位置,两张时序图分别展示「Run 启动锁 hash」与「灰度发布出问题切回」两个最关键的工程场景。

1.1 总体架构图

下图把 Skill Hub 的静态结构(manifest 注册 + 版本仓 + 灰度环 + Run 级 hash 关联)与三层定位叠加:横向 3 个 tier 带,T1 区是 manifest 注册 + 灰度配置 UI,T2 区把"选 Skill 版本"的决策落实,T3 区在 Run 启动时锁定 manifest_hash 并贯穿整个 Run。

T1 · Skill Hub 注册 + 灰度配置 REGISTRY UI SkillManifest id · schema · perm · 副作用 SkillVersion ★ semver · manifest_hash · 状态 RolloutConfig 灰度比例 · 白名单 · 时间窗 FallbackChain manifest 内声明备用 Skill 1:n 1:1 声明 LIFECYCLE · 三态 published → deprecated → disabled 紧急下架可逆转 ★ SkillVersion 是 published 后不可修改的契约实体;要改就发新版本,hash 随之变化 RolloutConfig 与 FallbackChain 不进入 hash —— 它们是治理面,不影响 Skill 行为契约 T2 · Runtime 选 Skill 版本 VERSION RESOLVER ① 解析灰度环 读 Agent 启用版本 + 灰度命中 未命中走老版本,命中走新版本 不允许吃 latest ② 兼容性自动检 schema diff + perm diff 破坏性变更强制 major 不通过禁止 publish ③ Fallback 决策 主 Skill 熔断 → 切备 读 manifest.fallback_skill 连环切换上限 1 次 ④ 影响面查询 disable / 升级前必查 —— 哪些 Agent / Run 在用 + 是否有可用 fallback;admin UI 强制弹确认 T3 · Run Kernel 锁 hash · 写 Trace RUNCONTEXT BIND ⑤ Run 启动锁定 RunContext.skill_versions = { skill_id → manifest_hash } 整个 Run 不变;半路 disable 不影响当前 Run Run 失败重启时 hash 重新计算 ⑥ Trace 留痕 每条 ToolInvocation 必带 manifest_hash 事后定位「那次调用用的哪个 hash」 灰度命中标记进 AuditLog
关键约束:manifest_hash 是 Skill 的不可变指纹,published 后随 manifest 一起冻结;灰度配置不影响 hash —— 它是治理面,可以随时改;Run 级锁定把"用哪个版本"的决策提前到 Run 启动那一刻,整个 Run 期间不再变化。

1.2 两个关键场景时序图

下面 2 张时序图分别可视化「Run 启动锁 hash」和「灰度发布出问题切回」两个最关键场景。点击 lifeline 头部色块跳到下方对应章节。

Scenario A · Skill 加载 + 版本锁定

Run 启动那一刻锁 manifest_hash,整个 Run 不变;中途即使 admin disable 了该版本,当前 Run 也能跑完。

CallerUSER / AGENT T1 · Skill HubMANIFEST REGISTRY T2 · Resolver灰度 · 选版本 T3 · Run Kernel锁 manifest_hash Trace · AuditPERSIST — PHASE A · RUN 启动:解析 + 锁定 — 1 触发 Agent Run(Agent 已启用 SkillX@1.4.x) 2 resolve(skill_id, agent.policy) 3 查 manifest + rollout v1.4.2 · hash:abc123 4 ↻ user 命中 10% 灰度环 → 选 v1.5.0-rc 5 返回 { v1.5.0-rc, hash:def456 } 6 ↻ RunContext.skill_versions[X]=def456 — PHASE B · TOOL 调用 + 中途 DISABLE 不影响当前 RUN — 7 写 ToolInvocationTrace · hash:def456 ⚠ admin disable v1.5.0-rc 8 9 第二次调用仍用 def456 · Run 不受影响 10 Run 完成 · 全程 hash 一致
↻ = 同层自循环 · ⚠ = 外部干扰(admin disable)。关键不变量:步骤 6 锁定的 manifest_hash 在整个 Run 内不变;步骤 8 的 disable 操作只影响后续新 Run,不会让当前 Run 半途切版本。
Scenario B · 灰度发布 + 出问题秒级切回

v1.5.0 灰度 10% 发现错误率飙升,admin 把灰度比例改回 0%,新 Run 立即只走 v1.4.2 老版本。已经在跑的灰度 Run 不被打断(参见 Scenario A 的不变量)。

AdminOPERATOR T1 · Skill HubROLLOUT CONFIG T2 · Resolver灰度命中 T3 · 新 RunRUN_N+1 Metrics · Trace观测面 — PHASE A · 灰度上线(10%)— 1 设置 v1.5.0 rollout=10% — 一段时间内 · 灰度环跑 1000+ Run — 灰度 Run · v1.5.0-hash:def456 2 ⚠ alert · 错误率 +35% 3 通知 admin — PHASE B · 秒级切回(rollout → 0%)— 4 查 impact API 187 user · 3 active Run 5 rollout=0% · 写 audit — PHASE C · 新 RUN 自动走老版本 — 6 不命中灰度 → 选 v1.4.2 hash:abc123(老稳定版) 7 写 Trace · skill_version_resolved
关键差异:改 rollout 比例(治理面)≠ disable 版本(紧急下架)。比例切回是常态运维动作,秒级生效但不打断已锁定的 Run;disable 才是紧急下架手段,配套自动 fallback。

1.3 为什么要做版本治理

不治理的痛点:

痛点具体表现影响
破坏性升级把跑着的 Run 搞挂开发者直接改 manifest(删字段 / 改类型 / 加 OAuth scope),生产 Agent 启动时报 schema 不兼容线上事故;用户看到的是 Agent 突然不能用
升级范围不可控「修个 bug」直接上 latest,所有 Agent 一夜之间换版本,无法分批验证问题暴露面 100%;回滚也是 100% 影响
问题事后无法定位同一个 Skill 跑了多次,每次行为不同,但日志里只有 skill_id 没有版本指纹客户报问题没法复现;root cause 分析全靠猜
主 Skill 故障无兜底飞书发消息 Skill 临时挂了,所有依赖的 Agent 全部中断单点故障扩散;没有降级路径
下架影响面盲查要 disable 一个版本,不知道哪些 Agent / 哪些 Run 在用下架决策不敢做,老版本越积越多

版本治理解决的具体技术问题:

  1. 把 Skill 行为契约冻结成 manifest_hash——published 后不可改,要改就发新版本,Run 启动时锁住一个 hash 整段不变
  2. 把升级动作变成「显式选版本」——Agent 启用具体 SkillVersion,不吃 latest,不会被悄悄换掉
  3. 把发布动作变成可灰度可回滚——三态生命周期 + 比例 / 白名单 / 时间窗多维度控制 + 一键改比例切回
  4. 把 fallback 写进 manifest——主 Skill 熔断时按声明切备,1 次连环切换上限
  5. 把 Trace 强约束扩展到版本维度——每条 ToolInvocation 必带 manifest_hash,支持版本级聚合查询

1.4 三层职责定位

层承担不承担
Tier 1 · Skill HubSkill / SkillVersion 实体的 CRUD;manifest 注册 + schema 校验;rollout 配置 UI;fallback 链路声明;前端 Skill Market 上架审核不参与运行时版本选择决策;不感知 Run / RunState
Tier 2 · Runtime读 Agent 启用记录 + rollout 配置选择具体 SkillVersion;命中灰度后返回选定 manifest_hash;主 Skill 熔断时按 fallback 链路重试;影响面查询服务不存储 manifest;不暴露 Skill 管理 API
Tier 3 · Run KernelRun 启动时把 Tier 2 解析出的 manifest_hash 锁进 RunContext;整个 Run 期间用同一份 hash;ToolInvocationTrace 必带 manifest_hash 字段不感知 rollout / fallback 配置;不知道版本是新是旧;只认锁定的 hash
关键边界:版本治理是 Tier 1 + Tier 2 概念。Tier 3 内核只负责"用 Tier 2 给的 hash 跑完这个 Run",不感知灰度环、不感知三态生命周期。所有版本相关决策在 Run 启动那一刻就被 Tier 2 解析完毕,并通过 RunContext.skill_versions 注入;之后整个 Run 期间,T3 永远只看 RunContext,不会反查 Tier 1。

2. Manifest 结构

SECTION GOAL

列出 SkillManifest 的最小必填字段集,明确哪些字段进 hash(行为契约)、哪些字段不进 hash(治理面)。

2.1 必填字段

Skill 提交审核时 manifest 必须包含以下字段,缺一禁止 publish:

字段类型说明进 hash
idstringSkill 唯一标识(namespace.name 格式)是
versionsemvermajor.minor.patch,published 后冻结是
display_namestringUI 展示名否
descriptionstring面向用户与 LLM 的能力描述否
input_schemaJSON Schema入参契约,必须可机器校验是
output_schemaJSON Schema出参契约是
actions / triggerslist本版本暴露的动作 / 触发器列表是
permissionobjectOAuth scopes / 资源权限声明是
side_effectsenumread_only / write / external_send / cross_system / destructive(不可逆破坏:fs 删除 / DB drop / git reset --hard / 覆写已有文件)是
risk_levelenumlow / medium / high;high 强制 HITL是
sandboxobject沙箱声明:是否需要网络出站、文件系统、凭证是
fallbackobject?备用 Skill 声明(见 §6)是
ownerstring责任人 / 团队否
tagslist分类标签否

2.2 沙箱声明

沙箱字段定义 Skill 的执行隔离边界,平台据此分配沙箱资源、做安全审计:

子字段类型含义
sandbox.network_egresslist允许出站的域名白名单;空 = 不允许任何出站
sandbox.filesystemenumnone / read_only / scoped_write;scoped_write 必须给出可写路径
sandbox.credentialslist需要注入的凭证类型(user_oauth / workspace_secret / platform_secret)
sandbox.timeout_secondsint单次执行最大耗时;默认 30s
sandbox.memory_mbint沙箱内存上限

沙箱字段全部进 hash —— 因为它们直接影响 Skill 的可观察行为(能不能联网、能不能写盘),任意改动都视为新版本。

2.3 进 hash 与不进 hash 的边界

判定原则:影响 Skill 行为契约的字段进 hash;只影响治理 / 展示的字段不进 hash。

类别典型字段说明
进 hash(不可变)schema / permission / side_effects / risk_level / sandbox / fallback改了就是新版本,必须重新发布
不进 hash(可热改)display_name / description / tags / owner纯文案 / 责任人变更不影响行为,UI 直接改即可
外部治理面(不在 manifest)rollout 比例 / 白名单 / 启用列表属于 RolloutConfig,单独存储;可随时改

2.4 destructive 标记与参数级再校验

静态 manifest 标记解决不了一类边界问题:"什么都能跑"的通用 Skill(shell_exec / python_exec / file_io)—— 把整个 Skill 一刀切标成 destructive 太粗(用户大量只读命令也会被拦),不标又会漏过 rm -rf。规则分两层:

层作用示例
Skill 注册时(静态)专用破坏性 Skill 在 manifest 里直接标 side_effects: destructive,Policy 一律走 HITLworkspace.delete_path / db.drop_table / git.reset_hard
Tool 调用时(动态)通用 Skill 在每次调用时由 Tool 控制器对入参再过一次 Policy;命中关键字模式即升级到 destructive,否则按 manifest 默认档shell_exec("rm -rf …") / shell_exec("mv -f …") / 任何带 > 覆写重定向 / git reset --hard

关键字白名单 / 黑名单由 Policy 引擎集中维护(不在 manifest 里),可热更;命中后强制 HITL,文案带原始命令 + 命中规则 + 影响 path 与文件数,对接 治理与 HITL 设计 ↗ 风险矩阵 M8(用户 workspace 不可逆操作)。沙箱挂载已经把作用范围圈在用户 workspace 内(详见 沙箱边界 §F ↗),这一层是"用户私有空间内的二次确认"。


3. Semver 规则

SECTION GOAL

明确什么算破坏性变更,让兼容性自动检查可机器执行;不允许开发者「随手」升 patch 但实际改了 schema。

3.1 三档变更分级

升级类型触发条件示例
PATCH(1.4.1 → 1.4.2)行为不变的 bugfix;文案 / 错误码细化;观测埋点完善修一个边界条件 bug;改 description 文案
MINOR(1.4.x → 1.5.0)新增能力但向后兼容:input_schema 加 optional 字段、output_schema 加字段、新 action / 新 trigger给已有 action 加可选参数;新增一个 trigger
MAJOR(1.x.x → 2.0.0)任何破坏性变更;旧调用方按老 schema 写的代码会挂删 required 字段;改字段类型;加 OAuth scope;提升 risk_level

3.2 破坏性变更清单

以下变更自动判定为 breaking,发布时强制要求 major 升级,否则禁止 publish:

变更为什么算破坏性
input_schema 删 required 字段旧调用方继续传该字段会校验失败
input_schema 字段类型变化(string → int)旧调用方传的值类型不再匹配
input_schema 加新 required 字段旧调用方没传新字段会被拒绝
output_schema 删字段下游消费方读不到该字段
output_schema 字段类型变化下游消费方解析失败
permission 增加 OAuth scope消费方需要重新授权,老凭证不再够用
side_effects 升级(read_only → write / external_send / cross_system / destructive)策略层风险评估结果不同;任意升级都可能从「无需 HITL」变成「必须 HITL」
risk_level 升级(low → high)HITL 决策树命中条件变化;强制 HITL 审核
sandbox.network_egress 加新域名引入新外部依赖,安全边界扩大
删除某个 action / trigger调用方按旧 action 调会找不到

3.3 兼容性自动检查

开发者点击「发布新版本」时,平台自动跑 manifest diff:

核心原则:开发者只能「升得比规则要求高」,不能「升得比规则要求低」。这把"假装兼容"的路径堵死。


4. manifest_hash 锁定

SECTION GOAL

说明 hash 怎么算、什么时候锁、锁住后哪里看得到,让"那次调用用的哪个版本"成为可机器查询的事实。

4.1 hash 计算

manifest_hash 是规范化后的 manifest 的 SHA-256 摘要,只覆盖 §2.3 中标记「进 hash」的字段。规范化步骤:

  1. 抽取所有「进 hash」字段,丢弃其余
  2. 对 JSON Schema / 列表类型按 key 排序,确保字段顺序变化不影响 hash
  3. 对字符串做 NFC 归一化、去除尾随空白
  4. 整体序列化为 canonical JSON,对结果取 SHA-256,截取前 16 位作为短 hash 用于 UI 展示

同一 manifest 不同机器算出来必须一致;任何「进 hash」字段改动都会让 hash 变化。

4.2 锁定时机

时机动作
SkillVersion publish 时计算 manifest_hash,写入 SkillVersion.manifest_hash 列;published 后该列不可改
Run 启动时(Tier 3)对 Agent 启用的每个 Skill,调用 Tier 2 Resolver 拿到当前应使用的 SkillVersion,把 manifest_hash 写进 RunContext.skill_versions
Run 失败重启时视为新 Run,重新走解析流程,可能拿到新的 hash(比如灰度环已经切回老版本)
子 Run 派生时父 Run 的 RunContext.skill_versions 不透传给子 Run;子 Run 独立解析(避免父 Run 锁定的旧 hash 干扰子 Run 的灰度命中)

4.3 RunContext 内的存储

RunContext.skill_versions 是一个 skill_id 到 manifest_hash 的映射,类型为只读字典;Run 期间任何 Tool 调用都必须用映射里的 hash,不允许绕过去查 Tier 1:

键值含义
skill_idmanifest_hash这次 Run 期间该 skill 一直用这个 hash
(skill_id, version)—同时记录一份 semver 字符串供 Trace 展示,但 hash 才是真正的契约

4.4 Trace 留痕

位置字段用途
ToolInvocationTraceskill_id / skill_version / manifest_hash每条工具调用都能定位到具体 hash;事后审计 / 复盘 / 对比
RunTraceskill_versions(启动快照)整个 Run 用了哪些版本一目了然
AuditLogskill_version_resolved 事件记录解析过程:用户视角的版本 vs 实际生效的版本(灰度命中)

Trace 留痕的核心承诺:拿到任意一条 ToolInvocationTrace,就能精确回答「那次调用用的是哪个 manifest_hash、属于哪个 published 版本、是不是命中了灰度」三个问题。


5. 灰度发布

SECTION GOAL

定义新版本如何分批触达用户、如何监控、出问题如何切回,全程不需要改代码。

5.1 三态生命周期

SkillVersion 在生命周期上只有三个长期态,转换由 admin 操作驱动:

状态含义新 Run 可见性已锁定 Run
published已发布、可被启用按 rollout 决定正常运行
deprecated不建议新启用,但已启用的继续可用不出现在 Skill Market 选择列表;已启用的 Agent 维持正常运行
disabled紧急下架,所有未启动的 Run 不再可用不可解析,必须 fallback正在跑的 Run 跑完,新 Run 走 fallback

开发期的 draft / submitted / rejected 不属于这里讨论的范围 —— 那是上架审核流程,不是治理面。

5.2 灰度维度

同一 published 版本可按多种维度灰度,命中任一维度即纳入灰度环:

维度配置典型用法
用户比例rollout_percent10% 比例渐进放量;按 user_id hash 分流,保证同一 user 在灰度持续期内归属稳定
Workspace 白名单rollout_workspaces先在内部 / 客户 dogfood workspace 上线
用户白名单rollout_users开发者自测;少量种子用户灰度
时间窗rollout_start / rollout_end到期自动停止灰度;过了 end_time 强制全量或回退

5.3 灰度期间的可见性约束

5.4 灰度指标 + 告警

每个灰度发布跟踪以下指标,与 runtime-governance §2 的观测体系联动:

指标采集告警阈值
调用成功率新版本 vs 老版本对比新版本下降 > 20% 触发 alert
平均耗时p50 / p95p95 上升 > 50% 触发 alert
错误类型分布异常 code 维度统计出现新错误类型超过总量 5% 触发 alert
用户主动回退率用户从新版本切回老版本的比例> 10% 触发 alert

5.5 切回开关

灰度出问题时 admin 有两档操作:

操作动作影响
rollout=0(治理面切回)把灰度比例改为 0%,新 Run 不再命中秒级生效;已锁定的 Run 不被打断;版本状态仍是 published
disable(紧急下架)把版本状态改为 disabled所有未启动的 Run 走 fallback;正在跑的 Run 跑完;写 audit;通知所有受影响 Agent owner

大多数灰度异常用 rollout=0 即可;只有发现安全漏洞 / 严重数据问题时才 disable。disable 前必须先看影响面(admin UI 强制弹确认)。


6. Fallback 声明

SECTION GOAL

让主 Skill 故障时有明确的备份路径,避免单点故障扩散;fallback 写在 manifest 内,是开发者声明的契约,不是平台兜底的暗箱逻辑。

6.1 manifest 内的 fallback 字段

Skill 开发者可在 manifest 里声明备用 Skill;不写则视为无 fallback,主 Skill 故障即向上抛错。fallback 字段结构:

子字段类型含义
fallback.skill_idstring备用 Skill 的 id(必须已 published)
fallback.version_constraintsemver range允许的版本范围,例如 ^2.0.0;解析时取范围内最高 published 版本
fallback.triggerenum切备触发条件:timeout / 5xx / circuit_break / disabled
fallback.input_mappingobject?主 Skill 入参到备用 Skill 入参的字段映射;缺省即直接透传
fallback.notesstring给运维看的说明:为什么这个备用合适

6.2 触发条件

条件判定
主 Skill 调用超时超过 sandbox.timeout_seconds 仍未返回
主 Skill 5xx底层服务返回 5xx;连续 N 次后熔断器打开
主 Skill 熔断器打开错误率超过阈值,本 Run 直接走 fallback 不再尝试主 Skill
主 Skill 版本 disabledResolver 解析时发现 hash 已下架,直接选 fallback

6.3 切换上限与防环

6.4 Fallback 不解决的问题

fallback 是最后一道兜底,不替代以下治理手段:

问题正确手段
新版本质量不稳定用灰度发布渐进放量,不要靠 fallback 兜
主 Skill 经常超时修主 Skill 性能,调整 sandbox.timeout_seconds,不要靠备用兜
权限模型不一致主备应使用同一权限模型;不同模型不该互为 fallback
跨 vendor 切换由 Tier 2 Provider 抽象层处理,不应该在 Skill manifest 层做 vendor 切换

核心原则:fallback 解决「主 Skill 暂时不可用」的问题,不解决「主 Skill 有缺陷」的问题。