我把一个开源 PR 全权交给 AI 后,差点把维护者变成免费 Reviewer

查看 29|回复 2
作者:yuiffy   
前几天,我给 BililiveRecorder 提交的一个 Bug 修复 PR 合并了:
https://github.com/BililiveRecorder/BililiveRecorder/pull/801
最终改动很小:两个文件,18 行新增。其中真正的行为修复只有 4 行,另外 14 行是回归测试。
但走到这 18 行之前,我和 AI 绕了很大一圈,也给仓库维护者制造了不少额外工作。这篇不是模型横评,也不是成功案例包装,而是一次关于 AI 编程、开源协作和责任边界的复盘。
文中提到 5.6 Luna 和 Sol ,只是为了还原当时的任务分工。真正值得讨论的不是哪个模型更强,而是:当提交者把工作交给 AI 后,应该怎样避免把 AI 尚未完成的推理成本转嫁给维护者。
起因:我想帮忙,但不想深入学习整个仓库
我在使用 BililiveRecorder 时遇到了一个偶发问题:直播已经结束,但录制任务没有正常退出。界面显示仍在录制,速率是 0.00 Mbps ,也没有创建新文件,房间一直被异常任务占用。
这是一个真实存在、也影响继续录制的问题。我既然碰到了,就想顺手帮开源项目把它修掉。
但这个仓库不是我熟悉的项目。我不太想投入很多时间重新学习它的录制状态机、网络层、FLV 解析器和测试结构,于是选择把排查、改代码、跑测试、提 PR 、回复 Review 几乎全部交给 AI 。
有点像三国杀里的刘禅:我负责说“放权”,然后期待 AI 自己把事情办完。
这本身未必错。AI 的价值之一,就是降低进入陌生仓库的成本。问题在于,我不仅把执行交了出去,也把判断和验收一起放弃了。
第一次错误:把“听起来合理”当成“已经找到原因”
最开始使用的是偏快的 5.6 Luna 。它根据日志和表面状态,很快给出了几种听起来都很合理的解释:
  • 下播后接口短时间仍返回直播中,所以又启动了一个录制任务;
  • 启动阶段的网络请求没有完整响应取消;
  • 已经收到流但没有创建第一个文件,现有 watchdog 没有覆盖这个分支;
  • 旧会话结束后应该延迟重试,再刷新一次房间状态。

    这些都不是完全胡说。它们在代码层面确实是值得检查的边界,有些修改甚至能通过测试和 CI 。
    但它们没有回答最关键的问题:异常任务当时到底卡在哪里?
    我们只有“直播结束后任务没退出”这个现象,却已经开始改 Room 生命周期、取消令牌传播、重试时序和 watchdog 。每出现一个新疑问,AI 就再补一个机制。改动越来越像重构,根因却仍然只是猜测。
    第二次错误:把 PR 当成了 AI 的公开调试现场
    更糟的是,我没有在本地把这些猜测拦下来,而是让 AI 直接把它们放进 PR 。
    维护者随后提出了几个很关键的问题:
  • 现有的无数据检测按理应该会取消任务,为什么没有触发?
  • 从开始接收流到创建文件之间,真正可能卡住的代码范围有多大?
  • 新增的 cancellation token 或 watchdog 是否在真实故障上验证过?
  • 如果判断来自诊断日志,日志里为什么没有新版本增加的字段?
  • 能不能在故障发生时抓原始数据或者进程 dump ?

    这些本来应该是我在提交 PR 之前完成的内部 Review 。
    但当时我的处理方式仍然是:把维护者的问题继续交给 AI ,让 AI 写解释、换假设、改代码、补评论。PR 逐渐变成了 AI 推理过程的流水账。维护者不得不代替我检查证据是否成立、改动是否真的命中问题、测试是否有意义。
    这实际上是把我的节省,变成了维护者的额外成本。
    维护者有一句反馈很直接:这个过程有点像在当 AI 的“肉身代理”。模型没有把事情想清楚,我却负责把它的输出搬进真实协作场景。
    这句话并不好听,但很准确。
    一个特别典型的失误:诊断版本没有被证明真的运行过
    中间我们编译过一个增加大量 verbose 日志的版本,理论上会记录网络读取、Pipe 状态、FLV 解析、文件创建和 watchdog 数据。
    后来又出现了真实故障,我上传了日志,并基于它推导出新的修复方向。
    维护者指出:这份日志里根本没有诊断提交新增的字段,看起来和发布版日志没有区别。
    这意味着我们连“当时运行的是诊断版本并且日志开关已经生效”都无法证明。既然诊断工具没有被验证,它的缺失字段也就不能支持任何新结论。
    正确流程应该是:
    [ol]
  • 给诊断版本加入明确的 commit 或版本标记;
  • 在本地实际执行相关路径;
  • 确认新日志字段确实出现;
  • 再把这个构建部署到真实环境等待复现。
    [/ol]
    我们跳过了中间的验证,却开始解释结果。这是典型的“有了工具的形式,却没有获得可信证据”。
    转折:异常进程还活着,先别重启,抓 dump
    真正的转折发生在故障现场还没有消失的时候。
    当时异常录制任务仍然存在。我本来需要清理它,否则后续直播无法正常录制。在重启之前,我们先对进程抓了一份约 1.36 GB 的 full-memory dump 。
    dump 通过 Windows dbghelp.dll 的 MiniDumpWriteDump 获取,分析使用 .NET 的 ClrMD 。没有公开上传 dump ,因为完整进程内存可能包含 Cookie 、URL 和其他敏感数据。
    这次换成 Sol 继续分析。它从托管堆里找到对应房间和录制任务,再沿异步状态机查看录制循环、TagGroupReader 、FlvTagPipeReader 和底层 Pipe 的状态。
    dump 显示:
  • 录制任务仍处于运行状态,没有正常结束;
  • 网络字节数为 0 ,还没有解析出 FLV header 或任何 tag ;
  • Pipe 缓冲区为空;
  • Pipe 的 writer 已经完成,但 reader 还没有完成;
  • 调用链仍停留在 ReadNextTagAsync;
  • 进程持续消耗 CPU 。

    结合源码和 .NET Pipelines 的语义,根因终于清楚了:
    当输入不足 9 字节、无法解析 FLV header 时,旧代码直接执行 continue,没有先检查这次读取结果的 IsCompleted。
    而一个 writer 已完成、buffer 为空的 Pipe ,再次调用 ReadAsync 会同步返回同一个 completed 结果。于是代码不断经历:
    读取 completed + empty
    解析 header 失败
    continue
    再次同步读取 completed + empty
    它形成了一个无限同步循环,既不退出,也不给正常清理逻辑机会。
    修复只需要在继续循环前检查 result.IsCompleted 并退出。随后增加一个测试:完成一个空 Pipe ,调用解析器,应该立即返回 null,不能挂住。
    此前所有 Room 轮询、延迟重试、watchdog 、网络取消相关改动都被回退。最终 PR 相对比较基线只剩两个文件和 18 行新增,并在 CI 全绿后合并。
    Sol 找到了答案,但“换强模型”不是完整结论
    这次体验确实让我感受到:面对陌生大型仓库、异步状态机、运行时对象和 dump ,强推理模型更适合承担最后的因果分析和收敛工作。5.6 Luna 在这次任务里更容易沿着表面线索快速产出“看起来能修”的改动,而 Sol 更能坚持把对象状态、运行时语义、源码和测试串成完整证据链。
    但如果把结论简单写成“Luna 不行,换 Sol 就好了”,仍然是在逃避责任。
    Sol 能收敛问题,一方面是模型能力,另一方面是任务条件终于变了:我们有了真实现场 dump ,并明确要求先证明根因、回退未经验证的改动、让 PR 尽可能小。
    如果没有 dump ,没有证据门槛,只是继续要求更强模型“赶紧修掉”,它同样可能给出一个更有说服力、但仍然未经验证的故事。
    真正需要负责的是提交者
    我是 PR 的提交者。无论代码由我写、AI 写,还是多种模型接力写,维护者看到的提交都代表我。
    我可以不熟悉整个仓库,也可以依赖 AI 降低学习成本,但至少要承担以下责任:
  • 确认问题描述和证据没有互相矛盾;
  • 确认诊断版本真的运行过;
  • 确认修复方向来自证据,而不是相关性猜测;
  • 确认测试覆盖的是实际原因;
  • 确认 PR diff 已经去掉探索阶段的无关改动;
  • 在推给维护者之前,自己或用更强模型完成一次内部 Review 。

    我当时没有做到。因为想少花精力,我把判断完全交给 AI ;因为不想自己 Review ,我实际上让维护者替我 Review AI 的中间产物。
    这不是合理使用开源维护者的时间。
    下次我会采用的流程
    以后再遇到类似问题,我会把流程固定为:
    准确描述现象
        ↓
    盘点已有日志、dump 、trace 和版本信息
        ↓
    证据不足:只准备诊断工具,不先修改行为
        ↓
    验证诊断构建和日志开关确实生效
        ↓
    等待复现,并在清理现场前保存日志、dump 或原始输入
        ↓
    从证据确认原因,而不是从症状猜修复
        ↓
    写一个能在旧代码失败的最小测试
        ↓
    做最小修复,回退探索性改动
        ↓
    内部 Review 、测试、CI 、检查完整 diff
        ↓
    最后才提交或更新 PR
    模型也会按任务分工:
  • 快速模型适合搜索代码、整理日志、执行机械修改和运行测试;
  • 强推理模型用于选择假设、分析 dump 、处理并发或异步问题,以及最终 Review ;
  • 任何模型的“合理解释”都不能替代日志、dump 、trace 或可重复测试;
  • PR 不是探索用的草稿箱。假设还在频繁变化时,工作应该留在本地。

    还有一个简单的提交门槛:如果我不能用几句话说明“证据是什么、原因是什么、这几行代码为什么能解决它、测试如何证明”,就还不应该把修复交给维护者。
    结语
    我仍然认为 AI 会显著降低普通用户参与开源项目的门槛。过去需要熟悉几周才能进入的仓库,现在可能几小时就能完成定位和修复。
    但 AI 加速的不只是正确过程,也会加速猜测、代码膨胀和没有证据的自信。如果提交者完全退出判断环节,节省下来的时间很可能只是转移到了维护者身上。
    这次 PR 最后得到了正确且很小的修复,也顺利合并。但前面的弯路本来可以避免。
    下次我仍然会让 AI 干大部分活,只是不再当完全放权的刘禅。AI 可以代写代码、分析日志甚至检查 dump ,但提交者必须对证据、范围和最终交付负责。

    协作, 责任, 证据

  • metalvest   
    转人工
    Rickkkkkkk   
    用 ai 写文章我感觉可以,但写完自己整理一遍提高信息量也是对读者基本的尊重吧。比如你这一句
    “文中提到 5.6 Luna 和 Sol ,只是为了还原当时的任务分工。真正值得讨论的不是哪个模型更强,而是:当提交者把工作交给 AI 后,应该怎样避免把 AI 尚未完成的推理成本转嫁给维护者。”
    全部删掉对整个文章想要传递的内容有任何影响吗?
    @Livid 废话连篇毫无营养的 AI 文
    您需要登录后才可以回帖 登录 | 立即注册

    返回顶部