# 调查与恢复

> 找到证据，止损，恢复受影响的流程和记录。

运维与恢复 · /zh/incidents/ · 2026-11-05

## 1. 弄清情况

场景：用户反馈今天早上开始收不到 OTP 验证码。

**较差的 prompt**

```text
OTP 挂了。重启短信 worker，把发送失败的重试一遍。
```

**更好的 prompt**

```text
调查 [环境] 中 [开始时间] 到 [结束时间，含时区] 之间的 [症状和受影响的流程]。已知标识：[应用版本、ID，或未知]。
用已批准的只读权限，沿着任务、外部调用和错误追踪这次尝试，并和已部署的代码、最近的改动做对比。
给出时间线，每一项附上证据出处；列出有证据支持的假设、缺少的证据或权限，以及下一步最值得做的检查。
不要发消息、重放操作、绕过检查、重启服务，也不要修改生产数据或设置。
```

原因：重启会抹掉证据，重试可能把付费短信发两遍。先用只读权限查清流程断在哪里，再采取措施。

- 从[发布与收尾](/zh/release-mgmt/)里的发布记录和没通过的上线后检查入手；和发布无关时，从上报的症状入手。
- 指定一位事故负责人，下一步怎么做由负责人决定。拉上 PM 和 QA；PM 按项目的事故流程对接客户。
- 重大节假日期间，由值班工程师和 PM、QA 一起查日志。值班安排写在项目的 runbook 里。
- 让 agent 引用匹配的记录，不要粘贴整段日志。用操作 ID、请求 ID 或任务 ID 关联事件；时间戳接近，不能证明两个事件有关联。
- 对比的对象是已部署的 artifact，不是当前分支。给事件排序之前，先写明时区换算。
- 重启或修改配置之前，先保存日志和记录，除非这样会耽误止损。密钥和个人数据要脱敏。

* **INC-1** (高风险; 由代码评审检查): **每条结论都要注明证据：** 日志行、记录或操作 ID。没有证据的解释只是假设，不管 agent 说得多肯定。

> **示例: OTP 调查记录 · 尝试 A-17**
>
> 用户反馈一直没收到验证码。范围：生产环境，应用 4.12.0，09\:40–09\:45 UTC。
>
> | 证据                      | 观察                          | 能说明什么                          |
> | ----------------------- | --------------------------- | ------------------------------ |
> | 09\:41\:02 · 客户端诊断 C-17 | 用户点了“发送验证码”，人机验证开始。         | 流程已经启动；验证结果未知。                 |
> | 后端日志搜索 Q-17             | 没有 A-17 的匹配记录。              | 暂时说明不了什么：采样、缺 ID 或查错日志源都可能是原因。 |
> | 09\:41\:03 · 错误事件 E-17  | 人机验证回调之后，客户端处理 token 时抛出异常。 | 是应用出了错；人机验证本身未必失败。             |
> | 与 E-17 对应的已部署应用源码       | 异常发生在发起后端请求之前。              | 这次尝试根本没到后端。                    |
>
> 结论：这次尝试停在了应用里，所以暂时不用去短信服务商那边查。下一步检查：用合成账号在应用 4.12.0 中复现这个回调，并确认没有别的路径会到达后端。

## 2. 止损

**Prompt**

```text
[环境] 出了 [事故]。提出能止住 [损害] 的最小可逆操作。
从当前生效的配置里读出账号、环境和目标，并报告出来。给出确切的命令或设置、预期效果、会耽误哪些正常业务，以及怎么撤销。
不要执行它，也不要修改生产数据或设置。
```

- 选能止损的最小可逆操作：暂停受影响的任务、关掉功能开关，或者用[奖励发放](/zh/value-abuse/)或[付费发送](/zh/otp/)的 kill switch。说明它会耽误哪些正常业务。
- 写明确切的目标、操作、预期效果和撤销方法。生产环境操作和破坏性操作需要[有人明确同意](/zh/security/#r-ai-2)；同意读日志不等于同意写入。
- 临执行前，再核对一遍账号、环境和目标。
- 回滚发布按它的[恢复计划](/zh/release-mgmt/#2-plan-how-each-change-is-undone)执行。回滚能阻止新的损害，但撤销不了已经造成的影响。
- 密钥泄露时，按[访问、密钥与数据](/zh/security/)处理：先吊销，再查它被怎么用过。

## 3. 恢复流程并核对受影响的操作

**Prompt**

```text
[事故] 发生在 [开始时间] 到 [结束时间，含时区] 之间。列出受影响的用户流程、待处理或失败的操作、改动过的数据和外部影响；外部服务有记录的，拿我们的记录和它对比。
提出恢复方案，写明确切的目标、预期效果和验证方法，并列出未解决的事项和负责人。
只用读权限。不要重放操作、修改记录，也不要发起会产生实际影响的调用。
```

- 先恢复并测试受影响的用户流程，再核对待处理和失败的操作。页面能用，不代表队列里的任务已经完成。
- 检查外部影响，比如已发出的消息和已创建的订单；回滚撤销不了这些。
- 结果未知时，重试之前先用原操作的标识查询；查清之前保持待处理状态，并指定负责人。见[后台任务与外部调用](/zh/jobs/)。
- 更正余额时，[新记一条分录指向原来那条](/zh/value-abuse/#r-val-2)，不改历史记录。
- 恢复数据库按这个服务的 runbook 操作。弄清恢复点，以及恢复会丢掉哪些数据；先恢复到一个单独的实例，保留受损现场，重新开放写入之前核对外部操作。

* **INC-2** (高风险; 由代码评审检查): **恢复要让受影响的用户流程重新可用，并核对受影响的操作。** 对照现有记录，检查待处理和失败的操作、改动过的数据和外部影响，必要时也要看服务商那边的证据。每个未解决的事项都要有负责人和下一步检查。

> **失败案例 (示意): 把失败的都重试一遍**
>
> 导出 worker 写完文件，还没记录完成状态就崩溃了。agent 用新的任务 ID 把所有待处理的导出重新入队，客户因此收到了重复的文件和重复的通知。如果先查原来的操作和已有的文件，就能知道哪些已经做完了。

## 4. 关闭事故

**Prompt**

```text
为 [事故] 起草事故记录：时间线、原因、采取的措施、证据引用，以及仍未完结的操作和负责人。
从 [基础分支] 拉一个分支，写一个在原始故障上会失败的回归测试，并展示它在 [修复前的 commit] 上失败。
列出误导了人或 agent 的说明和文档，以及没能找到的证据。
先不要 push 或开 PR。
```

- 确认不再产生新的损害，积压的任务已经处理完。挑一个恢复后的操作，从头到尾跟一遍。
- 在项目存放事故记录的地方写一份简短记录：时间线、原因、采取的措施、证据，以及仍未完结的操作和负责人。
- 把这次故障写成回归检查，并证明原故障存在时它会失败。改正误导了人或 agent 的说明。
- 生产问题不能等到下一个版本再修复时，把正式修复作为[紧急修复](/zh/branching-strategy/#handle-an-urgent-production-fix)发布。
- 查不到的证据，逐一建 ticket。缺失的操作 ID 或日志行，就是下一次事故的盲区。

* **INC-3** (中风险; 由代码评审检查): **每次事故都要留下一个回归检查，原故障存在时它会失败。** 正式修复走正常评审；再紧急也不能跳过。

下一步：按[交付改动](/zh/deliver-a-change/)完成正式修复。
