桃子桃子快讯
返回首页
工具

Claude 与 GPT-5.5 互审揪出 sqlite-utils 数据丢失 bug

Simon Willison 让 Claude Fable 与 GPT-5.5 互相审查 sqlite-utils 4.…

2026.07.05 · 周日4 分钟阅读

sqlite-utils 是知名的 Python SQLite 工具库,作者 Simon Willison 在准备 4.0 正式版前,借助 Anthropic 的 Claude Fable 与 OpenAI 的 GPT-5.5 两个大模型互相审查代码,发现了一个会引发数据丢失的严重 bug,以及若干 P1 级问题。

最关键的发现:delete_where() 不提交且污染连接

在 iPhone 的 Claude Code 网页版中,Simon 让 Fable 对即将发布的版本做最终审查。Fable 将 5 个问题标为「release blockers」,其中最严重的一处位于 Table.delete_where()

  • 该方法直接通过 self.db.execute() 执行 DELETE,缺少 atomic() 包装,导致连接停留在 in_transaction=True
  • 之后所有 atomic() 调用都会走 savepoint 分支,永远不会真正提交。
  • 复现脚本显示:先 delete_where、再对其他表 insert、关闭数据库后重开——所有变更(包括删除、行 50、表 u 的内容)全部丢失。

Simon 表示「非常庆幸自己没把这版发出去」,并补充说这类问题至少可以在 4.0.1 点版本里修复,不需要为此再升一个 5.0。

工作量与成本

整个 RC1 → RC2 过程的数据如下:

  • 提示轮次:37
  • 提交次数:34
  • 涉及文件:30
  • 代码变更:+1,321 / -190 行
  • 总花费:约 $149.25

Simon 提到一个有趣的体验:难题反而给了 AI 代理 10–15 分钟思考的空档,期间他可以抽身去参加 Half Moon Bay 的独立日巡游,用手机时不时发下一条提示。

新的事务模型与 Python 3.12 兼容性

早期 RC 引入的事务处理是这次的主线改动,新 RC 给出了更清晰的文档说明,原文要点如下:

库内所有写入方法(insertupsertupdatedeletedelete_wheretransformcreate_tablecreate_indexenable_fts 等)都在各自的事务中执行,方法返回前已自动提交;db.execute() 直接执行的写语句同样如此。用户只在两种场景下需要关心事务:想把多个写入合并成原子操作(用 db.atomic()),或者自己用 db.begin() 显式管理事务。

在审查文档过程中,Simon 还意识到 sqlite-utils 对 Python 3.12+ 引入的 sqlite3.connect(..., autocommit=True/False) 模式没有做适配——该模式下 commit()rollback() 行为不同,几乎整套测试都会失败。最终他和模型一起补上了相关处理。

GPT-5.5 反向复核再挖出两个 P1

Simon 曾对「让一个 AI 审另一个 AI」的做法半信半疑,但近几个月他已经习惯让 Anthropic 的最强模型去审 OpenAI 的工作,反之亦然。这次他让 Codex 桌面版与 GPT-5.5 xhigh 复核变更和新 changelog,得到两个值得调查的 P1 级发现:

  • db.query() 在拒绝非 row 语句前会先提交写入,导致 db.query("update ...") 已经在数据库落库后才抛 ValueError,与文档「只能用于返回行的 SQL」的说法不一致。
  • 通过 db.query() 执行 INSERT ... RETURNING 时,事务只在生成器被完全消费后才提交;若调用方不迭代或只用一次 next(),写入会在连接关闭时被回滚,与 changelog 的描述相悖。

Simon 表示这种「跨厂商互审」已经不止一次给出有价值的结果,未来会继续沿用。

信源