乔木博客
全部
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
·
向阳乔木
📌 文中提及的人物和组织
人物: Lalit Maganti, Andrej Karpathy
公司/组织: Google
产品/模型: Perfetto, PerfettoSQL, syntaqlite, Claude Code, SQLite