上周四下午四点半,距离下班还有半小时。
监控告警突然炸了。
[ERROR] 2026-03-03 16:32:07 OrderService - 订单创建失败
java.lang.NullPointerException: Cannot invoke "String.length()" because "str" is null
at com.xxx.service.OrderService.createOrder(OrderService.java:87)
at com.xxx.controller.OrderController.create(OrderController.java:43)
用户在投诉:下单页面一直转圈,提交不了。
我打开钉钉,看了一眼错误率:3分钟内,下单接口报错率从0.1%飙到了38%。
这不是小事。
以前遇到这种情况,我的第一反应是:打开IDEA,连日志,开始逐行看堆栈。
这次,我做了一个不一样的决定:
把整个排查过程,交给AI来主导。
我想看看,AI到底能做什么,不能做什么。
结果出乎我意料。
第一步:把现场信息喂给AI(3分钟)
我把能拿到的信息全部复制给Claude:
生产环境告警,请帮我分析:
错误日志:
[ERROR] OrderService - 订单创建失败
java.lang.NullPointerException: Cannot invoke "String.length()"
because "str" is null
at OrderService.createOrder(OrderService.java:87)
监控数据:
- 报错开始时间:16:32
- 报错率:38%(3分钟内从0.1%飙升)
- 影响接口:POST /api/order/create
- 其他接口正常
最近变更:
- 今天14:00,发布了一个版本,主要改了优惠券模块
请帮我分析:
1. 可能的原因是什么?
2. 排查思路是什么?
3. 需要我提供哪些额外信息?
AI的回答很快,而且超出了我的预期:
初步判断: NullPointerException发生在OrderService.java第87行,
str为null。 结合变更信息(优惠券模块),推测最可能的原因:高概率(70%): 订单创建时调用了优惠券相关逻辑,优惠券码为空时没有做null检查, 直接调用了str.length()。
中概率(20%): 优惠券模块的修改引入了新的数据库字段或配置,但生产环境未同步, 导致查询结果中某个字段为null。
低概率(10%): 并发问题,多线程场景下对象状态被意外清空。
需要你提供:
- OrderService.java第87行的代码片段
- 今天14:00发布的具体diff
- 报错的请求参数(用没有填优惠券的订单试一下,是否复现)
我愣了一下。
AI给出了概率排序,还知道要让我用不填优惠券的订单复现——这是个老手才会想到的排查动作。
我立刻试了:不带优惠券码下单,正常。带优惠券码下单,报错。
问题范围直接锁定。
第二步:AI读懂代码,指出真正的问题(5分钟)
我把第87行前后的代码片段给了AI:
// OrderService.java 第80-95行
public OrderDTO createOrder(CreateOrderRequest request) {
// 计算优惠金额
BigDecimal discountAmount = BigDecimal.ZERO;
if (request.getCouponCode() != null) {
CouponDTO coupon = couponService.getCouponByCode(request.getCouponCode());
// 第87行:
if (coupon.getDiscountType().equals("PERCENTAGE")) {
discountAmount = request.getTotalAmount()
.multiply(coupon.getDiscountValue())
.divide(new BigDecimal("100"));
} else {
discountAmount = coupon.getDiscountValue();
}
}
// ...
}
AI的分析:
找到了。
第87行问题:
coupon.getDiscountType()返回null,导致.equals()抛出NullPointerException。根本原因:
couponService.getCouponByCode()查不到优惠券时,没有返回null, 而是返回了一个空的CouponDTO对象(字段全为null)。这是今天14:00发布的那次修改引入的:之前这个方法在找不到优惠券时 直接抛异常,新版本改成了返回空对象,但调用方没有同步修改。
修复方案(两种):
方案A(快,适合紧急修复):
CouponDTO coupon = couponService.getCouponByCode(request.getCouponCode()); if (coupon == null || coupon.getDiscountType() == null) { throw new BusinessException("优惠券不存在或已失效"); }方案B(正确,适合根本性修复): 在couponService.getCouponByCode()里,查不到时直接返回null而不是空对象, 调用方统一判断null即可。这样语义更清晰,避免以后再踩同类坑。
建议: 先用方案A紧急修复,发版止血。问题恢复后再做方案B的根本修复。
我看完这段分析,立刻去翻了14:00的发版diff。
找到了。一模一样。
第三步:AI做的事,和AI做不到的事
这时候,我停下来,认真想了一下这20分钟发生了什么。
AI做到的:
- 根据日志和上下文,在3分钟内给出了有概率权重的排查方向
- 读懂了代码,准确定位了真正的根因(不是第87行,而是getCouponByCode的返回值设计)
- 给出了两种修复方案,区分了"救火"和"根本修复"的优先级
- 全程逻辑清晰,没有废话
换句话说:AI把我过去要花40分钟做的"分析和推理"部分,压缩到了5分钟。
AI做不到的:
但整个过程,有几件事AI完全帮不上:
第一,它不知道下午14:00发布了什么。
是我主动把"今天14:00发版,改了优惠券模块"这条信息喂给它的。如果我不说,AI只能对着日志猜。
这条信息,在我们团队的钉钉群里,不在任何日志里。AI看不到。
第二,它不知道这个bug的业务影响程度。
报错率38%看起来很高,但我知道这38%里,大多数是同一批用优惠券的测试用户在反复重试。真实影响的用户可能只有几十个,不需要立刻回滚,发一个hotfix就够了。
这个判断,需要对业务的理解,AI没有。
第三,紧急修复之后,它不知道要通知谁。
hotfix发完,我要给客服发一条话术让他们安抚用户,要给运营说一声下午的优惠券活动暂停推送,要给老板发一条简报说明事故原因和处理结果。
这些"人的工作",AI都不在场。
一个真实的数据对比
我把这次排查过程,和3个月前一次类似的线上问题做了对比。
3个月前(纯手工排查):
16:32 告警触发
16:35 确认报警不是误报
16:40 登上服务器查完整日志
16:55 根据堆栈找到出问题的代码
17:10 翻git log,找到引入问题的commit
17:25 确认根因,讨论修复方案
17:40 hotfix发版
17:50 确认恢复
总耗时:78分钟
我的状态:肾上腺素爆表,下班时精疲力竭
这次(AI辅助排查):
16:32 告警触发
16:35 整理现场信息,喂给AI
16:38 AI给出排查方向,立刻验证复现路径
16:43 把代码片段给AI,AI定位根因
16:48 确认根因,AI给出修复方案
16:55 hotfix发版
17:02 确认恢复
总耗时:30分钟
我的状态:焦虑感明显低,因为排查过程有逻辑,不是乱撞
时间缩短了一半,更重要的是:排查过程有了结构。
以前我排查问题靠的是直觉和经验,走哪条路全凭感觉。AI做的事,是帮我把"可能的原因"显式地列出来,每条都有概率,每条都有验证方法。
这个思路,让我少走了很多弯路。
3个用AI排查生产问题的实战技巧
用了几次之后,我总结出了一套固定的喂料方式。
技巧1:现场信息要"四件套"
喂给AI的信息,要包含这四样:
1. 完整的错误日志(包含堆栈,不要只截图)
2. 监控数据(报错率、报错时间、影响范围)
3. 最近的变更记录(最近一次发版改了什么)
4. 复现路径(能不能复现,怎么触发)
缺了任何一样,AI的分析质量会大幅下降。
信息越完整,AI越像一个刚接手这个项目的高级工程师。
信息不完整,AI就只能对着日志猜,给你一大堆"可能是A也可能是B也可能是C",完全没用。
技巧2:让AI给出概率排序,而不是所有可能性
很多人拿到AI的分析,发现列了七八条"可能的原因",不知道先查哪个,最后还是一头雾水。
改一下Prompt:
❌ "这个报错可能是什么原因?"
✅ "这个报错最可能的原因是什么?
按照可能性从高到低排序,
每条说明为什么你认为概率高,
以及怎么验证。"
这样AI给你的是一个"排查顺序",而不是一张所有可能性的清单。
技巧3:让AI区分"救火"和"根本修复"
线上出问题的时候,你同时有两个目标:
- 立刻止血:让报错停下来,用户能正常使用
- 根本修复:找到问题根源,避免下次再出
这两个目标,有时候解决方案不一样。
在这次案例里,方案A(加null判断)可以1分钟写完马上发版,方案B(修改getCouponByCode的返回值设计)才是真正解决根源,但影响面更大,需要更多测试。
让AI明确区分这两种方案:
"请给出两种修复方案:
一种是最快能止血的临时方案,
一种是从根本上解决问题的正确方案,
说明各自的优缺点。"
这样你可以先发临时方案止血,下班之后再做正确方案。
写在最后
用AI排查这次Bug之后,我想清楚了一件事。
AI在这个场景里,扮演的不是"解决问题的人",而是"帮你结构化思考的人"。
我以前排查问题,靠的是12年积累的直觉:看一眼日志,凭经验猜几个方向,然后一个个验证。
这套方法有效,但效率取决于"今天直觉准不准"。
AI做的事是:把我脑子里隐性的排查逻辑,变成了显性的步骤。
你喂给它的信息越完整,它给你的推理就越准确。它不替你做决定,但它让你的决定过程更有据可依。
但有一件事我想说清楚:
AI排查问题的能力上限,取决于你给它的信息质量。
这次我能快速定位问题,不是因为AI多厉害,而是因为我知道要把"14:00发版改了优惠券模块"这条信息告诉它。
这条信息,是我从钉钉群里看到的,从团队协作里知道的,是12年工作经验给我的习惯——出了问题,第一件事是看最近有没有发版。
AI不知道这条信息。是我喂给它的。
AI是放大镜,不是眼睛。
你先得看到,它才能帮你放大。
你们遇到生产问题,有没有试过用AI辅助排查?效果怎么样?
欢迎评论区分享你的经历。
后端AI实验室 不讲概念,只谈实战 代码开源,每周更新