实测 Codex:它是如何精准地把“正常代码”修出死循环的?

现在的 AI 修 BUG 确实名声在外,号称能替代程序员。刚好手里攒了个 Codex 的坑位,我便拿自家的 定时任务系统(haskell-periodic) 开了刀。

这个项目非常底层,对并发和数据流的要求极严。本以为是请了个"神医",结果发现这哥们儿是个"顶级庸医",差点没把我代码库给送走。


准备工作:配置 Codex

安装和配置倒是不难,几步搞定:

  1. 安装: npm install @openai/codex

  2. 配置 Azure GPT-5.3-Codex: 先随便填个无效 Key 把程序跑起来,然后去 $HOME/.codex 修改配置文件:

    Ini, TOML

    ini 复制代码
    model = "gpt-5.3-codex"
    model_provider = "azure"
    model_reasoning_effort = "medium"
    approvals_reviewer = "user"
    
    [model_providers.azure]
    name = "Azure OpenAI"
    base_url = "https://your-server.cognitiveservices.azure.com/openai"
    query_params = { api-version = "2025-04-01-preview" }
    env_key = "AZURE_OPENAI_API_KEY"
    wire_api = "responses"

    记得把真正的 Azure Key 塞进环境变量里。

改完后先打个招呼测试一下,等它回个"你好"确认成功后,真正的"好戏"就开始了。


现场实测:AI 的"无限修复"表演

我移步到项目目录,给 Codex 下了个指令:"analyze haskell code, find bug and fix it"

Codex 表现得非常专业,扫描代码、定位 BUG、给出修改建议。每改完一个,它都会乖乖等我确认。我当时心想:这效率,以后我是不是可以只负责点 continue 了?

结果,我接受了几个建议后,越想越不对劲。这哪是修 BUG,这简直是在制造"赛博地雷"!

1. 制造隐性错误的"防崩"修法

原代码在读取数字类型的命令行参数时,直接用了 read s。如果参数输错了,程序会报错崩溃。Codex 觉得这不优雅,给我改成了:

Haskell

ini 复制代码
-- AI 修正后的 safeRead
safeRead :: Read a => a -> String -> a
safeRead def s = fromMaybe def $ readMaybe s

细思极恐:

这看起来很安全,但其实造了一个超高级 BUG

在定时任务这种系统里,参数输错了必须报错 ,让我立马知道配置有问题。现在好了,它默默给个默认值继续跑,我可能半天都发现不了系统正在按错误的逻辑运行。这种静默失败比程序崩溃要致命得多。

2. 把正常的逻辑改"丢"了

在处理命令行参数解析时,原来的代码是:

Haskell

rust 复制代码
-- 原代码
getFlag :: [String] -> [String] -> String
getFlag _ []        = ""
getFlag _ [_]       = ""
getFlag ks (x:y:xs) = if x `elem` ks then y else getFlag ks (y:xs)

Codex 大手一挥,把它改成了:

Haskell

rust 复制代码
-- AI 修改后
getFlag :: [String] -> [String] -> String
getFlag _ []        = ""
getFlag _ [_]       = ""
getFlag ks (x:y:xs) = if x `elem` ks then y else getFlag ks xs

感受到 BUG 了吗?

原代码在匹配不到 flag 时,会递归检查 (y:xs),因为 y 可能是下一个 flag。但 AI 改完后直接跳过了 y。这意味着如果参数里连续出现两个 flag 或者格式稍微复杂点,部分参数就会直接凭空消失。乍看逻辑通顺,实则业务全乱。

3. 亲手撸出一个"死循环"

这是最精彩的部分,关于网络包接收的代码:

Haskell

ruby 复制代码
-- 修改前的关键逻辑
case decodeOrFail (BL.fromStrict hbs) of
  Left (_, _, e0) -> do
    putBack $ hbs <> crcbs -- 这里只放回了 header 和 crc
    throwIO $ PacketDecodeError $ "Packet header: " <> e0

AI 觉得这里的 putBack 不够完整,帮我"优化"了一下:

Haskell

ruby 复制代码
-- AI 修改后
case decodeOrFail (BL.fromStrict hbs) of
  Left (_, _, e0) -> do
    putBack $ magicbs <> hbs <> crcbs -- AI 贴心地把 magicbs 也塞回去了
    throwIO $ PacketDecodeError $ "Packet header: " <> e0

真是一口老血喷出来:

在协议解析里,magicbs 是已经探测完的包头标识。AI 把 magicbs 重新塞回缓存区,下一次循环它又会把这一段当成新包去解析,然后解析失败,再塞回去......一个逻辑完美的死循环诞生了。

4. 送你一个"空间泄漏"大礼包

Haskell 程序员最怕的就是 Space Leak,AI 显然想帮我体验一下这种痛苦:

Haskell

vbnet 复制代码
-- 原代码:用 forever 运行
runCheckNodeState ... = void . async . forever $ do ...

-- AI 修改后:改成了递归 loop
runCheckNodeState serveStateTVar ... = void $ async loop
  where loop = do
          ...
          serving <- readTVarIO serveStateTVar
          when serving $ loop

一眼看穿:

这种手写的递归 loop 如果没有处理好惰性求值,在长时间运行后,堆栈里会堆积大量的 Thunk。系统跑得越久,占用的内存就越多,直到内存耗尽。这种内存泄漏在本地测试很难发现,一旦上线就是定时炸弹。


思考总结:AI 写代码,心要大,眼要尖

经过这一番折腾,我悟出了一个道理:

AI 修 BUG 确实很牛,但它目前还不具备"业务常识"和"全局视野"。 它看到的只是局部代码的美观和健壮,却不理解底层网络流的不可逆性,也不理解 Haskell 严苛的求值模型。

用 AI 写代码,真的得长点心。如果你对自己的项目没有绝对的掌控力,AI 丢给你的可能不是"解药",而是包装得非常漂亮、甚至连你也看不懂的高级 BUG

结论: 别让 AI 掌握"最终解释权",Review 代码的时候,要把自己当成它最严厉的教官!


老哥们,你们用 AI 修代码还遇到过什么奇葩坑?评论区聊聊。

相关推荐
树獭叔叔3 小时前
Agent 记忆系统设计全景:从短期对话到长期知识沉淀
后端·aigc·openai
xixixi777775 小时前
AI 用于漏洞检测、威胁狩猎、合规审查;安全沙箱 / 隐私计算保障 AI 模型与数据可信
人工智能·网络安全·ai·openai·数据·多模型
俊劫6 小时前
AI Harness - 2026 AI 工程新范式
前端·openai·ai编程
安思派Anspire7 小时前
Ghost互联网
aigc·openai
超爱柠檬7 小时前
Skill—— AI 能力封装与复用
openai·ai编程
用户479492835691519 小时前
Superpowers-vs-GSD-深度拆解两大-Claude-Code-Skill-框架
openai·agent·claude
Cyeam19 小时前
爆火的 OpenClaw,赢在生态创新
程序员·开源·openai
卡尔特斯1 天前
Ultralytics YOLO26 自动对指定标注文件夹区分标注素材脚本与训练脚本
python·openai
树獭叔叔1 天前
FFN 激活函数深度解析:从 ReLU 到 SwiGLU 的演进之路
算法·aigc·openai