AI 平台工程 / 可观测性与可靠性
GenAI 可观测性与故障恢复实验台
用 OpenTelemetry SDK、Collector、Jaeger、持久队列和可控故障代理验证 GenAI trace 合同、业务结果关联、重复投递、partial success、容量、磁盘与恢复边界。
Reuse contract
复用合同
- 最近核验
- 2026/07/18
- 固定版本
83952b3bae58
输入
- 固定 GenAI trace fixture、semantic convention 合同、合成价格与业务结果
- 固定镜像 digest、Collector/Jaeger 配置、稳定 trace/span ID 和 payload hash
- 响应丢失、partial success、下游停机、队列满、max_size、ENOSPC、只读与 EIO 故障合同
输出
- trace 结构、隐私、token、合成成本、失败层与业务终态的合同违规列表
- Collector accepted/refused/sent/failed、queue、storage 与恢复阶段指标
- Jaeger 最终 unique、duplicate、missing span 以及重放和 volume 身份证据
可执行命令
trace-test测试npm run lab:trace:test- 平台
- macOS、Linux 或 Windows;离线 Node.js
- 权威输出
- TAP stdout;trace 拓扑、隐私、token、成本、失败层与业务终态合同
- 副作用
- 不修改状态
- Node.js >=22.12.0
- 仓库根目录中的固定 trace fixture
trace-capture采集npm run lab:trace:capture- 平台
- macOS 或 Linux;离线 Node.js
- 权威输出
- /tmp/younis-ai-lab-genai-traces.json
- 副作用
- 会修改运行环境
- Node.js >=22.12.0
- POSIX /tmp 可写
otel-integration-test测试npm run lab:otel-integration:test- 平台
- macOS、Linux 或 Windows;离线 fixture
- 权威输出
- TAP stdout;SDK batching、采样、属性治理、exporter 故障与最终查询合同
- 副作用
- 不修改状态
- Node.js >=22.12.0
- 仓库根目录已安装锁定 OpenTelemetry 依赖
otel-integration-run运行npm run lab:otel-integration:run- 平台
- macOS Docker Desktop 或 Linux Docker
- 权威输出
- /tmp/younis-ai-lab-genai-otel-integration.json
- 副作用
- 会修改运行环境
- Docker Compose 与固定镜像 digest
- POSIX /tmp 可写且实验端口可用
otel-recovery-test测试npm run lab:otel-recovery:test- 平台
- macOS、Linux 或 Windows;离线 fixture
- 权威输出
- TAP stdout;SIGKILL、named volume、queue drain 与 Jaeger 终态合同
- 副作用
- 不修改状态
- Node.js >=22.12.0
- 仓库根目录中的固定结果与 Compose 配置
otel-recovery-run运行npm run lab:otel-recovery:run- 平台
- macOS Docker Desktop 或 Linux Docker
- 权威输出
- /tmp/younis-ai-lab-otel-persistent-recovery.json
- 副作用
- 会修改运行环境
- Docker Compose 与固定 Collector/Jaeger 镜像
- 专用端口和 named volumes 可创建
otel-saturation-test测试npm run lab:otel-saturation:test- 平台
- macOS、Linux 或 Windows;离线 fixture
- 权威输出
- TAP stdout;queue capacity、503、拒绝账本、恢复与定向重放合同
- 副作用
- 不修改状态
- Node.js >=22.12.0
- 仓库根目录中的固定结果与配置
otel-saturation-run运行npm run lab:otel-saturation:run- 平台
- macOS Docker Desktop 或 Linux Docker
- 权威输出
- /tmp/younis-ai-lab-otel-queue-saturation.json
- 副作用
- 会修改运行环境
- Docker Compose 与固定镜像
- 专用端口和 named volumes 可创建
otel-ambiguity-test测试npm run lab:otel-ambiguity:test- 平台
- macOS、Linux 或 Windows;离线 fixture
- 权威输出
- TAP stdout;commit 后响应丢失、字节一致重试与 Jaeger 去重终态
- 副作用
- 不修改状态
- Node.js >=22.12.0
- 仓库根目录中的固定代理结果
otel-ambiguity-run运行npm run lab:otel-ambiguity:run- 平台
- macOS Docker Desktop 或 Linux Docker
- 权威输出
- /tmp/younis-ai-lab-otel-commit-ambiguity.json
- 副作用
- 会修改运行环境
- Docker Compose 与固定镜像
- 故障代理和实验端口可用
otel-partial-test测试npm run lab:otel-partial:test- 平台
- macOS、Linux 或 Windows;离线 fixture
- 权威输出
- TAP stdout;HTTP 200 partial success、Collector metrics 与缺失 span 合同
- 副作用
- 不修改状态
- Node.js >=22.12.0
- 仓库根目录中的固定 partial-success 结果
otel-partial-run运行npm run lab:otel-partial:run- 平台
- macOS Docker Desktop 或 Linux Docker
- 权威输出
- /tmp/younis-ai-lab-otel-partial-success.json
- 副作用
- 会修改运行环境
- Docker Compose 与固定镜像
- partial-success 代理和实验端口可用
otel-drain-test测试npm run lab:otel-drain:test- 平台
- macOS、Linux 或 Windows;离线 fixture
- 权威输出
- TAP stdout;12 批顺序、排空速率、queue 指标和 bbolt 文件增长合同
- 副作用
- 不修改状态
- Node.js >=22.12.0
- 仓库根目录中的固定 drain 结果
otel-drain-run运行npm run lab:otel-drain:run- 平台
- macOS Docker Desktop 或 Linux Docker
- 权威输出
- /tmp/younis-ai-lab-otel-drain-rate.json
- 副作用
- 会修改运行环境
- Docker Compose 与固定镜像
- 专用 volume、端口与 POSIX /tmp
otel-storage-full-test测试npm run lab:otel-storage-full:test- 平台
- macOS、Linux 或 Windows;离线 fixture
- 权威输出
- TAP stdout;bbolt max_size、queue gauge 漂移、恢复与重放合同
- 副作用
- 不修改状态
- Node.js >=22.12.0
- 仓库根目录中的固定 max_size 结果
otel-storage-full-run运行npm run lab:otel-storage-full:run- 平台
- macOS Docker Desktop 或 Linux Docker
- 权威输出
- /tmp/younis-ai-lab-otel-storage-full.json
- 副作用
- 会修改运行环境
- Docker Compose 与固定镜像
- 专用 volume、端口与 POSIX /tmp
otel-enospc-test测试npm run lab:otel-enospc:test- 平台
- macOS、Linux 或 Windows;离线 fixture
- 权威输出
- TAP stdout;物理 ENOSPC、字节一致复制、恢复和定向重放合同
- 副作用
- 不修改状态
- Node.js >=22.12.0
- 仓库根目录中的固定 ENOSPC 结果
otel-enospc-run运行npm run lab:otel-enospc:run- 平台
- macOS Docker Desktop 或 Linux Docker
- 权威输出
- /tmp/younis-ai-lab-otel-enospc-recovery.json
- 副作用
- 会修改运行环境
- Docker Compose、固定镜像与 tmpfs volume 支持
- 专用端口和 POSIX /tmp
otel-readonly-test测试npm run lab:otel-readonly:test- 平台
- macOS、Linux 或 Windows;离线 fixture
- 权威输出
- TAP stdout;rw-ro-rw、启动 fail closed、queue 恢复与重放合同
- 副作用
- 不修改状态
- Node.js >=22.12.0
- 仓库根目录中的固定只读恢复结果
otel-readonly-run运行npm run lab:otel-readonly:run- 平台
- macOS Docker Desktop 或 Linux Docker
- 权威输出
- /tmp/younis-ai-lab-otel-readonly-storage.json
- 副作用
- 会修改运行环境
- Docker Compose 与固定镜像
- 可切换挂载模式的专用 volume 和端口
otel-eio-test测试npm run lab:otel-eio:test- 平台
- macOS、Linux 或 Windows;离线 fixture
- 权威输出
- TAP stdout;linear-error-linear、health/OTLP 分叉、fsck、恢复与重放合同
- 副作用
- 不修改状态
- Node.js >=22.12.0
- 仓库根目录中的固定 EIO 结果
otel-eio-run运行npm run lab:otel-eio:run- 平台
- Linux amd64/arm64;需要 privileged device-mapper
- 权威输出
- /tmp/younis-ai-lab-otel-runtime-eio.json
- 副作用
- 会修改运行环境
- Linux root、Docker、device-mapper、loop device 与 ext4 工具
- 专用块设备、端口和 POSIX /tmp
可直接复用
- 为 GenAI Agent 定义不记录原始 prompt 的低基数 trace 与业务结果 join 合同
- 验证 Collector 持久队列在进程崩溃、容量耗尽和存储故障下的真实终态
- 设计稳定 ID、拒绝账本、定向重放、磁盘水位和 queue age 告警
明确边界
- 输入主要是合成 span 和确定性故障,结果不是生产 trace 样本、模型质量或用户满意度证据。
- 多数容器实验运行在单机 Docker Desktop;不覆盖节点丢失、跨节点故障转移、真实 SSD 损坏或长期负载。
- Jaeger/Collector 对重复 span、partial success、health 和错误码的行为与固定版本相关,升级后必须重新验收。
固定结果工件
- GenAI trace 合同校准结果
trace-contract-result- 仓库路径
labs/genai-trace-contract/results/2026-07-15-results.json- SHA-256
8f877139c703ba19020b5e39042db5ee30b8fb44b3f2745d318093a72519a82b- 生成/验证命令
trace-capture
- SDK 到 Collector/Jaeger 集成结果
otel-integration-result- 仓库路径
labs/genai-otel-integration/results/2026-07-15-results.json- SHA-256
f97f23147880aa0e437b93750e800290c7fb14afb62e9e7f6db5801c04e779a9- 生成/验证命令
otel-integration-run
- SIGKILL 持久队列恢复结果
otel-recovery-result- 仓库路径
labs/otel-persistent-recovery/results/2026-07-15-results.json- SHA-256
7e9c5f17cc28b6ceb7522e45b8b78f541fd695085a19eff36be20fd39ee9f053- 生成/验证命令
otel-recovery-run
- 队列饱和与定向重放结果
otel-saturation-result- 仓库路径
labs/otel-queue-saturation/results/2026-07-15-results.json- SHA-256
002a85a03d15555fd36105149038977a43d92c158f1efe62bdd9400feb2ae13a- 生成/验证命令
otel-saturation-run
- 后端 commit ambiguity 结果
otel-ambiguity-result- 仓库路径
labs/otel-commit-ambiguity/results/2026-07-15-results.json- SHA-256
0f467578e373ff928791e46cd6870f4cd98ffabf2de9833d28d59efe380aeab3- 生成/验证命令
otel-ambiguity-run
- OTLP partial success 结果
otel-partial-result- 仓库路径
labs/otel-partial-success/results/2026-07-15-results.json- SHA-256
ef3981e27f13ac26ec33dc4bbff5c498bffa4d0c7b23f0e7e9a6e43a8616d5c3- 生成/验证命令
otel-partial-run
- 队列排空与磁盘增长结果
otel-drain-result- 仓库路径
labs/otel-drain-rate/results/2026-07-15-results.json- SHA-256
169580ecc1c700f6a551143bb3f15b190b8a31f689838a5426a31a859310e236- 生成/验证命令
otel-drain-run
- bbolt max_size 满盘结果
otel-storage-full-result- 仓库路径
labs/otel-storage-full/results/2026-07-15-results.json- SHA-256
319b1bbaee0458ee1e4b7a164c76ba32a38439230d722da56a089d5474d155be- 生成/验证命令
otel-storage-full-run
- tmpfs ENOSPC 异卷恢复结果
otel-enospc-result- 仓库路径
labs/otel-enospc-recovery/results/2026-07-15-results.json- SHA-256
294a7776647affe5b847b182b701a4a7f9a0059b18d3981e31f1ed5f760302ba- 生成/验证命令
otel-enospc-run
- 只读队列启动恢复结果
otel-readonly-result- 仓库路径
labs/otel-readonly-storage/results/2026-07-16-results.json- SHA-256
01a0e7be8e4e105e81beae1848d8cf661d1de8fa94bad15c8407d059f3a82725- 生成/验证命令
otel-readonly-run
- 运行期 device-mapper EIO 结果
otel-eio-result- 仓库路径
labs/otel-runtime-eio/results/2026-07-16-results.json- SHA-256
cbb2006ee1481ac7ce0437b804a19c9043fdfbd0463fdc75517839bd964b8a6b- 生成/验证命令
otel-eio-run
项目范围
Agent 可观测性不能只回答“模型调用有没有报错”。一条可交付链路还需要回答:失败发生在 model、retrieval 还是 tool;token 与成本属于哪个业务尝试;技术 span 成功是否真的对应业务完成;Collector 已接受的数据最终是否进入后端;持久队列在响应丢失、容量和存储故障下还能恢复哪些批次。
项目用 11 个独立 lab 把这些问题拆成可判定合同。每个容器实验固定镜像、输入文件哈希、稳定 trace/span ID、故障窗口和最终 Jaeger 查询;结果 JSON 保存原始合同的结构化摘要,而不是只保留终端截图。
Trace 与业务结果
genai-trace-contract 使用仓库内 fixture 校准 Agent、chat、retrieval 与 tool 父子结构、错误归因、token、合成价格、隐私负例和业务结果 join。6 条校准轨迹全部通过,12 条负向控制全部被识别;5 个 chat span 汇总 4,940 input、620 output tokens 和 0.02414 美元合成成本。这里的价格是 fixture,不是供应商账单。
genai-otel-integration 使用真实 OpenTelemetry SDK,经 OTLP/HTTP 发往 Collector 和 Jaeger。24 个 span 分 5 批送达,20/20 条预期 trace 可由 API 查询;实验同时验证 50% head sampling 的固定 drop、receiver 不可达、六类属性治理、技术成功但业务 rejected,以及 memory Jaeger 重建后数据丢失的保留边界。
九组投递与存储故障
| Lab | 受控变量 | 权威验收 |
|---|---|---|
otel-persistent-recovery |
Jaeger 停机后 SIGKILL 并删除 Collector/Jaeger | 原 volumes 恢复 7 unique queued spans,0 duplicate |
otel-queue-saturation |
queue capacity 固定为 2 batches | 第 3 批 503;恢复两批,显式重放后 3/3 |
otel-commit-ambiguity |
Jaeger 200 后代理重置响应 | Collector 重投相同 payload;后端最终 3 unique、0 duplicate |
otel-partial-success |
4 spans 中只转发 2 个并返回 HTTP 200 partial success | Collector sent=4/failed=0,但 Jaeger 2 present、2 missing |
otel-drain-rate |
12 批积压、单 consumer、后端每次延迟 250 ms | 3.8342 spans/s 排空;queue 归零后 bbolt 仍为 262,144 bytes |
otel-storage-full |
bbolt max_size 65,536 bytes,queue capacity 20 | 4 批可恢复、2 批被拒;定向重放后 6 unique |
otel-enospc-recovery |
64 KiB tmpfs 真实写满 | 异卷字节一致复制恢复 5 批,只重放第 6 批 |
otel-readonly-storage |
replacement 只读挂载原 queue volume | 启动失败且数据库哈希不变;恢复读写加载 3 批并重放第 4 批 |
otel-runtime-eio |
运行期 device-mapper 从 linear 切到 error | health 仍 200 但第 4 批 503;修复与重放后 4 unique |
这些实验共同说明 HTTP 200、Collector accepted、queue gauge、health endpoint 和 Jaeger exporter metrics 都不是单独的业务成功证明。恢复决策至少需要稳定 ID、拒绝批次账本、磁盘与 queue 指标、后端最终查询和明确的 replay 规则。
运行方式
纯合同实验可以直接执行 Node 测试;容器实验需要 Docker Engine、Compose、各 README 列出的空闲本机端口,以及 EIO 实验所需的 Docker Desktop LinuxKit privileged device-mapper 能力。
npm run lab:trace:test
npm run lab:otel-integration:test
npm run lab:otel-recovery:test
npm run lab:otel-saturation:test
npm run lab:otel-ambiguity:test
npm run lab:otel-partial:test
npm run lab:otel-drain:test
npm run lab:otel-storage-full:test
npm run lab:otel-enospc:test
npm run lab:otel-readonly:test
npm run lab:otel-eio:test
对应 :run 或 :capture 命令会重建受控环境并写新报告。运行脚本只应操作各自 Compose project、named volume、tmpfs 或专用 loop-backed device;不要把结果工件与 Docker 临时目录混用。
复用原则
先复用 trace contract 和 stable identity,再按系统风险选择故障实验。没有持久队列的系统可先做 exporter failure 与 partial success;已启用 file storage 的系统必须继续验证 max_size、底层容量、只读、恢复挂载和排空速率。每次 Collector、Jaeger、存储或 SDK 升级都应冻结新镜像 identity 并重跑最终状态断言。
生产设计还需增加 queue age、磁盘实际水位、refused/enqueue failed、partial success 和后端查询抽样。拒绝请求只能按稳定业务 ID 定向重放;无法确定后端是否 commit 的批次不能无条件重发。
证据边界
11 个 lab 均为 reproduced,不是生产持续运行数据。单机 Docker host 上保留 volume 的恢复不等于节点丢失恢复,device-mapper error 不等于所有真实磁盘故障,固定 Jaeger 行为也不等于任意后端 exactly-once。项目证明的是可重放的故障合同和观察方法,生产结论必须在目标版本、目标存储与真实流量约束下重新验证。
Research links
关联文章
这些文章分别提供问题背景、设计依据、实验验证或运行证据。
- 发布 2026/07/15
本文定义 Agent、model、retrieval 与 tool span 合同,并校准隐私、token、合成成本、失败层和业务结果关联。
- 发布 2026/07/15
本文验证真实 OpenTelemetry SDK 导出的 24 个 span 穿过 Collector 后可由 Jaeger API 查询,并覆盖采样、属性治理和 exporter 故障。
- 发布 2026/07/15
本文验证 Collector 与 Jaeger 被 SIGKILL 并删除后,保留 named volumes 可恢复 7 个 queued span,最终 7 unique、0 duplicate。
- 发布 2026/07/15
本文证明 2-batch 队列在第 3 个请求返回 503,恢复只包含已接收两条,显式重放稳定 ID 后才达到 3/3。
- 发布 2026/07/15
本文制造 Jaeger 已写入但响应被重置的 commit ambiguity,观察 Collector 重投相同 payload 与后端最终去重状态。
- 发布 2026/07/15
本文验证 HTTP 200 partial success 下 Collector sent=4 仍可能只有 2 个 span 到达 Jaeger,缺失必须由响应与终态共同识别。
- 发布 2026/07/15
本文记录 12 批积压的排空时间、顺序和 bbolt 文件增长;在固定版本、配置与本次进程生命周期内,queue 归零后文件尺寸保持不变。
- 发布 2026/07/15
本文验证 bbolt max_size 先于 queue capacity 拒绝写入,并证明 queue gauge 可高估可恢复 batch 数。
- 发布 2026/07/15
本文在 64 KiB tmpfs 制造真实 ENOSPC,执行字节一致异卷复制,恢复已接受五批并只重放被拒第六批。
- 发布 2026/07/16
本文把原 queue volume 只读挂载给 replacement,验证进程 fail closed、数据库未变,恢复读写后加载三批并定向重放。
- 发布 2026/07/16
本文在线切换 device-mapper error target,证明 health 仍为 200 时 OTLP 已 503;修复、fsck、重启与定向重放后终态完整。