- 人工智能
- AI Agent
- 多智能体
- Agent 编排
- Agent 工作流
【免费下载链接】open-multi-agent
Self-hosted TypeScript agent runtime with durable approvals and verifiable run records. Own it, approve it, audit it.
open-multi-agent的遥测层(v2 TraceRecord、TraceSink/BatchingTraceSink、TraceStore与可选@open-multi-agent/otel适配器)是否会让业务执行变慢、占用多少额外内存,是每次发布前必须回答的问题。docs/internal/observability-performance.md 记录了 OBS-5 这一可复现的工程快照:它定义了「专用微秒级预算 + 宽松 CI 报警门」两套验收口径,给出了无 sink 回归、保留内存、legacy 回调、批量入队、OTel 转换五条硬性门槛的实测数字,并把存储、队列压力、多 Agent 负载等矩阵行全部落盘。读完本文,你将掌握这套基准的命令用法、环境变量覆盖方式、当前 OBS-5 的实测结论,以及如何正确解读 InMemory/FileTraceStore 的存储数字。
定位:这是一份工程快照,不是性能承诺
文档开篇即明确:这是一份「可复现的工程快照」(reproducible engineering snapshot),而不是永久性的营销承诺。绝对耗时取决于 Node 版本、CPU、操作系统、文件系统、电源状态和后台负载。因此项目的取舍非常明确:
- 发布决策使用同主机(same-host)中位数和 RFC 预算;
- CI刻意使用放宽的门槛,避免共享运行器(shared runner)的硬件抖动把发布卡死。
这一「两套门」的设计也直接体现在仓库中:专用基准脚本与 scripts/observability-benchmark-ci.mjs 里的 CI 判定逻辑是分开实现的,CI 脚本的失败条件全部按 10 倍微秒预算取值。阅读本快照时应把它当作「在某次提交、某台机器上的一次测量」,后续代码演进后以 docs/observability.md 的用户文档为准。
预算体系:专用门与 CI 门
| 路径 | 专用基准门 | CI 守护 |
|---|---|---|
| 无 sink 墙钟时间 | 相对 OBS-1A identity/status 基线的中位回归<1% | 在专用发布运行中测量,不做浅层 CI checkout |
| 无 sink 保留内存 | 相对 OBS-1A 每个保留的顶层结果额外<1 KiB | 用--expose-gc上报,不是绝对共享运行器门槛 |
| legacy 同步回调分发 | 每个完成事件 p95<10 µs | p95<100 µs |
| 批量入队(batching enqueue) | 每条记录 p95<20 µs | p95<200 µs |
| OTel 转换 + 内存 processor | 每条记录 p95<50 µs | p95<500 µs |
| 同主机整次运行 sink 开销 | 相对无 sink 上报 | <1,000%,用于捕获数量级回归 |
关键原则:CI 阈值是专用微秒预算的 10 倍,它只是回归报警器,不是发布验收结果。可以对照 scripts/observability-benchmark-ci.mjs 中的判定代码:legacy dispatch>= 100 µs、batch emit>= 200 µs、OTel 转换>= 500 µs、整次运行开销>= 1000%才会失败;同时它把默认迭代/轮数压到OMA_BENCH_ITERATIONS=500、OMA_BENCH_ROUNDS=5,以缩短共享运行器上的执行时间。
矩阵与方法:共享的基准脚本
既有基准脚本就是整个矩阵的统一 harness,共九行:
| 矩阵行 | Harness 与统计量 |
|---|---|
| 无 sink | observability-no-sink.mjs;交替 baseline/candidate 轮次,取墙钟时间中位数 |
| 无 sink 内存 | 同一 harness 加--expose-gc;保留结果数组,交替中位字节/结果 |
| legacy 回调 | observability-sinks.mjs;整次运行中位数 + 直接 legacy 分发 p95 |
BatchingTraceSink | 整次运行中位数 + 同步入队 p95 |
InMemoryTraceStore | 1k/10k 追加、首页查询、估算保留堆 |
FileTraceStore | 1k/10k 追加、fsync、重开、全量查询、压缩、文件大小、批次大小对比 |
| OTel 适配器 | 官方InMemorySpanExporter+SimpleSpanProcessor;每条记录 p95 与 1k/10k 批次 |
| 1/10/100-Agent 等价信封 | 纯元数据 start/end 记录集;字节数与入队 p95 |
| 流式元数据 | 10k 条无 payload 的stream_chunk事件;字节数与入队 p95 |
| 队列压力/丢弃 | 有界队列的记录数与字节压力快照 |
从仓库根目录执行
构建完成后,在仓库根目录运行:
# 同主机历史对比。baseline 必须是单独构建的 OBS-1A/core dist,candidate 是本仓库 checkout 的 core dist。 node --expose-gc packages/core/benchmarks/observability-no-sink.mjs \ /tmp/oma-obs1a-baseline/packages/core/dist/index.js \ packages/core/dist/index.js # Sink/Store/OTel 矩阵。 node --expose-gc packages/core/benchmarks/observability-sinks.mjs \ packages/core/dist/index.js packages/otel/dist/index.js # 文件持久性与查询边界。 node --expose-gc packages/core/benchmarks/file-trace-store.mjs # 宽松 CI 门。 npm run bench:observability:ci其中npm run bench:observability:ci的脚本入口定义在根 package.json。历史对比的默认值是9 轮交替 × 2,000 次顶层运行;可用环境变量覆盖:OMA_BENCH_ITERATIONS、OMA_BENCH_ROUNDS、OMA_BENCH_MEMORY_ITERATIONS、OMA_BENCH_MEMORY_ROUNDS。每次覆盖都必须随结果一并记录,保证快照可复现。
脚本内部的测量方法(源码视角)
- 无 sink 对比:脚本把 baseline 与 candidate 两个 dist 用带标签的 URL query 加载进同一进程,先各跑 200 次预热,再按
round % 2交替执行,防止顺序偏差;内存测量通过global.gc()前后heapUsed差值除以保留的 run 数得到每 run 保留字节(见 observability-no-sink.mjs)。 - Sink 矩阵:
makeBatchSink默认diagnostics: 'silent',同一进程内对比 noSink / 同步 callback / batchSink 三种 runner 的中位数,并输出相对无 sink 的开销百分比与每 run 微秒中位数。 - OTel:使用官方
BasicTracerProvider+InMemorySpanExporter+SimpleSpanProcessor,先 200 次预热再取 1,000 次 post-warm-up 导出的 p95,随后单独测 1k/10k 批次的整批耗时与导出数。
当前发布快照(OBS-5 专用运行)
最终 OBS-5 专用运行环境:Nodev22.22.3,macOS/Darwin25.5.0darwin-arm64,Apple M1。历史墙钟对比为 2,000 次运行 × 9 轮交替;保留内存对比为 2,000 个保留结果 × 3 轮交替;微秒 p95 采样为 10,000 个 legacy/batching 事件与 1,000 个预热后的 OTel 记录。
| 检查项 | 结果 | 门 |
|---|---|---|
| 无 sink 中位回归 | -0.530%(baseline 18.743 ms;candidate 18.644 ms) | <1% |
| 每条结果额外保留字节 | -1.18 B(baseline 1,154.16 B;candidate 1,152.98 B) | <1,024 B |
| legacy 分发 p95 | 0.125 µs | <10 µs |
| 批量入队 p95 | 1.250 µs | <20 µs |
| OTel 转换/processor p95 | 15.208 µs | <50 µs |
五条专用门全部通过。历史 baseline 是构建的 OBS-1A 合并提交58096804a04c241a4c02943050acc4c89c884a85;测量前,baseline 快照中的关键源码 blob 与该校验和做了哈希匹配。这套数字与 docs/internal/observability-release-readiness.md 的最终结论一致(该记录同样引用-0.530%、-1.18 B/run、0.125 µs、1.250 µs、15.208 µsp95),可作为审计交叉验证。
矩阵快照
| 路径 | 1k 记录 | 10k 记录 |
|---|---|---|
| InMemory 追加 | 5.14 ms | 36.33 ms |
| InMemory 首页查询 | 15.20 ms | 42.46 ms |
| InMemory 估算保留堆 | 414,384 B | 1,878,976 B |
| OTel 转换 + 内存处理 | 12.24 ms | 79.00 ms |
| 文件追加(写完成) | 12.47 ms | 84.80 ms |
| 文件 fsync | 3.56 ms | 7.60 ms |
| 文件重开/索引重建 | 9.77 ms | 63.51 ms |
| 文件全量分页查询 | 16.33 ms | 339.57 ms |
| 文件压缩 | 32.65 ms | 933.37 ms |
文件行代表 500 / 5,000 个逻辑运行(每条运行两条记录)。文件压缩前大小为 431,818 / 4,393,820 字节,压缩后为 427,810 / 4,353,812 字节。对 1,000 条记录,按批次大小分解的追加耗时分别为:158.99 ms(批 1)、26.62 ms(批 10)、9.04 ms(批 100)、7.92 ms(批 1,000)——这印证了 file-trace-store.mjs 中按batchSize循环追加的测量逻辑,也说明批量提交对 append 吞吐有数量级影响。
多 Agent 信封与流式元数据
等价元数据信封产生的入队 p95:1 Agent 为 1.291 µs、10 Agent 为 1.250 µs、100 Agent 为 1.250 µs,每行至少 1,000 个计时样本。有代表性的 100-Agent 信封为 402 条记录、202,500 字节,仅占默认 16 MiB 队列的 1.21%。10,000 条无 payload 的流式元数据事件占用 4,196,674 字节,入队 p95 为 1.084 µs。
队列压力注入
压力注入的行为符合设计:100 条记录容量的队列接受 1,000 个事件、保留 100、报告 900 次丢弃;4 条记录等价的字节上限接受 100、保留 4、报告 96 次丢弃。未观察到无界增长或静默丢失。这一行为与 packages/core/src/observability/batching.ts 中的makeRoom/recordPriority实现互相印证:队列满时按stream_chunk(优先级 0)→ 其他事件(1)→span_start(2)→ 自包含span_end(3)的顺序丢弃最旧记录,而超限记录在入队前就被拒绝并计数。
端到端整次运行中位数
确定性整次运行中位数为:无 sink 9.001 µs/run、legacy 回调 15.185 µs/run、批量 sink 59.117 µs/run。这些端到端相对数字包含每次运行六条 v2 记录的创建与 JSON 字节核算;RFC 发布门是热路径 p95 与历史无 sink 对比,而不是「启用追踪后总工作量为零」的承诺。
内容捕获边界:content-on 明确是非目标
本版本没有公开的 content-on 模式。TraceCapturePolicy保持默认的纯元数据契约,@open-multi-agent/otel只暴露一个禁用的内容捕获扩展点。因此「content-on 基准」是明确的非目标(non-goal),而不是缺失的矩阵行。不会为了凑矩阵而添加合成的 prompt 或工具 payload 路径;纯元数据仍是产品基线。这与 docs/observability.md 的默认隐私边界一致:v2 默认不采集 prompt、completion、工具参数或结果,SensitiveDataProcessor负责过滤,即使启用observability.capture,结构化凭据字段也会被移除。
正确解读存储数字
InMemoryTraceStore的数字包含进程内索引,不代表持久性承诺;它只用于单元测试、本地检查和短生命周期进程。FileTraceStore.append()是写完成(write-complete),而flush()与close()才是fsync 边界。两者必须分开上报。- 重开时间包含对追加日志的完整扫描和内存索引重建。
- 压缩(compaction)是同目录临时写入、fsync、原子重命名,以及对父目录尽力而为的 fsync;它不是数据库 vacuum,也不是多进程测试。
- 队列压力输出预期会出现丢弃。通过门的含义是:内存保持有界、丢弃出现在统计/诊断中,而不是「任何记录都不会被丢弃」。
这些语义在 docs/observability.md 的FileTraceStore小节有完整展开:append 只表示完整的提交信封被操作系统文件写入接受、描述符已关闭,并不表示 fsync 完成;进程级崩溃通常可恢复内核已接受的写入,但 OS/断电可能丢失「已写未 fsync」的批次。基准中的 append/fsync/重开/查询/压缩五段测量,正是为了把这几层边界各自量化。
如何把这份快照用起来
- 发布前复跑专用门:按上文命令在专用、同主机环境下分别构建 OBS-1A baseline 与本仓库 candidate dist,用
--expose-gc跑 no-sink 对比,记录所有环境变量覆盖与硬件信息。 - CI 只跑报警门:
npm run bench:observability:ci用 10 倍预算和更少轮次,只负责捕获数量级回归;不要把共享运行器上的 jitter 变成发布阻塞。 - 存储取舍参考矩阵数字:1k/10k 的追加、查询、压缩耗时与字节数可用于预估本地单进程服务的规模上限;若需要多进程写入或网络文件系统一致性,则应实现存储介质无关的
TraceStore契约,而不是把FileTraceStore当共享数据库用。 - 把快照当作审计证据:性能数字应与 docs/internal/observability-release-readiness.md 中的失败注入矩阵、隐私证据和包版本决策放在一起读,形成完整的发布审计链。
- 人工智能
- AI Agent
- 多智能体
- Agent 编排
- Agent 工作流
【免费下载链接】open-multi-agent
Self-hosted TypeScript agent runtime with durable approvals and verifiable run records. Own it, approve it, audit it.
相关推荐
open-multi-agent Observability v2 发布就绪指南:公共契约、故障注入矩阵与发布验证
open multi agent Observability v2 发布就绪指南:公共契约、故障注入矩阵与发布验证 本篇指南基于 open multi agen
人工智能AI Agent多智能体Agent 编排Agent 工作流一文读懂 RuboCop v0.35.0:inherit_gem 一行共享团队规范,新增检查项与自动纠正修复全指南
一文读懂 RuboCop v0.35.0:inherit_gem 一行共享团队规范,新增检查项与自动纠正修复全指南 RuboCop v0.35.0 最大的变化是
人工智能AI Agent多智能体Agent 编排Agent 工作流使用 CocoIndex 对 Google Drive 文件夹构建语义搜索:从 Drive 文档到 pgvector 的增量索引实战
使用 CocoIndex 对 Google Drive 文件夹构建语义搜索:从 Drive 文档到 pgvector 的增量索引实战 本文围绕 CocoInde
人工智能AI Agent多智能体Agent 编排Agent 工作流
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考