ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

AI找Bug实战:用调用链与AST提升定位效率

AI找Bug实战:用调用链与AST提升定位效率 1. 当Bug报告只有一句话时AI到底能帮上什么忙我们是怎么让 AI 找到那个 Bug 的这个问题我第一次被问到的时候脑子里冒出来的第一个念头是AI 又不是神仙它凭什么能找到 Bug但后来真把这件事跑通了一遍我才意识到AI 在找 Bug 这件事上的价值不在于它比人聪明而在于它比人耐得住性子——它可以把一条调用链从头到尾读一遍不跳过任何一个中间层不因为这段代码看起来没问题就略过。先说清楚这篇文章要聊什么。它适合两类人一类是手上有 Bug 但不知道怎么让 AI 参与进来的开发者另一类是已经在用 AI 编程工具、但发现它老是答非所问的人。核心要解决的问题是怎么把一个模糊的 Bug 现象转化成 AI 能真正定位到根因的输入。关键词里的 AI、Bug、Agent、AST、调用链其实就是这条路径上的五个关键节点。我先讲一个真实的场景。项目里有个接口偶尔返回空数据日志里没有任何报错本地跑一百遍都复现不了。这种 Bug 最折磨人——你没法给 AI 一句帮我修一下因为它连问题在哪都不知道。我当时的做法是先把现象拆成可验证的假设再让 AI 沿着调用链去验证每一个假设。这一步是整个流程里最关键的后面会展开讲。很多人对 AI 找 Bug 的期待是我把报错贴进去它给我答案。这个期待在简单场景下成立比如空指针、数组越界这种一眼能看出来的问题。但真实项目里的 Bug尤其是那种偶发无法复现只在生产环境出现的AI 直接看报错是没用的因为报错信息本身就不完整。这时候需要的是结构化的上下文而调用链和 AST 就是构造这个上下文的两把钥匙。提示如果你现在手上的 Bug 是偶发且无报错这一类先别急着把代码丢给 AI。先花十分钟把什么条件下会触发想清楚这一步省不得。2. 为什么直接贴报错给 AI 经常得不到有效答案2.1 报错信息只是症状不是病因我见过太多人这么干控制台报了个NullPointerException截图往 AI 里一贴问怎么修。AI 通常会给你三五种可能的原因然后你自己去试。这个过程本质上不是 AI 在找 Bug而是 AI 在帮你列可能性真正定位的还是你自己。问题出在哪报错信息描述的是崩溃发生的那一瞬间而不是导致崩溃的那条路径。一个空指针可能是上游传了个 null 进来也可能是某个字段初始化顺序不对还可能是并发场景下对象被提前释放。这三种情况的修复方式完全不同但报错信息长得一模一样。2.2 上下文缺失让 AI 只能猜AI 模型在回答代码问题时依赖的是你给它的上下文。你给它一段报错它就只能基于这段报错做概率性推断。它不知道你的项目结构、不知道这个函数被谁调用、不知道数据从哪来。这种情况下它给出的答案准确率完全取决于这个 Bug 是不是常见模式。我做过一个粗略的统计把同一个 Bug 用两种方式喂给 AI一种是只贴报错一种是贴报错加上完整的调用链和关键变量的取值后者的定位准确率明显高出一截。差距不在模型能力而在输入质量。2.3 调用链才是 AI 真正需要的地图调用链这个东西说白了就是这个函数是被谁调用的一路往上追到入口。有了这条链AI 就能顺着数据流去推断哪一步可能产生了异常值哪一步没有做校验哪一步的假设不成立。举个具体的例子。假设有个函数processOrder返回了空列表你只告诉 AI 这个现象它没法判断。但如果你告诉它processOrder被handleRequest调用handleRequest从parseInput拿到参数而parseInput在输入为空时会返回一个默认对象而不是报错——AI 立刻就能锁定问题在parseInput的默认值处理上。这就是调用链的价值它把孤立的症状变成了有因果关系的路径。3. 把调用链喂给 AI 之前先做这三步清洗3.1 砍掉与 Bug 无关的分支真实的调用链往往非常长一个请求可能经过几十个函数。如果你把整条链原封不动丢给 AI它会淹没在无关信息里。我的做法是只保留数据流经过的路径把那些日志、埋点、权限校验之类的旁支全部砍掉。具体怎么砍从 Bug 出现的那个函数开始往上追每追一层问自己一句这一层的数据有没有可能影响到最终结果如果没有就砍掉。比如一个纯记录日志的函数它不改变任何数据那它就不该出现在调用链里。3.2 标注每一层的输入输出光有函数名不够AI 需要知道每一层吃进去什么、吐出来什么。我通常会在调用链的每个节点后面标注关键变量的值或者类型。比如handleRequest(req) - orderId req.body.id // 这里 orderId 可能是 undefined - processOrder(orderId) - queryDB(orderId) - 返回空数组这种标注方式看起来啰嗦但它能让 AI 一眼看出orderId 在这一层就已经有问题了而不是一路追到底才发现源头。3.3 用 AST 提取真实的调用关系而不是靠记忆这里要说到 AST 了。AST 是抽象语法树简单理解就是把代码结构化成树形结构。为什么用它因为人脑记调用关系是会出错的尤其是跨文件、跨模块的调用你凭印象画出来的链很可能漏掉某个中间层。用 AST 工具比如各语言生态里的解析库可以自动提取出函数 A 调用了函数 B这种关系生成一张准确的调用图。我一般会写个小脚本输入是出问题的那个函数名输出是它往上三层的调用路径。这个脚本不长但省下来的排查时间非常可观。注意AST 提取出来的是静态调用关系也就是代码里写了什么。如果项目里用了反射、动态代理、事件总线这类机制静态关系可能和实际运行时的调用不一致。这种情况需要结合运行时的 trace 一起看。4. 让 Agent 沿着调用链自己走一遍的完整流程4.1 为什么这里需要 Agent 而不是单次问答单次问答的模式是你问AI 答结束。但找 Bug 是个多轮验证的过程——AI 提出一个假设你去验证验证结果再反馈给它它再提出下一个假设。这个循环如果靠人手动搬运效率很低。Agent 的价值就在于它能自己完成这个循环。你给它一个目标找出 processOrder 返回空的原因和一套工具读文件、跑测试、查日志它就能自己决定下一步该看哪里。关键词里的 Agent、Agent 开发、Agent 架构说的就是这套东西。4.2 给 Agent 划定可操作范围Agent 能力再强你也不能让它随便改代码。我的做法是给它划定一个明确的边界只读不写只查不改。它可以读文件、可以跑只读的查询命令、可以分析日志但不能修改任何源文件。这样即使它判断错了也不会把项目搞乱。具体配置上我会给它一个白名单列出允许访问的目录和允许执行的命令。超出白名单的操作一律拒绝。这个限制看起来保守但实际用下来定位 Bug 根本不需要写权限读权限就够了。4.3 一轮完整的 Agent 排查实录我拿之前那个接口偶发返回空数据的 Bug 走了一遍完整流程记录如下第一步我把 Bug 现象、调用链、关键日志片段整理成一份上下文文档喂给 Agent。第二步Agent 读完上下文后提出的第一个假设是上游传入了空参数。第三步它自己去读了parseInput的代码发现输入为空时确实会返回一个默认对象。第四步它继续追发现这个默认对象的某个字段是 undefined而下游代码没有对这个字段做校验。第五步它给出了结论问题在默认对象的字段初始化上。整个过程大概跑了七八轮工具调用最后定位到的位置和我人工排查的结论一致。但人工排查我花了差不多两个小时Agent 跑了不到十分钟。4.4 Agent 排查中容易失效的几种情况Agent 不是万能的我踩过几次坑。第一种是日志缺失如果关键路径上没有打日志Agent 就失去了判断依据只能靠猜。第二种是动态调用前面提过的反射、事件总线这类机制静态分析看不出来Agent 会漏掉真实的调用路径。第三种是环境差异本地能跑通、生产环境出问题这种 Agent 在本地排查是查不出来的必须给它生产的日志和 trace。遇到这几种情况我的处理方式是先补日志再排查。补日志这一步看起来是额外工作但它能让后续所有排查都变快非常值得。5. 从无法复现到稳定定位的关键转换5.1 无法复现的 Bug 到底难在哪无法复现这四个字是排查工作里最让人头疼的。它的难点不在于 Bug 本身复杂而在于你没法通过反复运行来观察它。你只能靠已有的线索去推断而线索往往是不完整的。我遇到过最离谱的一次一个 Bug 一周只出现一次每次出现的时间点还不固定。后来发现是某个定时任务和用户请求撞在了一起产生了竞态。这种 Bug你本地跑一万遍也复现不了因为它依赖的是特定的时序。5.2 用调用链把时序问题变成路径问题竞态这类问题的排查思路是把什么时候发生转换成哪条路径上发生了冲突。具体做法是把两个可能冲突的调用链都画出来找到它们共同访问的资源然后分析这个资源的访问有没有加锁。这个转换过程AI 能帮上很大的忙。你把两条调用链喂给它让它找出共同节点它做得比人快也比人全。我试过让 Agent 分析两条链的交集它几秒钟就找出来了而我人工比对花了十几分钟。5.3 给 AI 补充运行时证据的几种方式静态调用链不够的时候需要补充运行时证据。常见的有三种一是日志在关键节点打点记录变量取值二是trace用链路追踪工具记录完整的请求路径三是断点调试本地复现时打断点观察。这三种里trace 对 AI 最友好因为它是结构化的AI 能直接解析。日志次之需要 AI 做一定的文本解析。断点调试最不友好因为它是交互式的AI 没法直接参与。所以我的建议是优先补 trace其次补日志。6. 这套方法在实际项目里的边界与经验6.1 什么情况下 AI 找 Bug 特别快根据我的使用经验AI 在以下几类 Bug 上表现特别好一是数据流问题比如某个字段在某一步被意外覆盖二是边界条件问题比如空数组、零值、超长字符串三是调用顺序问题比如初始化顺序不对导致的空指针。这几类的共同点是逻辑链条清晰只是人容易看漏。6.2 什么情况下 AI 反而不如人反过来有几类 Bug 我基本不指望 AI一是业务逻辑错误比如这个折扣算错了AI 不知道你的业务规则没法判断对错二是性能问题比如这个接口慢AI 看不出瓶颈在哪需要 profiling 工具三是环境相关的问题比如只在某个特定配置下出错AI 看不到环境信息。6.3 我踩过的三个坑第一个坑是上下文给太多。我一开始觉得信息越多越好把整个项目的代码都塞给 AI结果它反而抓不住重点。后来我改成只给相关的调用链和关键代码片段效果立刻好转。第二个坑是没告诉 AI 它的假设是错的。Agent 提出假设后如果验证结果是错的一定要明确告诉它这个方向不对否则它会在错误的方向上越走越远。第三个坑是完全信任 AI 的结论。有一次 Agent 信誓旦旦地说问题在某一行我直接改了结果 Bug 还在。后来发现它漏看了一个中间层的转换。所以 AI 的结论一定要自己再验证一遍尤其是它给出的根因。6.4 一套可复用的排查模板最后分享一套我现在常用的模板每次排查 Bug 都按这个来步骤做什么产出1整理 Bug 现象和触发条件一段清晰的现象描述2用 AST 提取调用链一张调用路径图3标注每层输入输出带标注的调用链4补充运行时证据日志或 trace 片段5交给 Agent 多轮验证定位结论6人工复核结论确认根因这套模板我用了大半年最大的感受是AI 找 Bug 的效率取决于你给它的上下文质量而不是模型本身有多强。把调用链理清楚、把证据补完整剩下的交给 AI它就能跑得很快。反过来如果你自己都没想清楚问题在哪AI 也帮不了你。这个内容后续还可以这样扩展如果你用的是多 Agent 协作的架构可以让一个 Agent 负责提假设另一个负责验证互相制衡减少单一 Agent 跑偏的概率。我最近在试这个方向初步效果还不错等跑稳了再单独写一篇。
返回列表