编程 Zerolang 深度拆解:当「源码」不再是文本——Vercel 把编译器数据库直接交给 AI Agent

2026-07-30 10:44:29 +0800 CST views 11

Zerolang 深度拆解:当「源码」不再是文本——Vercel 把编译器数据库直接交给 AI Agent

一、背景:Agent 写代码这件事,卡在哪了

2026 年的今天,让 AI Agent 写代码已经不是新闻。Claude Code、Cursor、Codex 们每天在生产环境里提交成千上万个 PR。但如果你认真观察过 Agent 的工作日志,会发现一个反复出现的模式:

  1. Agent 读文件(烧掉几千 token)
  2. Agent 生成一个基于「行号 + 上下文匹配」的文本 patch
  3. patch 应用失败——因为文件在它读取之后被格式化过 / 行号漂移了 / 匹配串出现了两次
  4. Agent 重新读文件,重试
  5. 编译报错,Agent 根据错误信息猜测原因,再改一轮

这个循环的本质问题是:Agent 在操作一个它无法精确寻址的介质。源代码文本对人类是友好的——我们靠缩进、命名和空间记忆来导航。但对 Agent 来说,文本是一种有损的、脆弱的接口:它拿不到符号表,拿不到类型信息,拿不到调用图,只能靠「重新读一遍再猜」来重建这些编译器早就算好了的事实。

换句话说:编译器知道一切,但 Agent 只能隔着文本的毛玻璃去猜编译器知道什么。

Vercel 实验室的 Zerolang(GitHub: vercel-labs/zero)就是冲着这块毛玻璃来的。这个项目从 2026 年 5 月发布 v0.1 开始就带着一个激进的口号——「The programming language for agents」(为 Agent 设计的编程语言)。而 7 月下旬的最新迭代,它干了一件更激进的事:把语义图(semantic graph)直接升格为程序数据库(program database),文本源码降级为「投影」(projection)

这意味着什么?意味着在 Zerolang 的世界里,真正的「源码」是一个叫 zero.graph 的结构化数据库,而你看到的 .0 后缀的文本文件,只是这个数据库的一份人类可读的导出视图。

这是编程语言设计史上一次很有意思的翻转。本文我们把它拆开看:它到底怎么工作、图寻址为什么能消灭一整类 Agent 编辑错误、代码实战跑一遍完整工作流,以及——冷静地聊聊它的边界在哪。

二、核心概念:从「文本为真」到「图为真」

2.1 传统 Agent 编码循环的信息损耗

先量化一下问题。传统的 Agent 编码循环长这样:

agent 写文本 → 语法检查 → 格式化 → 构建 → 解析报错 → 回到第一步

每一步都在丢信息:

  • 写文本时:Agent 输出的是字符流,编译器需要的是 AST。Agent 必须在脑内(也就是权重里)模拟一个 parser,任何一个括号配对失误都要等到 check 阶段才暴露。
  • 定位编辑点时:Agent 用「第 42 行」或者「匹配 def foo( 这段文本」来寻址。行号会漂移,匹配串会重复,这两种寻址方式都是不稳定的。
  • 读报错时:编译器把结构化的诊断信息渲染成人类可读的文本,Agent 再从文本里反解析出「哪个符号、什么类型、缺什么」。一来一回,两次有损转换。

据实际使用 Agent 编码工具的经验,这类「机械性失败」(patch 不上、行号错位、改错同名函数)占 Agent 重试次数的相当大比例——它们和智力无关,纯粹是接口造成的浪费。

2.2 Zerolang 的答案:程序数据库

Zerolang 的架构里,编译产物链是反过来的:

zero.graph(程序数据库,唯一事实来源)
    ↓ zero export
.0 文本文件(人类可读投影,用于 review)
    ↓ zero import(人类改完文本后显式导入)
zero.graph(回写,经过完整校验)

zero.graph 里存的不是文本,而是一整套编译器级别的语义事实:

  • 符号与节点 ID:每个函数、类型、变量都有稳定的图节点 ID,不随格式化漂移
  • 图哈希(graph hash):每次编辑基于一个哈希快照,提交的 patch 如果基于过期哈希会被直接拒绝——这是数据库的乐观并发控制(OCC)搬到了编译器里
  • 类型与效果(effects):函数会不会抛错、有没有副作用,是图里的一等公民
  • 所有权事实(ownership facts):谁拥有哪块数据,编译期可查
  • 能力(capabilities):程序能碰哪些外部资源(文件、网络、时钟),显式声明
  • 导入关系与调用边:调用图不需要 Agent 自己扫代码重建,直接查

Agent 的工作方式因此变成:

agent 查询图 → agent 提交带校验的 patch → 编译器接受或拒绝
    → 拒绝(哈希过期/类型错误/结构非法)→ 重新查询
    → 接受 → 跑任务级验证 → 人类在需要时 review 文本投影

注意这里的关键差异:校验发生在写入之前,而不是写入之后。传统流程里 Agent 先把文本写进文件,然后 build 才发现错了;Zerolang 里非法的 patch 根本进不了存储。这就像数据库事务的约束检查——违反外键的 INSERT 不会先写进去再报错。

2.3 为什么说这是「把编译器当数据库用」

如果你做过后端,会发现 Zerolang 的设计处处是数据库的影子:

数据库概念Zerolang 对应物
表/行图节点(函数、类型、模块)
主键稳定节点 ID
事务 + 约束检查zero patch 的原子校验写入
乐观锁(版本号)graph hash,过期即拒绝
SQL 查询zero query 查符号/类型/调用边
视图(View).0 文本投影
视图物化回写zero import

这个类比不是修辞——它揭示了 Zerolang 真正的赌注:Agent 时代的编程接口,应该长得像数据库 API,而不是像文本编辑器。 数据库四十年的工程积累(约束、事务、并发控制、查询优化)恰好就是「让不可靠的客户端安全地修改共享状态」的最佳实践,而 Agent 恰好就是一个不可靠的客户端。

三、架构分析:三层结构与两个边界

3.1 整体分层

Zerolang 的工具链可以分成三层:

第一层:图存储层(zero.graph)

经过校验的编译器输入。所有语义事实的唯一权威来源。它不是给任何人「手写」的——不管是人还是 Agent,都只能通过受控接口修改它。

第二层:操作接口层(CLI 命令族)

  • zero query:只读查询。拿符号列表、类型签名、调用关系、目标平台事实
  • zero patch --op '...':写入。提交一个或多个语义操作(op),原子地校验并应用
  • zero check / zero test / zero run:编译期检查、测试、执行
  • zero inspect:深挖单个节点的完整事实

第三层:投影边界层(export/import)

  • zero export:把图导出为 .0 文本,给人 review
  • zero verify-projection:校验文本投影和图是否一致(防止「文本和真相悄悄分叉」)
  • zero import:人类改了文本之后,显式地把改动导入回图,走完整校验

这个设计里最值得琢磨的是两个边界

边界一:Agent 与图之间——只有 checked patch 一条路。 Agent 不能绕过校验直接写存储。所有会导致图非法的操作(类型不匹配、引用不存在的符号、基于过期快照的修改)在写入前被拦截。这从根上消灭了「Agent 把代码库改坏了然后越修越坏」的死亡螺旋。

边界二:人类与图之间——投影是显式的双向门。 人类可以看文本、改文本,但改动必须经过 zero import 的显式校验导入。不存在「人偷偷改了文件,Agent 的图缓存过期了还不知道」这种状态——因为文件根本不是事实来源,verify-projection 随时可以检测分叉。

3.2 语言本身长什么样

看一个最小的 Zerolang 程序(.0 投影文本):

pub fn main(world: World) -> Void raises {
    check world.out.write("hello from zero\n")
}

麻雀虽小,几个设计决策全在里面:

  • world: World 参数:没有全局的 print。所有外部世界的能力(stdout、文件系统、网络)都通过显式传入的 World 能力对象获取。这是能力安全(capability security)的经典做法——函数能干什么坏事,看签名就知道,Agent 审计代码时不需要全局数据流分析。
  • raises 效果标注:这个函数可能失败,写在类型里。调用方必须处理,编译器强制。
  • check 关键字:显式的错误传播点。类似 Rust 的 ?,但语法上更醒目——对 Agent 来说,每一个可能的失败路径都是图上一个显式节点,可以被查询。

再看它如何被「写」出来的。Agent 不需要生成上面这段文本,而是:

zero init
zero patch --op 'addMain' --op 'addCheckWrite fn="main" text="hello from zero\n"'
zero run

两个语义操作:addMain(往图里加一个 main 函数节点)、addCheckWrite(往指定函数体里加一个带错误检查的写调用)。没有字符串拼接,没有缩进对齐,没有括号配对。Agent 输出的是意图,编译器负责把意图物化为合法结构。

3.3 运行时目标:为 Agent 的资源模型设计

Zerolang 的运行时目标清单也很有意思,每一条都对着 Agent 场景:

  • Token 高效的自省zero query 的输出为 LLM 上下文窗口优化,返回紧凑的结构化事实而非整个文件。传统流程里 Agent 为了改一个函数要把整个文件读进上下文;图查询只拉需要的节点。
  • 快启动、快构建:Agent 的编辑-验证循环频率远高于人类(人类一分钟保存几次,Agent 一分钟可能验证几十次),编译器延迟直接乘在循环次数上。原生编译器用 C 实现(仓库里的 native/zero-c),就是为了把这个循环压到极限。
  • 显式能力:上面说过——安全审计从全局分析降级为局部签名检查。
  • 小而无依赖的产物:Agent 生成的程序要能被丢进沙箱大规模并行执行,产物越小、依赖越少,编排成本越低。

四、代码实战:走一遍完整的 Agent 工作流

下面用一个稍微真实一点的例子——写一个带测试的字符串工具模块——走一遍 Zerolang 的完整循环。以下命令基于当前仓库文档梳理,实际语法以 zero skills get language 输出为准(这门语言迭代很快,这也是它「实验性」的一部分)。

4.1 安装与初始化

# 安装编译器
curl -fsSL https://zerolang.ai/install.sh | bash
export PATH="$HOME/.zero/bin:$PATH"
zero --version

# 给你的 Agent 装 bootstrap skill(Claude Code / Codex 等通用)
npx skills add vercel-labs/zerolang

注意第二步:Zerolang 官方分发的不只是编译器,还有一套版本匹配的 Agent skill。编译器自带文档服务:

zero skills            # 列出内置 skill
zero skills get agent    # Agent 工作流约定
zero skills get graph    # 图操作详解
zero skills get language # 语言语法
zero skills get stdlib   # 标准库

这是个容易被忽略但很聪明的设计:语言文档随编译器版本走,Agent 永远拿到和本地编译器精确匹配的文档,不存在「模型训练数据里是 v0.1 语法,本地装的是 v0.2」的漂移问题。对于一门每周都在 breaking change 的实验语言,这几乎是 Agent 可用性的生命线。

4.2 初始化包并查询图

mkdir strutil && cd strutil
zero init
zero query

zero query 此时会返回包的初始图状态:包元数据、空的符号表、目标平台事实。Agent 从这里开始,不需要「读文件猜项目结构」。

4.3 用 patch 构建程序

添加一个公开函数和对应测试(op 语法示意):

# 查询可用的 patch 操作
zero patch --op help

# 提交一组原子操作
zero patch \
  --op 'addFn name="repeat" vis=pub params="s: String, n: Int" ret="String"' \
  --op 'setFnBody fn="repeat" body="..."' \
  --op 'addTest name="repeat basic" target="repeat"'

关键点:这一组 op 是原子的——要么全部通过校验一起写入,要么全部拒绝。如果 addTest 引用的 repeat 在同批 addFn 里,编译器按依赖顺序校验。Agent 不会陷入「函数加进去了但测试没加上,图处于中间状态」的窘境。

如果另一个 Agent(或者人)在你查询之后动过图,你的 patch 会因为 graph hash 过期被拒绝:

error: patch rejected: stale graph hash
  expected: 8f3a…
  actual:   d41c…
hint: re-run `zero query` and rebase your ops

这就是编译器级的乐观并发控制。多 Agent 并行改同一个代码库——这个传统上需要靠 git 分支 + 人肉解决冲突的问题——在图模型下变成了数据库早就解决过的问题。

4.4 验证与运行

zero check   # 类型/效果/所有权检查(增量,走图)
zero test    # 跑测试
zero run -- hello 3

4.5 人类介入:review 与手改

人想看代码了:

zero export            # 图 → .0 文本投影
zero verify-projection # 确认投影和图一致

打开 .0 文件 review。如果人直接改了文本:

zero import   # 文本改动 → 校验 → 写回图
zero check

如果 import 的改动非法(比如引用了不存在的符号),会在导入时报错,图保持原状。人类的手改和 Agent 的 patch 走的是同一套校验,谁也不能把图弄脏。

4.6 对比:同样的任务在传统流程下

作为参照,同样「加函数 + 加测试」在文本流程下的 Agent 操作:

1. 读 src/strutil.py(2000 token 进上下文)
2. 读 tests/test_strutil.py(1500 token)
3. 生成 diff,锚定「class TestStrUtil 的末尾」
4. 应用失败:文件被 black 格式化过,锚点文本变了
5. 重读文件(又 3500 token)
6. 重新生成 diff,应用成功
7. 跑 pytest,发现 import 漏了
8. 再来一轮

图流程下:查询(紧凑事实,几百 token)→ 提交 op → 一次通过或得到结构化拒绝原因。省的不只是 token,更是消灭了 4、7 两步这类「与任务本身无关的失败」。

五、横向对比:Zerolang 不是孤例,而是一条路线的极限版本

把 Zerolang 放到坐标系里看,会更清楚它的位置。「让 Agent 更可靠地改代码」这个问题,业界目前有四条路线:

5.1 路线一:更聪明的文本 diff(现状主流)

Claude Code、Cursor、Aider 们的做法:优化 diff 格式(unified diff / search-replace 块 / whole-file 重写)、加重试、加 lint 反馈循环。优点是零迁移成本,适配一切语言;缺点是前面说的——寻址不稳定、校验滞后、token 浪费,天花板肉眼可见。Aider 的作者做过著名的 diff 格式基准测试,不同 diff 格式在同一模型上的编辑成功率能差出两位数百分点——这恰恰说明「文本作为编辑接口」有多脆弱:接口的序列化格式居然能这么大地影响成败。

5.2 路线二:AST 级结构化编辑(中间层修补)

以 tree-sitter 系工具、各语言的 codemod 框架(jscodeshift、libcst、gofmt -r)为代表:在现有语言上架一层语法树操作 API。比路线一稳(寻址用节点而非行号),但有两个天然短板:一是只有语法没有语义——AST 知道这是个函数调用,不知道它调的是哪个符号、类型对不对,校验还是要等编译器;二是改完还是要序列化回文本,格式化、注释保留、并发写入这些老问题一个不少。这条路线可以看作「Zerolang 思路的 40% 实现」。

5.3 路线三:Image-based / 结构化存储的历史先驱

Zerolang「图为真、文本为投影」的思路其实有几十年的家谱。Smalltalk 的 image-based 开发把整个程序状态存成镜像;Unison 语言把每个定义按内容哈希存进代码库,名字只是哈希的别名——重命名不产生 diff,依赖永不冲突。Unison 尤其值得一提:它证明了「内容寻址的结构化代码库」在工程上完全可行,也证明了这条路的最大阻力不是技术而是与 git/GitHub 文本工具链的生态摩擦。Zerolang 比 Unison 多的东西,是把这套结构对准了一个新客户:Agent。人类嫌结构化编辑麻烦(宁可用文本编辑器的自由),Agent 却恰恰需要这种约束——同一个设计,换一类用户,价值判断完全翻转。这是时代给这条老路线的新机会。

5.4 路线四:语义图 + 事务校验(Zerolang 的位置)

Zerolang = 路线二的结构寻址 + 编译器全量语义事实 + 数据库式事务写入 + 为此重新设计的语言。它是四条路线里最激进的,也是唯一一个「不兼容存量代码」的。这个不兼容是有意为之:效果系统、能力安全、所有权这些让图校验真正有牙齿的特性,没有一个能无痛塞进 Python 或 TypeScript。要么在旧语言上做 40% 的图,要么为 100% 的图做新语言——Zerolang 选了后者,这是它最大的技术诚实,也是最大的商业风险。

5.5 Token 经济学:一笔实在账

再算一笔具体的账。假设一个中等任务:在 2000 行的模块里改一个函数并补测试。

文本流程:读整个文件约 6000 token 进上下文;生成 diff 约 500 token;假设 30% 概率 patch 失败重读重试,期望额外开销约 2000 token;编译报错文本一轮约 800 token。合计期望约 9000+ token,其中真正「有效」的(描述改动意图的)不到 1000。

图流程zero query 拉目标函数及其邻接节点的事实,约 400 token;提交 op 约 200 token;失败时返回结构化拒绝原因约 100 token,且失败原因精确(不需要 Agent 猜)。合计约 700-1000 token。

一个数量级的差距。放到 Agent 集群一天跑几十万次编辑循环的场景里,这就不是优化,是成本结构的差异。当然,这笔账的前提是模型写 Zerolang op 的一次通过率不显著低于写 Python diff——又回到了 5.1 节说的那个最关键的未知数。

六、冷静分析:赌注、代价与边界

吹完了,泼点冷水。Zerolang 的设计很漂亮,但它下的是几个非常大的赌注,每个都可能不成立。

6.1 赌注一:Agent 愿意学一门新语言吗

LLM 的代码能力来自训练数据,而训练数据里 Python/TypeScript 的浓度是 Zerolang 的百万倍量级。一个残酷的现实是:Agent 写热门语言的「野生直觉」,可能比它拿着完美工具写冷门语言更有效

Zerolang 的对冲手段是版本匹配的内置 skill(zero skills)加上极小的语言表面积——赌的是「工具引导 + 结构校验」能补上训练数据的缺口。这个赌能不能成,目前没有公开的大规模 eval 数据(仓库里有 evals/ 目录,说明团队自己在测,但结论未公开)。这是判断这门语言前景最关键的缺失数据。

6.2 赌注二:生态从零开始的冷启动

.0 后缀、C 写的自研编译器、自建标准库——Zerolang 没有选择做「某语言的图前端」,而是整个从零来。好处是设计不背包袱(效果系统、能力安全、所有权这些都很难塞进现有语言),代价是没有包生态、没有 Stack Overflow、没有现成的员工池。

它的目标场景大概率也不是「替代 TypeScript 写业务」,而是Agent 大规模生成的、小而独立的、沙箱执行的工具程序——这个场景下生态依赖本来就少,能力安全和小产物反而是硬需求。如果 Agent 编排平台(比如 Vercel 自家的基础设施)原生集成 Zerolang 沙箱,冷启动问题会小很多。这可能才是 Vercel 做这个项目的真实战略位置。

6.3 赌注三:「图为真」对人类协作的摩擦

程序数据库是二进制/结构化存储,git diff 不再直接可读,code review 要走 export 出来的投影。虽然 verify-projection 保证了一致性,但整个人类协作工具链(GitHub PR、blame、bisect)都是围着文本设计的。Zerolang 要么把投影文件也提交进 git 当「生成的可读副本」,要么就得重建一套 review 基础设施。这类「和现有工具链的摩擦」历史上杀死过很多技术上更优的方案(想想 Smalltalk 的 image-based 开发)。

6.4 它真正证明了什么

即便 Zerolang 本身最后没成,它验证的方向也大概率是对的:

  1. 结构化编辑接口 > 文本 patch。这一点不需要新语言也能部分落地——已经有工具在给现有语言做「AST 级编辑 API + LSP 事实查询」的中间层。Zerolang 是这个思路的极限版本。
  2. 写前校验 > 写后报错。把编译器从「裁判」变成「守门员」,对 Agent 循环效率的提升是数量级的。
  3. 文档与工具版本绑定分发zero skills 这个模式值得所有快速迭代的工具学习。

一个合理的预测:五年后我们未必都在写 .0 文件,但主流语言的工具链里大概率会长出「语义图查询 + 校验式结构编辑」的 Agent 接口。到时候回头看,Zerolang 就是那个把话挑明的项目。

七、总结与展望

Zerolang 用一句话概括:它把「编译器内部早就存在的那个图」翻到了台面上,让它成为程序的唯一事实来源,然后给 Agent 一套数据库式的查询/事务接口去操作它——文本源码从「真身」降级为「投影」。

对不同的读者,我的建议分别是:

  • 如果你在做 Agent 编码工具:仓库值得精读,尤其是 patch op 的设计和 graph hash 的并发模型。哪怕你的目标语言是 TypeScript,「查询-校验-原子写入」这套循环也可以用 LSP + AST 工具近似复刻。
  • 如果你是普通工程师:现在不需要学它写业务,但值得花半小时跑一遍 hello world,感受一下「不写文本写意图」的工作流——这可能是未来五年你和 AI 协作方式变化的预告片。
  • 如果你在评估技术选型:它是实验品,官方自己都标着 Safety warning,别碰生产。

编程语言的历史,本质上是一部「为当时最重要的程序员设计接口」的历史:汇编为硬件工程师,C 为系统程序员,Python 为科学家和胶水工人。如果未来写代码最多的「程序员」是 Agent,那为它设计一门语言就不是噱头,而是必然。Zerolang 未必是答案,但它问对了问题。


参考资料:vercel-labs/zero 仓库 README 与文档(zerolang.ai)、GitHub Trending 2026-07 周报数据。项目处于快速迭代期,文中命令与语法以官方最新文档为准。

推荐文章

在Vue3中实现代码分割和懒加载
2024-11-17 06:18:00 +0800 CST
JS中 `sleep` 方法的实现
2024-11-19 08:10:32 +0800 CST
JavaScript中设置器和获取器
2024-11-17 19:54:27 +0800 CST
前端如何优化资源加载
2024-11-18 13:35:45 +0800 CST
php常用的正则表达式
2024-11-19 03:48:35 +0800 CST
使用Rust进行跨平台GUI开发
2024-11-18 20:51:20 +0800 CST
程序员茄子在线接单