新人第一次把 AI 用进排障流程时,最容易被惊艳到的能力,通常不是“它能写代码”,而是它能把一段混乱的报错、日志和调用链解释得非常像那么回事。
你把异常栈贴进去,它会告诉你:
- 这个问题大概率是缓存失效;
- 那个字段可能存在并发竞争;
- 这里应该补一个幂等判断;
- 这段重试逻辑可能造成了重复执行;
- 修复方式可以改成某种写法。
而且它说得往往很完整:有原因、有链路、有代码、有结论。
真正危险的时刻,也正是在这里。
不是它完全胡说八道,而是它给出的解释刚好符合你当下看到的现象,于是你很容易把“一个合理假设”误当成“已经确认的根因”。
GitHub 在 AI 代码审查与代理工具的官方说明里明确提醒:模型可能遗漏问题,也可能产生基于误解的错误反馈;看上去有效的代码建议,也不一定语义正确、足够安全,仍需要人工审查和测试。
这不是 AI 独有的问题。
很多新人自己 Debug 时,也会犯同一个错误:看到一个最像答案的解释,就立刻开始改代码。
只是 AI 会把这个错误放大,因为它能让解释显得更完整、更有逻辑、更像一份已经结束的事故复盘。
一个典型场景:Webhook 重复投递,到底是不是“接口被调用了两次”
假设你维护一个支付回调服务。
业务方反馈:偶尔会出现同一笔订单被重复处理,导致库存扣减两次,或者积分被重复发放。
你从日志里看到两条几乎一样的请求:
POST /payment/callback
event_id=evt_8f2a
order_id=20260623001
status=paid
时间间隔只有几百毫秒。
于是你把相关代码、日志和错误现象交给 AI。它很快给出一个解释:
这是第三方平台重复投递回调导致的。
建议在业务层先查询 event_id 是否存在;如果存在,就直接跳过。
它还顺手给出一段代码:
const existed = await eventRepository.findByEventId(eventId);
if (!existed) {
await eventRepository.create({
eventId,
orderId,
status: "paid",
});
await orderService.markPaid(orderId);
}
新人看到这里,很容易觉得:有道理,重复回调嘛,先查再写,不就解决了吗?
问题是,这段代码看起来防重复,实际上仍然可能重复。
因为两个请求可能同时走到这里:
请求 A:findByEventId -> 没找到
请求 B:findByEventId -> 没找到
请求 A:create
请求 B:create
如果数据库层没有唯一约束,或者后续的状态更新、库存扣减没有被事务保护,重复处理依然可能发生。
这就是 AI 辅助 Debug 里很典型的一种陷阱:
它找到了一个“现象合理”的解释,但没有验证这个解释是否覆盖了真正的并发条件。
新人最容易跳过的一步:先证明“它不是别的问题”
很多排障流程习惯这样走:
看到报错
↓
让 AI 给原因
↓
选一个最像的解释
↓
改代码
↓
本地不报错
↓
以为问题解决
这个流程速度很快,但它缺了最重要的一步:
你有没有主动去找一个能推翻当前结论的证据?
真正稳一点的排障顺序应该是:
观察现象
↓
提出假设
↓
设计反证
↓
最小复现
↓
确认触发条件
↓
修复
↓
验证修复是否覆盖原始问题
这里的“反证”听起来有点抽象,其实就是问一句:
假如 AI 说的根因是错的,我该怎么最快发现它错了?
回到重复 Webhook 的例子。
AI 的假设是:第三方重复投递导致业务重复执行。
那你至少要验证三件事:
| 问题 | 需要看的证据 | 能排除什么 |
|---|---|---|
| 是否真的收到重复请求 | 请求 ID、事件 ID、时间戳、签名信息 | 不是内部代码重复触发 |
| 两次请求是否并发进入 | Trace、请求耗时、数据库写入时间 | 不是串行重试导致 |
| 数据库是否有唯一约束 | 表结构、索引、执行计划 | 不是“先查再写”竞争窗口 |
| 业务动作是否具备幂等性 | 库存、积分、订单状态的更新路径 | 不是下游重复消费 |
你会发现,AI 给你的“重复回调”解释,最多只是排障的起点。
它不是终点。
不要问“根因是什么”,先问“有哪些可能的根因”
新人向 AI 提问时,常见写法是:
为什么我的支付回调会重复扣库存?帮我分析根因并给出修复代码。
这个问题太容易让模型直接收束到一个答案。
更稳妥的问法,是先让它列出竞争假设,再要求每个假设都给出验证路径:
请不要直接给出唯一根因。
当前现象:
- 同一 event_id 偶尔触发两次业务处理;
- 两次请求间隔小于 1 秒;
- 日志中存在相同 order_id;
- 数据库当前没有确认是否存在唯一索引。
请按以下格式输出:
1. 至少列出 4 个可能原因;
2. 每个原因的支持证据;
3. 每个原因的反证方式;
4. 最小复现方法;
5. 如果验证为真,推荐的修复方向;
6. 哪些结论仍需要人工确认。
不要假设第三方重复投递一定是唯一原因。
这个 Prompt 的关键不在于“让 AI 更聪明”,而在于强迫它保留不确定性。
因为排障最怕的不是不知道答案。
最怕的是太早以为自己已经知道答案。
一个可复用的 Debug 记录模板
我建议新人做排障时,把 AI 的回答先放进下面这张表,而不是直接复制它给出的代码。
| 项目 | 需要记录的内容 |
|---|---|
| 已确认事实 | 日志、监控、调用链、错误码、时间范围 |
| 当前现象 | 到底发生了什么,而不是你猜测为什么发生 |
| 假设 A | 例如第三方重复投递 |
| 假设 B | 例如内部消息重试导致重复消费 |
| 假设 C | 例如数据库并发窗口导致重复写入 |
| 可证伪方式 | 怎样的结果会推翻这个假设 |
| 最小复现 | 本地或测试环境如何构造同样条件 |
| 修复候选 | 业务层幂等、数据库唯一约束、事务、消息去重 |
| 验证结果 | 修复后哪些路径已经覆盖,哪些仍未覆盖 |
这张表最大的价值,是让你把“AI 的解释”降级成“待验证假设”。
你不再需要和模型争论它对不对。
你只需要问:我手里有没有证据能验证它?
最小复现,比“加一堆日志再观察”更有价值
重复请求这类问题,最容易犯的错是线上加几条日志,然后等下次问题再出现。
这不是不能做,但太被动。
更好的方式是,在测试环境里直接制造并发条件。
例如,下面是一个很简化的并发测试思路:
it("同一个 event_id 并发进入时,只应处理一次", async () => {
const event = {
eventId: "evt_8f2a",
orderId: "order_1001",
status: "paid",
};
await Promise.all([
handlePaymentCallback(event),
handlePaymentCallback(event),
]);
expect(await orderRepository.getPaidCount(event.orderId)).toBe(1);
expect(await inventoryRepository.getDeductionCount(event.orderId)).toBe(1);
});
这段测试不保证你已经修好问题,但它至少能暴露一个事实:
你的“防重复逻辑”到底能不能抵抗并发,而不是只抵抗串行重复。
如果测试失败,说明问题不是“加一个 if 判断”能解决的。
这时 AI 可以继续帮你列出候选方案,例如:
- 数据库唯一索引;
INSERT ... ON CONFLICT;- 分布式锁;
- 消息去重表;
- 幂等状态机;
- 事务与 outbox 模式。
但哪一种适合你,取决于业务一致性要求、数据库能力、失败恢复方式和下游副作用。
这部分不能只靠“模型给出的最佳实践”决定。
OpenAI 的 Codex 官方最佳实践也建议,不要停在“让工具改代码”这一步,而应要求它生成必要测试、运行对应检查、确认结果并审查改动;前提是人要先告诉它什么才算“好结果”。
把 AI 的结论改写成“需要被打脸的假设”
这是我觉得新人最值得建立的一个习惯。
当 AI 给出一句类似:
根因应该是缓存 TTL 配置错误。
不要马上问:
那你帮我修。
先改成:
把“缓存 TTL 配置错误”视为一个待验证假设。
请告诉我:
1. 哪些日志或指标必须存在,才能支持该假设;
2. 哪些现象一旦出现,就能推翻该假设;
3. 如何构造一个最小测试验证 TTL 是否导致问题;
4. 如果 TTL 正常,还有哪些优先级更高的备选原因;
5. 当前信息不足以判断的部分有哪些。
这类提问会让 AI 从“答案生成器”变成“排障助手”。
差别很大。
前者容易让你直接接受结论。
后者会迫使你保留验证意识。
新人把 AI 纳入日常排障前,先补一张检查清单
当 ChatGPT Plus 这类工具开始参与日志整理、报错解释、测试补充和 Debug 辅助时,新人最需要准备的不是一套万能 Prompt,而是一份不会跳过验证的检查清单。
第一次把 AI 工具纳入开发工作流时,建议把使用说明、异常处理和必要信息留存一起写进检查清单;相关准备项可按需要参考: gpt328
下面这份清单可以直接拿去用:
【AI 辅助排障检查清单】
□ 我提供的是已脱敏日志,没有 Token、Cookie、密钥、真实用户信息。
□ 我区分了“已确认事实”和“AI 推测原因”。
□ 我至少保留了两个备选假设。
□ 每个主要假设都有可执行的反证方法。
□ 我能在本地或测试环境构造最小复现。
□ 修复方案没有直接改动生产数据。
□ 我知道修复失败时如何回滚。
□ 我确认了测试覆盖的是原始触发条件,而不是理想路径。
□ 我没有把“测试通过”误当成“根因已确认”。
□ 最终合并前,仍由人确认业务副作用和边界。
有些信息,不能为了“让 AI 看得更懂”就直接贴
排障时最常见的冲动,是把完整线上日志、请求体、数据库记录原样发给 AI。
这很危险。
因为日志里常常混着:
- 用户手机号、邮箱、地址;
- Token、Cookie、Session;
- 内部域名和服务地址;
- 支付标识、订单号;
- 数据库连接参数;
- 内部接口字段;
- 错误堆栈里的敏感上下文。
更稳的原则是:
只提供解决当前问题必须提供的信息。
例如:
- 订单号可以替换为固定占位符;
- 用户标识可以哈希或截断;
- Token 只保留结构,不保留真实值;
- 内部域名替换为
service-a、service-b; - SQL 参数保留类型和数量,不保留真实数据。
GitHub 的官方责任使用说明同样提醒,AI 生成或分析的内容可能暴露敏感信息或引入安全问题;对于安全敏感场景,需要持续进行人工审查和充分测试。
修复后别急着宣布“已解决”,还要验证三层结果
真正完成一次排障,至少要验证三层。
第一层:现象消失了吗
原本会重复处理的并发请求,在测试环境里是否已经只执行一次?
这是最基础的一层。
第二层:修复有没有带来新问题
例如你给数据库加了唯一约束后:
- 正常重试会不会被误判成异常;
- 回调顺序变化会不会导致合法请求被丢弃;
- 旧数据是否存在重复值,迁移会不会失败;
- 失败后是否能重新处理。
第三层:生产环境能不能接住异常
即使修复正确,也要考虑:
- 唯一冲突出现时,日志是否能定位;
- 告警是否能区分重复投递和真正失败;
- 是否能灰度;
- 是否支持快速回滚;
- 是否有人负责确认最终业务结果。
GitHub 对 AI 生成代码的审查建议,也把功能检查、上下文与意图验证、依赖风险、协作审查列为完整验证流程的一部分,而不是只看代码是否能运行。
最后:AI 可以帮你缩短“猜”的时间,但不能省掉“证”的过程
新人使用 AI 做 Debug,真正要防的不是它偶尔答错。
而是它答得太顺,让你失去继续验证的耐心。
AI 很适合帮你:
- 整理错误栈;
- 提取调用链;
- 列出可疑模块;
- 生成复现步骤;
- 补测试样例;
- 提供多个修复候选;
- 帮你审查修复后的 Diff。
但它不应该替你决定:
- 哪个才是真正根因;
- 哪个修复方案符合业务约束;
- 哪些数据可以处理;
- 哪个改动能安全上线;
- 出问题后谁承担责任。
排障不是“找到一个听起来合理的答案”。
排障是把一个答案不断拿证据去验证,直到它经得起反例、并发、异常输入和真实业务边界。
AI 能缩短第一轮分析时间。
但最后一轮判断,还是得由开发者自己完成。