神秘密室
95.62M · 2026-02-24
AI Agent 的能力越来越依赖 Skill(技能插件)。OpenClaw 的 SundialHub 上已经有 4 万多个 Skill,各种 Agent 框架也在构建自己的 Skill 生态。
但有一个根本问题:Skill 执行过程完全不透明。
你调用一个 Skill,它做了什么?调了哪些 API?读了哪些文件?成功还是失败?你不知道。
这带来几个实际痛点:
这就像早期的微服务——没有 tracing、没有 metrics、没有 health check,出了问题全靠经验和运气。
后来 SRE 领域发展出了可观测性三支柱(Logs、Metrics、Traces),微服务的运维才变得可控。
STOP 要做的,就是把这套方法论搬到 Skill 层。
STOP(Skill Transparency & Observability Protocol)是一个开放规范,定义了:
核心设计原则:
项目地址:github.com/echoVic/sto…
Manifest 是 STOP 的基础——一个 skill.yaml 文件,声明 Skill 的输入、输出、使用的工具、副作用等。
把它理解为 Skill 的 package.json,但关注点是可观测性和信任,而不是依赖管理。
sop: "0.1"
name: juejin-publish
version: 1.2.0
description: 发布 Markdown 文章到掘金
inputs:
- name: article_path
type: file_path
required: true
description: Markdown 文章路径
constraints:
pattern: "\.md$"
outputs:
- name: article_url
type: url
description: 发布后的文章链接
guaranteed: true
- name: article_id
type: string
description: 掘金文章 ID
guaranteed: true
tools_used:
- exec
- web_fetch
- read
side_effects:
- type: filesystem
access: read
paths: ["${inputs.article_path}"]
- type: network
description: POST 请求到掘金 API
destinations: ["juejin.cn"]
requirements:
env_vars: [JUEJIN_SESSION_ID]
有了这个文件,你立刻能知道:
这就是 L0 的全部——一个 YAML 文件,零运行时改动。
skill.yaml 和 SKILL.md 是互补关系:
| 维度 | SKILL.md | skill.yaml |
|---|---|---|
| 受众 | Agent(LLM) | Runtime(机器) |
| 格式 | 自由 Markdown | 结构化 YAML |
| 用途 | 教 Agent 怎么用 | 告诉 Runtime 做了什么 |
Trace 是 Skill 的「飞行记录仪」——记录运行时发生了什么、什么顺序、花了多久、是否成功。
采用 OpenTelemetry 的 span 树模型:
Trace
└── Root Span (skill execution)
├── Span: read article.md
├── Span: exec python3 publish.py
│ └── Span: POST juejin.cn/api
└── Span: assertions check
每个 span 的结构:
interface Span {
span_id: string;
trace_id: string;
parent_span_id?: string;
start_time: string; // ISO-8601
end_time: string;
duration_ms: number;
kind: SpanKind; // skill.execute | tool.call | file.read | http.request | ...
name: string;
status: "ok" | "error" | "skipped";
attributes: Record<string, any>;
}
Trace 输出为 NDJSON 格式(每行一个 span),存储在 .sop/traces/ 目录:
{"trace_id":"t_abc","span_id":"s_001","kind":"skill.execute","name":"juejin-publish","status":"ok","duration_ms":3420}
{"trace_id":"t_abc","span_id":"s_002","parent_span_id":"s_001","kind":"file.read","name":"read article","duration_ms":12}
{"trace_id":"t_abc","span_id":"s_003","parent_span_id":"s_001","kind":"tool.call","name":"exec: python3 publish.py","duration_ms":3100}
{"trace_id":"t_abc","span_id":"s_004","parent_span_id":"s_003","kind":"http.request","name":"POST juejin.cn/api","duration_ms":2200}
关键设计决策:
Assertions 回答一个关键问题:「这个 Skill 真的成功了吗?」
没有断言时,Skill 成功的判断标准是:
有了断言,成功变成可机器验证的:
assertions:
pre:
- check: file_exists
path: "${inputs.article_path}"
message: "文章文件必须存在"
- check: env_var
name: JUEJIN_SESSION_ID
message: "需要掘金 Session ID"
post:
- check: output.article_url
matches: "^\.cn/post/\d+$"
- check: output.article_id
not_empty: true
支持的检查类型:
| 类型 | 用途 |
|---|---|
env_var | 环境变量是否存在 |
file_exists | 文件是否存在 |
file_not_empty | 文件是否非空 |
file_matches | 文件内容是否匹配正则 |
tool_available | 工具是否可用 |
output.* | 输出字段验证(matches/equals/not_empty/greater_than) |
duration | 执行时间是否在限制内 |
custom | 自定义脚本验证 |
基于历史断言通过率,还可以计算 Trust Score:
| 分数 | 标签 | 含义 |
|---|---|---|
| 0.95+ | Trusted | 稳定通过所有断言 |
| 0.80-0.94 | ️ Unstable | 偶尔失败 |
| < 0.80 | Unreliable | 频繁失败 |
Skill 平台(如 SundialHub)可以展示 Trust Score,帮用户选择可靠的 Skill。
STOP 不要求一步到位,定义了四个等级:
| 等级 | 名称 | 你需要做什么 | 你能获得什么 |
|---|---|---|---|
| L0 | Manifest | 写一个 skill.yaml | 静态分析、依赖审计、副作用可见 |
| L1 | Trace | Runtime 自动输出(无需 Skill 作者改动) | 执行时间线、工具调用审计 |
| L2 | Assertions | 在 skill.yaml 里加断言规则 | 自动成功验证、Trust Score |
| L3 | Full | 定义自定义指标和基线 | 成本追踪、异常检测、SLA 监控 |
决策树:
个人/内部 Skill? → L0
需要调试失败? → L1
需要用户/平台信任? → L2
生产环境大规模运行? → L3
L0 的成本是零——只需要一个 YAML 文件。 这是刻意设计的,降低采纳门槛。
为了让开发者快速上手,我们提供了 stop-cli:
# 安装
npm install -g stop-cli
# 或直接用 npx
npx stop-cli init
stop init交互式生成 skill.yaml:
$ stop init
stop init — Generate skill.yaml
Skill name (kebab-case) (my-skill): juejin-publish
Version (1.0.0): 1.2.0
Description: Publish markdown articles to Juejin
Author: echoVic
Observability level (L0/L1/L2/L3) (L0): L2
Tools used (comma-separated): exec,read,web_fetch
Created skill.yaml
stop validate校验 skill.yaml 是否符合规范:
$ stop validate
skill.yaml is valid
如果有问题会明确报错:
$ stop validate bad-skill.yaml
Missing required field: version
Input "foo": unknown type "invalid_type"
Side effect: unknown type "banana"
️ name should be kebab-case: "BAD_NAME"
3 error(s), 1 warning(s)
校验内容包括:
${inputs.x} 插值引用检查stop-runtime 是给 Agent Runtime 集成用的 SDK,提供三个核心能力:
npm install stop-runtime
import { loadManifest, parseManifest } from 'stop-runtime';
// 从文件加载
const manifest = loadManifest('./skill.yaml');
// 从字符串解析
const manifest = parseManifest(yamlString);
import { runAssertions } from 'stop-runtime';
// 跑 pre-checks
const preResults = runAssertions(manifest.assertions.pre, {
env: process.env,
inputs: { article_path: './article.md' },
tools: ['exec', 'read', 'web_fetch'],
}, 'pre');
// 跑 post-checks
const postResults = runAssertions(manifest.assertions.post, {
outputs: {
article_url: 'https://juejin.cn/post/123456',
article_id: '123456',
},
duration_ms: 3420,
}, 'post');
// 检查结果
for (const r of postResults) {
console.log(`${r.check}: ${r.status}`); // output.article_url: pass
}
每个 assertion 结果包含:
interface AssertionResult {
check: string; // 检查类型
status: 'pass' | 'fail';
severity: 'error' | 'warn';
message?: string;
value?: any;
}
import { createTracer } from 'stop-runtime';
const tracer = createTracer(manifest);
// 记录工具调用
const spanId = tracer.startSpan('tool.call', 'exec: python3 publish.py');
// ... 执行工具 ...
tracer.endSpan(spanId, 'ok', { 'tool.name': 'exec' });
// 记录 HTTP 请求
const httpSpan = tracer.startSpan('http.request', 'POST juejin.cn/api', spanId);
tracer.endSpan(httpSpan, 'ok', { 'http.status_code': 200 });
// 完成并输出
tracer.finish('ok');
// 导出 NDJSON
console.log(tracer.toNDJSON());
// 或写入文件(.sop/traces/)
tracer.writeTo();
以 juejin-publish Skill 为例,完整的 STOP 集成流程:
1. 创建 manifest(L0)
cd skills/juejin-publish/
stop init
# 填写信息,生成 skill.yaml
2. 添加断言(L2)
在 skill.yaml 中加入 assertions 部分(见上文示例)。
3. Runtime 集成
import { loadManifest, runAssertions, createTracer } from 'stop-runtime';
async function executeSkill(skillDir: string, inputs: Record<string, any>) {
const manifest = loadManifest(`${skillDir}/skill.yaml`);
const tracer = createTracer(manifest);
// Pre-checks
const preResults = runAssertions(manifest.assertions?.pre ?? [], {
env: process.env,
inputs,
tools: ['exec', 'read', 'web_fetch'],
}, 'pre');
const preErrors = preResults.filter(r => r.status === 'fail' && r.severity === 'error');
if (preErrors.length > 0) {
tracer.finish('error');
throw new Error(`Pre-check failed: ${preErrors.map(e => e.message).join(', ')}`);
}
// Execute skill
const execSpan = tracer.startSpan('tool.call', 'exec: python3 publish.py');
const outputs = await runPublishScript(inputs);
tracer.endSpan(execSpan, 'ok');
// Post-checks
const postResults = runAssertions(manifest.assertions?.post ?? [], {
outputs,
}, 'post');
const status = postResults.some(r => r.status === 'fail' && r.severity === 'error') ? 'error' : 'ok';
tracer.finish(status);
tracer.writeTo();
return { outputs, assertions: postResults, traceId: tracer.traceId };
}
执行后,.sop/traces/ 目录下会生成 trace 文件,可以用来调试、审计、或对接监控系统。
STOP 协议的核心思路很简单:把 SRE 的可观测性方法论搬到 Agent Skill 层。
工具已经可用:
# CLI
npx stop-cli init
npx stop-cli validate
# SDK
npm install stop-runtime
项目地址:github.com/echoVic/sto…
这是一个早期规范(0.1.0-draft),欢迎参与讨论和贡献。Skill 生态需要可观测性,就像微服务需要 tracing 一样。