GitHub Copilot CLI Rubber Duck模式:AI追问式调试让代码排查事半功倍 📅 发布时间:2026/9/9 18:19:59 👁 浏览次数: 在程序员圈子里橡皮鸭调试法不算什么新鲜事。早些年《程序员修炼之道》里提过一嘴遇到难缠的 bug找一只橡皮鸭放在桌上把代码逻辑从头到尾讲给它听讲着讲着思路就顺了。我年轻时候觉得这方法玄乎直到有次线上问题实在没头绪硬着头皮对着桌上一个搪瓷杯讲了十分钟讲到一半突然意识到是自己把参数顺序搞反了——那一刻我信了。但现在这个玩法升级了。GitHub Copilot CLI 最近更新的 Rubber Duck 模式把“对着鸭子说话”这件事直接搬进终端而且这只会接话。你描述问题的过程中它会像同事一样追问“然后呢”“你确定这个分支真的走了吗”“要不要再看看 headers 那边”。不少用过的同行都说这是“给代码装了个第二脑子”我觉得这个说法挺贴切但更准确地说它是让你的脑子在 Debug 时真的转起来。这篇文章我会聊清楚 Rubber Duck 模式到底是什么、跟直接用 AI 生成修复代码有什么本质区别、在真实调试场景里怎么用、什么情况下用它最划算还有我实测踩过的一些坑。如果你想找一种能真正提升自己排查能力的 AI 用法而不是让 AI 替你打工那这篇会比较对胃口。1. 从“对着鸭子说话”说起橡皮鸭调试法为什么有效1.1 橡皮鸭调试法的来历橡皮鸭调试法Rubber Duck Debugging最早出现在《程序员修炼之道》这本书里作者 David Thomas 和 Andrew Hunt 讲了一个很朴素的故事程序员桌上摆着一只橡皮鸭当代码出问题时你要把代码逐行解释给鸭子听。听起来荒诞但很多人试过之后都承认解释的过程中问题往往自己就暴露了。为什么是鸭子而不是同事因为同事听你说完一堆低水平问题之后可能会忍不住翻白眼而鸭子永远面带微笑、不做评判。这个设定很关键它降低了“开口”的心理门槛。你不需要组织好完美的措辞再说话想到哪儿讲到哪儿就行而正是在这种“不讲逻辑的碎碎念”里大脑会不由自主地补全那些平时忽略掉的假设。我第一次尝试时也觉得矫情但真正讲起来才发现叙述本身有一种强制力你嘴上说着“这里调用了一个函数参数是 user.id”大脑会立刻跳出来反问一句“等等这个 user 真的是你从 localStorage 里拿到的那个 user 吗”。这种自我校验就是橡皮鸭调试法的核心引擎。1.2 为什么“说出来”就能救命从认知科学的角度看这很好解释。我们思考时用的是一套高度压缩的内心语言很多细节被大脑自动“脑补”掉了但一旦要开口说给别人听哪怕是说给鸭子听这些压缩的略号就会被迫展开成完整句子。展开的过程中逻辑缺口、顺序颠倒、边界遗漏统统藏不住。我习惯把“讲出来”类比成开导航你自己开车时觉得每条路都熟但如果旁边坐着一个人要你口述怎么走你会突然发现自己在某个路口根本说不清该左转还是右转。代码调试也是同理你以为自己对某个模块了如指掌但只有当你逐行解释它在干什么时那只藏在条件判断里的“幽灵逻辑”才会现身。这个方法和“打日志”也有一点相似之处但比打日志更厉害。打日志只能看到数据流而叙述能看到你的心智模型和数据流之间的偏差。很多时候 bug 不在代码里而在你“以为代码应该怎么跑”和“代码实际怎么跑”的不一致上。自言自语式的调试其实就是把这种不一致暴露出来。1.3 传统橡皮鸭的三块硬伤但传统橡皮鸭调试法也有明显的天花板我用下来感受最深的是三块硬伤。第一鸭子不会提问。它永远只是听。如果你描述问题时本身就漏掉了某个关键线索那你说得再起劲也没用因为没人会在旁边补一句“等一下你刚说的这个变量是从哪来的”。实际场景里我自己漏掉的东西太多了比如某个函数在异常分支提前 return 了比如某个对象在循环里被意外修改了。这些问题往往不是“讲出来”就能发现的而是需要在讲述过程中被“追问”才可能触碰到的。第二鸭子没有上下文。它不知道你的项目里有什么文件、有什么接口、数据是怎么流转的一切全靠你描述。这意味着描述本身的质量直接决定调试效果。可问题恰恰是大多数人在 debugging 时描述不清楚因为如果描述清楚了bug 一般也就找到了。这是一个死循环你越需要帮助越说不清楚越说不清楚传统鸭子越帮不上忙。第三没有反馈闭环。说完就完了鸭子不会给你任何“下一步该验证什么”的建议也不会基于你新提供的信息修正之前的猜测。而真实调试往往不是一条直线它是螺旋向前的提出假设、验证假设、排除假设、再生成新假设。传统橡皮鸭只覆盖了“叙述”这一环后面的验证环节完全没参与。这也是为什么当 GitHub Copilot CLI 把“会追问”“能看上下文”“懂推理”的模型塞进橡皮鸭这个角色里时我整个人精神了。它补上了传统鸭子最缺的三样东西而且不改变“让开发者自己得出结论”这个核心体验。2. GitHub Copilot CLI 的 Rubber Duck 到底是什么2.1 从“会听的鸭子”到“会问的鸭子”GitHub Copilot CLI 是 GitHub 官方出的命令行 AI 工具最早它解决的是“在终端里不想切到浏览器用 Copilot”这个痛点。你可以在终端里直接让它解释一段代码、给一个 shell 命令、写一段脚本甚至让它根据需求生成代码片段。我平时用gh copilot explain和gh copilot suggest比较多这两个子命令一个负责“读代码讲人话”一个负责“把需求翻译成代码”。Rubber Duck 模式则完全是另一种定位。你可以把它理解成传统橡皮鸭有了嘴而且是一张会说话的嘴。它不再是单方面听你讲而是在你描述的过程中不断提出问题来推敲你的思路。举个例子传统鸭子听到你说“这个接口返回了 401”它只能静默而 Rubber Duck 会追问“你把 token 放到请求头了吗放在哪个字段后端解析时用的是 split( )[1] 还是直接读整个值”这些问题看起来不复杂但它们恰恰能把模糊的矛盾拉回到具体的代码细节上。我在第一次用 Rubber Duck 模式时感受最强烈的不是“它帮我解决了 bug”而是“它让我自己说出了 bug 的答案”。那种感觉非常奇妙就像是旁边坐了个非常懂行但又非常克制的前辈整场对话都在帮你梳理思路却始终不把结论直接塞给你。2.2 安装与进入 Rubber Duck 模式如果你还没装 GitHub Copilot CLI需要先装一下 gh 命令行工具并登录 GitHub 账号。安装过程很简单macOS 上可以用 HomebrewLinux 和 Windows 也有对应的包管理器这里不再赘述。装完 gh 之后确认一下登录状态gh --version gh auth status接着安装 Copilot CLI 扩展gh extension install github/gh-copilot装完后直接在终端里运行gh copilot这时候会进入一个交互式命令行界面里面能看到几个模式选项包括 explain、suggest 以及 Rubber Duck。在我当前使用的这个 CLI 版本里也可以更直接地用子命令进入gh copilot duck不同版本入口可能会有一点差异如果你敲完发现没有这个子命令就先在交互面板里找找 Rubber Duck 的菜单项或者更新一下 gh-copilot 扩展gh extension upgrade gh-copilot进入之后你会看到一个对话式的终端界面和 ChatGPT 的对话框类似只是没有网页也没有漂亮的字体。你描述问题它提问你再回答它再提问直到你觉得思路已经清晰了或者你已经自己找到了修复方向。这里要提醒一句Rubber Duck 模式和 Copilot CLI 的其他功能一样会把你的输入以及相关代码片段发送到远端模型处理。公司内部有保密要求的项目使用前一定确认清楚合规边界。也不要图省事直接把密钥、数据库连接串、内网地址贴进对话里这个习惯再好的工具也救不了你。2.3 它和“直接用 AI 改代码”有什么本质区别这一点值得展开聊。很多人拿到一个能用自然语言对话的工具第一反应是“太好了直接把 bug 描述给它让它给我修复代码”。Rubber Duck 模式恰恰反着来它不给修复代码它只给问题。这就引出了两种完全不同的 AI 使用哲学。一种是“AI 替我干活”典型代表是 Copilot 直接生成补丁或者你在对话里说“帮我改一下这段代码”。效率确实高但很多时候你只拿回了结果没拿到理解。如果这段代码之后出了问题你还是得从零开始排查模型给的修复方案为什么这么改你并不清楚。这在一次性任务里没问题但作为长期开发能力它对个人的提升非常有限。另一种是“AI 帮我干活”Rubber Duck 模式的核心就是这种。它默认你的代码里藏着答案只是你自己还没看见它通过提问帮你去掉思维盲区让你自己拼出完整拼图。它当然知道答案但它选择不直接说。这个“克制”的设计在初学时容易被当成鸡肋但在复杂问题上价值会越来越明显。我自己比较常用的一个比喻是suggest 模式像请了个代驾你指个地方它把你送到Rubber Duck 模式像请了个陪练它跟你说“前面路口你觉得应该走哪条为什么”前者舒服后者长本事。两者没有绝对优劣关键看场景后面我会专门说怎么组合用。3. 实操让 Rubber Duck 帮你理清一个真实的 Bug3.1 一个典型的前后端联调场景空谈理论太干我拿一个真实场景模拟一遍。假设你现在在做一个登录功能前端是 React后端是 Express JWT。需求是用户登录成功后前端带着 token 去请求/api/user/me拿用户信息。结果用户登录后页面一直白屏控制台显示这个接口返回 401但你在 Postman 里手动带同一个 token 去请求接口是好的一切正常。这种“Postman 好、浏览器不行”的谜题几乎每个做前后端联调的人都遇到过。它的问题不固定可能是前端把 token 放错了 header可能是后端解析方式不对也可能 token 在某个环节被截断了。如果直接把这个问题丢给普通 AI 聊天它大概率会直接列出一堆可能原因甚至直接贴一段修复代码。这看起来很快但你有没有想过如果你不改完还是不行你能从这次对话里得到什么你可能只知道“试了五六种方案最后一种成功了”但并不知道为什么前面五种不行。用 Rubber Duck 模式走一遍体验完全不同。3.2 一次完整的 Rubber Duck 对话复盘下面是我模拟的一次对话实际使用时你描述的语言会更口语化没关系它听得懂。Me我的前端登录后去拿用户信息/api/user/me 一直 401但 Postman 里用同样的 token 请求是通的。Duck先确认一下请求头。前端代码里 Authorization 是怎么拼的是 Bearer token还是只传了 tokenMe我用了 fetchheaders 里写的是 Authorization: token没有加 Bearer。Duck那后端解析 JWT 的时候是用哪种方式比如 jwt.verify(req.headers.authorization.split( )[1])还是直接拿整个 authorization 做校验Me我看了下后端代码是 split( )[1]所以如果前端没带 Bearer 前缀它解析出来的就是 undefined。Duck听起来你已经定位到第一个问题了。那再顺着看一下你前端存 token 用的 localStorage 还是 sessionStorage有没有可能在页面刷新后读不到导致这次请求其实没把 token 带上Me用的是 localStorage刷新应该还在。不过我想起来了登录接口返回的数据结构里 token 字段是 data.token我存的时候写成了 data.access_token所以其实存进去的是 undefined。看到没有整个对话里Duck 没有一次说“你的 bug 是 XXX”它只是在最关键的两个节点上各推了一把。第一个问题把“为什么 Postman 行、浏览器不行”的矛盾聚焦到 header 格式上第二个问题又把“如果 token 本身没存对会怎样”这个分支引出来最后是我自己脱口而出“我存错了字段名”。这个体验和心理感受跟“AI 直接给答案”完全不一样。直接拿到答案时你可能心里还会嘀咕“它说得对吗”但亲口说出“哦是我存错了字段名”的那一刻答案的确定性是百分之百的。因为你回忆、对照、排除最后是你自己走完了一遍完整的因果链。3.3 从对话里学到的提问技巧“提问”这件事看起来简单但要真让 Rubber Duck 发挥出最大价值你的姿势也得调整一下。第一描述问题时尽量用“期望 vs 实际”的格式来开场。不要只说“登录后白屏了”试着说“用户登录成功后应该显示个人信息页但现在请求 /api/user/me 返回 401页面停在 loading 状态”。信息密度越高Duck 的提问就越精准。第二别一次倒出五个症状。我开始用的时候喜欢把报错、现象、怀疑、变量值一股脑全贴进去结果 Duck 只能挑其中一个先聊其他线索它根本顾不过来反而容易绕晕。更好的做法是从最核心的异常点切入聊清楚一个再展开下一个。第三当你已经排除了某个假设请明确告诉它。比如“我确认过 token 在 localStorage 里是存在的console.log 打过”。这等于帮它剪掉了推理树的某一支后续提问会更聚焦。它不是你肚子里的蛔虫你说得越清楚它问得越准。第四抱有“形式主义”的心态也很重要。哪怕你已经知道答案了也试着用几句话说给 Duck 听。有时候你以为自己懂了但真要组织语言时才会发现逻辑里有裂缝。这个习惯持续下来对排查问题能力的提升非常明显。4. 什么时候用 Rubber Duck什么时候直接让它改代码4.1 一张表说清不同场景的选择Rubber Duck 模式不是万能的它有自己的舒适区。我用了这段时间对“什么时候该用它”有一个很明确的分界线如果你需要的是对问题的深入理解用 Duck如果你只需要尽快恢复线上服务别犹豫直接 suggest 或改代码。场景推荐工具原因线上紧急故障服务宕机suggest 或手动修复优先恢复可用性没时间慢慢梳理逻辑一个反复出现的疑难 bugRubber Duck 模式反复出现说明有盲区值得花时间理解根因而不是缝缝补补接手别人的项目想搞懂某块逻辑explain 为主先用 explain 快速建立全局认识再用 Duck 梳理具体疑问写代码前先理清设计思路Rubber Duck 模式它会把方案里你没想清楚的边界条件一个个揪出来提交前自查 code review 逻辑Rubber Duck 模式强制你把“为什么这么改”说清楚能挡掉一批自我感觉良好的冲动提交想快速生成一段标准工具代码suggest查文档还不如直接让它给这种场景不需要多少思考这个表格没有绝对答案但我个人强烈建议任何时候只要时间允许对疑难问题都先走一遍 Rubber Duck 模式再动手改代码。它不是效率的敌人恰恰相反它通过让你想清楚避免了你后面在错误方向上反复返工。五分钟的思考能省掉两小时的无头苍蝇式排查这笔账怎么算都划算。4.2 组合打法explain、duck、suggest 的配合单用 Rubber Duck 肯定不够真正舒服的工作流是把它和 Copilot CLI 的其他能力串起来形成一个“读代码—理思路—写修复—复盘”的闭环。第一步用 explain 读懂代码。接手一块陌生代码时别急着叫 Duck先让它解释整体流程了解每个函数大概在干什么。有了底稿之后你的问题描述才不会浮在表面。第二步用 Rubber Duck 做推理。把“读代码时产生的模糊不适感”提炼成具体问题交给 Duck 陪同你梳理。这一步的目的是从“我觉得这里好像不对”变成“我知道了是这个条件分支在特定情况下没被处理”。第三步用 suggest 拿修复方案。当你和 Duck 已经把问题锁定到非常具体的函数、变量、条件分支时再请 suggest 生成修改建议。这样做出来的修改你知道它为什么这么改它的副作用你也能预判。第四步修复完成后再开一轮 Rubber Duck 复盘。把改动讲给它听或者让它就“这次改动可能会影响哪些地方”提问。这一步能帮你把“修好一个 bug带出三个新 bug”的风险压到最低。这套组合拳用下来你会发现自己对代码仓库的掌控感比单纯依赖 Copilot 高很多。代码还是 AI 在写但判断“哪些地方需要写、写成什么样、跑了之后会发生什么”的人始终是你AI 只是那个帮你做家务和当陪练的搭档。4.3 团队协作中的三种玩法Rubber Duck 模式在单人调试之外放在团队里其实也有很巧妙的应用场景。第一种是新人带教。以前老带新最耗时间的环节是新人描述不清问题。你要么靠猜要么一步步问他“报什么错”“点哪里触发的”“传了什么参数”。现在可以让新人先对 Rubber Duck 讲一遍Duck 会不断追问等新人带着一轮被追问过的问题来找你时沟通效率完全不在一个量级。Duck 帮你做了那层最枯燥的“需求澄清”。第二种是 code review 前的自检。我现在提交 PR 之前会先把关键 diff 讲给 Rubber Duck 听一遍让它扮演一个爱挑刺的 reviewer。它经常会问到你没考虑过的边界情况比如“如果这个字段是空字符串呢”“如果并发请求同时走到这里呢”。这些追问能帮你在提交之前挡掉很多低级的 review 意见。第三种是远程结对编程。两地协作时一个人改代码另一个人在旁边看容易产生带宽瓶颈。现在我们有时会用 Rubber Duck 作为“虚拟结对搭档”负责在开发者 coding 时不断提问逼着开发者把思路说出来。两边代码仓库同步Duck 会话存下来发群里对方随时可以看推理过程不用专门约时间拉会对齐。5. 常见问题与避坑指南5.1 高频问题速查表用了一段时间我也总结了一些大家容易遇到的情况以及处理思路整理成了表格方便你直接查阅。现象原因处理方式Duck 总是在重复同一个问题你提供的信息不够丰富它只能基于现有信息反复确认尝试补充已验证的事实比如“我已经确认 token 存在”“我已经检查过后端日志”Duck 给的方向跑偏了推理链中途断了或者你前面描述中存在误导性信息直接纠正它“这个假设我已经排除了我们换个方向”Duck 好像没“记住”我贴的代码CLI 模式的上下文窗口有限长文件会被截断只贴关键函数片段不要整个文件糊上去回答过于泛泛像万能模板问题描述太模糊缺少具体报错信息或代码上下文按“期望 vs 实际 报错栈 相关代码”三段式重新描述不确定是否理解业务逻辑它只能看到你贴进去的片段看不到整个业务流程先用 explain 让它理解上下文或者用自然语言补充业务背景会话太长导致越讲越乱前面的上下文本该被丢掉了但角色还沉浸在前面的假设里主动 /exit 结束会话开一个新会话重新组织问题个人经验是Rubber Duck 模式更适合“精炼而准确”的输入。它不是搜索引擎你喂给它混乱的噪音它只能还你混乱的问题。在对话前花两分钟把问题理清楚收益远远大于依赖模型去推理你到底想问什么。5.2 我踩过的几个真实坑第一个坑是拿它当“标准答案生成器”。有次我图省事直接丢给 Duck 一个函数说“这行代码对么”它问我“你觉得哪里可能有问题”我一下就懵了。说实话我当时的期望是让它帮我背答案不是让它反问我。但冷静下来才发现如果连“哪里可能有问题”都答不上来我大概率也没资格判断它给的答案对不对。这个模式逼着你先成为自己代码的审问者逃避不了。第二个坑是贴文件贴太多。Rubber Duck 的上下文处理能力和 IDE 里的 Copilot 不太一样一次会话塞太多代码会让推理质量快速下降。我自己实测下来单个会话里贴超过两百行代码之后它的提问会开始变得重复甚至会重新问前面已经确认过的问题。现在我的习惯是保持小火慢炖一次只贴一个函数、一个报错栈聊完一个话题再开一个新的 Duck 会话。第三个坑是安全边界。有次我在公司机器上调试一个内部服务顺手把带内网 IP 的日志贴进去了贴完才意识到不对。虽然 Copilot CLI 本身有明确的隐私条款和合规政策但自己负责的项目里哪些信息能出内网只有你最清楚。这个底线一定要守住不可逆的信息一旦出去了就没有后悔药。第四个坑是忘了清理会话。Rubber Duck 对话记录在终端里不会自动销毁如果你在公司共用机器上跑了长时间会话离开前记得退出避免下一个人接着你的上下文继续聊也避免敏感内容留在别人的终端缓冲区里。5.3 给新手的三个锦囊最后给刚开始尝试 Rubber Duck 模式的朋友三个我验证过的习惯。一是每天固定十分钟“Duck 时间”。哪怕当天没有遇到疑难 bug也找一段自己最近写的代码主动讲给 Rubber Duck 听。它提出的问题往往就是你自己思维里的“路灯盲区”每天补几个盲区一个月后看代码的视角会完全不一样。二是把 Duck 会话记录下来。终端里不方便直接保存但可以定期把自己回答里最精彩的几句“啊我知道了”复制出来整理成自己的 bug 笔记。我翻看这些笔记时发现很多当时卡住的问题底层原因就那么几类参数没对上、生命周期没弄对、缓存没刷新、边界没处理。记下来之后以后再遇到类似问题定位速度会快非常多。三是用它来准备 code review 的说辞。提交 PR 之前把改动核心逻辑讲给 Duck 听一遍如果过程中发现了说不通的地方那多半就是 review 时会被挑战的地方。提前把这些漏洞补上review 通过的效率会高出一截。最后说点实在的我用 Rubber Duck 模式这段时间最大的改变不是 bug 修得更快了而是我对“调试”这件事的心态变了。以前打开终端碰到报警第一反应是烦躁接着想赶紧找个工具把错误压下去现在反而先提醒自己能触发这么明确的报错说明代码里一定有一个具体的因果链等着我去拆问题本质上是另一个自己埋下的谜题。这个模式今天能帮你逐步发现问题等到你习惯把思路讲清楚之后再回头写代码写法也会变得更稳。因为你连自己写的每一行代码为什么要存在都想清楚了模型生成的代码在你眼里就不再是黑盒而是一个可以被审查、被反驳的草稿。技术工具迭代得再快最后拉开差距的仍然是你对问题的理解深度。Rubber Duck 能帮的就是把你脑子里的混沌一点点问成清爽的答案。