“今天的 AI 编程,是在不确定性中、在混沌中、在混乱中的一种编程。”
延伸阅读:Debug 工作流如何嵌入整体工程循环,参见Test–Code 循环:为什么测试代码比功能代码更重要与AI-Native 工作流:Plan–Act、Test–Code、Doc–Code–Doc。
本章通过一个扑克游戏动画队列的真实案例,来展示 AI-native 团队在“深水区”如何和 AI 协作调试,以及在哪些地方,人类必须亲自下场。
1. 案例:扑克摊牌动画消失的深层 bug
这个 bug 的现象是,如果一个牌局打到了最后一条街(river),具体情况是:
- 如果是用户先手的一个牌局,在最后一条街用户 check,然后对手跟着 check
- 这个时候理应是出一个摊牌结算的动画的,但是摊牌结算的动画就不会出
- 只有在这样特定的牌局下才会暴露这个问题
最后一路排查,这个牌局需要是用户先手,然后打到了最后的一条街。同时用户先手选择 check,然后对手选择 check。这个时候理应期待的是这两个 check 动画播完,然后再接着播摊牌结算的动画,但是最后摊牌结算动画就不会被播放。
最后发现是前端没有错,问题在于:后端传过来的数据里就少了这个摊牌结算的信息,所以前端根本没有动画可以播。
这个链路非常深,因为代码库本身已经相当庞大,这就直接把我们带进了一个问题:在用 AI 写代码的过程中,软件工程质量要怎么把控?这其实是 AI 编程的一个核心问题。
2. 第一步:不要急着改代码,先让 AI 建立上下文
假设我是代码库的一个新读者,我会怎么解决它?
第一步,我不会一上来就把 issue 丢给 AI,而是先让 AI 建立它自己的上下文:
- 我知道问题跟动画队列有关
- 我会让某个 AI(比如 codex,找快一点的模型)帮我先找到:有哪些文档、哪些代码和动画队列有关
- 这一步,一方面是我自己也找不全,另一方面是让 AI 自己先对“所有和动画队列有关的东西”建立一个完整的上下文
如果你一上来就把 issue 告诉它,它可能会过于 focus 在这个 issue 本身,而漏掉一些关键逻辑和模块。
这里"先建上下文、再动手"的模式,与AI 的无状态性与 Context Window中关于 session 接力的讨论是一致的。
3. 第二步:精确描述问题,而不是一句“有 bug”
在 AI 对上下文有了一定了解之后,才是讲述问题现象的时机。比如:
“In a specific scenario, where the hero is out of position (OOP), and we play through the game till the river street. The hero checks. And the villain checks back. We should see a showdown animation. Right now, we are only seeing the hero check and the villain check, but not the showdown. Please find out why.”
这里的关键点是:
尽可能用现象和场景去描述问题,而不要一上来就给结论。
4. 第三步:先加 debug logs,而不是让 AI 立刻改代码
对于 codex 或者别的 AI,它有一个天然倾向:
一听到“有 bug”,就会立刻去改代码。
很多时候,我们并不希望它马上改代码,所以需要提前给它一个约束:
“Don't make changes yet.”
在我们的例子里,我们已经知道 animation queue 的设计,在 river 这一条街里本来应该有三个动画:
我 check,然后对手 check,然后有一个摊牌动画。但我们现在只看到了前两个。
所以,此时问 AI 的正确方式不是“帮我改好”,而是:
- 告诉它预期的动画队列
- 让它在关键位置帮你 加 debug logs
- 然后再用这些 logs 去反推问题在哪里
5. 第四步:用 debug logs 校正 AI 的思路
为什么要打 debug logs?
- 因为 AI 对代码的理解不一定是对的
- 它给出的 debug 结论也经常是错的,而且会“陷在自己那套解释里”
如果你直接让它根据自己的理解去改,很可能会越改越偏。但如果:
- 先让它在关键路径上加好 debug logs
- 然后你把真实的 logs dump 给它
- 再让它在 logs 的基础上重新分析
它的思考过程往往会被纠正很多。
如果你发现:
— 你已经贴了 debug logs,它还是想不对,那就要回头想一想,是不是你对问题现象的描述本身就不够清楚,甚至是错的。
