news 2026/9/20 10:12:57

oh-my-openagent 遥测 Schema 注册的对抗式验证:`parallelism_summary` 事件契约的完整审计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
oh-my-openagent 遥测 Schema 注册的对抗式验证:`parallelism_summary` 事件契约的完整审计

oh-my-openagent 遥测 Schema 注册的对抗式验证:parallelism_summary事件契约的完整审计

【免费下载链接】oh-my-openagentOmO: Just type "mass ulw" keyword with your prompt. Now you are the master of graph engineering.项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent

本文围绕 oh-my-openagent 仓库中telemetry-parallel-latency-v2工作流的第 5 号任务(todo 5:为parallelism_summary事件注册遥测 schema)展开,完整呈现一份独立验证者对提交de7416776所做的对抗式(adversarial)审计过程与结论。读者将从中掌握该项目的遥测事件契约如何被精确约束——15 个属性的冻结 schema、文档字节级一致性门禁、直方图的 64 字符隐私截断防线、以及 allowlist 在传输边界的强制执行——并可直接复现全部验证命令。

验证背景与隔离方法

被验证的提交是de7416776feat(omo-senpi): register parallelism_summary event schema),对应任务文档 task-5.md。验证者独立于实现者,全程不参与 todo 5 的编码,仅以"黑盒 + 探针"方式检验"完成声明"是否成立。

关键的方法学要点是验证与实现彻底隔离

  1. git worktree add --detach /tmp/vt5-scratch de7416776创建的分离式 scratch 工作树中运行全部探针,HEAD 精确钉在de7416776
  2. node_modules从父工作树符号链接(symlink)而来,不执行bun install,避免依赖漂移;
  3. 活动工作树中零跟踪文件被修改——所有变更探针都在 scratch 副本上执行,验证结束前恢复。
$ git worktree add --detach /tmp/vt5-scratch de7416776 HEAD is now at de7416776 feat(omo-senpi): register parallelism_summary event schema

这种"分离工作树 + 符号链接依赖 + 变更后恢复"的组合,是该仓库证据链(evidence)体系的标准做法,保证验证结论可审计、可复现,且不污染主线工作区。

注册的属性契约:恰好 15 个属性,不多不少

验证的第一项核心工作,是从冻结对象(frozen object)中直接读取运行时 schema,而不是信任代码注释或文档叙述。在 scratch 工作树中导入product-identity.ts并 dumpOMO_NATIVE_EVENT_SCHEMAS.parallelism_summary,得到与规格逐条相等的 15 个键:

属性类型说明
$session_id{"type":"string"}会话标识(哈希后)
clock_anomalies{"type":"number"}时钟异常计数
eval_only_duration_ms{"type":"number"}纯 eval 桶耗时(毫秒)
eval_only_waves{"type":"number"}纯 eval 桶波数
incomplete_calls{"type":"number"}未配对调用数(数据质量计数)
measured_turn_duration_ms_total{"type":"number"}实测回合总耗时
mixed_waves{"type":"number"}混合桶波数
modeled_wallclock_saved_ms{"type":"number"}建模节省的墙钟时间
non_eval_joined_calls{"type":"number"}非 eval 桶加入的调用数
non_eval_saved_round_trips{"type":"number"}非 eval 桶节省的往返次数
non_eval_wave_size_histogram{"type":"string"}波大小直方图(位置编码、无标签)
non_eval_waves_multi{"type":"number"}非 eval 桶多波数
non_eval_waves_total{"type":"number"}非 eval 桶总波数
schema_kind{"type":"string","values":["parallelism_v1"]}单值枚举,schema 版本标识
upper_bound_saved_ms{"type":"number"}诚实标注的上界节省值

从实现侧看,该条目由 parallelism-schema.ts 导出PARALLELISM_SUMMARY_SCHEMA,并在 event-schemas.ts 中以parallelism_summary: PARALLELISM_SUMMARY_SCHEMA挂入OMO_NATIVE_EVENT_SCHEMAS。schema 对象统一使用共享的BOOLEAN_PROPERTY/NUMBER_PROPERTY/STRING_PROPERTY冻结常量和enumProperty([...] as const)枚举构造器,并整体套Object.freeze({ ... })——这意味着属性集合在运行时不可被任何调用方悄悄扩缩。

类型正确性(验收标准 1b)

运行时 dump 显示:$session_idnon_eval_wave_size_histogram{"type":"string"};其余 12 个计数/时延属性全部为{"type":"number"}schema_kind{"type":"string","values":["parallelism_v1"]}(单值)。与任务规格逐项吻合。

non_eval_前缀纪律(验收标准 1c)

规格要求"所有由工具调用(tool-call)派生的 COUNT 必须携带non_eval_前缀"。验证者逐一核对四个波/调用计数:non_eval_waves_totalnon_eval_waves_multinon_eval_joined_callsnon_eval_saved_round_trips均符合;而eval_only_wavesmixed_waves属于计划中明确分离的 eval 桶,incomplete_callsclock_anomalies属于数据质量计数,不属于 non_eval 聚合域。结论:不存在漏掉前缀的 non_eval 计数。

文档门禁(Doc Gate):让文档漂移变成测试失败

遥测 schema 的参考文档位于 docs/reference/senpi-telemetry.md,其中夹着一段由<!-- BEGIN GENERATED SCHEMA --><!-- END GENERATED SCHEMA -->两个哨兵(sentinel)包围的生成块。该文档块不是手写的,而是由生成器generateTelemetrySchemaBlock()(位于 script/telemetry-schema-block.mjs,被 schema-doc.test.ts 动态 import)按 schema 对象逐行生成。

门禁如何工作

schema-doc.test.ts 包含两个测试:

  1. 字节级一致性测试:读取docs/reference/senpi-telemetry.md,用indexOf精确提取两个哨兵之间的文本,与generateTelemetrySchemaBlock()的输出做===全等比较。不等则抛出Telemetry schema documentation drifted.并把应粘贴的完整生成块打印出来;
  2. 空 schema 拒绝测试:对空事件{}调用生成器必须抛错(Telemetry event empty_event must contain at least one property schema),防止生成损坏的空块。
// schema-doc.test.ts 中的关键校验 const expected = generateTelemetrySchemaBlock() const actual = extractGeneratedBlock(await readFile(DOC_PATH, "utf8")) if (actual !== expected) { throw new Error([ "Telemetry schema documentation drifted.", "Paste this exact generated block into docs/reference/senpi-telemetry.md:", expected, ].join("\n\n")) }

探针 A:门禁真实触发

验证者在 scratch 副本的OMO_NATIVE_EVENT_SCHEMAS.parallelism_summary中插入一个多余属性zz_probe_property: NUMBER_PROPERTY不触碰docs/reference/senpi-telemetry.md。结果:

$ bun test packages/omo-senpi/src/components/telemetry/schema-doc.test.ts error: Telemetry schema documentation drifted. (fail) ... #then the generated schema block is byte exact [16.09ms] 1 pass 1 fail

恢复文件后回到2 pass / 0 fail。这证明文档门禁不是装饰性的——它会在 schema 与文档失同步的瞬间亮红灯。

探针 B:生成块确实是"生成"的

独立对比generateTelemetrySchemaBlock()的输出与哨兵之间的既有文本:actual bytes: 5921 expected bytes: 5921byte-exact identical: true。零字节差异,说明文档块从未被手工编辑过。

RED / GREEN 开发流程

任务文档 task-5.md 还记录了实现者的标准流程:先加 schema(RED,门禁失败并打印期望块)→ 用生成器脚本重新拼接文档(GREEN)→ 恢复2 pass / 0 fail。重新生成的命令可以直接复用:

bun -e ' const fs = require("node:fs"); const { generateTelemetrySchemaBlock } = await import("./script/telemetry-schema-block.mjs"); const path = "docs/reference/senpi-telemetry.md"; const doc = fs.readFileSync(path, "utf8"); const B = "<!-- BEGIN GENERATED SCHEMA -->", E = "<!-- END GENERATED SCHEMA -->"; const b = doc.indexOf(B), e = doc.indexOf(E); fs.writeFileSync(path, doc.slice(0, b) + generateTelemetrySchemaBlock() + doc.slice(e + E.length)); console.log("replaced", b, e); '

直方图编码:位置编码如何躲过 64 字符隐私截断

non_eval_wave_size_histogram是该 schema 中最"刁钻"的属性:它是一条字符串直方图,而传输层会对所有字符串属性做 64 字符截断(见下文)。验证者独立测量了两种编码在真实上界MAX_TRACKED_CALLS = 2000下的长度:

编码MAX_TRACKED_CALLS = 2000时的字符串长度能否安全通过 64 字符截断
位置编码(已发布的契约)2000:2000:2000:2000:2000:2000:2000:200039能——原样保留,余 25 字符余量
带标签编码(被禁止)1=2000:2=2000:3=2000:4=2000:5_8=2000:9_16=2000:17_32=2000:33plus=200069否——被截断为...:33plus,最后一个桶的值被破坏

39 = 8×4 + 7,由"8 个桶 × 每桶最多 4 位数字 + 7 个分隔冒号"构成。上界来源是 wave-assembler.ts 中导出的MAX_TRACKED_CALLS = 2000:波组装器最多追踪 2000 个调用,因此任何一个桶的计数都不可能超过 4 位——这是位置编码始终不超限的根本前提。

验证者还通过真实的捕获路径(而非单纯slice())观察到了 69 → 64 的截断:labelled input length: 69 captured length: 64silently truncated to exactly 64: true。这正是位置编码要规避的"损坏机制":带标签形式被截断后,33plus=2000变成...:33plus,被截断的半截 token 仍能解析成看似合理的数字,属于静默数据损坏。

防护测试非空:变异探针(Mutation Probe)

光有约定不够,还要证明"守卫测试本身不是空转的"。验证者在 scratch 副本中删除 schema 里的non_eval_wave_size_histogram: STRING_PROPERTY一行,然后运行 product-identity.test.ts:

(fail) OmO Native product identity > #given the tracked-call cap #when the widest wave histogram is encoded #then it stays inside the 64 character privacy limit [0.20ms] 8 pass 1 fail

8 pass / 1 fail,失败的正是一个新加的直方图守卫测试。恢复后git diff --stat为空。测试的断言逻辑见 product-identity.test.ts:从真实导出的MAX_TRACKED_CALLS推导最宽桶值(String(MAX_TRACKED_CALLS)长度为 4),构造最坏情况直方图,断言toHaveLength(bucketCount * 4 + (bucketCount - 1))(即 39)且<= 64,并同时断言 schema 中该属性的类型与 allowlist 成员身份——它耦合的是真实 schema 对象,属性缺席时必然失败,不可能空洞通过。

Allowlist 强制执行:传输边界的最终防线

机制:投影而非信任

遥测客户端 telemetry-core/src/events.ts 的createEventTelemetryClientcaptureEvent中对每个属性执行三层过滤:

  1. 禁止键检查isForbiddenKey):$ip永远拒绝;字符串值且键名以_text/_path/_prompt结尾(FORBIDDEN_SUFFIX = /_(?:text|path|prompt)$/)直接拒绝;$前缀键只允许$os$os_version$process_person_profile$session_id
  2. allowlist 检查:键不在该事件的允许集合内 → 丢弃并上报telemetry_event_property_dropped
  3. 值类型检查:只接受 string / boolean / finite number;字符串进一步value.slice(0, 64)静默截断到 64 字符。

allowlist 本身不是手写的,而是由 event-schemas.ts 从OMO_NATIVE_EVENT_SCHEMAS的键集合自动派生OMO_NATIVE_PROPERTY_ALLOWLISTS),schema 与 allowlist 因此不存在双份漂移的可能。

探针:真实客户端 + 真实记录器

验证者用真实的createEventTelemetryClient(携带OMO_NATIVE_PROPERTY_ALLOWLISTS)配合仓库的createTransportRecorder()发出一个parallelism_summary事件,同时塞入三个非 allowlisted 的"入侵者"属性:median_wave_sizeeval_cell_sourceprompt_text。结果:

allowlisted count: 15 every allowlisted arrived intact: true missing allowlisted: [] intruders present: [] # 三个入侵者全部被丢弃 labelled input length: 69 captured length: 64 silently truncated to exactly 64: true

15 个 allowlisted 属性全部原样到达传输层;三个入侵者(包括一个带_text后缀、会被FORBIDDEN_SUFFIX结构性拦截的prompt_text)全部被丢弃。任务文档中的手工 QA 还展示了完整 payload:除 15 个业务属性外,platformproduct_namepackage_versionschema_version$process_person_profile是客户端包装层在投影之后追加的共享属性,不是调用方数据。

必须不存在项(Must-NOT-Have)检查

对抗式验证同样关心"不该动的有没有动":

  • turn_completed未受影响git show de7416776 -- 'packages/omo-senpi/**' | grep '^-'为空——整个提交是纯新增(pure additions),没有任何 schema 条目被修改;diff 中仅有的turn_completed字样来自task-5.md的叙述性文字;
  • 无 median / 均值 / 比率属性:15 键 dump 中没有任何属性名含medianavgaverageratioupper_bound_saved_ms携带了规格要求的_upper_bound式诚实标注;
  • _text/_path/_prompt后缀:15 个键无一命中,且在 events.ts 的FORBIDDEN_SUFFIX层面被结构性禁止;
  • 禁止路径零改动git show --name-only --format="" de7416776只列出 4 个文件(task-5.mddocs/reference/senpi-telemetry.mdproduct-identity.test.tsproduct-identity.ts),对telemetry-core/omo-codex/omo-opencode/plugin/extensions/的 grep 计数为 0。

验收标准全表

以下为验证者依据探针结果逐项裁决的验收标准:

#标准结果决定性观察
1Schema 恰好包含 15 个指定属性,不多不少PASS运行时 dumpOMO_NATIVE_EVENT_SCHEMAS.parallelism_summary:恰好 15 键,与规格列表集合相等
1b类型正确($session_idstring;12 个 number;直方图 string;schema_kind枚举)PASSdump 显示 string/number/单值枚举均与规格一致
1c每个工具调用派生的 COUNT 都带non_eval_前缀PASS四个波/调用计数均符合;其余为计划中独立域(eval 桶、数据质量计数)
2a文档门禁真实触发PASS插入zz_probe_propertyschema-doc.test.ts从 2/0 变为 1/1,报Telemetry schema documentation drifted.;恢复后回到 2/0
2b文档块是生成的而非手写PASSgenerateTelemetrySchemaBlock()与哨兵间文本均为 5921 字节,===为 true,零字节差异
3a位置编码在真实上界内成立PASS独立复现:8 个2000:连接 = 39 字符 = 8×4+7,64 字符切片后原样保留
3b带标签形式会被截断PASS独立复现:69 字符;64 切片裁到...:33plus,丢失末桶值
3c测试断言诚实的 39 字符上界而非虚构最坏情况PASS测试从真实导出的MAX_TRACKED_CALLS(= 2000)推导宽度,断言8*4+7= 39 且<= 64,未硬编码膨胀位数
4a15 个 allowlisted 属性全部原样到达PASS真实客户端 + 仓库createTransportRecorder():捕获的非共享键集合恰好 15 个,every allowlisted arrived intact: true
4b非 allowlisted 属性被丢弃PASS混入median_wave_sizeeval_cell_sourceprompt_textintruders present: []
4c超过 64 字符的字符串被截断到恰好 64PASS69 字符带标签字符串进、64 字符出,silently truncated to exactly 64: true
5变异探针:守卫测试非空转PASS删除non_eval_wave_size_histogramproduct-identity.test.ts变 8/1,失败的正是新直方图守卫
6aturn_completedschema 未变PASSdiff 为纯新增,唯一提及在task-5.md叙述文字中
6b无 median / 均值 / 比率属性PASS15 键无命中;upper_bound_saved_ms带诚实上界标注
6c_text/_path/_prompt后缀PASS15 键无命中,且被FORBIDDEN_SUFFIX结构性拦截
6dtelemetry-core/omo-codex/omo-opencode/plugin/extensions零改动PASSgit show --name-only仅 4 文件;禁止路径 grep 计数 0
7agiven/when/then 约定PASS新增测试使用行内// given:/// when:/// then:注释(项目 AGENTS.md 明确允许),并与文件既有 8 个测试的#given/#when/#then标题约定一致
7bas any、无@ts-ignorePASS对新增源码行 grep 各 0 命中;enumProperty([...] as const)是 const 断言而非as any
7c新增源码无 emoji、无 em dashPASSgrep + perl Unicode 扫描 0 命中(em dash 仅存在于任务文档叙述中)
7d文件低于 250 纯 LOC 上限PASSproduct-identity.ts220→237;product-identity.test.ts114→125,均在 250 以内
8证据数字可复现PASS9 / 2 / 116-0 / exit-0 全部精确复现

证据复现与类型检查

验证者在 scratch 工作树中逐项复现任务文档声明的数字:

$ bun test packages/omo-senpi/src/components/telemetry/product-identity.test.ts 9 pass / 0 fail / 160 expect() calls (claim: 9 pass) MATCH $ bun test packages/omo-senpi/src/components/telemetry/schema-doc.test.ts 2 pass / 0 fail (claim: 2 pass) MATCH $ bun test packages/omo-senpi/src/components/telemetry/ 116 pass / 0 fail / 457 expect() calls across 14 files (claim: 116/0) MATCH $ bun run --cwd packages/omo-senpi typecheck # 在活动工作树中 $ tsgo --noEmit -p tsconfig.json EXITCODE=0 (claim: exit 0, no diagnostics) MATCH

未能攻破的尝试

对抗式验证的价值在于"试图失败而失败不了"。验证者明确记录了五类失败的攻击:

  1. 寻找缺少non_eval_前缀的工具调用派生计数——唯一的无前缀计数是计划中明确分离的 eval 桶与数据质量计数器;
  2. 让文档门禁变成空操作——第一次变异就触发了失败;
  3. 删除守卫测试断言的对象使其空洞通过——测试大声失败(8/1)而非静默通过;
  4. 把非 allowlisted 与禁止后缀属性偷渡进真实传输通道——三个入侵者全部被丢弃;
  5. 寻找对turn_completed或禁止包的隐藏修改——diff 只有 4 个文件且为纯新增。

已知非阻塞观察

验证者同时诚实记录了三个不构成缺陷的观察项:

  1. Scratch 专属的类型检查噪音:在 scratch 工作树中运行 typecheck 时,packages/utils/src/runtime/file.ts第 35 行出现一个TS2322。这是node_modules符号链接将该包解析回父工作树路径(重复 lib 解析)造成的伪影,该文件未被此提交触碰,真实工作树中同一命令以 0 诊断退出,因此不可归因于 todo 5;
  2. 软 LOC 限制product-identity.ts位于 237 纯 LOC,超过项目 AGENTS.md 的 200 LOC 软限制,但提交前已是 220(+17 是注册该 schema 所需的最小增量),且在本验证适用的 250 硬上限之内,仅作信息提示;
  3. 跨泳道耦合:守卫测试从wave-assembler.ts(todo 1 所属)导入MAX_TRACKED_CALLS。验证者核对了在途泳道对该文件的 diff:常量保持 2000 不变,4 位数字假设依然成立。若后续泳道把该常量提升到 9999 以上,守卫会正确地失败而非静默发出可截断的字符串——这是特性而非隐患

清理凭证(Cleanup Receipt)

验证结束后的环境是干净的:

  • scratch 工作树/tmp/vt5-scratch:先移除所有node_modules符号链接,再git worktree remove --force /tmp/vt5-scratch;移除前 scratch 内git status --short为空(两次变异探针均已恢复);
  • 临时文件/tmp/vt5-pi.bak/tmp/vt5-probe.ts/tmp/vt5-tc-real.txt全部删除,ls /tmp/vt5-*返回No such file or directory
  • 活动工作树:无跟踪文件被本验证修改。最终git status --short仅显示?? omo-native-parallel.test.ts?? omo-native-parallel.ts——那是另一位协作者在途 todo 4 的文件,与本验证无关。

结论:confirmed

parallelism_summaryschema 注册的完成声明经受住了全部探针。属性集合恰好是指定的 15 项且类型正确;文档块是受门禁守护的字节级生成器产物(门禁被证明真实触发);直方图守卫非空转,并在真实的 64 字符截断阈值下断言诚实的 39 字符上界;allowlist 在丢弃每一个入侵者的同时完整放行 15 个属性;所有 must-NOT-have 均未违反;每一条声明的数字都精确复现。验证者给出的置信度为 0.95,未发现缺陷。

补充一点时间维度的事实:上述验证钉死在提交de7416776的 15 属性契约上。在当前仓库中,该 schema 已随后续任务继续演进——parallelism-schema.ts 现包含eval_execution_*eval_nested_tool_call_*等更多计数器,schema_kind枚举扩展为parallelism_v1 | parallelism_v2,senpi-telemetry.md 的生成块也随之增长。但本文审计的方法论——冻结 schema 精确断言、字节级文档门禁、上界驱动的编码设计、传输边界 allowlist、变异探针防空转——正是这一整套演进能够持续安全进行的制度保障,也是本仓库遥测工程质量的核心所在。

【免费下载链接】oh-my-openagentOmO: Just type "mass ulw" keyword with your prompt. Now you are the master of graph engineering.项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 10:11:12

WeKnora 私有 RAG 知识库:Docker Compose 部署上线完整实战

WeKnora 私有 RAG 知识库&#xff1a;Docker Compose 部署上线完整实战 【免费下载链接】WeKnora Open-source LLM knowledge platform: turn raw documents into a queryable RAG, an autonomous reasoning agent, and a self-maintaining Wiki. 项目地址: https://gitcode.…

作者头像 李华
网站建设 2026/9/20 10:10:51

MMPose 姿态估计指南:从安装到人体关键点推理

MMPose 姿态估计指南&#xff1a;从安装到人体关键点推理 【免费下载链接】mmpose OpenMMLab Pose Estimation Toolbox and Benchmark. 项目地址: https://gitcode.com/GitHub_Trending/mm/mmpose MMPose 是 OpenMMLab 出品的姿态估计工具箱&#xff1a;输入一张图片或一…

作者头像 李华
网站建设 2026/9/20 10:10:27

ANSYS Workbench物理建模入门:从零构建可信仿真能力

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 10:09:17

ESP32+MAX30102健康监测仪:从硬件选型到数据滤波的完整实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/20 10:05:33

Win10安装JDK 1.8全攻略:国内镜像下载与环境变量配置详解

这两年只要聊到 JDK 版本&#xff0c;总有人问我&#xff1a;“都什么年代了&#xff0c;新项目还用 JDK 1.8 吗&#xff1f;” 问这种问题的人&#xff0c;多半还没经历过被某个老系统的依赖链锁死的绝望&#xff0c;也没体会过“明明本地跑得好好的&#xff0c;一上服务器就各…

作者头像 李华
网站建设 2026/9/20 10:04:04

2026数据治理平台AI能力分化:Agent接管与辅助路线选型指南

1. 数据治理路线分化的底层逻辑1.1 从“人治”到“智治”的拐点2026年这个时间节点&#xff0c;数据治理领域正在经历一次静默但深刻的分化。过去十年&#xff0c;绝大多数企业的数据治理工作本质上还是“人治”——靠数据治理专员手动梳理元数据、靠DBA逐条配置质量规则、靠数…

作者头像 李华