banner
NEWS LETTER

AI 辅助调试流程:从日志到最小复现

Scroll down

调试最怕的不是问题复杂,而是信息散落在日志、配置、代码分支和环境差异里,最后只能靠猜。AI 工具能帮我们快速阅读日志、追踪调用链、整理假设,但如果一开始就问“这个问题怎么修”,它很容易跳过验证,直接给出看似合理的修改建议。

更稳的做法,是把 AI 放进一个可检查的调试流程里:先描述现象,再收集证据,然后缩小范围,最后产出最小复现和修复方案。这样 AI 不只是回答问题,而是参与工程排障。

一、先把现象写成可验证描述

很多调试对话一开始就太模糊,例如:

1
页面偶尔打不开,帮我看看。

这句话对人和 AI 都不够用。它缺少触发条件、错误表现、影响范围和期望行为。更好的描述应该接近一次缺陷报告:

1
2
3
4
5
问题:Android 设置页点击保存后,偶发停留在 loading 状态。
触发条件:弱网环境下连续点击保存,或者切到后台再回来。
实际表现:按钮不可点击,页面没有错误提示。
期望表现:请求成功后退出 loading,请求失败后显示错误并允许重试。
已知范围:只在设置页出现,登录页和资料页没有复现。

这样的输入能让 AI 先围绕现象建立边界,而不是马上猜测“可能是网络问题”。

二、日志要带上下文,不要只贴最后一行

调试时经常会看到一段异常堆栈,然后直接把最后的报错交给 AI。这样容易丢掉关键线索。

更实用的日志材料包括:

  • 异常堆栈的完整调用链。
  • 异常前后的关键业务日志。
  • 请求参数、响应码和错误体。
  • 线程、进程、设备型号或运行环境。
  • 最近一次相关改动的 diff 或提交说明。

如果是 Linux 服务,除了应用日志,还应该补充:

1
2
3
4
systemctl status app.service
journalctl -u app.service --since "10 minutes ago"
df -h
free -m

如果是 Android 问题,可以补充:

1
2
3
adb logcat -v time
adb shell dumpsys activity top
adb shell dumpsys meminfo <package_name>

重点不是把所有日志都贴进去,而是保留能解释时间顺序的信息。AI 需要知道问题发生前发生了什么,问题发生后系统进入了什么状态。

三、让 AI 先整理事实和假设

AI 很容易把推测说得像结论。调试时可以明确要求它分层输出:

1
2
3
4
5
6
请把下面材料分成三部分:
1. 可以从日志直接确认的事实。
2. 基于事实推导出的可能原因。
3. 还需要补充验证的信息。

不要直接给修复代码。

这个提示能把调试节奏放慢一点。比如日志里出现 SocketTimeoutException,可以确认的是请求超时,不一定能确认服务端不可用。可能原因还包括 DNS、代理、弱网、超时时间过短、请求被取消或主线程阻塞。

事实和假设分清楚之后,排查方向会更干净。否则很容易改了一个“看起来像原因”的地方,却没有真正解决问题。

四、按调用链缩小范围

复杂问题通常跨越多个层级。以一个常见的客户端保存流程为例:

1
View -> ViewModel -> Repository -> ApiClient -> Server

调试时不要让 AI 只盯着报错文件。可以要求它先画出调用链:

1
2
请根据当前代码和日志,整理保存按钮触发后的调用链。
标出每一层的输入、输出、异常处理和状态更新位置。

调用链整理出来后,再逐层定位风险点:

  • View 是否重复触发点击事件。
  • ViewModel 是否正确处理 loading 状态。
  • Repository 是否吞掉异常或返回了错误默认值。
  • ApiClient 是否正确区分超时、取消和业务失败。
  • 服务端返回是否符合客户端解析约定。

这一步的价值在于把“偶发问题”拆成多个可检查节点。每个节点都可以通过日志、断点、测试或 mock 数据单独验证。

五、优先构造最小复现

没有复现的修复,很难判断是否有效。AI 可以帮忙把复杂场景压缩成最小复现。

可以这样提问:

1
2
3
4
5
6
请基于上面的现象和调用链,设计一个最小复现场景。
要求:
1. 输入条件尽量少。
2. 能稳定触发问题。
3. 能说明观察指标。
4. 不依赖完整线上环境。

例如 Android loading 卡住的问题,最小复现可能不是“打开 App 操作十步”,而是一个 ViewModel 单元测试:

1
2
3
4
5
6
7
8
9
@Test
fun save_failed_should_clear_loading() = runTest {
repository.result = Result.failure(IOException("timeout"))

viewModel.save()

assertFalse(viewModel.uiState.value.loading)
assertNotNull(viewModel.uiState.value.errorMessage)
}

这个测试不一定覆盖所有真实环境,但它能验证核心状态机:失败后必须退出 loading。如果这个测试失败,说明问题已经被压缩到足够小的范围。

六、修复前先列验证清单

很多调试会在“我知道原因了”之后变得草率。真正修改代码前,最好先让 AI 列出验证清单:

1
2
在修改前,请列出这次修复必须满足的验证点。
按正常路径、失败路径、边界路径和回归路径分类。

一个合格的验证清单应该包含:

  • 正常路径:请求成功后状态恢复,页面显示正确结果。
  • 失败路径:超时、断网、服务端错误都有可见反馈。
  • 边界路径:重复点击、取消请求、页面销毁后回调返回。
  • 回归路径:已有接口协议和旧数据行为不变。

这份清单能防止修复只覆盖当前日志里的单个错误。很多线上问题并不是没有修,而是修复时只处理了最明显的一条路径。

七、让 AI 输出小改动,而不是重写模块

调试修复应该尽量小。问题越紧急,越不适合顺手重构。

可以明确约束:

1
2
3
请只修改导致该问题的最小代码路径。
不要重构无关模块,不要调整命名,不要改变接口协议。
修改后说明每一处改动对应哪个验证点。

这类约束对 AI 很重要。它经常会在修 bug 时顺手整理结构,结果 diff 变大,review 成本上升,还可能引入新的行为变化。

小改动不是保守,而是为了让因果关系清楚:哪个问题、哪个证据、哪处修改、哪个测试验证,能够一一对应。

八、复盘要沉淀成下一次上下文

一次调试结束后,最容易被忽略的是复盘。对个人项目来说,复盘不需要写成长文,几句话就够:

1
2
3
4
5
现象:保存失败后 loading 没有恢复。
原因:Repository 抛出的异常被 ViewModel 捕获后没有更新失败状态。
修复:失败分支统一设置 loading=false,并补充单元测试。
验证:成功、失败、重复点击三个路径通过。
预防:后续新增异步状态时必须覆盖失败路径。

这段内容可以放进 issue、提交说明、代码注释或团队知识库。下一次遇到相似问题时,它就是非常好的 AI 上下文。

AI 调试的价值,不只是当下解决一个 bug,更是把排障过程变成可复用的工程资产。

九、一个可复用提示词

日常调试时,可以直接使用这个模板:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
请按调试流程协助分析问题。

现象:
环境:
复现步骤:
关键日志:
相关代码:
已尝试操作:
期望结果:

要求:
1. 先区分事实、假设和待验证信息。
2. 再整理调用链和可能断点。
3. 设计最小复现场景。
4. 提出最小修复方案。
5. 给出验证清单。

如果问题涉及版本、服务状态、价格、政策或其他实时信息,还应该明确要求先查询可信来源,并记录信息获取时间。没有实时依据时,不要把猜测写成事实。

十、总结

AI 辅助调试的关键,不是让它一次性猜中答案,而是让它帮助我们组织证据、缩小范围、构造复现和检查修复。

一个稳定的调试流程通常包括:

  • 把现象写成可验证描述。
  • 提供带上下文的日志和代码。
  • 区分事实、假设和待验证信息。
  • 沿调用链逐层缩小范围。
  • 先构造最小复现,再做最小修复。
  • 用验证清单确认问题闭环。

当 AI 被放进这样的流程里,它更像一个能持续协助排障的工程伙伴,而不是一个只会根据报错生成答案的工具。

其他文章
目录导航 置顶
  1. 1. 一、先把现象写成可验证描述
  2. 2. 二、日志要带上下文,不要只贴最后一行
  3. 3. 三、让 AI 先整理事实和假设
  4. 4. 四、按调用链缩小范围
  5. 5. 五、优先构造最小复现
  6. 6. 六、修复前先列验证清单
  7. 7. 七、让 AI 输出小改动,而不是重写模块
  8. 8. 八、复盘要沉淀成下一次上下文
  9. 9. 九、一个可复用提示词
  10. 10. 十、总结
请输入关键词进行搜索