用 AI 造了一个等了 8 年的工具,他想说点真话 · 乔木博客 向阳乔木 2026-04-21

乔木博客

全部

AI工具

AI教程

AI生成

AI资讯

健脑房

播客解读

论文学习

用 AI 造了一个等了 8 年的工具,他想说点真话

AI教程

·

2026年4月21日

·

856 次阅读

·

约 20 分钟

Lalit Maganti 在 Google 做 Perfetto 项目,维护一个叫 PerfettoSQL 的查询语言。

Perfetto 是 Google 开发的性能分析平台,用来追踪 Android、Chrome 等系统的运行状态。

PerfettoSQL 是在它上面跑的查询语言,基本上就是 SQLite 加了一些扩展,让工程师能用 SQL 的方式去分析性能数据。

这个语言在 Google 内部有超过十万行代码,被各种团队广泛使用。

用的人多了,自然就有需求:格式化工具、代码检查、编辑器插件。

他去找开源社区的 SQLite 工具,结果一个能用的都没有,要么不够准确,要么太慢,要么根本没法扩展到 PerfettoSQL 的方言。

这个遗憾在他脑子里压了整整八年。

今年三月,他终于发布了 syntaqlite。

用了三个月,250 小时,全靠 AI 编程工具才做到。

他写了一篇很长的复盘,没有吹嘘,也没有装清醒。

这是目前关于 AI 辅助编程最诚实的一篇文章之一。

原文:https://lalitm.com/post/building-syntaqlite-ai/

为什么 SQLite 的开发工具一直是空白

SQLite 是世界上部署最广泛的数据库,没有之一。

每一部智能手机、每一个主流浏览器、无数嵌入式系统里都跑着它。

但围绕它的开发者工具,一直是个令人困惑的空白地带。

devtools(开发者工具)在这里具体指三样东西:

formatter(格式化器):自动把 SQL 代码排版整齐,就像 Prettier 对 JavaScript 做的事

linter(代码检查器):静态分析代码,找出潜在的错误或不规范写法

language server / LSP(语言服务器):让编辑器能提供自动补全、跳转定义、错误提示等智能功能,TypeScript 在 VS Code 里的那种体验

Lalit 找过市面上的工具,要么格式化器会"吃掉"注释,要么 linter 不支持 SQLite 特有的语法,要么语言服务器功能残缺。

根本原因在于,这些工具的解析器都是"近似"SQLite 语法,而不是精确还原它。

为什么精确解析 SQLite 这么难?

通常一门语言要做开发工具,第一步是拿到它的正式语法规范,然后按规范写解析器。

但 SQLite 没有正式规范,它也不暴露解析器的 API 给外部使用。

更奇特的是,SQLite 内部实现根本不生成语法树,它直接从 SQL 文本编译成字节码,就像跳过了中间步骤。

这意味着,想做一个真正高质量的 SQLite 解析器,唯一合理的路是:钻进 SQLite 的 C 源码,把相关部分挖出来,重新组装成一个能生成语法树的解析器。

具体来说分三步:

从 SQLite 源码中提取词法分析器(tokenizer,负责把 SQL 文本切成一个个词)

提取语法规则,用 SQLite 自带的 Lemon 解析器生成工具,造出自己的解析器

手动处理超过 400 条语法规则,逐一决定每条规则在语法树里对应什么节点

SQLite 的源码是出了名的难读,用极其紧凑的 C 风格写成。

Lalit 说他光是理解虚拟表 API(virtual table API,一种让 SQLite 能查询外部数据源的机制)就花了好几天,而他已经用这个 API 做了八年 Perfetto 了。

这种项目最可怕的地方在于:它同时满足"太难"和"太无聊"两个条件。

任何一个单独存在都还好,两个加在一起,动力就彻底死了。

还有一个额外的复杂性:他不想只做一个服务 Perfetto 的工具,他想让它对所有 SQLite 用户都有用,同时还要支持 PerfettoSQL 这样的方言扩展,而不需要 fork 整个项目。

这个设计目标让架构难度又上了一个台阶。

三个月的过程,分两个截然不同的阶段

第一阶段:一个月的 vibe coding 实验

2025 年底,他观察到 AI 模型的质量似乎有了一次明显的跃升。

Andrej Karpathy(OpenAI 联合创始人之一)当时说过一句话,他觉得说得很准:

"编程 Agent 在十二月之前基本上不能用。模型的质量、长期连贯性和执行力有了显著提升,能够完成大型、长周期的任务,对默认的编程工作流造成了极大冲击。"

圣诞节期间,他给自己出了一道题:能不能用最极端的方式,完全靠 AI 把这个项目做出来?

他订了 Claude Code 的 Max 计划(每月 200 英镑),把自己定位成"半技术经理",几乎把所有设计和实现都委托给 Claude。

整个一月,他就这么干。

功能上最终到了一个还算能用的地方:一个用 Python 脚本从 SQLite 源码提取的 C 解析器,一个建在上面的格式化器,同时支持 SQLite 和 PerfettoSQL,还有一个网页演示环境。

但一月底,他仔细审查了整个代码库,结论很直接:完全是意大利面条代码。

他在项目日志里写道:

"我很快就精疲力竭,开始接受太多随机代码。一旦搞乱了,不把它扔掉就很难恢复。"

Python 提取流水线的大部分他自己都看不懂,函数散落在各个随机文件里没有清晰结构,有几个文件膨胀到了好几千行。

代码解决了眼前的问题,但永远不可能支撑他更大的愿景,更别说集成进 Perfetto 的工具链了。

唯一的收获:证明了这条路走得通,以及 500 多个可以复用的测试用例。

第二阶段:全部推倒,换语言,换角色

他做了两个决定。

第一,把所有代码扔掉,从零重写。

第二,把大部分代码库切换到 Rust。

原因很实际:C 语言会让格式化器、验证器、语言服务器这些上层组件很难写,而 Rust 可以让提取流水线和运行时用同一种语言,不用再在 C 和 Python 之间来回切换。

(词法分析器和解析器本身仍然保留 C,因为它们直接从 SQLite 源码提取。)

更重要的是,他完全改变了自己在项目里的角色。

他在和 Claude 的对话记录里,开头第一句话是:

"我想要的工作方式是:我负责所有决策和方向,我来告诉你做什么。我不想让你规划,不想让你独立行动。清楚了吗?"

他把 AI 从"项目负责人"变成了"超级自动补全",套在一个更严格的流程里:

先做有主见的设计,每次改动都仔细审查,发现问题立刻修,投入精力建脚手架(linting、验证、非平凡的测试)来自动检查 AI 的输出。

核心功能在二月完成,最后的收尾阶段(上游测试验证、编辑器插件、打包、文档)在三月中旬完成,发布了 0.1 版本。

AI 真正做到了什么

打破了第一步的惯性

Lalit 承认自己有一个老毛病:面对大项目容易拖延。

他在日志里写道:

"用它来超快速地原型化改动,然后回来把所有代码删掉,再以更结构化的方式重新构建。"

AI 改变了这件事的方式很有意思。

他不再需要坐在那里想"我该怎么理解 SQLite 的解析机制",而是变成"让 AI 先给我一个方案,我来看哪里不对"。

有个具体的东西可以批评,比对着空气思考要容易太多。

AI 生成的原型,哪怕最后全扔掉,也把抽象的不确定性变成了具体的东西可以反应,证明了这条路走得通。

写"显而易见的代码"比人快

他的原话是:如果能把问题拆解成"写一个有这个行为和参数的函数",AI 会比他写得更快,而且风格可能对未来的读者更直觉友好。

AI 会写他懒得写的注释,会保持代码风格和项目其他部分一致,会用语言的"标准写法"。

他在日志里提到,他既让 AI 完整生成整个模块,也会在需要的时候亲手写 SIMD 指令并在 Compiler Explorer 里看汇编输出,完全取决于具体问题。

标准代码是绝大多数,手写的是例外。

但这个"标准化"是双刃剑。

对于项目里大多数普通代码,标准化是好事。

但每个项目都有它的核心难点,那些需要做非显而易见决策的地方,AI 的本能是把它"正常化",反而有害。syntaqlite 的提取流水线和解析器架构就是这样,Lalit 最后选择自己设计、自己写。

重构和生成一样快

他在日志里反复强调这一点:

"如果你用 AI 在工业规模上生成代码,你必须持续不断地重构。如果不这样做,事情会立刻失控。"

"每次大量生成代码之后,值得作为人类停下来问'这个丑吗',如果是,就做点什么。"

有时候 AI 能自己清理。有时候有一个大规模的抽象层 AI 看不到,但他能看到,他给出方向,让 AI 执行。

他的日志里写道:"审计加重构,两者配合效果惊人。"

如果你有品味,犯错的代价会大幅降低,因为你能快速重构。

大幅降低了学习新领域的成本

他举了一个很典型的例子:做格式化器的时候,他需要了解一种叫 Wadler-Lindig 的算法。

Wadler-Lindig 是一种"漂亮打印"(pretty-printing)算法,让你能声明式地描述文档的排版方式,由打印器自动决定在哪里换行,而不是手动计算每行的宽度。

在格式化器设计的对话记录里,Claude 主动提出了"从一开始就用 Wadler-Lindig 文档模型做宽度感知格式化",Lalit 能立刻参与权衡讨论,而不是先花好几天才发现这个方法的存在。

他对这类跨领域切换的总结是:

"我对 C++/Android 性能非常熟悉,但对 Python/Go/Rust 工具完全不了解。有了 AI,这不是问题:基础是一样的,术语是相似的。我现在能在领域之间切换得非常快。"

VS Code 插件也是类似的情况。

他对这套 API 完全陌生,但一小时内就有了一个能跑的扩展。

让 AI 充当代码库的"即时顾问"

他在日志里描述了一种很实用的用法:

"你可以控制深入程度:表层概览用于快速复习,详细线性讲解用于深入了解,定向审计用于追查问题。"

当他切换上下文太多、忘了某个模块是怎么工作的,他可以直接问 AI:"给我讲讲这个组件",或者"审计这个 repo 里所有 unsafe 的用法"。

这种按需获取上下文的能力,在频繁切换任务的业余项目里格外有价值。

让项目变得更完整

没有 AI,他大概会发布一个核心功能可用、但边角料全缺的工具。

有了 AI,这些东西都做出来了:VS Code 插件、文档网站、crates.io(Rust 包)、PyPI(Python 包)、npm(JavaScript 包)、Homebrew(macOS 包管理器)发布、Zed 编辑器插件。

每一个单独拿出来都是一个周末项目。

更重要的是,他有了余力去思考用户体验。 他在日志里写道:

"不只是'我应该解决什么问题',还有'什么错误提示最有用'和'怎么让这个真的简单易用'。"

具体落地的例子:类似 rustc 风格的多错误诊断(带"你是不是想写……"的建议)、快速修复代码操作、syntaqlite.toml 配置文件(让用户不用每次都写命令行参数)。

AI 没有只是让同一个项目变快,它改变了这个项目是什么。

AI 带来了什么代价

像老虎机一样让人上瘾

他用了一个很准确的比喻:发一个 prompt,等待,要么得到很好的东西,要么得到废物。

这个随机性本身就让人上瘾。

他在日志里写道:

"我在某些时候感到上瘾,沉迷于它的'老虎机'特性,耗尽时间,从长远来看也耗损了健康。"

他发现自己会在深夜"再试一个 prompt",即使知道大概率没用。

沉没成本效应也在发挥作用:已经在这个任务上花了这么多时间,再试一次吧,换个说法也许就行了。

疲劳会让这个循环变得更糟。 他的日志里写道:

"我对 AI 的精确度与我的疲劳程度成正比。当我精力充沛时,我能非常精确,效率很高。但当我疲惫时,我开始说'做 X 事情'而没有太多细节,AI 的输出会变得糟糕很多很多很多。"

在这种状态下,AI 其实比他自己写还慢,但很难从这个循环里跳出来。

失去对代码库的感知

他多次描述了一种感觉:突然意识到自己不知道代码里发生了什么。

不是不懂整体架构,而是那些日常细节,函数在哪里,谁调用谁,那些小决策是怎么积累成现在这个样子的。

他在日志里写道:

"有好几次我'失去了'对代码库的感知,出现了令人惊讶的问题,我只能说'AI,请帮我调试',我讨厌那种感觉。"

更深的问题是:失去感知之后,你没法有效地和 AI 沟通。 他的日志里写道:

"你失去了'正在发生什么'的心理线索,就无法有意义地、清晰地沟通,每次交流都变得更长更冗长,需要 Agent 做更多工作。"

本来可以说"把 FooClass 改成做 X",现在只能说"把那个做 Bar 的东西改成做 X"。

AI 要先猜 Bar 是什么,再猜它对应哪个类,有时候猜错了,问题变得更复杂。

他说这和工程师们抱怨"不懂代码的管理者提需求"是完全一样的问题,只是现在那个不懂代码的管理者变成了你自己。

他的解决方法是:每次 AI 实现完一段代码,立刻自己读一遍,主动问自己"我会怎么写这个"。

有一点值得注意:这个问题和"几个月前自己写的代码也会忘"有些类似,但 AI 让这个漂移发生得快得多,因为你没有经历最初打出那些代码时建立的肌肉记忆。

推迟了真正重要的决策

因为重构很便宜,他总是可以说"这个问题以后再处理"。

但推迟设计决策有一个隐性代价:代码库一直处于混乱状态,这会持续消耗他理清思路的能力。

他在日志里写道:

"AI 让我拖延真正做关键设计决策,因为重构很容易,有好几次我能说'我以后再处理这个'。但与此同时,它腐蚀了我清晰思考的能力,因为代码库一直很混乱。"

vibe coding 那个月是最极端的版本。

他理解问题所在,但如果他更早、更果断地做出艰难的设计决策,他本可以更快收敛到正确的架构。

测试带来的虚假安全感

500 多个测试让他感觉很踏实,AI 也让生成更多测试变得很容易。但他在日志里写道:

"人类和 AI 都没有足够的创造力来预见你未来可能遇到的那些奇怪情况。如果你没有某种基本的基础,你将永远追着 bug 跑。"

vibe coding 阶段的原型有 500 多个测试,照样彻底崩了。

重写时,他换了策略:投入精力做幂等性测试(同样的输入总是产生同样的输出),以及一个更关键的验证机制,写了一个 TCL 驱动程序,把 SQLite 自己的约 1390 个上游测试文件接进来,每条 SQL 语句同时跑真实 SQLite(sqlite3_prepare_v2)和 syntaqlite 的解析器做对比,SQLite 接受的语句 syntaqlite 必须接受,SQLite 拒绝的 syntaqlite 必须拒绝。这能抓住手写测试永远发现不了的一类 bug。

AI 没有时间感

这是他观察到的一个更深层的问题。他在日志里写道:

"模型没有时间感。它们看到代码库处于某种状态,是的,它们可以通过上下文/文档/记忆获得一些感觉。但它们感受不到时间,方式和人类不同。比如,我能告诉你使用一个 API 是什么感觉,以及它随时间的演变,以及为什么事情是现在这个样子。模型只能通过明确的记录来获得这些,而这样做的成本非常高。"

这和失去一个资深工程师很像。

他们携带的历史和上下文不存在于任何文档里,是真正的隐性知识,指导着周围所有人。

理论上可以把这些都写进文档,但这件事本身极其昂贵。

AI 可以帮起草这些文档,但因为没有办法自动验证它准确捕捉到了真正重要的东西,人还是要手动审查,依然耗时。

还有一个上下文污染问题:你永远不知道关于 API A 的设计笔记什么时候会影响到 API B。

代码库的一致性需要的不只是当前工作的上下文,还需要其他以类似方式设计的东西的上下文。

判断什么是相关的,恰恰需要机构知识本身才能提供的那种判断力。

什么时候 AI 好用,什么时候不好用

他总结出了一个很清晰的规律,沿着两个维度展开。

第一个维度:你对这件事的理解有多深。

当他对一件事已经理解很深时,AI 表现出色。他能立刻判断输出对不对,能快速迭代。

语法规则生成就是最清楚的例子,他在日志里写道:"Agent 团队的成功超出了我最疯狂的想象,能够在一个晚上完成所有工作。"

但前提是他先做了脚手架工程:重构项目结构让 Agent 能在不同文件上并行工作,写了一个差异比较脚本把错误分组成可操作的反馈。

然后还有不可避免的手动检查:"需要逐一检查每一个测试,发现了一堆问题(标志没有正确格式化,缺少字段名等)。"

当他能描述想要什么但还不完全懂时,AI 有用但需要更多投入。

学 Wadler-Lindig 算法就是这种情况,他能判断方向对不对,能从解释里学到东西,但不能完全放手。

当他连自己想要什么都不清楚时,AI 从无用到有害。

项目架构设计就是最典型的例子。

他在早期花了好几周跟着 AI 走进死胡同,探索那些看起来有进展但经不起推敲的设计。

他说他有时候会想,那段时间如果完全不用 AI,直接自己想清楚,说不定反而更快。

第二个维度:答案能不能被客观验证。

他在日志里写道:

"一个属性对于人类或 AI 来说越容易验证正确性,AI 就越擅长处理那些任务。"

代码实现有客观标准:编译通过,测试通过,输出符合预期。

API 设计没有。"这个 API 用起来舒不舒服"、"这个 API 能不能帮用户解决他们的问题",没有测试能回答这两个问题,所以 AI 在这里表现很差。

他在三月初花了好几天专门做 API 重构,手动修复任何有经验的工程师都会本能避开、但 AI 搞得一团糟的东西。

他用了一个物理学的比喻来总结这一切:在局部小范围内,物理定律看起来简单,符合牛顿力学。

但放大来看,时空会以你从局部图景里预测不到的方式弯曲。

代码也一样,在函数或类的层面,通常有清晰的正确答案,AI 在这里很强。

但架构是所有局部组件相互作用的结果,你没法靠把局部正确的部分拼在一起得到全局正确的系统。

知道自己在这两个维度上处于什么位置,是有效使用 AI 的核心技能。

最后

八年的念头,三个月做出来了。

但这个过程不是那种干净的成功故事。

他浪费了一整个月在 vibe coding 上,掉进了管理一个自己根本不理解的代码库的陷阱,最后付出了全部重写的代价。

他希望看到更多这样的文章:诚实的、有细节的、关于用 AI 构建真实软件的记录。

不是周末玩具,是那种要经历用户、bug 报告、以及你自己不断变化的想法的软件。

他的结论很简单:AI 是实现层面的力量倍增器,但是设计层面的危险替代品。

它能给你一个具体技术问题的正确答案,但它没有历史感,没有品味,不知道一个真实的人用你的 API 会有什么感受。

如果你把软件的"灵魂"外包给它,你只是会更快地撞墙。

© 2026

·

向阳乔木

📌 文中提及的人物和组织