---
title: 关于证据，而非仪表盘
description: 为什么 sigiro 的回答是一份排序列表、每一行都带一条可执行的查询，而不是一张图表。什么是基线，以及这个设计拒绝做什么。
sidebar:
  order: 3
---
问 sigiro 某个服务为什么不健康，它会返回一整块结构化的证据。这一块以一份排序列表开
头，列出值得关注的内容。每一项都带有一行含具体数字的摘要，以及一条可以运行来验证该
结论的 SQL 查询。

它不会返回图表，也永远不会。本页解释这个选择：sigiro 如何判定什么是异常、如何对发现
的内容排序、为什么查询要随答案一同交付，以及这个设计拒绝做什么。

## 读者看不见

对人来说，图表是个好答案。人扫一眼形状，注意到 14:10 处的台阶，然后继续往下走。几十
年来的可观测性工具都围绕这一刻构建，而且构建得很好。

但如今修复生产环境的是 agent，而 agent 看不见图表。它只能读文本。于是在修复之前，闭
环就断了：agent 能写补丁，却查不出哪里坏了，也判断不出自己的补丁是否奏效。

sigiro 就是弥合这个缺口的部分。它是仪表盘之下的一层，而不是仪表盘的替代品。如果你想
要面板，那就自带面板，让它们指向同一批表。

## 基线，而非阈值

这里没有什么需要配置，因为没有阈值需要配置。

每条序列都与它自身的近期历史作比较。sigiro 在 7 天窗口内把每个信号归入 5 分钟的桶，
然后对分桶后的序列运行贝叶斯在线变点检测。变点是序列统计特征发生变化的位置。整个检
测器唯一的结构性选择是一个方向：当变点之后的均值比之前更糟时，sigiro 才保留这次偏
移。错误更多、更慢、更吵、更贵。这是一次比较，而不是某个人随手挑的数字。

被检测的信号有五种：

| 信号 | 键 | 度量 |
| --- | --- | --- |
| `error_rate` | 服务与操作 | 每个桶中错误 span 的百分比 |
| `latency_p95` | 服务与操作 | 每个桶中 span 时长的 p95 |
| `log_volume` | 服务 | 每个桶中的日志条数 |
| `error_log_rate` | 服务 | 每个桶中 ERROR 级日志的百分比 |
| `profile_cost` | 服务 | 每个桶中的性能剖析总开销 |

由此有两个推论，两个都会让人意外。

一直很慢的服务不是异常。如果 p95 整周都是两秒，那两秒对这个服务就是正常的，sigiro
什么都不会说。这是正确的，同时也是一个真实的局限：sigiro 告诉你的是什么_变了_，而长
期存在的故障并不会变。长期故障请用查询来找。

刚起来的服务器什么也查不出。检测器需要若干个桶，序列才有可供偏离的统计特征。常量序列
和极短序列在构造上都产生不了变点，这也是为什么这里同样没有"最少数据点"的选项。

5 分钟的桶也是刻意留的钝边。它让检测器只标记持续性的偏移，忽略一分钟的抖动。一分钟的
中断对这个检测器是不可见的，呼叫值班的告警系统才是应对它的合适工具。

## 从偏移到事件，再到排序列表

真正的故障会同时推动多个信号。错误上升、延迟上升、日志量上升，而这三者是一个事件，不
是三个。

因此在排序之前先做关联。落在同一个 5 分钟检测桶、或落在相邻桶中的两次偏移，会合并为
一个_事件_条目，该条目携带其成员偏移，以及在时间上邻近的可疑发布（如果有）。没有同伴
的偏移则保留为单信号条目。

然后对列表排序。第一排序键是严重程度。在此之内，异常类条目先按关联信号的数量排，再按
偏移幅度排，最后按原始差值排，每个键都从大到小。于是一个三信号事件会排在孤立的延迟偏
移之前——对于只有一个问题、注意力有限的读者来说，这才是正确的顺序。

幅度这一项值得单说一句，因为它天真的版本是错的。sigiro 使用对称相对变化
`(after − before) / (after + before)`，而不是原始差值。原始差值无法把以微秒计的延迟
偏移与以百分比计的错误率偏移放在一起比较：不管微秒意味着什么，它每次都赢。相对形式是
无量纲的，因此不同信号的偏移之间能够诚实地排序。

不属于异常的条目排在最后：各类不一致，比如有错误却没有日志；你可以采取行动的覆盖缺
口；以及遥测管道自身的故障，比如重复 span 或孤儿 span。最后这一组回答的是读者本应最
先问的问题：这些数据到底可不可信？

## 查询随答案一同交付

每一条给出结论的行都带有 `drill_down_sql`：产生该结论的那条查询，可以直接发给
`/v1/query`。

这是我们会最坚决捍卫的设计部分，理由并不是方便，而是：可以验证的结论是另一种性质的结
论。你不必信任一份自动诊断，你的 agent 也不必。运行那条查询。把窗口放宽。改掉过滤条
件，看看结论是否还成立。

它还修掉了另一种方案修不掉的失败模式。无法验证的摘要只能被相信或被丢弃，而拿到一份不
可证伪摘要的 agent 会选择相信它。查询是可证伪的。当 sigiro 判断错了——它有时确实会错
——查询就是你以极低成本发现这一点的途径。

还有第二个更不张扬的好处。查询也是你下一个问题的起点。复制它，改一个谓词，你就问出了
sigiro 从未想到的东西。这也是为什么下钻查询是针对真实表的原始 SQL，而不是一个指向已
保存视图的链接。

## 为什么是一次调用而不是五次

老式的排查形态是五次往返。查指标存储、推理，查追踪存储、推理，查日志存储、推理，查事
件存储、推理。每次往返都很便宜，每次模型调用都不便宜，于是模型主导了墙上时钟，总耗时
攀升到数十秒。而且随着上下文窗口被碎片填满，agent 还会丢掉早先的上下文。

`/v1/diagnose` 在 sigiro 内部并行运行这些查询，返回一整块关联好的结果。排序列表是在
Rust 里用这些查询已经返回的数据组装出来的，因此不额外花费任何查询，也不额外占用内
存。一次往返、一次模型调用、一幅连贯的图景。一个完整的块是几十 KB 的结构化证据，而不
是原始 span 转储，对现代上下文窗口而言只占很小一部分。

这个设计为此付出的代价是更大的响应体和更慢的单次调用。当读者在两次调用之间要推理数秒
时，这个取舍是对的；而对于每十秒刷新五十个面板的仪表盘来说，它是错的。sigiro 是为前
一种读者构建的。

## 这个设计拒绝做的事

一份诚实的清单，因为其中每一项都是合理的期待。

**它不会告警。**没有通知通道，也没有向人升级的路径。`GET /v1/anomalies` 是一张供你
轮询的表。预期的调用方是定时 agent 或 cron 作业。

**它不按业务影响排序。**排序是统计意义上的。sigiro 不知道你哪个服务在收款，所以后台
worker 的一次大幅偏移可能排在结账链路的一次小幅偏移之前。哪个服务重要由你知道；哪条
序列动了由 sigiro 知道。

**它不解释原因。**排序列表说明什么变了、变了多少、哪些信号一起动了，并且在时间上邻近
时点出一个可疑发布。时间上的相关不等于因果。证据和下钻查询是交给你的判断，或你 agent
的判断的，而诊断是一个假设，附带产生它的那条查询。

**它不画任何东西。**没有面板要搭，也没有面板会腐烂。这正是它的用意所在，同时也是某些
读者最会怀念的东西。

## 继续阅读

- [关于这些表，以及它们描述的是哪台机器](/docs/explanation/tables)
- [关于发布检测，以及你的 span 必须携带什么](/docs/explanation/deploys)
  —— 可疑发布从何而来
- [API 参考](/docs/reference) —— `Finding` 与 `DiagnosisBlock` 字段全集
