2026-08-25 07:00:00
本文永久链接 – https://tonybai.com/2026/08/25/cursor-continuity-git-walgit-spacex
大家好,我是Tony Bai。
【导读】
SpaceX 以 600 亿美元收购 Cursor 的消息还没冷却,Cursor 又在官网甩出一篇长达 27 分钟阅读时长的技术博客,详解自己憋了许久的「Origin」平台背后的 Git 存储引擎——Continuity。这篇博客把 GitHub 二十年踩过的坑、Spokes 系统的设计得失,以及一种全新的「WAL + 对象存储」思路讲得明明白白。更戏剧性的是:Shopify CEO Tobi Lütke 只用了一个周末,就把这套架构原封不动地用Rust实现了出来,开源发布,取名 walgit。这篇文章带你把这两件事一次看懂。
【文章要点】

2026 年 8 月,AI 编程工具 Cursor 的开发商 Anysphere,正式并入刚刚完成史上最大规模 IPO 的 SpaceX,交易金额高达 600 亿美元、全股票支付。这笔收购把 Cursor 四位联合创始人的身价直接推上了十亿美元俱乐部,也让 SpaceX 一跃成为美股市值第四的公司。
但真正让技术圈炸锅的,不是这笔交易本身,而是收购官宣后不久,Cursor 在官网发布的一篇博客——《Git at any scale》(万物皆可扩展的 Git)。作者 Vicent Martí 曾是 GitHub 早期核心工程师,也是 libgit2 的创造者之一,这篇文章的分量,从作者履历上就能看出来。
文章的核心,是 Cursor 内部憋了很久的一个大动作:自研 Git 存储引擎 Continuity,并将其包装成一个即将对外提供的产品——Origin,目标很直接:成为 GitHub 的替代品,尤其是在「超大规模仓库」和「AI Agent 高频读写」这两个 GitHub 传统架构并不擅长的场景里。
这篇博客与其说是产品发布通稿,不如说是一篇系统架构论文。它把「为什么托管 Git 仓库这么难」、「GitHub 过去二十年怎么一步步踩坑」与「业界标准方案 Spokes 到底好在哪、又卡在哪」,讲得极其透彻,最后才给出 Continuity 的答案。
Linus Torvalds 设计 Git 的初衷,只是为了取代 BitKeeper,服务于 Linux 内核这种高度去中心化、由无数独立维护者协作的项目。「分布式」是 Git 与生俱来的基因——每一份仓库的拷贝都是平等的、完整的,服务器上的仓库和你笔记本上的仓库,本质上没有任何区别。
这个设计哲学放在个人开发者身上没问题,但放到「一家公司需要集中托管成千上万个仓库,并让全球开发者和 CI 系统高并发读写」的场景里,就成了噩梦的开端。
问题的根源在于 Git 的存储格式:packfile(打包文件)。Git 仓库里的代码和元数据(文件、提交、树对象)都会被压缩进 packfile 里,这是 Git 存储和网络传输共用的基础单元——无论是 git push 还是 git fetch,传的都是 packfile。
packfile 的问题是:它是为了「体积最小」而设计的,对象在包里几乎是随机排列的,而且大多数对象并不是完整存储,而是相对另一个对象的「增量」(delta)。这意味着读取单个对象,往往需要在磁盘上做一次随机跳转式的「寻址接力」。

Git 的提交历史本质上是一张有向无环图(DAG)。哪怕是列出「最近的变更」这种简单操作,也必须沿着这张图一步步走下去——而且走到下一步之前,你根本不知道下一个指针指向哪里。
这也是为什么早期把 Git 对象直接扔进分布式哈希表(比如 Google 内部工程师 Shawn Pearce 基于 JGit 做的实验)最终没有成功——虽然常规操作没问题,但 git clone 的性能差到无法接受,因为 Git 协议本身要求网络传输的单位必须是 packfile,服务器内部存储方式再怎么创新,也逃不开这层限制。
GitHub 早期是一个跑在单台机器上的 Rails 单体应用,仓库就放在这台机器的本地磁盘上。Rails 应用本身很好水平扩展,但 Git 仓库要怎么「分身」到多台机器上,成了绕不开的难题。
GitHub 工程师们尝试过三条路:
根本原因还是那句话:packfile 的物理布局和逻辑图结构毫无关联,这种「跨 GB 级数据的随机游走」在网络文件系统上是灾难性的——除非你能把整个文件缓存在本地,可当仓库数量达到数十万级别时,「全部缓存在本地」这个选项直接不存在。
最终 GitHub 放弃了「分布式文件系统」这条路,转而搭建了一套 RPC 系统,让仓库固定托管在专门的文件服务器上、应用层远程调用。这解决了一部分水平扩展问题,但没有解决可用性问题——每个仓库依然只存在于一台机器上。
2013 年前后,GitHub 发展出了 Spokes 架构,这套思路后来成了整个行业的事实标准(包括 GitLab、Gitea 等在内的大多数 Git 托管方案,本质上都是 Spokes 的变体)。Spokes 做对了三件事:
第三点是关键。Git 客户端对「最终一致性」极不友好——如果你刚推送完一个提交,紧接着的 fetch 却读不到它,Git 会直接懵掉;如果 CI 流水线的一百个 runner 里有三个克隆不到刚推送的提交,同样是灾难。所以 Spokes 选择了一条很「重」的路:**用三阶段提交协议(3PC)**在多个副本之间做共识。

如上图所示:Spokes 用三阶段提交(3PC)在多个副本间达成共识。一次 git push 被拆成 packfile(大文件,直接并行分发到所有副本)和引用事务(reference transaction,体积小得多,用 3PC 同步),只有引用被更新之后,这次提交才算真正「可见」。
这套设计过去十几年确实稳,但 Cursor 博客点出了两个越来越致命的问题:
问题一:3PC 天生水平扩展性受限。
Spokes 刚发布时,「三副本」是甜蜜点——足够冗余,又不至于太贵。但到了 2026 年,企业级仓库普遍是巨型 monorepo,三个副本根本扛不住 CI 的并发读取压力。而 3PC 作为共识算法,每一步的延迟都取决于集群里最慢的那台机器——副本越多,推送吞吐反而越差,这是所谓「规模的尾部效应」(tail at scale)问题。
反过来也成立:当 AI Agent 开始大批量创建「用完即弃」的小仓库时,Spokes 依然要求每个仓库都维持三个副本,哪怕这个仓库几乎从未被访问——大量近乎闲置的副本,占着资源却裁不掉,因为裁掉就打破了一致性。下限太高,上限太低,Cursor 博客原话如此评价。
问题二:运维成本高到离谱。
因为每个副本上的仓库都是「共识的一部分」,你必须精确知道每个仓库存在于哪几台机器上——这意味着你需要一张巨大的路由表(外部数据库),仓库要不断校验和更新校验和,一旦发现损坏就要立刻修复,因为磁盘上的仓库本身就是「真相源」。用 Cursor 的比喻:Spokes 把仓库当「宠物」养,而不是「牲口」——每一份拷贝都金贵得不能出任何差错。
看到这里,Continuity 的设计动机已经很清楚了:能不能既保留 Spokes「本地磁盘存真实 Git 仓库、复用上游 Git 工具链」的优点,又摆脱「共识协议 + 路由表 + 宠物式运维」这套沉重的包袱?
Cursor 给出的答案是:把写前日志(Write-Ahead Log,WAL)存进 S3 兼容的对象存储里,让它成为唯一真相源;本地磁盘上的仓库,退化成一份可以随时重建的「热缓存」。

我们看到:Continuity 的核心链路。一次 push 只有在 packfile 被完整写入 S3、且引用事务通过 CAS 成功更新索引文件之后,才会向客户端返回成功——这保证了所有推送都是可线性化的(linearizable)。
这套设计里,有几个反直觉但极其精妙的选择:
1. 仓库放在哪台机器上,不再重要。
Continuity 是无状态的,没有路由表,也没有关系型数据库需要运维。如果某台机器上没有你要访问的仓库,系统会直接从 WAL 里把它「物化」出来。生产环境里用的是 rendezvous hashing(会合哈希) 来决定「理论上」该由哪些节点服务哪个仓库,但这只是一种效率优化——就算这个映射过时了(比如某个节点挂了),系统照样能在下一个节点上重新物化出仓库,不会出任何数据问题。
2. 谁是「主节点」,同样不重要。
没有选举,没有共识协议。所有对 WAL 的更新都通过 S3 的原子比较并交换(CAS,Compare-And-Swap)操作来同步,这意味着任何一个副本都可以安全地接受一次推送。正常情况下系统会固定选择 rendezvous hashing 排序中的第一个节点作为「主节点」以减少 CAS 冲突重试,但即便在部署更新、故障转移、网络抖动这些边缘情况下选错了「主节点」,也完全没有关系——系统在退化时依然正确,在健康时依然快。

如图示:两个节点同时尝试推送时,CAS 天然扮演了「共识」的角色——谁先成功更新 etag,谁的推送就先生效;失败方只需重新拉取、rebase、重试。
3. 复制不再靠「强同步」,靠「随便传传,反正读的时候会自己校验」。
Continuity 用 UDP 广播(gossip)把「有新数据了」这件事随意地告诉集群里的其他节点——丢包完全没关系。因为每个副本在处理一次读请求时,都会先拿着自己记录的 etag,向 S3 发一次条件 GET(conditional GET):返回 304 说明数据没变,几乎是零延迟的元数据操作(平均不到 10 毫秒);返回 200 则带着最新的索引,副本用它「追平」进度后再响应请求。换句话说,副本之间的数据到底同没同步,根本不重要,因为每次读都会向 S3 这个真相源核对一遍。
这种设计带来了一个非常直接的好处:副本数量可以在两个方向上自由伸缩。一个巨型 monorepo 可以铺开成百上千个副本来扛住 CI 的读取洪峰;而 AI Agent 批量创建的海量小仓库,一个副本就够,因为可用性已经由 S3 兜底了;如果某个仓库长时间没有流量,甚至可以把它从本地磁盘上彻底垃圾回收掉,下次访问再从 WAL 重新物化。
4. 压缩(compaction)只做一次,其他副本「白嫖」结果。
WAL 系统都需要定期压缩,Git 仓库本身也需要定期 repack(否则大量小 packfile 会拖慢对象查找)。在 Spokes 里,repack 是个 CPU 密集型操作,且必须在每个副本上各自执行一遍——两个节点同时对同一仓库触发维护操作,很容易直接把仓库拖挂。Continuity 的做法是:只有「主节点」做压缩,压缩结果同时写回本地仓库和 WAL,其余副本只需要从 S3 下载已经压缩好的 pack,用带宽换 CPU,不用各自重复计算。
Cursor 在博客里给出了压测数据:

值得一提的是,Continuity 并不是「第一个把 packfile 存进对象存储」的系统——微软的 Azure DevOps 早就在用 blob 存储存 packfile,但引用(ref)数据仍然放在关系型数据库(MS SQL Server)里。Cursor 的判断是:Git 数据的一致性,比引入一个额外的数据库带来的便利更重要,这也是他们最终选择「完全基于 WAL、不依赖任何外部数据库」这条更难走的路的原因。
如果故事到这里结束,这只是一篇优秀的架构博客。真正让这件事出圈的,是 Shopify CEO Tobi Lütke 在 X(原 Twitter)上的一条推文:
「Cursor 那篇《Git at Scale》是我近期读过最有意思的博客之一,而且它出现的时机正好是我对 Shopify 内部 Git 系统感到沮丧的时候。作为一个练习,我用一个周末把它实现成了开源项目。这是一个单文件 Rust 二进制程序,可以指向任意 S3 类型的对象存储。它使用 WAL 和 CAS 原语,不需要任何其他数据存储。它还实现了 bundle-uri,让像我们的 monorepo 这样的大仓库能作为一串静态 bundle 文件被极速下载。还带了基本的、大家熟悉的操作界面。」
这个项目叫 walgit,托管在 github.com/tobi/walgit,短短时间内已经收获上百个 Star。它的 README 开门见山:
walgit —— 一个「单文件二进制 + 对象存储」的 Git 服务器,没有数据库,没有主节点,没有任何真正重要的本地状态。
walgit 本质上就是 Continuity 架构的一份忠实(且工程上更进一步)的 Rust 复刻实现,README 里甚至直接写明:「这是对 Cursor 在《Git at any scale》中描述的架构(他们称之为 Continuity)的 Rust 实现,并做了一些改动,让它能在比仓库本身还小的机器上运行。」 原文甚至被逐字保存在了仓库的 docs/reference/cursor-git-at-any-scale.md 里。
walgit serve,第一次 git push 就会自动创建仓库——没有任何额外的初始化步骤。clone 只需要下载「最新的全量 bundle + 之上的增量链」,服务器只需要补齐剩下的一点点差异;一次日常的 fetch 也只需要下载自己错过的那几个增量片段。repos.js)、基于 Webhook 的事件推送、per-repo 的推送策略(保护分支等)、以及 none / token / oidc 三种鉴权模式。
如图,walgit 的整体架构——每台机器都是「可丢弃的缓存」,对象存储桶才是真正的仓库;bundle-uri 让「首次克隆」这种最消耗服务器资源的操作,直接绕过服务器,走静态文件分发。*
这条推文下面,Tobi Lütke 特别 @ 了 Cursor 博客作者 Vicent Martí(其 X 账号为 @vmg),感谢他这篇「amazing writeup」。一篇架构博客,从发布到被行业里另一位知名 CEO 用一个周末逆向实现并开源,中间几乎没有信息损耗——这本身也是这个 AI 编程时代的一个缩影:当写代码的边际成本被 AI 大幅拉低之后,「读懂一篇硬核架构文章,然后把它变成可运行的系统」这件事,正在变得越来越快。
抛开「大厂互撕」、「CEO 周末搓开源项目」这些吃瓜元素,这件事背后有几层更值得琢磨的信号:
第一,AI Agent 正在从根本上改变 Git 基础设施的负载模型。
过去 Git 托管系统的假设是「人类开发者,有限并发」;而现在,一个团队里可能同时跑着成百上千个 AI Agent,各自创建、废弃、重写着海量的小仓库和分支,CI 触发频率也水涨船高。Cursor 博客里那句「Spokes 对付海量小仓库时,下限太高、上限太低」,说的正是这个新负载模型下暴露出的旧架构短板。
第二,「无状态 + 对象存储做真相源」正在成为一种新的系统设计范式。
从数据库(Neon、别的 serverless Postgres)到现在的 Git 托管,越来越多的基础设施选择把「本地磁盘」降级为缓存,把「对象存储」提升为真相源——这背后的驱动力,是云对象存储在可用性、持久性和成本上的持续进化,让「什么都往 S3 里怼」从一个笑话变成了一种严肃的架构选择。
第三,开源社区对「巨头架构」的复现速度,正在急剧压缩。
一篇博客发布不到几天,一个足够熟悉系统设计的资深工程师就能用 Rust 独立复刻出一个可用的开源实现,这在几年前是难以想象的效率。对于任何还想靠「架构护城河」构筑壁垒的公司来说,这是一个值得警惕的信号。
第四,也是最直接的:Git 托管这个被 GitHub 垄断了近二十年的市场,可能正在迎来一次新的洗牌。
Cursor 把 Continuity 包装成产品 Origin 对外提供,摆明了要正面挑战 GitHub;而 walgit 这样的开源实现,则给了那些不想被单一厂商绑定、又对自建 GitLab / Gitea 集群的运维成本感到头疼的团队,多了一个「单文件 + 对象存储」的轻量选项。
一篇博客,两套系统,一场围观。SpaceX 收购 Cursor 的商业新闻很快会被下一条新闻覆盖,但 Continuity 这套「用对象存储里的写前日志取代共识协议和关系型数据库」的设计思路,以及 walgit 这个周末产物证明的「架构一旦讲透,落地可以有多快」,或许才是这几天真正值得被记住的东西。
如果你的团队也正被 Git 仓库的扩展性问题折磨——不管是巨型 monorepo 拖垮 CI,还是 AI Agent 批量创建仓库把自建 GitLab 集群压垮——这篇 Cursor 博客原文,以及 walgit 这个开源项目,都值得花时间读一读、跑一跑。
参考链接
还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 《从0 开始构建 Agent Harness》 将带你:
扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。

还在为“复制粘贴喂AI”而烦恼?我的新专栏 《AI原生开发工作流实战》 将带你:
扫描下方二维码,开启你的AI原生开发之旅。

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

2026-08-24 07:00:00
本文永久链接 – https://tonybai.com/2026/08/24/codex-as-a-platform-open-agent-harness
大家好,我是Tony Bai。
【导读】
在三大模型厂商的编程 Agent 竞赛里,Anthropic 的 Claude Code、Google 的 Antigravity(前身 Gemini CLI)都是闭源产品,只有 OpenAI 的 Codex 用 Rust 重写并全面开源。近日 OpenAI 开发者博客发文《Codex as a platform:build on the open agent harness》,第一次系统性地把 Codex 拆成“Harness”与“应用层”两部分,告诉所有开发者:你不需要再造一个聊天框,直接把这台引擎装进你自己的产品就行。并给出三种官方集成路径。
【文章要点】
codex exec(无交互批处理)、Codex SDK(程序化调用)、app-server(长连接 + 流式事件 + 审批),分别对应脚本任务、应用集成、产品级嵌入三种场景。
目前编程 Agent 赛道上活跃的三个重量级选手是:Anthropic 的 Claude Code、Google 的 Antigravity(前身 Gemini CLI),以及 OpenAI 的 Codex。前两者都是闭源产品,企业只能在官方划定的边界内使用;而 Codex 在 2025 年用 Rust 重写核心后,选择了一条完全不同的路——把驱动 App、CLI、IDE 插件的底层系统全部开源,代码仓库就放在 openai/codex,Apache-2.0 协议。
这个选择正在换来实实在在的市场份额增长。而就在近期,OpenAI 开发者博客发布了一篇态度鲜明的文章——《Codex as a platform:build on the open agent harness》,第一次把“为什么开源”和“开源之后能干什么”讲清楚了。
这篇文章的核心论点只有一句话:你平时用的 Codex App、命令行、IDE 插件,只是同一套底层系统的三种Harness,而这套系统本身是开放的、可以被搬进任何产品里的。
大多数人对 Codex 的认知,停留在 App、命令行(CLI)、IDE 插件这三种交互形式上。OpenAI 在文章里明确指出,这三者只是冰山一角——真正驱动它们的,是开源的 Codex harness。
“Harness”这个词在这里可以理解为“智能体的运行Harness”或者“执行框架”。它负责:
按照 OpenAI 的说法,这一层过去是“藏”在产品背后的私有能力,现在被拿出来公开,开发者可以直接检查它、理解它的行为,并按自己产品的需要改造这层“胶水”。
这意味着,OpenAI 想传达的信息是:不要逼着每个团队都把工作流搬进一个通用的编程助手里,而是把 Agent 能力嵌进团队本来就在用的软件——工程工作流、运维看板、安全事件调查台、客服控制台,或者任何一个为某个具体岗位定制的内部系统。
OpenAI 把一个“够格”的 Agent 需要具备的能力列得很直白:理解任务、维持长期上下文、检索相关信息、调用工具、暴露执行进度、处理失败、在必要时请求人工审批、最终返回一个有用的结果。
围绕这套能力运转的执行系统,就是 harness。Codex harness 具体负责:
而 Codex app-server 把这些能力包装成一份文档化的客户端协议:应用可以创建会话线程(threads)、发起一轮任务(turns)、接收事件流,并处理审批请求。换句话说,如果你的软件需要一个 Agent,不用再从零造一个运行时,直接接入 Codex,再决定“应用层”要自己掌控哪些部分。下面示意图展示了“应用层(界面、业务上下文、审批)、Codex app-server(Agent 循环、沙箱执行)和 应用自有的 MCP 数据与动作”三段式架构。

文章里提到一个很值得单独拎出来说的数据:在 ARC-AGI-3 测试中,仅仅是调整Harness层面的两个设置——保留推理内容(retained reasoning)和上下文压缩(context compaction)——GPT-5.6 Sol 的得分就从 13.3% 提升到了 38.3%,接近翻三倍,同时输出 token 消耗还降低到原来的六分之一。
这组数字说明一件事:模型能力固然重要,但“怎么把上下文喂给模型、怎么在多轮任务里维持状态、什么时候该压缩历史”这些看似工程细节的问题,同样能决定 Agent 的最终表现上限。这也是 OpenAI 强调“harness 值得被开源、被审视、被改造”的底气所在——它不是一层无关紧要的壳,而是直接影响结果的关键变量。
OpenAI 没有要求所有开发者用同一种方式接入 Codex,而是按照场景给出了三条路径:
| 集成方式 | 适用场景 | 特点 |
|---|---|---|
codex exec(非交互模式) |
脚本、CI 任务、一次性后台工作 | 运行一个有边界的 Agent 工作流,返回结构化结果,不需要维持会话 |
| Codex SDK | 应用代码需要启动、恢复、流式获取 Codex 任务 | 提供直接的程序化接口,适合把 Agent 能力嵌进已有系统 |
| Codex app-server | Agent 本身就是产品的一部分 | 支持本地进程连接、长会话、流式事件、中断任务、暴露工具、处理审批请求 |
用 OpenAI 原文的说法:“SDK 简化常见的程序化工作流;app-server 则把生命周期和用户体验的控制权直接交给产品团队。”
这三层刚好对应从“跑一次任务”到“嵌入一个持续运行的产品体验”的复杂度递增。想快速试一下效果,从 codex exec 开始;要往现有系统里接 Agent 能力,用 SDK;要做一个“Agent 常驻在界面里”的产品,用 app-server。
为了把这套理念落到实处,OpenAI 团队自己动手做了一个示例应用 Relay——一个基于 Codex app-server 搭建的虚构物流运营看板。
它的交互逻辑很有代表性:用户不是从“打开一个空白对话框、自己写 prompt”开始的,而是先在看板上选中一个具体的运单,点击一个业务动作,比如“比较恢复方案(Compare recovery)”。这时应用会把相关上下文喂给 Agent,Codex 通过应用自有的 MCP 工具拉取最新的运营数据,解释可选方案;如果这个动作会产生实质性影响(比如重新预订一批货物),就必须经过人工审批才能执行。执行完成后,应用会刷新自己的业务视图——Agent 循环、对话状态、流式动作都由 harness 负责,产品始终掌控着自己的看板、记录和控制权。

Relay 用的是虚构的种子数据,但这套集成模式是通用的——同样的思路可以套用在事件响应、账户运维、调研工作流,或者任何“Agent 应该在一个已有产品体验里工作”的场景上。
文章列举了几个已经公开的实践案例:
OpenAI 特别强调,这套模式不局限于工程团队——客服团队排查客户问题、运维团队协调工作流、安全团队分诊事件、销售团队调研客户、市场团队筹备campaign,都是同一个模式:应用提供上下文、工具和审批环节,Codex 负责底层的 Agent 循环。
回到开头的问题:为什么这篇博客值得被认真对待?
因为它把“开源”这件事从口号变成了一套可操作的分层架构——Harness开源、集成路径公开、示例应用可参考、真实客户案例可查证。对企业和开发者来说,这直接决定了几件事能不能做:
当然,需要说明的是,开源的只是“Harness与集成层”,模型访问权限和托管服务本身仍然是独立的、闭源的部分——OpenAI 在文中也明确划清了这条线。这不是“完全开源”的叙事,而是“把可复用、可审查的工程部分开放出来,把真正的护城河(模型)留在自己手里”的务实策略。这也解释了为什么开源没有削弱 OpenAI 的商业模式,反而可能通过更广泛的生态集成,扩大 Codex 的分发面。
如果读完想立刻上手,可以按这个顺序推进:
codex exec;已有系统要接入 Agent 能力 → Codex SDK;要做一个 Agent 常驻的产品体验 → app-server;三大模型厂商在编程 Agent 赛道上的路线分化,正变得越来越清晰:Anthropic 用 Claude Code 巩固闭源产品体验的护城河,Google 把 Antigravity 收进自己的生态位,而 OpenAI 选择把最容易被复制、最应该被信任的那部分——Agent 的执行Harness——开放出来,换取更广的生态位和更快的集成速度。
这篇博客与其说是一次产品发布,不如说是 OpenAI 对“编程 Agent 的下一阶段应该长什么样”给出的一份路线图:Agent 不该是一个必须打开的新窗口,而应该是任何产品都能装进去的一个引擎。
至于这条路线最终能不能真正撬动 Claude Code 和 Antigravity 把守的市场,还要看开发者用脚投票的结果。
延伸阅读
还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 《从0 开始构建 Agent Harness》 将带你:
扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。

还在为“复制粘贴喂AI”而烦恼?我的新专栏 《AI原生开发工作流实战》 将带你:
扫描下方二维码,开启你的AI原生开发之旅。

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

2026-08-23 06:00:00
本文永久链接 – https://tonybai.com/2026/08/23/russ-cox-acm-interview-tactical-tornado-ai-agent-warning
大家好,我是Tony Bai。
【导读】
Go语言技术负责人长达十余年的Russ Cox,近日接受了ACM「People of ACM」栏目的深度专访。这篇文章很快被转发到Hacker News,很快就收获超过数十余条热评。真正引爆讨论的,是他借用「战术龙卷风」这一概念,对AI coding agent发出的一句警告——管理者若不够谨慎,AI agent很可能轻而易举地变成史上最强的战术龙卷风。这句话,说出了无数正在与AI agent协作的工程师,过去大半年里说不清道不明的焦虑。
【文章要点】

在Hacker News上,只要出现「Russ Cox」这个名字,评论区往往会立刻热闹起来。有网友直言,自己是个简单的人,只要看到和Russ Cox有关的内容,就会点进去读;也有人补了一句,同样的待遇他只给过Fabellard(法布里斯·贝拉,一位法国著名程序员,因FFmpeg、QEMU等项目而闻名业内)。
这种「信徒式」的追随不是没有原因的。Russ Cox长期担任Go语言技术负责人,早年是Bell Labs Plan 9操作系统团队的一员,后来又打造了Google Code Search——那个曾经让全世界开发者可以直接搜索开源代码的工具。如今,他是ACM Queue编委会成员,同时还是「整数数列在线百科全书」(OEIS)基金会主席。他很少接受长篇采访,但每次开口,几乎都能把复杂的工程直觉,浓缩成一句让人拍案叫绝的话。
这一次,ACM「People of ACM」栏目的专访再次证明了这一点。
很多人对Go语言的第一印象是简单、无聊和没有花哨特性。但在Russ Cox看来,这种简单从来不是设计目标本身,而是解决了具体问题之后的自然结果。
Go项目启动于2007年前后的Google内部,当时团队面对的现实是:海量代码、成百上千名工程师协同开发、成千上万台联网机器,每台机器又有大量核心。现有的编程环境几乎在每一个维度上都跟不上这种规模,团队真正需要的,不是一门「更酷」的语言,而是一套能撑住这种规模的工程环境。也正因为是从零开始,Go团队才有机会重新思考依赖管理、构建系统这些通常不被语言设计者放在第一位的问题,顺带解决了低延迟垃圾回收之类的底层实现难题。
换句话说,Go语言的诞生,本质上是一次面向工程规模的系统性重构,而并发模型只是这次重构里最显眼的一小部分。
Russ Cox在采访中反复强调一个观点:面对复杂问题时,工程师的第一反应往往是设计一个复杂的解决方案;但如果肯花时间去简化问题本身,通常能得到开销更低、同样好用、还更容易维护的简单方案。
他举了一个具体的例子。C++和Java的构建系统长期被库之间的循环依赖折磨——因为一旦出现循环,就要把一大堆模块捆成一个整体来处理,围绕这个问题,社区已经堆砌了大量复杂的工程方案。而Go从一开始就选择了一条更硬核的路:直接禁止循环依赖。如果A导入了B,那么B就不能反过来导入A,哪怕是间接的也不行。
这不是一个讨巧的语法糖,而是一种「用约束换简单」的工程哲学:宁可在设计阶段多一点限制,也不要在规模扩大之后被自己的灵活性反噬。十几年过去,Go生态在依赖管理上依然比很多同代语言更清爽,这套哲学功不可没。
采访中一个容易被忽略、但其实非常关键的部分,是Russ Cox对自动化测试的看法。
他回顾了早期没有完善测试体系时的工作方式:每次改动都要极其谨慎,靠人工逐一排查可能受影响的地方。这种方式能用,但完全不具备可扩展性。
相反,如果有一套覆盖了核心行为、并且可以随时重新运行的自动化测试,团队几乎可以「零心理负担」地做很多事情——评估新编译器、升级依赖库版本、重构甚至重写复杂代码,都变得可控。
在这份清单的末尾,Russ Cox补上了一句意味深长的话:好的测试体系,也让团队更容易接受来自不熟悉这套系统的工程师,或者coding agent提交的改动。
软件工程里几乎所有事情,都会随着测试质量的提升而变得更容易——这句话放在今天AI辅助编程越来越普及的背景下,几乎是一句预言:测试基础设施的好坏,将直接决定一个团队能在多大程度上安全地拥抱AI agent。
如果说这篇专访里有一句话让整个HN评论区集体破防,那一定是这句关于AI agent的警告,它也是这次热议的绝对核心。
Russ Cox在谈到AI对软件工程的影响时,引用了John Ousterhout在《A Philosophy of Software Design》一书中提出的一组概念——「战术编程」与「战略编程」。前者是不计代价地先把眼前的功能做出来,后者是花时间把系统设计打磨扎实,以从容应对未来的新需求。两者都有各自适用的场景,但多数团队的失败模式,都源于战术工作太多、战略投入太少。
Ousterhout为此发明了一个词,叫「战术龙卷风」:指那种能疯狂产出大量能跑起来、但极其复杂、和现有系统格格不入的代码的工程师。更糟糕的是,一个缺乏判断力的管理者,往往会把这种人当成团队里最高产的成员,却意识不到他们留下的复杂度和技术债,最终都要由其他人来买单。
Russ Cox接着补上了那句让HN网友集体沉默的话:如果管理者不够谨慎,AI agent很可能会轻而易举地变成终极版的战术龙卷风。
一位HN网友的留言几乎道出了很多工程师过去半年多说不清道不明的感受,他说这段话精准地表达了自己的心声。
要知道,AI agent的产出速度是人类工程师的数倍甚至数十倍,一旦缺乏「战略层面」的把关,它制造技术债的效率同样会呈指数级放大。这条警告之所以引发共鸣,正是因为它把很多人模糊的不安,第一次说得如此具体。也有网友当场调侃,觉得这个词造得实在传神,甚至联想到自己团队里那些「修好了一个bug、却弄垮了整个设计范式」的经历。
除了「战术龙卷风」,另一段被HN网友称为「黄金内容」、值得每隔几年重读一次的部分,来自Russ Cox对Peter Naur 1985年论文《Programming as Theory Building》的引用。
Naur认为,维护一个程序最核心的东西,并不是代码本身,而是团队脑子里那套关于「现实需求如何映射到程序设计与结构」的连贯理论。
这套理论本质上无法被完整写下来或传授,因为它最重要的价值,恰恰体现在如何应对那些设计之初根本没想到的新需求。几乎每个工程师都有过这样的经历:为了实现一个新需求,绞尽脑汁在系统里做了大量改动,结果原作者随口一句话,就点出了一个自己从未想到的简单方案。
Naur由此得出一个略显冷峻的结论:当拥有这套理论的团队解散时,一个程序也就随之「死亡」了——即便代码本身一行没变,它也已经失去了灵魂。
Russ Cox把这个结论延伸到了AI agent身上:一个没有长期记忆、无法真正理解系统演化脉络的AI agent,很难避免在不知不觉中,加速一个程序的「死亡」。
AI agent确实可以像精力充沛的新人工程师一样,写出能跑起来的代码,但结果往往比实际需要的更复杂——因为它缺的正是那套无法被完整传授的「理论」,缺的是连贯性与简洁性背后的判断力。
面对「AI会不会给软件开发带来全新范式」这个近乎必答题的问题,Russ Cox给出了一个克制得多的回答。他更愿意用「连续统」(continuum)而不是「范式革命」,来描述AI正在发生的影响。
他的逻辑是:软件工程与软件系统的近未来,其实早已经藏在最近的研究成果和实践进展之中,只是还没有被广泛看见。他所参与的ACM Queue,工作方式正是识别出这些重要但尚未普及的话题,邀请真正的专家撰写深度文章,把这部分已经存在、只是还不够显眼的未来,尽早呈现给更多人看到。而随着这种深度、系统性的技术写作在业内变得越来越稀缺,它的价值反而越来越高。
这种态度和他一贯的工程气质是一致的:不追逐话题热度,也不轻易被宏大叙事裹挟,更倾向于把复杂的变化,拆解成一个个可以被理性讨论、被测试验证的具体问题。
除了正经的技术讨论,HN评论区照例贡献了不少有意思的细节。
有网友提到,Russ Cox个人博客上最近更新的内容,是关于浮点数快速打印与解析算法的系列文章,据说相关的性能优化已经被吸收进了最新版本的Go——这也侧面印证了,即便不再担任Go技术负责人,他依然活跃在最硬核的系统编程一线。
还有网友「考古」发现,Russ Cox同时是整数数列在线百科全书(OEIS)基金会的主席,曾经为这个项目编写过软件——这个身份很少被主流报道提及,却让人对他的技术版图有了更立体的认识。
采访中还有一句被反复引用的话,被网友评价为「可以直接刻在墙上」:软件工程,就是当时间和其他人被加进「编程」这件事之后,所发生的一切。有网友补充说,Abseil团队所著的《Software Engineering at Google》一书,对这个观点做了系统性的展开,并附上了免费在线阅读的链接,评价这本书「值得一读」。也有网友顺势追问:既然「战术龙卷风」描述的是加入时间和他人之后的失控状态,那当我们把prompt和AI agent也加进这个方程式,是不是也该有一个新词来形容它?评论区随即有人打趣,觉得「slop(劣质产物)」和「vibe coding(氛围编程)」这两个已经流行开来的说法,多少已经顶上了这个空缺。
当然,评论区也不全是赞美。有人认为这篇专访整体不错,但部分内容偏套路化;也有人直言不太认同Russ Cox在AI风险问题上的乐观程度,认为「战术龙卷风」式的AI agent恐怕会比他预想的更快成为常态——原因很现实:比起代码质量,很多管理者更在意「东西有没有做出来」,而AI agent恰恰最擅长制造这种「看起来很快做出来了」的错觉。
还有个别评论则把矛头指向了他所在的公司本身,认为技术理念和商业现实之间存在落差。
这种混杂着敬佩、认同与质疑的讨论氛围,恰恰说明这篇专访触碰到了当下工程师群体最真实的焦虑。
从「简洁是最好的扩展策略」,到「好测试是拥抱AI agent的前提」,再到「警惕AI agent变成终极战术龙卷风」,Russ Cox这篇专访串起来的,其实是一条清晰的工程逻辑:无论是十几年前设计Go语言,还是今天审视AI coding agent,他关心的始终不是某个具体的技术选型有多炫酷,而是一个系统能否在时间和人(或者agent)的双重考验下,依然保持简单和可控。
对于今天每一个正在与AI agent协作的工程师和管理者来说,这或许才是这篇专访最值得反复咀嚼的地方:AI能极大地提升产出速度,但能不能守住系统的「战略层面」,依然取决于人——取决于我们愿不愿意投入时间去建立那套连贯的「理论」,愿不愿意把测试基础设施打磨扎实,愿不愿意在效率和复杂度之间,做出清醒的取舍。
毕竟,龙卷风跑得再快,也终究需要有人来清理它留下的废墟。
参考链接
你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?
继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!
我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。
目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!

「Go & AI 精进营」已全面升级为「Go, Rust & AI 精进营」知识星球,快来加入星球,开启你的技术跃迁之旅吧!
我们致力于打造一个高品质的 现代系统级开发(Go / Rust) 与 前沿 Agentic AI 工程实践 平台。在这里,你将获得:
衷心希望「Go, Rust & AI 精进营」能成为你拓宽技术边界、持续精进与交流的温暖港湾。让我们在此相聚,享受代码与工程之美以及技术精进的快乐!欢迎你的加入!

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

2026-08-22 06:00:00
本文永久链接 – https://tonybai.com/2026/08/22/rust-official-learning-rust-journey
大家好,我是Tony Bai。
【导读】
Rust 官方用半年时间,向 4200 名开发者发放问卷、对 70 多人进行深度访谈,试图回答一个老问题:学 Rust 到底难在哪?答案和很多人的直觉不一样——真正卡住大家的不是语法,而是"忘不掉"旧语言的思维习惯;编译器本身就是最好的老师;而 LLM 正在悄悄改写"谁能成为 Rust 工程师"这件事的门槛。
【文章要点】

Rust以难学著称,这几乎是编程语言圈的“政治正确”。
但难在哪里?是语法太怪、借用检查器(borrow checker)太严,还是别的什么原因?大部分讨论都停留在“感觉”层面,很少有人真正系统地问过Rust学习者本人。
Rust 官方这次不一样。今年,Rust项目组内部成立了一个叫Vision Doc的工作组,花了近一年时间,先后完成了一份4200多份回复的问卷调查,又对70多位来自不同背景的Rust用户做了每次约45分钟的深度访谈。他们把访谈过程和发现整理成了一个系列博客,其中一篇专门聚焦“人们是怎么学会Rust的”,干货密度非常高。
这篇文章,就带大家梳理这份调研到底挖出了什么。
先说说这次调研是怎么做的,因为方法本身就值得借鉴。
项目组请来了一位专业的用户研究顾问Holly Ellis,学习了一套标准的用户研究方法论。团队很快意识到一件事:不能直接问受访者“你觉得借用检查器难吗”,因为这种问法会诱导对方给出一个“应该说”的答案,而不是真实感受。
于是他们改用侧面提问的方式,比如不问"你最大的痛点是什么",而是问:
“你上一次被一条报错信息搞懵是什么时候?”
然后顺着这个具体场景继续追问下去。这种非引导性的提问方式虽然操作起来更麻烦,但往往能挖出更真实、更意外的信息。
调研也暴露了一个有意思的局限:讨厌Rust的人,普遍不愿意站出来说话。问卷里,49%的受访者给自己的Rust使用体验打了4分或5分(满分5分),只有18.5%打了1分或2分,而这部分人里愿意留联系方式接受进一步访谈的少之又少。用项目组自己的话说:
“不喜欢Rust的人,大多不会去读Rust官方博客,也不太想和一群Rust’粉丝’聊这个话题。”
这也是为什么后续的访谈样本,本质上是“留下来的人”的视角——这一点在后文的“静默流失”部分还会再提到。
调研发现,真正因为“我要学一门新语言”而主动选择Rust的人,只是众多路径中的一种。更常见的情况是:好奇心驱动、因为要做嵌入式项目、就业市场压力、公司整体转型采用Rust、或者干脆是团队重组后“被分配”去写Rust。
最后一种路径尤其值得注意,因为这意味着很多学习者根本不是从零开始“评估”要不要学Rust,而是Rust已经先落地了,人要去追赶。一位身兼多职的CTO这样描述自己的经历:
“说来好笑,我以前推荐过比Rust还小众的语言。现在Rust已经不算小众语言了,但它也还不是Java。”
大部分受访者的第一站是官方教材《The Rust Programming Language》(俗称“the book”),配合编译器给出的提示边学边练:
“我从官方文档开始学,因为里面有很多例子,能讲清楚借用检查器这类特性到底是怎么工作的。” —— 一家汽车零部件供应商的软件工程师
但也有相当一部分人需要反复“过几遍”,并借助社区资源,比如Rustlings、《The Little Book of Rust Macros》、《Learn Rust With Entirely Too Many Linked Lists》:
“官方书、Rustlings、《Zero to Production in Rust》、Jon Gjengset的教程……看了一堆书。这不是一遍就能读完的东西,我都记不清自己翻了多少遍了。” —— 一位从事视频流媒体与存储的工程师
值得关注的是,不止一位受访者提到,觉得官方教材“跟不上语言的迭代速度”:
“我们本来想用官方那本书,但发现它有点过时了。我们去看了GitHub仓库,发现里面堆了很多没解决的issue和没合并的PR。” —— 某受监管行业里负责Rust落地的首席工程师
这个感受是否“属实”其实没那么重要——重要的是,随着越来越多公司认真评估是否采用Rust,会有越来越多“新用户”用挑剔的眼光去审视这些学习材料,任何看起来“没人打理”的迹象都会被放大解读。
这大概是整篇调研里最反直觉、也最值得国内团队参考的一条结论:Rust学习曲线的真正难点,往往不是语法本身,而是“卸载”此前语言留下的思维定式。
对绝大多数人来说,Rust不是他们的第一门语言,而是第二门、第三门甚至第N门。他们往往会先用最熟悉的方式——C++思维、Java思维、Go思维——把Rust“翻译”一遍,写上几个月甚至几年,才慢慢过渡到符合Rust习惯的写法。
“如果你本来就很熟C,那用Rust确实会有一段生产力下降期,因为你在学新规则、新语法。” —— 一位移动机器人领域的首席固件工程师
“刚开始的时候,我基本就是到处摸索,加加减减
&和*这些符号,试图搞明白mut和不mut到底是怎么回事。” —— 一位拥有20年Java经验、现从事云和物联网开发的资深工程师
更有意思的是,有受访者提到,完全没有编程背景的人,反而可能更容易学会Rust:
“我们团队有个人以前几乎没怎么写过程序,直接上手负责我们Rust项目的内部实现,她适应得很好。反倒是资深工程师更容易挣扎,因为他们得先’忘掉’那些在别的语言里行得通、但不是’Rust方式’的做法。” —— 某汽车OEM研发实验室的研究员
这条线索很有价值:没有“磨出旧习惯的沟槽”,或许本身就是一种优势,值得进一步研究。
关于最让人头疼的borrow checker,调研归纳出了几条比较典型的学习路径。
路径一:编译器就是老师。 多位受访者表示,Rust的报错信息本身承担了大量教学功能,尤其是关于生命周期(lifetime)的部分:
“如果你手写代码时把生命周期搞乱了,Rust的报错通常都会讲得很清楚。” —— 一位研究Rust静态分析的研究员
“缺什么,编译器基本都会告诉我:它会说’你需要给这个引用声明生命周期’,然后我就知道该怎么改了。整体来说这套机制运作得相当好。” —— 一位资深软件工程师
路径二:靠写代码“熬”出来的。 也有人是通过大量实战项目、编码挑战反复练习之后才真正“开窍”:
“我真的是写了大量Rust代码之后,才理解借用检查器到底是怎么回事的。” —— 一家Rust创业公司的创始人
“除了自己做项目练手,我还做了Advent of Code之类的编程挑战……到某个节点突然就’咔哒’一下通了,不再是我和Rust较劲,而是Rust开始为我所用。当程序终于符合Rust的要求时,它就是能跑,我不用花时间调试。” —— 一家大型SaaS厂商的首席工程师
路径三:放下“clone洁癖”。 这一条尤其值得单独拎出来说。不少新手一开始就给自己设定了“零clone、零拷贝、生命周期贯穿始终”的高标准,结果反而把自己绕进死胡同:
“我第一个项目的时候,心想’我绝对不要clone或copy任何东西’,于是小心翼翼地把生命周期全部串起来,结果把自己困住了。后来我看到别人直接clone了我正在折腾的那个struct,而且开销很小。有时候直接clone就完全没问题。” —— 一位大学研究员
几乎所有资深Rust开发者给出的建议都高度一致:学习阶段大胆clone,先跑起来,理解问题之后再优化。这种“clone洁癖”本质上是Rust“高性能、高可靠”的名声反过来给新手加的心理包袱——很多人在写出第一个能跑的程序之前,就已经先入为主地认定“不够优雅就是错的”。
调研里有一段来自大学课堂的观察格外有意思。一位教授同时教Java、C++和Rust相关课程,在嵌入式课程上做了一个对照实验:一半学生用C,一半用Rust。
“在嵌入式这块,我没看出Rust和C有多大差别。Rust班的反馈整体更差一些,主要是因为他们得自己搭项目。C班的学生直接找LLM生成一个,完全没问题。” —— 该大学教授
也就是说,C语言学生靠LLM“抄”作业毫无压力,Rust学生却做不到——目前还没人能给出确切原因,这也是官方明确标注为“有待进一步研究”的一个悬而未决的问题。
但另一个现象却很清晰:Rust班的学生在遇到嵌入式驱动相关的问题时,教授鼓励他们直接去GitHub上给维护者提issue、提问,而维护者也确实认真回复了。很多从没参与过开源的学生,第一次就得到了代码原作者的亲自解答——这构成了他们对Rust社区最初、也最深刻的好印象。
LLM是这份调研里绕不开的话题,尽管Vision Doc团队特意声明,讨论范围只限定在“作为学习工具”这一层面,不涉及AI在软件开发中更广泛的争议。
一部分资深从业者把LLM当作快速上手一个陌生领域的辅助工具:
“我挺乐观的,觉得有办法把LLM用进来,缩短学习曲线。这类工具最大的价值之一,就是帮你在一个完全陌生的领域里快速找到方向。” —— 某大型开源Rust crate的维护者
也有人保持谨慎,把LLM当作“更聪明版的Stack Overflow”:
“我大概一个月会试一次LLM,主要是用来生成示例代码。就像读Stack Overflow上的答案一样,你得仔细读、真正理解,而不是复制粘贴——最好是自己用自己的话把代码重新敲一遍再检查,因为魔鬼往往就藏在那些细节里。” —— 一家Rust创业公司的创始人
但最有冲击力的一段访谈,来自一位培训机构的创始人。他的公司在招募完全没有系统编程背景的人,用“LLM+Rust编译器”的组合把他们训练成能干活的Rust工程师:
“一开始我挺担心的,但现在有了LLM帮忙,语言本身的难度已经不是问题了。我在强类型运行时语言(比如Rust)身上看到了巨大的机会……我们在[某发展中国家]招募20到25名高中毕业生,把他们培养成Rust程序员,然后让他们加入我们全球的团队。” —— 某咨询公司创始人
Vision Doc团队对此的态度也很克制:这只是一个案例,不代表能被普遍复制。这些开发者能留多久、能不能独立维护复杂代码、这套培养模式换个组织结构还灵不灵,都是未知数。但如果这个结论真的能推广开来,那意味着“谁能成为Rust工程师”这个人才池,可能比大家想象的要大得多。
对于正在或计划批量引入Rust的团队,调研给出的画像高度一致,几乎是一套“标准流程”:
“开这门培训课,而不是让大家’各自去看Rust那本书’,用意就是让每个人有一个统一的起点。” —— 移动机器人领域首席固件工程师
“通常我们会让大家先过一遍Rustlings,再过一遍官方教材,然后开始接手一些低风险的票据(ticket)。” —— 某大型SaaS公司首席工程师
一些企业还发现,与其花两年时间苦苦寻找一个C++高手,不如直接招不懂Rust的人,慢慢带起来:
“他们需要维护和扩展一个C++代码库,原来有个C++大神,结果他们花了差不多两年都没找到同等水平的替代者。最后干脆招了完全不懂Rust的人,慢慢带起来,还从C++那边做了FFI绑定,让新人能在Rust那侧工作。能感觉到,借用检查器正在’教’这些人用正确的方式处理系统层面的问题。” —— 某汽车OEM首席工程师
如果说前面几部分讲的是“留下来的人怎么学会的”,那这一段讲的是“离开的人为什么走”——这也是整篇调研里,笔者认为最值得警惕的一个发现。
有受访者提到,一位朋友因为受不了Rust嵌入式生态的严格约束而彻底放弃:
“那种嵌入式生态对一个习惯了C的人来说会非常令人抓狂:为什么我不能直接拿到一个指向外设的指针、然后往寄存器里写数据?你们到底在搞什么?……我朋友一直没能过这个坎,看了看就说’我不想折腾这个’,然后就走了。” —— 另一位大学教授
更值得关注的是社区氛围本身可能正在“劝退”一部分人:
“大家其实挺乐于助人的,但普遍的态度是:如果你的程序写得很复杂,那基本就是水平问题。人们卡壳的时候,很少能得到真正的共情,很多人就这样被悄悄推开了。可能有大量人默默地放弃了写Rust,因为写到一定复杂度之后,得到的反馈却是’你显然需要提升自己的水平’。” —— 某SaaS公司软件工程师
问卷里也收集到了类似经历,有人是在1.0版本发布前“折戟”、之后又重新捡起来学会的:
“我在1.0之前就开始学了,很快就卡在把C++的思维模式往Rust上套(借用检查的问题)。1.0之后我又试了一次,这次就学会了。” —— 问卷受访者A
Vision Doc团队坦诚地指出,这部分内容其实是整份调研里样本最不完整的一块:几乎所有能被访谈到的人,都是那些坚持下来、依然活跃在Rust渠道里的人;真正“悄悄离开、再也不回来”的那批人,几乎无法通过现有渠道触达。如果后续真的成立专门的用户研究团队,“访谈那些放弃了Rust的人”会是一个很好的切入点。
基于这些发现,Vision Doc团队给出了几条值得尝试的方向:
这份调研最打动人的地方,不是“证明了Rust好学”或者“证明了Rust难学”,而是把过去停留在论坛吵架和个人经验层面的争论,第一次拉到了系统性的证据层面。
对国内正在或计划引入Rust的团队来说,这份调研里有几条经验其实可以直接落地:与其让新人自己摸索官方教材,不如先明确告诉他们“哪些C++/Java的老习惯在Rust里行不通”;与其纠结要不要招“懂Rust”的人,不如参考这些企业的经验,招人进来、用培训+低风险任务把团队一起带起来;而团队内部对“卡壳”的容忍度,可能比任何一份学习资料都更决定新人能不能留下来。
至于LLM能在多大程度上重塑Rust的学习门槛,目前还只是一堆值得追踪的线索,而不是定论。但可以确定的是,这件事正在发生,也值得持续关注。
参考资料:
还在为“复制粘贴喂AI”而烦恼?我的新专栏 《AI原生开发工作流实战》 将带你:
扫描下方二维码,开启你的AI原生开发之旅。

你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?
继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!
我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。
目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

2026-08-21 06:00:00
本文永久链接 – https://tonybai.com/2026/08/21/victoriametrics-vlagent-log-collectors-benchmark-2026
大家好,我是Tony Bai。
【导读】
K8s 里每天产生的海量日志,最先经过的那个“搬运工”——日志采集器,性能差距到底有多大?VictoriaMetrics 联合创始人在最新演讲中公布了一份耗时数月的压测报告:在完全相同的 1 核 1GB 资源限制、零调优的条件下,自家新品 vlagent 单节点吞吐量冲到 14.3 万条/秒,是排名第二的 Fluent Bit 的 4.5 倍,是老牌选手 Fluentd 的 28 倍。更值得警惕的是,测试中还揪出了 Fluent Bit 和 Vector 在日志文件轮转时“偷偷丢字段”、Vector 默认配置下会“悄悄漏日志”等一系列容易被忽视的正确性问题。
【文章要点】

如果你的团队在 Kubernetes 上跑着 Fluent Bit、Vector 或者 Filebeat 采集日志,这篇文章可能会让你重新审视一下资源配置表里那几行不起眼的 CPU、内存 request。
时序数据库领域的知名玩家 VictoriaMetrics,最近把目光从“存储日志”转向了“采集日志”这一环。他们开源了一个新的日志采集器 vlagent,并且没有止步于自卖自夸,而是拉来了市面上几乎所有叫得上名字的日志采集器,在同一个测试环境里“真刀真枪”打了一场。测试代码、配置全部开源,任何人都可以自行复现验证。
这篇文章更像一份行业体检报告——它顺带揭出了几个连许多资深 SRE 都没意识到的坑。
在可观测性的“三驾马车”(Metrics、Logs、Traces)里,日志一直是数据量最大、结构最杂的一块。而日志采集器(log collector)作为整条链路的第一环,承担着从容器标准输出中“抢救”日志、解析、打标签、转发的重任。
一旦采集器扛不住高吞吐,后果通常是两种:丢日志,或者把宿主机的 CPU / 内存资源吃干抹净,反过来影响同节点上正在跑的业务 Pod。在生产环境里,日志采集器往往和业务容器抢同一批资源,这个“隐形税”常年被低估。
VictoriaMetrics 这次测试想回答的问题很直接:在完全相同的资源限制下,谁能扛住更大的日志洪峰,谁的资源效率更高,谁又会在压力下悄悄“作弊”(丢日志、丢字段)?
整套测试系统由四个部分组成:
sequence_id,用于后续校验是否丢包;/var/log/pods 或 /var/log/containers 里 tail 日志,解析后按各自协议(JSON Lines、Loki、Elasticsearch Bulk、OpenTelemetry)转发出去;sequence_id,两者之差就是丢失的日志数量,同时统计端到端延迟;下面示意图还原了整体架构:

测试环境非常“抠门”:一台 Google Cloud n2-highcpu-32(32 核、32GB 内存、本地 SSD)虚拟机,跑着一个单节点 kind 集群,所有 9 款采集器同时运行、共享同一个节点,模拟生产环境里资源被其他工作负载抢占的真实场景。每个采集器都通过官方 Helm Chart 部署,限制死 1 核 CPU、1GiB 内存,并且不做任何调优——不改缓冲区大小,不改批量提交参数,不改 GC 参数,全部用官方默认值。
这个设定很关键:这不是“各自跑在专属机器上飙分”的理想化测试,而是模拟了绝大多数团队实际部署时“没空调优、资源有限”的真实处境。
测试选用 100 个日志生成 Pod 并发写日志作为主要对比场景——这个数字并非随意选取,Kubernetes 官方建议单节点 Pod 数量上限为 110,100 个 Pod 已经是单节点部署下具有代表性的压力上限。

上图展示了 vlagent(蓝线)冲到约 14.3 万条/秒峰值,其余采集器均在 5k–40k 区间见顶。
具体100 个 Pod 场景下各采集器的最大吞吐量如下表:
| 采集器 | 最大吞吐(条/秒) | 与领先者倍差 |
|---|---|---|
| vlagent | 143 000 | 1.0x |
| Fluent Bit | 31 300 | 4.5x |
| Vector | 25 000 | 5.7x |
| OpenTelemetry Collector | 20 500 | 6.9x |
| Grafana Alloy | 15 700 | 9.1x |
| Grafana Agent | 14 800 | 9.7x |
| Promtail | 13 400 | 10.6x |
| Filebeat | 5 250 | 27.2x |
| Fluentd | 5 100 | 28.0x |
从曲线上可以看出一个更值得关注的细节:vlagent 的吞吐量随负载几乎呈线性增长,而其他采集器普遍在某个阈值之后就“撞墙”了——不是慢慢爬升到极限,而是很快触顶,即使继续加压也无法再提升。这意味着在真实的日志突发流量(比如故障排查时应用疯狂打印堆栈)场景下,非 vlagent 的采集器更容易率先出现积压甚至丢日志。
峰值吞吐固然亮眼,但更能反映“日常性价比”的,是在同样负载下谁用的资源更少。测试选取了 10k 条/秒(2 个 Pod、各 5000 条/秒)这个所有采集器(除已经开始丢日志的 Filebeat 和 Fluentd)都还能正常工作的负载点,来对比 CPU 和内存消耗。

10k 条/秒下的 CPU 占用:
| 采集器 | CPU 占用(核) | 与领先者倍差 |
|---|---|---|
| vlagent | 0.062 | 1.0x |
| Fluent Bit | 0.260 | 4.2x |
| Vector | 0.412 | 6.6x |
| OpenTelemetry Collector | 0.491 | 7.9x |
| Grafana Agent | 0.552 | 8.9x |
| Grafana Alloy | 0.578 | 9.3x |
| Promtail | 0.655 | 10.5x |

10k 条/秒下的内存占用:
| 采集器 | 平均内存 | 与领先者倍差 |
|---|---|---|
| vlagent | 27.91 MiB | 1.0x |
| Promtail | 63.00 MiB | 2.2x |
| Grafana Alloy | 66.44 MiB | 2.4x |
| Grafana Agent | 72.49 MiB | 2.6x |
| Fluent Bit | 78.10 MiB | 2.8x |
| OpenTelemetry Collector | 106.83 MiB | 3.8x |
| Vector | 153.50 MiB | 5.5x |
一个值得关注的细节是:Fluent Bit 和 Filebeat 在高负载下的内存峰值超过了 1GiB 限制,导致容器被 OOM Killer 强制杀死,这也是它们在压力图中出现断档的原因。换句话说,如果你的生产环境给日志采集器分配的内存不够宽裕,Fluent Bit 和 Filebeat 存在被系统“物理超度”的风险。
性能测试打分固然直观,但这份报告最有价值的部分,其实是它顺带揪出的几个正确性 bug——这些问题在常规的吞吐量测试里几乎不会被发现,却可能在生产环境里悄无声息地污染你的日志数据。
容器日志文件会定期轮转(rotate)。在高吞吐场景下,containerd 有可能在写入过程中恰好触发轮转,导致一条完整的日志记录被“腰斩”——前半段写入旧文件,后半段写入新文件。
理论上,任何采集器在自身 CPU 不足、来不及跟上写入速度时都可能撞上这个时机差,vlagent 也不例外。但测试中发现了一个反常现象:
Fluent Bit 和 Vector 即便在 CPU 占用很低、完全没有性能压力的情况下,也会产生断句记录——原因是它们没有把轮转前后两个文件里的片段重新拼接成完整记录,而是直接把两个残缺片段分别当作两条独立日志转发了出去。在 1 小时、10k 条/秒的相同压力测试下,Fluent Bit 产生了 34 条残缺记录,Vector 产生了 2 条;而测试中其余采集器(包括 vlagent)在多次数小时的重复测试里从未出现过这个问题。
如果你的日志处理管道依赖某些字段做路由、过滤或告警,这种“看起来正常收到了、内容却是坏的”记录,排查起来会格外头疼。VictoriaMetrics 团队已经把这个问题分别提交给了 Fluent Bit 和 Vector 官方仓库。
除了断句问题,测试还专门给 Vector 列了一节“默认配置踩坑指南”:
新 Pod 日志静默丢失:Vector 有一个 glob_minimum_cooldown_ms 参数,控制它多久重新扫描一次文件系统来发现新的日志文件,默认值是 60 秒。这意味着一个新起的 Pod 在头 60 秒内产生的日志,很可能在 Vector 反应过来之前就已经丢失,而且是静默丢失,没有任何报错。作为对比,Fluent Bit 的同类参数默认只有 10 秒,因此不会踩到这个坑。测试中把该参数调到 10 秒后问题消失——但这也说明,默认配置本身是有陷阱的。
文件描述符泄漏:高负载下,Vector 打开日志文件的速度比处理速度快,导致文件描述符数量持续增长。由于文件被进程持有着无法真正删除,轮转后的旧日志文件会在磁盘上不断堆积,极端情况下可能把节点磁盘空间撑爆。
如果负载回落,Vector 最终会追上积压、把日志全部投递完;但如果磁盘先被撑满,或者 Vector 在积压过程中重启,文件描述符会被释放、文件被系统删除,这部分日志就永久丢失了。Vector 提供了 rotate_wait_secs 参数来控制持有轮转文件句柄的时长,但调低它意味着用“可能丢日志”换取“不占满磁盘”——本质上是一个两难权衡,而不是真正的解法。
Pod 元数据丢失:如果一个 Pod 在 Vector 还有它的日志积压未处理完时就被删除了,Vector 会丢失这个 Pod 的标签、注解等 Kubernetes 元数据。日志本身最终还是会被投递,但元数据没了,下游依赖标签做过滤路由的规则可能因此失效。
这三个问题共同指向一个道理:日志采集器的“默认配置”未必是为高负载场景准备的,直接拿官方默认值上生产,本身就是一种未被充分评估的风险。
报告最后也很坦诚地列出了 vlagent 目前的短板:
如果你的管道强依赖这两个能力,现阶段还是应该继续用更成熟、配置更灵活的采集器。VictoriaMetrics 团队表示相关功能正在开发中,计划基于自家的 LogsQL 查询语言实现。
但如果你的诉求相对“朴素”——稳定采集 K8s 里的 JSON 结构化日志、不丢数据、资源占用可控,vlagent 已经具备了几个开箱即用的能力:
值得一提的是,vlagent 并不绑定 VictoriaLogs——它同样支持把日志转发给 Fluent Bit、Vector、ClickHouse 等下游系统,也可以和现有采集器并行部署,逐步替换,不需要"推倒重来"。
不管你最终选择哪款采集器,这份报告里有几条建议是通用的:
glob_minimum_cooldown_ms 这种参数,默认值可能是为了通用场景做的保守选择,未必适合你的吞吐量级别,上线前最好逐项核对。日志采集器这个组件长期处在可观测性体系里“没人关心”的位置——大家更愿意讨论后端存储怎么选、怎么建索引,却很少有人认真算过采集这一环到底吃了多少 CPU、多少内存,更别说去验证它有没有偷偷丢数据。这次 VictoriaMetrics 的测试,某种程度上是把这个长期被忽视的问题摆到了台面上:性能差距可以是数量级的,而正确性问题往往比性能问题更难被察觉。
如果你的团队正在规划或者重新评估 K8s 上的日志采集方案,不妨去把测试代码拉下来,在自己的环境里跑一遍,比看任何一篇文章都更有说服力。
参考资料:
还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 《从0 开始构建 Agent Harness》 将带你:
扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。

你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?
继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!
我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。
目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

2026-08-20 05:00:00
本文永久链接 – https://tonybai.com/2026/08/20/rust-polonius-alpha-borrow-checker-nightly
大家好,我是Tony Bai。
【导读】
Rust 最让人又爱又恨的「借用检查器」,这次真的要迎来十年来最大的一次升级了。北京时间 8 月 4 日,Rust 官方博客宣布:下一代借用检查器 Polonius Alpha 已经登陆 Nightly 版本,预计将在今年年底转正为 Stable。这意味着,那些曾经被编译器无情拒绝、却明明「逻辑上完全安全」的代码,很快就能顺利通过编译。
【文章重点】
-Zpolonius=off 一键关闭。
写过 Rust 的人,多少都经历过这样的瞬间:明明代码逻辑上完全没问题,编译器却坚决不放行,报出一长串关于生命周期(lifetime)的错误信息。你不得不加一堆看起来多余的克隆(clone)、重构函数结构,甚至绕一个大圈子,才能让编译器「满意」。
这种体验,很大程度上要归咎于 Rust 现有借用检查器的一个先天局限:它只会「机械地」看一个变量的生命周期跨度,却不会「聪明地」根据代码的执行路径去判断这个借用到底还有没有在用。
而 Rust 官方博客在 2026 年 8 月 4 日发布的文章《Enabling the next iteration of the borrow checker on nightly》,宣布的正是解决这个老问题的方案:新一代借用检查器 Polonius,其阶段性成果 Polonius Alpha,已经在 Nightly 版本上开启,目标是在今年年底前转正为 Stable。
要理解 Polonius Alpha 的意义,得先简单回顾一下 Rust 借用检查器的演进历史。

简单来说:
值得一提的是,Rust 团队原本计划在 2024 年就完成这次新方案的稳定化,但由于各种优先级调整被推迟了两年——这也从侧面说明,编译器底层机制的升级,远比表面上看起来要复杂得多。
Polonius Alpha 相对 NLL 最核心的能力提升,官方用一句话概括:对生命周期的「outlives」关系做流敏感(flow-sensitive)分析。
这句话听起来有点抽象,我们用两个官方给出的代码例子来直观感受一下。
fn reborrow(a: &mut u8) -> &mut u8 {
let b = &mut *a;
if true { b } else { a }
}
这段代码在当前 Stable Rust(NLL)下会编译失败,但在 Polonius Alpha 下可以顺利通过。原因在于:b 是从 a 重新借用出来的,NLL 没办法精确地区分「if 分支用 b」和「else 分支用 a」这两条路径上借用关系的差异,只能保守地报错。
fn get_mut_or_default<'r, K: Hash + Eq + Copy, V: Default>(
map: &'r mut HashMap<K, V>,
key: K,
) -> &'r mut V {
match map.get_mut(&key) {
Some(value) => value,
None => {
map.insert(key, V::default());
map.get_mut(&key).unwrap()
}
}
}
这是一个「有则取值、无则插入默认值再取值」的经典模式(类似 entry API 想手写实现的场景)。在 NLL 下,这段代码会编译失败,报错原因是:map.get_mut(&key) 产生的可变借用,由于返回类型标注了 &'r mut V,编译器会认为这个借用「活」到了整个函数结束——哪怕它其实只在 Some(value) 分支里被用到。既然编译器认为这个借用还「活着」,那 None 分支里再次调用 map.insert 和 map.get_mut 就会被判定为「借用冲突」,从而报错。
用一张示意图来说明这个问题:

简单说:NLL 只会看一个借用「理论上可能活多久」,而 Polonius Alpha 会真正跟踪这个借用在每一条具体执行路径上「实际有没有在用」。这正是「流敏感」和「流不敏感」的差别,也是 Polonius Alpha 真正的价值所在。
对大量 Rust 开发者来说,这个改动最直接的感受会是:以前那些「你必须重构成两个函数」、「你必须多克隆一次」、「你必须用 entry API 而不能手写等价逻辑」的场景,很多都会被解锁。
需要泼一盆冷水的是,Polonius Alpha 并不是终极形态,官方也在博客里非常坦诚地指出了它的局限。
早期那个「基于别名」的原始 Polonius 实现(也就是文中说的 legacy Polonius),虽然性能不达标,但它的表达能力其实更强,能编译通过一些 Polonius Alpha 依然会拒绝的代码。官方举了这样一个例子:
struct X { next: Option<Box<X>> }
fn conditional() {
let mut b = Some(Box::new(X { next: None }));
let mut p = &mut b;
while let Some(now) = p {
if true {
p = &mut now.next;
}
}
}
这段涉及链表结构反复重新借用的代码,legacy Polonius 能够编译通过,但 Polonius Alpha 目前还不行。这也是为什么它被命名为「Alpha」而不是「正式版 Polonius」——它是新公式化方案里,官方评估「已经没有已知遗留问题、可以安全稳定化」的一个子集,而不是 Polonius 理论上能力的完全体。
有意思的是,官方也补充说明:反过来的情况同样存在,有一些代码是 Polonius Alpha 能编译通过、但 legacy Polonius 编译不了的。所以严格来说,Polonius Alpha 并不是 legacy Polonius 的一个真子集,而是两套并不完全重合的能力集合。
「流敏感」分析听起来工作量更大,会不会拖慢编译速度?这是社区最关心的问题之一,官方也专门用了一个章节来回应。
结论是:Polonius Alpha 在计算量上肯定不会比 NLL 少(只会相等或更多),因此团队重点关注了潜在的性能回归风险。
官方对 crates.io 下载量最高的一万个 crate 做了实测对比,结果显示,出现「显著」编译耗时回归的库占比很低,即便被判定为「显著回归」的库,回归幅度通常也相当有限。

图中每个点代表一万个下载量最高的 crate 之一;黑线是官方设定的「显著回归」阈值(30 秒以内按二次曲线缩放、相当于 1% 的回归幅度);红点为超过该阈值的 crate;横轴为该 crate 在 NLL 下的(仅自身、不含依赖的)编译耗时,纵轴为 Polonius Alpha 相对 NLL 的耗时比值。

除了这一万个热门库之外,官方还专门排查了「借用数量特别多」的一批代码,这类极端场景下观察到的最差回归幅度是 2 到 3 倍。团队已经对这些回归的成因做了初步排查,并在评估修复方案;不过即便部分回归短期内无法修复,考虑到这类场景本身相当罕见,且 Polonius Alpha 带来的能力提升足够可观,官方认为这个取舍是合理的。
需要再次强调:这次变更目前只在 Nightly 版本上开启,Stable 用户完全不受影响,可以放心继续使用现有工作流。
如果你正在用 Nightly,并且不想启用 Polonius Alpha,只想继续使用 Stable 版本的 NLL,可以通过以下任意一种方式关闭:
方式一:命令行参数
rustc -Zpolonius=off
方式二:环境变量
RUSTFLAGS=-Zpolonius=off
方式三:项目级配置(.cargo/config.toml)
[target.x86_64-unknown-linux-gnu]
rustflags = ["-Zpolonius=off"]
官方也特别提到:如果你确实因为某些原因不得不关闭 Polonius Alpha,非常欢迎到 GitHub issue 或 Zulip 讨论区 反馈原因,这些反馈将直接影响正式稳定化前的打磨方向。
按照官方的规划,未来几个月团队会重点做三件事:
以上工作全部完成之后,团队的目标是在今年年底前完成 Stable 稳定化。
同时官方也很坦诚地表态:虽然目前仍有一些「大家希望能编译通过、但 NLL 和 Polonius Alpha 现在都还编译不过」的代码模式存在,但在 Polonius Alpha 稳定化之后,团队近期并没有继续在 Polonius 上做新功能开发的具体计划。团队会在一段时间内继续做性能优化和回归修复,但会把更多精力转向其他优先级更高的工作。换句话说,Polonius 后续会不会有「Beta」「更完整版」,目前官方的表态是「未来某个时间点会回来做,但不是现在」。
对绝大多数 Rust 开发者而言,Polonius Alpha 稳定化之后,最直观的感受会是:一部分曾经写起来别扭、需要绕路才能通过编译的代码,会变得更自然。尤其是涉及 match/if-else 分支中可变借用「按路径取用」的场景(比如上文的 get_mut_or_default 模式),会有实打实的体验提升。
而对于关心底层原理的开发者来说,这次升级也是一个很好的窗口,去理解 Rust 借用检查器背后「流不敏感」与「流敏感」这两种分析范式的本质区别,以及一门系统级语言的核心机制,是如何在长达十年的时间里,被一步步打磨到今天这个程度的。
Polonius Alpha 现在已经可以在 Nightly 上尝鲜,如果你的项目恰好有相关代码模式的诉求,不妨去试一试,说不定能帮官方团队提前发现一个隐藏的坑。
参考资料:
还在为写 Agent 框架频频死循环、上下文爆炸而束手无策?我的新专栏 《从0 开始构建 Agent Harness》 将带你:
扫描下方二维码,开启从 0 开始构建Agent Harness 的实战之旅。

「Go & AI 精进营」已全面升级为「Go, Rust & AI 精进营」知识星球,快来加入星球,开启你的技术跃迁之旅吧!
我们致力于打造一个高品质的 现代系统级开发(Go / Rust) 与 前沿 Agentic AI 工程实践 平台。在这里,你将获得:
衷心希望「Go, Rust & AI 精进营」能成为你拓宽技术边界、持续精进与交流的温暖港湾。让我们在此相聚,享受代码与工程之美以及技术精进的快乐!欢迎你的加入!

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。
