

Python极速工具链Astral创始人查理·马什被OpenAI收购后,反思AI编程对开源社区的冲击:AI虽提升编码速度,却导致PR质量下降、审核成本激增、开发者成长契约瓦解;他强调系统级设计优化(如Rust重构、架构创新)比微优化更重要,并指出开发者营销与自动化测试是应对AI时代挑战的关键。
他用 Rust 重写了 Python 的半壁江山,却在智能体时代感到窒息。
刚被 OpenAI 用数亿美元买下公司的查理·马什(Charlie Marsh),最近在一个技术播客里,聊了聊他自己写代码时的焦虑。
查理是 Python 极速工具链 Astral(旗下有 Ruff、uv)的创始人,现在并入了 OpenAI 的智能体(Codex)团队。作为写出过最硬核开源工具的顶级工程师,他坦言自己现在已经很久不在编辑器里亲手写代码了,全靠提示词让 AI 帮他干活。
但这带来了一个很尴尬的代价——组里的成员直接对大老板说:“查理,以前你提交 PR,我们扫一眼就敢过;现在我们必须打起十二分精神盯着,因为这代码是你用 AI 自动生成的。”

这种对效率和工具的折腾,贯穿了查理的创业史。在创办 Astral 之前,他只是一家生物公司的普通工程师,因为受够了原版 Python 工具的缓慢,动了自学系统编程的念头。他坦言,当时选 Rust 语言几乎完全是因为“跟风”(Hype),他甚至连什么是内存安全都不懂。但靠着极简的逻辑和对极致速度的执念,他写出的极速分析工具在 Hacker News 上一炮而红。
今年被 OpenAI 收购,其实也是因为大模型正在走向实体工程和智能体,OpenAI 极度需要他们团队在编译器和极速工具链上的物理工程积累,来撑起大模型写代码的底层逻辑。

在这场与 The Peterman Pod 主持人 Ryan Peterman 的对话中,查理聊到了一个工程师在智能体时代的真实困境:比如自动重写代码库背后隐藏的未知崩溃风险,以及营销能力对一个开源项目的生死影响。在他看来,当 AI 批量生产代码的时候,程序员的工作重心必须发生根本性的转变。以下是查理基于一线的开发体验,分享的几个极其真实的行业细节:
AI 让提交 PR 的门槛降到了零,但人类审核的成本却一点没少。以前新人向开源项目提交代码,是一个“收到反馈、纠错、下一次写得更好”的个人成长过程。但现在,任何人都能用 AI 在两秒钟内生成一个看起来能跑的合并请求。维护者需要花一个小时去人工排错,而提交者并没有从中学到任何东西,这正在破坏开源社区最健康的成长契约。
用 AI 自动重构整个代码库,是在拿已知的 Bug 去赌未知的崩溃。虽然大模型能把一个庞大的代码库自动重写成另一种语言(比如 Bun 从 Zig 转向 Rust),甚至通过所有测试。但测试套件永远只能证明代码在特定情况下的正确。自动重写极易改变那些未被测试覆盖的“隐性代码行为”(Hyrum 定律),最后把未知的 Bug 直接甩给真实的用户。
成千上万个惊艳的开源项目,因为不懂营销而烂在了 GitHub 的角落里。很多程序员傲慢地觉得“只要代码写得好,自然会有人用”。但现实是,这是一个注意力极度稀缺的时代,你只有 10 秒钟时间在 Readme 页面吸引读者的兴趣。不会做“开发者营销(Developer Marketing)”的优秀工具,注定无法被世界发现。
主持人:你刚开始做这些开发工具的时候,那个领域是什么样的?你为什么会觉得它本来可以做得更好?
Charlie Marsh:在创办 Astral 之前,我在一家计算生物学公司工作,当时我是第二位工程师。我没有生物学背景,也没有机器学习背景,所以我负责搭建做机器学习所需的各种软件系统。当时那一套基本全是 Python,所以我也是在那个过程中学习 Python。
后来我们也用了 Go 和 Rust。所以我其实横跨过很多不同的生态系统。我觉得,那个时候在 Python 这边,我们作为一个小团队,想做很多事情。我们的代码库——当然不能和那些超大型代码库相比——但相对来说已经算挺大了,而真正负责写软件、支撑整个团队的工程师,其实只有我们几个人。团队里的其他人可能是机器学习研究员,也可能是科学家。
所以我等于是在很多不同生态里都待过,见过很多工具,而当时我又正在做 Python。我们一直都在努力从工具中榨取尽可能多的杠杆效应,不管是类型检查器、linter,还是包管理器。随着项目规模变大,我们团队又很小,我们就希望能有非常好的静态分析、非常好的工具链,但在这个过程中我们总是不断碰到各种限制。
我觉得,某种程度上,我当时看 Python 生态,会觉得它不像其他生态那样有那么多实验性的东西,尤其不像 Web 生态。那边当时有很多看起来相当疯狂的事情在发生,而且大家对性能也更加重视。最开始是 ESBuild,用 Go 写的;接着有 SWC、Rust,然后又有 Bun、Deno,还有一大堆后来出现的工具。于是,“JavaScript 可以拥有原生工具链”这个想法就越来越被接受了,尤其是当应用越来越大、大家在本机上做的事情越来越多之后。
但这种事情在 Python 世界里并没有真正发生。所以对我来说,核心问题其实就是:为什么 Python 不行?因为我看到了 Web 世界里发生的这些事,也写过一些 Rust、写过一些 Go,知道这些工具链是怎么工作的,也知道它们在用户体验层面大体上是什么样。
然后再回头看 Python,就会觉得它的工具更少,很多别的生态中很有意思的想法,在这里都没有体现出来。而且那些工具几乎全都是用 Python 本身写的。所以对我来说,这就是一个很有意思的问题。
这几乎是我开始做这件事时最先问自己的问题。Ruff 刚发布的时候,我博客文章的标题大概是《Python 工具链本来可以快得多得多》。对我来说,这其实更像是一个假设:Python 工具链能不能快很多?后来我做了一个原型,而那个原型给出的答案是:可以,我觉得完全可以。
主持人:你当时写过一篇文章,算是在研究 MyPy,或者说发布你基于 MyPy 的一些结果。而那篇文章的发布日期——大概九天之后——你就发了你刚才提到的那篇文章,说 Python 工具可以快得多。
Charlie Marsh:这就挺有意思的。我之前都没意识到这两件事离得这么近。
主持人:那个概念验证就是在那九天里做出来的吗?
Charlie Marsh:对。我当时写那篇博客,主要是在讲“大规模类型检查”,因为我在想:有哪些是我学到但别人很少写出来的东西?然后我决定先做一个 linter,因为我觉得这会容易很多。我现在仍然这么认为:它确实比做类型检查器要容易得多。我们现在也在做类型检查器,也已经有类型检查器了,所以我可以明确地说,做类型检查器难多了。
但我先做 linter,是因为我觉得它能以一个更小的形态,验证很多相同的想法。无论是做创业公司,还是做一个工具,你都希望尽快把某个真正能让人上手使用、而且能证明其价值的东西交到用户手里;同时你也希望它能支持你快速迭代。
结果很奇怪,linter 最后反而成了一个近乎完美的产品形态。它的核心很简单,但规则可以有非常非常多,对吧?所以它在很多维度上都具有可扩展性,而用户又能通过一个小核心加上一些规则,很快获得价值。
所以我们很快就把东西发出去了,然后不断迭代、增加更多规则和更多功能。用户还能把它和其他工具一起配合使用。它几乎是立刻就有用了,我觉得这一点特别重要。相比之下,像类型检查器就是个很好的反例:一个只完成了 75% 的类型检查器,其实没什么太大用处。包管理器也是一样。
这些东西往往必须——当然,我也不想把这话说得太绝对,很多情况是可以挑战这种说法的——但重点在于,像这类产品更难以“持续可用”的方式迭代发布。可我认为,恰恰是这种能力,才是真正建立势能的关键属性之一,不管你做的是工具还是别的什么。
主持人:你发出那篇文章之后,反响怎么样?
Charlie Marsh:那篇博客本身,我发出来之后上了 Hacker News,也在 Twitter 上被很多人转发,整体上确实激起了不少兴奋和兴趣。
这种事——尤其是登上 Hacker News 首页——我其实有很多想法。它当然有一部分是运气,一部分是能力。它确实有帮助,但也绝不是成功的必要条件。反过来说,就算你上了 Hacker News 首页,也不代表你就一定会成功。能上当然很好,但这件事里随机性非常大。
不过我确实觉得有几点挺重要。第一,这件事发生之后,开始有一些人真的注意到这个项目了。于是我就非常努力地去接住这波关注,尽量保持高度参与。比如有人提 issue,我就会非常积极地争取尽快确认问题、尽快修复、尽快发版本。如果你能在一天之内完成这个闭环,它会是一个非常、非常强大的反馈循环。别的小伙伴会成为你的支持者,也会给项目带来更多势能。
另外,生态里也有一些比较有影响力的人开始注意到这个项目,比如 Sebastian Ramirez——他做了 FastAPI 以及很多别的东西。他后来也一直是我们项目非常好的支持者。早期的时候他就会说:“哦,这个挺有意思,我想试试看。” 然后我基本上会反问自己:那要做到什么程度,他才会真正用它?然后我就努力把那些事情都做到。所以我们确实尽量去放大这波势能。
但我还会想到另一件事:我确实经常思考“开发者营销”这件事。这个词多少有点像个脏词,或者说听起来不那么体面。我觉得很多工程师都希望,技术产品和工具只凭自身实力胜出。好像最好的技术方案就应该自然胜出、自然增长。
但我其实有一个有点傻的假设:GitHub 上可能有成千上万个非常惊艳的项目,它们只是根本不知道怎么营销自己,于是从来没被发现,也从来没真正走出去。我也不确定这是不是事实,但我对很多技术工作就是有这种感觉。
所以当我想一篇博客该写什么,或者 README 里该写什么时,我的想法并不是堆一百万个 emoji,也不是塞一百万张截图。真正重要的是:好,一个人要落到这个页面上了,而我大概只有 10 秒钟时间让他对我在做的事情产生兴趣,并且帮助他理解,为什么这件事和他有关、为什么它有用。所以这几乎成了我做任何事情时都会带进去的核心想法。
当然,这多少有点让人沮丧,因为这意味着:好吧,我可能只有 10 秒——但事实就是,人们真的没有太多注意力。这就是注意力经济,对吧?博客也是一样。说来也巧,我以前在 Spring 做生物机器学习的时候,我们也想发布一些内容来解释我们在做什么。我们有很多很有意思的洞见,于是会去看 OpenAI 的博客文章,尤其是 DALL·E 的那些早期文章。我们当时会想:哇,这真是非常厉害的博客,因为哪怕你一行正文都不看,你也能明白他们做了什么,而且你还是会觉得很震撼。哪怕一个人只是随便点进那个页面,他离开时也会带着一种印象,带着一些理解。我当时就觉得:哇,这真的太厉害了。
所以现在我写博客时,也会想:如果有人只是落到这个页面上,我必须做好准备,因为绝大多数人可能只会看 TL;DR,或者看第一张图,或者只读标题。当然,也会有人把全文读完。所以我当然应该很认真地打磨每一个字、每一句话,但大多数人其实几乎什么都不会读。
所以,再次抱歉,这听起来有点让人沮丧。但我确实觉得,认真考虑你的受众、思考你该如何真诚而诚实地告诉他们:为什么他们应该在意你做的这件事,这很值得。不是误导,不是骗术,而是你必须认真思考,怎样才能在非常短的时间内,把“为什么这件事重要”传达给别人。
主持人:能举个你自己的例子吗?
Charlie Marsh:我觉得就 Ruff 来说,我们在博客里放了一张非常好的 benchmark 图,那张图后来也在 Twitter 上传播开了。基本上,那张图可以说是无价的——抱歉,我不是说金钱意义上的无价,我的意思是在“吸引注意力”这件事上,它几乎无可替代。因为人一看就明白:哦,这里发生了什么非常清楚。
我觉得 benchmark 图非常有力量。当然,所有 benchmark 从某种意义上说都不完美,也都会有误差,所以关于这个还有很多可说的。但我确实觉得,如果你能有一个视觉上的“钩子”,让人迅速明白为什么该在意这件事、它到底是什么,再加上一句非常有力的标语——我们当时的核心就是说:我们会兼容你现有的东西,而且速度快得多,这几乎就是全部了。
然后你再往下推:好,如果它兼容、而且快很多,那为什么你该在意?因为它可能快上几个数量级,而且还能给你一样的体验。你要尽可能快地把这件事传达出去,对吧?
主持人:甚至连那篇博客的标题,《Python tooling could be much faster》,本身都挺有挑衅意味的。
Charlie Marsh:对。不过我其实一直挺反对标题党,或者任何带误导性的表达。你真正想做的,是尽可能有效地把最有意思的部分传达给别人,前提是,这些内容必须是你自己相信的、真实的、真诚的。
你当然可以胡吹一通,发一些非常刺激眼球、很有煽动性的东西,但我不知道。对我来说,那简直就是一场必输的仗。我根本不明白自己为什么要那么做。
主持人:你怎么看另一个问题——比如你们有那张图,但我也经常看到一种情况:图表的坐标轴不是从零开始的。
Charlie Marsh:哦,对。
主持人:所以整个范围其实只是在 50 到——
Charlie Marsh:图表犯罪。对,图表犯罪。我不会那样做。
不过我确实认为,benchmark 本身是一个很复杂、也很容易引发争议的话题。也许大多数人根本感受不到这种争议,但我感受得到,因为性能这件事本来就很细腻。你得考虑很多东西,比如有缓存和没缓存,这两种情况差别就非常大。又比如,你在哪个项目上跑,性能特征也会完全不同。
我们在 UV 上就深有体会。这件事特别难,因为有时候 UV 会非常快,但有时候你的包安装过程真正的瓶颈,其实是某个完全发生在它之外的 C++ 构建。那你就会想:好,那我该怎样准确地覆盖所有这些不同场景?而答案往往是:根本不可能准确覆盖所有场景。
但与此同时,如果你完全不去尝试向用户传达性能的重要性,或者不去真实地呈现“性能差异看起来会是什么样”“为什么它重要”,那也是对用户的不负责。所以你必须在两者之间取得平衡,而我觉得这其实很难。很多人都会把这件事做错。也许我们自己也错过几次,我愿意接受这一点。
但我确实觉得,这比人们想象的要复杂得多。而且我也认为,如果你不提供某种方式,帮助用户迅速感知一件东西“用起来是什么感觉”,那同样是一种很大的失职。
主持人:我最感兴趣的一点之一是——你提到了那张图。我看到那张图时,作为工程师的第一反应是:你们到底是怎么把它做得这么快的?你们的标语里有一句是“用 Rust 编写的 Python 开发工具”。所以我第一个很好奇的问题就是:为什么你会选择 Rust?它的优缺点又是什么?
Charlie Marsh:老实说,当时我之所以给那个项目选 Rust,很大程度上是因为“热度”。那时候我其实并没有对这些不同生态之间的技术取舍有多深入的理解,而且我也没真正做过很多系统编程。
所以,这是最诚实的答案。我是在很多地方都看到 Rust,它发展很快,大家都说它是写高性能软件的一种相对容易上手的方式。所以我就是因为这个开始的。
回过头看,我觉得那真是一个非常棒的决定,我绝对不会改,而且我认为它就是我们这类产品最好的选择。现在我也积累了更多 Rust 编程经验。
但这件事也挺有意思的,因为我有时候会想:如果我当初尝试用 C 或 C++ 来写这个东西,我大概很可能中途就放弃了。因为哪怕到现在,我依然觉得那两个生态更让人望而生畏,更难接近,也更难学。我常常觉得,Rust 一个很大的、但经常被低估的优势,其实就是工具链本身。因为你一进来就用 Cargo,而 Cargo 几乎就是你做所有事情的方式。
如果外面有个我们依赖的 crate,而我可能想给它提个 PR,我会非常有信心:我把它 clone 下来之后,几乎不用额外折腾,就能搞明白怎么运行、怎么构建。你只要 clone,然后跑 cargo run、cargo build 或 cargo test 就行,这真的很了不起。
尤其像我这种当时刚接触系统编程的人,我根本不想先去搞明白整套 C++ 工具链、构建系统,以及各种乱七八糟的东西。而 Rust 在这些方面是带有明确意见的。于是很多东西虽然学起来仍然很难,但我至少可以把时间花在那些“本来就应该难”的地方,而不是浪费在那些你根本不该花时间的地方,比如“怎么才能把这玩意儿编起来、跑起来”之类的问题。
我觉得,对我们来说 Rust 一直是一笔非常好的下注。它随着项目扩张也扩展得非常好。我的意思是,OpenAI 现在也大量使用 Rust,而且在非常坚定地押注 Rust。至于我自己,作为一个做开发工具的人,或者说试图为“造软件的人”去造软件的人,我对 Rust 是非常看好的。它的增长非常快,而且我觉得它还会继续大幅增长。
与此同时,就像我一开始说的,我从来不是那种对生态系统特别教条的人。我觉得每个生态都有很多可取之处。我也觉得 Zig 正在发生的事非常有意思,我只是希望自己有更多时间深入研究它,形成更细致的看法,看看它到底在哪些地方做得比 Rust 更好。我相信一定有一些地方它做得更好,也相信 Rust 也能从那个生态里学到东西。
类似地,也有很多人用 Go 做出了非常成功的东西。我也觉得这很好。我觉得这些都很好。但我真的很喜欢用 Rust。虽然我也会抱怨它的一些问题,但我更愿意把这些抱怨看成:这是我们应该继续改进的地方,尤其是在 LLM 正在改变我们构建软件方式的当下。
主持人:所以听起来你的意思是:如果是今天,C 和 C++ 因为工具链不够好,你不会考虑;但 Zig、Rust 和 Go,则更像是在不同的取舍光谱上。
Charlie Marsh:对。我知道很多人可能不会同意我,但除非有非常具体的技术原因,或者你必须处理现有的软件资产,否则我确实不太明白,为什么我要从零开始一个全新的 C 或 C++ 项目。
至少从我个人角度来说,我肯定不会。除非那就是你最熟悉的生态,那当然就很合理。但如果我是个刚想学系统编程的新手,或者说我对这些语言的熟悉程度差不多,那我真的不明白自己为什么会选 C 或 C++。我知道这话说出来可能会被很多人喷,但我其实没那么在乎。
而且现在我确实开始在意 Rust 给我的一些东西,而这些是我以前没有那么在意的,比如内存安全。说实话,我刚开始做 Ruff 的时候,甚至都不太明白“内存安全”到底是什么,也没多在乎它。而现在我觉得这件事真的非常棒。
我想造的是安全的东西,不会在用户手上崩掉、同时又非常快、性能很好,而且基本不需要为了这些目标做妥协。我觉得 Rust 能让我做到这一点。所以对我来说,我不知道,它有点像一种“接近完美”的语言。当然,它不是完美语言,但它确实是我现在最喜欢使用的语言。
主持人:在今天这个时代,几乎把整个现有代码库完整地转译或者转换成你想要的任何语言,这听起来都不算离谱。比如我记得——好像是在 Bun 那边——我有点记不清了,好像是从 Zig 到 Rust。
Charlie Marsh:对。
主持人:就是一口气全做了。如果你后来研究了另一门语言,并觉得它更好,你其实完全可以把现在所有 Rust 代码再改写成——我不知道——Zig 或 Go。比如对你已经做出来的这些开发工具来说,你会考虑这么做吗?
Charlie Marsh:现在真的是一个特别有意思的软件构建时代,因为一切都在变化。这不是什么新鲜观点,但变化真的太快了。
其实直到去年圣诞节,也就是大概 12 月假期那会儿,我才开始真正大量用 agent 编程。这其实离现在并不久。我的意思是,我之前也在用 Cursor 之类的东西,但现在我已经很久没有在编辑器里亲手改代码了。我做的所有事情都通过 Codex 完成,哪怕只是很小的修改,我也是直接写 prompt。完全不一样了。而这一切发生也不过是最近的事,对吧?所以六个月之后会是什么样,我真的不知道,很难说。
我之所以提这个,是因为对于 Bun 做那种重写——我自己现在不会这么做,但我反而很高兴他们在推动这件事。他们在实验、在拓宽“什么是可能的”的边界,而且是在面对很多批评的情况下这么做。最后用户会怎么感受这件事,会很有意思。
我觉得这里面有几件不同的事。先说为什么不这么做。第一点是:好,可能你最后已经不理解这份代码了,对吧?如果代码真的被“彻底重写”了,那么作为人类,你可能已经看不懂其中的大部分内容了,而这会成为一个大问题。当然,这里也有一些缓解因素,比如他们尽量做得非常直接地“转译”——我加引号是因为它不完全等同于翻译——但他们确实尽量一一对应地去做。这样至少有希望让人还保有对各种抽象的理解。
但如果他们本来就已经高度依赖 agent 来开发,那这件事还重要吗?他们到底需要在多大程度上理解代码的不同部分?说实话,我并不知道答案,而这正是我说“一切变化太快”的原因之一。现在我真的有点说不清,这种理解到底还重要到什么程度。
如果他们真的把整个代码库都转译了,那他们还需要在多深的抽象层级上理解系统?这其实是个非常复杂的问题。所以,第一层就是:你到底有多理解这份代码——这件事本身就已经很让人困惑了。
第二层是:做这种重写,本质上是在用已知问题去交换新的未知问题。想象一下,你合并了那个重写 PR,字面意义上它可能一口气关掉 GitHub 上 50 个已知 issue。好,这当然很好。但与此同时,你也可能引入了另外 50 个之前根本不存在、而你自己还不知道的新问题。
因为你的测试套件——哪怕是很好的测试套件——本质上也只是程序正确性的一种近似。还有个著名的 Hyrum 定律,大意是:软件中的任何实现细节,最终都会有人依赖它。具体原话我可能说不准,但核心就是:哪怕某些行为你本来并不打算作为正式行为暴露出去,它们最后也会在事实上变成 API 的一部分,不管你愿不愿意。
这听起来有点可怕,但意思就是:哪怕你整个测试套件都通过了,程序里仍然很可能有很多被隐式编码的行为已经改变了。更糟糕的是,这种代价最后往往会被转嫁到用户身上。是他们在使用过程中撞上这些问题,然后再来报 bug、再来帮你定位到底哪里出了问题。
所以,这其实才是我对自动化重写最担心的地方。说实话,我对模型本身已经足够信任,到了“如果条件合适,我会考虑让它们这么做”的程度。但真正更让我害怕的是:这会对用户造成什么影响?你要怎么安全地把这样的改动发布出去?
所以我也不知道。我们目前没有计划在自己的项目上这么做,但这个问题确实非常有意思。
另外还有第三点,是我现在也常常会想的,尤其是在当下这个“一切变化都非常快”、而“写软件的方式光谱突然变得极宽”的时期——这个比喻可能不太好,但现在做软件真的有太多不同方法了。从 Andrej Karpathy 所说的那种真正意义上的 vibe coding——也就是“完全别看代码”——一直到“完全不用 LLM”这种极端,中间其实有非常多不同层次的做法。
而且我现在对不同项目,确实会采取非常不同的策略。比如我上周写了一个工具,本质上就是个人软件:一个自定义的 Rust linter,用来帮助我们在项目里强制执行一些我一直想执行、但现有工具不太好表达的规则。那个项目我一行代码都没读,是 GPT-5 整个做完的。
这就意味着,我可以对它采用一种和 UV 完全不同的标准。因为那个项目只有我们自己在用。它对不对,非常容易判断。不是说要把项目细节讲得太深,但我们一眼就知道输出是否正确,而且也没有别的用户。
但 UV 不一样。它被数百万工程师和大量公司依赖,所以我们必须非常小心地决定:往里面发布什么、怎么发。可我确实觉得,当前有很大的空间去探索“我们到底怎样构建软件”。你只是必须根据项目本身、以及你对用户承担的责任,来决定你能把边界推到哪里。顺便说一句,这不是在影射 Bun 做得对不对,只是我自己思考“我们应该在哪些地方试探边界”的方式。
主持人:听你这么说,我会想到那个永恒不变的权衡:代码改动的风险有多高,以及你要用多大力度去验证它。因为如果风险不高,比如只是一个只有你我在用的内部开发工具——
Charlie Marsh:对。
主持人:那可能你就直接发了,盖章通过,不用太担心,反正也不会影响什么。但如果它是会阻塞客户下单的关键系统,那可能就得两个人一起审,那个区域还会加很多额外检查。所以我感觉,本质上还是同一条风险光谱,只不过现在光谱上又多了一层“更少的人为介入”——以前至少还是人写的;现在甚至可以是人根本不碰,直接发,甚至只是盖个章,可能根本都没人真正看过,对吧?
Charlie Marsh:对。这个问题我其实有很多反应。
还有一点我想顺带说一下:有时候我会去看他们的仓库,它和我们的仓库风格真的非常不一样,挺有意思的。因为你看一眼就会发现,头号贡献者基本上就是那个机器人。有时候你点开一些人类账号发的 PR,会看到里面是人类账号在来回对话,但你又很明显能感觉到,所有评论其实都是 agent 写的。你就会觉得:哇,这真的非常——
所以我会去看这些东西。但我自己的态度基本上是:好,我很高兴有人在尝试不同的软件构建方式。我不确定自己现在是否准备好了去那样做,但我确实认为,未来会有很多事情改变,而这种实验本身非常有意思。所以我的结论就是:我很高兴他们在做实验。
主持人:那如果有人在你们的仓库里,直接提交整段由 agent 生成的内容,你们会拦吗?
Charlie Marsh:会,我们会。对。所以我们现在有一套 AI policy。它的目的不是反 AI——因为我们自己确实大量使用 LLM开发——但更重要的是,它是为了回答一个问题:我们怎样保留那些真正有用的贡献,同时过滤掉那些对仓库整体来说净负面的互动?
因为一个贡献者跑过来,贴一段 agent 写的评论;然后我们问他一个问题;接着他又把 agent 的回复原样贴回来——这里面其实没什么价值,对吧?我们自己也可以直接去问 agent。我们真正想保留下来的,是人类贡献者能带来的那些有用东西:洞见、复现方式、以及那些能帮助我们把问题喂给自己的 agent 的信息。在 GitHub issue 里跟一个 LLM 来回对话,对我们来说其实没有意义。
所以我们的政策大概是:你必须理解你贴进来的内容,或者你提交的内容。听起来像是个很低的门槛,但其实并不是。
主持人:你们怎么判断、怎么执行这个政策?
Charlie Marsh:如果我们看不出来,而它确实是 agent 写的,那我猜也没什么问题,对吧?但至少现在,还是有很多明显的痕迹。比如它比人类会写得详细得多,细节多得过头,格式化得太过了,用一些人类不太会用的术语,发明一些奇怪的行话,塞大量链接——最明显的信号就是:它在很多根本没必要的地方投入了过量精力,而这种方式通常不是人类会做的。这样的内容,几乎总是 agent 写的。
不过这件事确实很难处理。Zig 的 LLM 政策就比我们严格得多,基本上就是:项目里不接受 LLM 写的代码,或者干脆不接受任何 LLM 辅助代码。这和我们差异非常大。我的意思是,我现在所有的 PR 几乎也都是 LLM 写的,对吧?LLM 写的。
他们还写过一篇博客,提到一个概念,大意类似“贡献者扑克”——也就是你其实是在对贡献者下注。过去在开源世界里,一个贡献者来了,他可能对项目还不熟,但你能从他身上看到潜力,或者能感觉到他真的很投入、很感兴趣。你给他反馈,他吸收反馈、修改 PR;下次再提 PR 时,他已经从上次反馈里学到了东西,对吧?
这里也是一样的:人会从反馈中学习,这种学习会复利,他们会越来越好,未来甚至还能去指导别人,对吧?所以你是在投资于人。从开源角度也是一样:你会希望把精力投入在优秀的贡献者身上,让他们逐步成长,未来既能帮助别人,也能成为自己的维护者。
但对那些由 agent 写出来的 PR 来说,这种机制基本上就失效了,因为人们实际上并没有学到什么。一个人可以提交一个 agent 写的 PR,你留下评论,然后他把评论贴回 agent,再让 agent 改一版,最后合并。整个过程里没有任何反馈复利。
所以我其实非常理解那种观点:维护者和贡献者之间的契约,现在已经和过去很不一样了。
另外,还有一个也不算新鲜的观察:提交一个“看起来合理”的 PR,成本已经降到零;但审查并验证一个“看起来合理”的 PR,成本还是一样高,甚至很高。尤其是像我们做的那些项目——我不是想夸大它们,只是它们真的很难。TY 是我们的类型检查器,可能是我做过的技术上最难合并改动的项目之一,因为它的架构非常复杂,问题域本身也非常复杂,真的特别特别难。
所以,如果有人给 TY 提一个“看起来合理”的 PR,他可能只花了两分钟,而我们可能要花一个小时才能把它看明白。于是这就给开源带来了非常糟糕的互动结构。我也不知道这个问题该怎么解决。我觉得在我们找到解决方式之前,它大概率还会继续恶化,直到某个时候也许会好起来。但这确实是我们在自己项目里已经切身感受到的事情。
主持人:听起来,review 正在越来越成为瓶颈。像 Bun 那次重写,你知道有没有人类真正看过那些代码吗?还是说就是——
Charlie Marsh:不,我觉得那应该没有经过人类审查。
主持人:对。所以就是 YOLO 式地把整个东西重写掉。
Charlie Marsh:对。某种意义上说,我觉得他们确实有——而且我也很期待——至少在我们录制这期的时候,Jared 还没把博客发出来。他一直在开玩笑,说那篇博客写得比重写本身还久。
但我其实真的很想看那篇博客,因为我觉得他们对于“怎么做这次重写”应该有一套相当成熟的方法,这会很值得读。它不是那种“给一个 prompt:把这段东西改写成 Rust”就完了。我从那个 PR 里看到的印象是,整个重写似乎分成了不同阶段,而且每个文件都要经过几轮不同的阶段,每一阶段还有对应的验证方式,用来判断它是否正确。
所以答案依然是否定的:我不觉得有人类真正读了大量那部分代码。基本上是不可能的。
主持人:如果今天我们俩都一致认为,Zig 在客观上比 Rust 更好,然后你团队里有人说:“我可以把这些代码都重写成 Zig。” 你会拦吗?
Charlie Marsh:我觉得我会拦。至于我这么做对不对,我不知道。但我觉得,至少以我们团队现在的状态来说,我们还没有准备好。而且未来也许会准备好,也许最终我们真的会这么看。但就现在而言,我觉得我们仍然相信:深入理解自己的代码、工具链和生态系统,依然非常有价值。所以我会说,我们现在不想这么做。当然,这可能也要看具体项目。我也不敢说得太绝对。
主持人:你们做出来的这些东西,几乎都比当时已有的方案快一个数量级。Rust 在那张图里,到底贡献了多大一部分?
Charlie Marsh:我觉得这得看具体项目。比如在 Ruff 上,很大一部分确实就是 Rust 带来的。然后随着时间推移,我们又在那之上做了很多优化。你甚至可以去看项目历史。希望现在还是这样,但总体上,项目会随着时间越来越快——当然有时候也会出现回退。
也许我换个说法:同样是用 Rust 写 Ruff,你可以有很多不同写法,而它们的性能特征会非常不一样。所以我的总体看法是,Rust 更像是一个下限,或者说一个基础盘——你一旦使用 Rust,默认性能起点就会显著更高。但即便如此,真正深入思考性能和设计,仍然能带来更多收益。
如果你把同一个程序分别用 Rust 和 Python 实现——尽可能接近的同一种实现——那 Rust 版一定会更快。但在 Rust 这个前提之上,你依然还能继续优化它,也许再快很多倍——我不确定是不是 10 倍,但我的重点是:哪怕都是用 Rust 写的,里面依然有巨大的空间,去思考怎样让它更高效,怎样去写真正高性能的软件。
再说一次,我刚开始做 Ruff 的时候,目标之一也是顺便学 Rust,所以我当时确实写了很多“烂代码”,这没关系。因为我已经把一个真正对别人有帮助的东西发出去了。但它随着时间推移变得越来越好,我们也让它越来越高效。
而在 UV 上,除了 Rust 本身之外,还有更多架构层面的创新。因为 UV 是包管理器,而包管理器会处理大量 I/O:通过网络下载文件、解压、写磁盘、挪文件。linter 不太需要做这些——当然它也要读你所有文件,但总体 I/O 量没那么大。包管理器则基本上就是 I/O 密集型场景,同时你还要尽可能高效地完成这一切。
所以在 UV 上,更多是——当然 Rust 也很重要——但我确实觉得,UV 里有更多是我们在架构上做的事,或者说我们在性能层面深思熟虑的设计。比如缓存设计就是非常有意为之的。正是这种设计,才使得在同一台机器上反复安装同一个包时,速度几乎是瞬时的,因为我们在缓存布局、以及从缓存安装到项目中的方式上,做了特殊设计。
它基本上意味着:如果某个包你之前已经安装过,那么再次安装它的代价会极低——无论从磁盘占用还是时间上看,都是如此。这和之前其他 Python 包管理器的设计方式都很不一样。
所以说到底,还是得看具体项目。但我总体上确实觉得,不管你是在 Rust 里写代码,还是在 Python 里写代码,性能这件事永远值得你去思考。你总能把东西写得更快,也总能把它写得更慢。哪怕你是在 Python 里写,只要你在性能和设计上想得更深入,也完全可以把它提速很多很多。所以最终总是一个混合因素,只是不同项目里占比不同。
主持人:OpenAI、Anthropic、Cursor 和 Vercel 都在用这个产品来提升他们的工作效率。它解决的问题是:当你在构建 SaaS 或 AI 产品,并且想卖给其他公司时,你会遇到很多必须满足的企业级要求,比如 SSO、SCIM、RBAC、审计日志。这些都需要集成时间,但它们又不是你应用的核心重点。WorkOS 提供了一个 API 层,只需几行代码,就能帮你满足所有这些要求。比如说,你有一个新的 SaaS产品,想卖给其他企业,WorkOS 就能帮你补上这些关键功能缺口。你可以访问 workos.com 了解更多并开始使用,也感谢他们支持我的工作,并在这个项目期间赞助这档播客。
你能不能讲几个在项目里实现过、你觉得技术上最有挑战或者最有意思的点?
Charlie Marsh:我特别喜欢我们团队里的 Andrew——也就是 BurntSushi,ripgrep 和很多其他项目的作者,他是个非常厉害的工程师——做的一个关于“版本表示方式”的优化,真的很酷。
简单来说,如果你想象一下解析并安装一个非常复杂的 Python 项目,你会发现程序内部要反复解析、创建大量版本号,比如 1.0.1、1.0.2 这种。我们会在程序里构造非常多这样的 version 对象。结果发现,考虑到规模之大,仅仅是为这些对象分配内存,本身就已经很昂贵了。
然后他想出了一种表示方法:对于百分之九十几的版本号,我们都可以只用一个 u64 整数来表示。这样效率就高太多了。围绕这个优化做出来的 benchmark,以及它本身的实现,我都觉得特别酷。那可能是我读过最酷的 PR 之一。
而在 TY——也就是我们的类型检查器——里,则有大量非常有意思的性能工作,尤其是为了让它支持增量更新。TY 的目标是同时成为一个类型检查器和语言服务器,所以整个系统从一开始就是高度增量化设计的。
这里的核心思想是:如果你在文本编辑器里打开一个文件,你其实不一定希望为了给这个文件做分析,就先把整个项目、所有依赖、项目里的每个文件类型检查一遍,因为你根本不需要。所以问题就变成:怎么让系统做到这一点?我会把这种方式叫作“懒”,你就是要尽量懒。
但除此之外,另一层需求是:如果你打开着一个文件并编辑它,或者你同时打开两个文件,只改了其中一个,你就只希望重新计算那些必须重算的部分。你不希望又把整个代码库完整地重新类型检查一遍。特别是当你在一个像 PyTorch 这样的大项目里工作时,你打开两个文件、改其中一个,总不能每次都等两秒钟,系统才重新把整个项目检查一遍并给你新的分析结果。
所以整个系统是围绕 query 来构建的,这一点很有意思。更多是一种宏观架构设计。我们是构建在一个叫 Salsa 的框架之上的,Rust Analyzer——也就是很流行的 Rust 语言服务器——也是基于它构建的。而现在,无论是有意还是无意,我们都已经成了 Salsa 的非常重要贡献者之一。整个系统围绕它来建,这在架构上一直都很有意思。
主持人:明白了。所以它既是 lazy 的,不会去检查整个代码库;同时又是 incremental 的。
Charlie Marsh:对。真正难的是 incremental 这一部分,因为你实际上需要一种方式,去建模代码里所有事情之间的依赖图。然后当你改动某个东西时,我们希望只把数据沿着那些真正相关的节点回流,只更新那些必要的部分。
这件事花了很多功夫,但最后确实成型了。最近我也在大量用 Codex 做优化,因为它在微优化方面往往特别擅长。尤其是在 TY 里,我们还必须非常关注内存,而不仅仅是速度。因为如果你在一个非常大的项目上工作,你不会希望仅仅为了跑一个语言服务器,就占掉好几个 GB 内存。理想状态下,它应该足够高效。
所以我现在花了很多时间,几乎是在持续不断地优化内存占用和性能。我甚至可以直接设一个目标,比如:“试着把这个项目里 Salsa 的内存占用降低 1%。”它通常都很擅长给出一些相当合理的改进方案。
所以我觉得这特别酷,因为某种意义上,你几乎可以对软件进行一种持续性的——我不想把话说得太大,不想叫它“自我优化”——但确实有点像“持续优化”。我很享受这个过程。不过这里大多数工作,还是在想办法找到更省内存的数据表示方式,或者从更宏观的层面做重构,去修复那些病态性能问题。
主持人:像刚才那个版本号优化,能想到把它限制为 u64 来表示——如果回头看这些更有创造性的优化,它们都还是“人类时代”的产物。你觉得如果只是,比如跑一遍 Codex,然后跟它说“别搞坏功能,但把内存降下来”,你会相信它自己能想出这种点子吗?还是说这种优化已经高出目前能力一个层级了?
Charlie Marsh:这是个非常有意思的问题。至少就我的经验而言,如果你只是让这些工具去降低内存占用,或者把性能提升一个中等幅度,它通常会先给你一些边边角角的小优化,而不是更大的重构,或者对系统设计本身做重新思考。但如果你通过 prompt 和 agent 协作,是可以一步步逼近那种更大级别的改造的。
所以,如果你进一步追问:“我们是不是应该从更大的层面想想,为什么非得用现在这种方式来表示数据?” 那么你就有机会走到更大的点子上去。像刚才那个版本表示法,也许如果你是通过不断提示来推进,最后确实可能抵达。比如你先说:好,我们先 profile 一下,看看时间到底花在哪。然后它可能最终会回来说:大量时间花在版本解析、版本对象分配之类的事情上。接着你再追问:那我们怎么才能把这些表示得更紧凑?
而它大概率会先从表示层面做微优化开始,比如“这个字段可以压缩一下”“那两个字段可以合并一下”之类。但如果你持续往前推,我觉得它是有可能走到那个层级的。只是,那不会是它默认第一时间就想到的事。
HashiCorp 的创始人之一 Mitchell Hashimoto,现在在做 Ghostty。他上周还是这个周末发过一篇很有启发的东西,讲的是他写的一个渲染器。大意可能会被我转述走样,但基本意思是:他故意先写了一个非常糟糕的渲染器,然后让一个 LLM 去优化它,结果性能提升了大概 10 倍。他说:“太好了,对吧?” 实际上并不是。因为他自己手写的版本,快了 100 倍。
如果你不自己动脑,不从第一性原理去思考“它理论上应该有多快”“这个系统本来应该怎么工作”,那你最后只是把一堆不断累积的——我未必想把它叫“糊代码”,但差不多——你就是在不断堆这些东西,然后说:“哇,我把它提速了 10 倍!”可实际上,它本来应该提速 100 倍才对。
主持人:如果你生成的是彻底的垃圾,那还比较容易识别。我觉得更麻烦的是那种灰色地带:部分是 AI 生成的“糊代码”,或者说它也还过得去,但达不到你们以前的标准。你们团队里有没有什么有效的方法,能对付这种灰色地带的 AI 糊代码?
Charlie Marsh:所以我确实觉得,现在要做一个职业生涯早期的软件工程师会非常困难。只是我现在接触这类工程师不算多,因为 Astral 本身团队很小,而且我们招人通常也偏资深。所以我很难具体想象:如果是我,我到底会怎么学?我的迭代闭环会是什么样?
感觉实在太容易掉进那些使用 agent 时会出现的坏模式里了。我也在很大程度上改变自己的写软件方式。团队里也有人跟我说过——而且我很高兴他们愿意这么跟我说——因为我现在大量在用 agent,而且某种程度上也在推动团队更多地去用 agent。
我之所以这么做,部分原因是:我们是在给软件工程师做工具,而现在我们的很多用户也已经在用 agent 了。如果我们所有做得好的事情,都建立在“我们足够理解用户以及他们的工作方式”这个前提上,那么我们自己大概也应该去用 agent,这样我们才知道“用 agent 做软件”到底是什么体验,进而才能为用户做出更好的工具。这是我一部分动机。另一部分动机则是,想看看它到底会怎样改变你的工作方式。我觉得自己现在确实交付了更多东西。
但这个过程并不轻松。团队里有人直接跟我说——我觉得这很好——他们说:“以前你提 PR 的时候,我其实不太需要花很多力气审,因为我对你、对你的工作质量很有信心。可现在,你提 PR 时我必须非常仔细地审,因为那已经不是你自己写的了,而是 agent 写的。”
我当时就觉得:哇,这很有意思。而且他们完全说得对。同样的事也会发生在我自己身上。如果我晚上提了一个 PR,第二天早上起来再去看,我会想:等等,这也太糟了吧。你懂我意思吗?人真的很容易被自己骗过去,以为一份其实达不到你原先标准的工作,也已经足够好了。
而我们其实还不知道该怎么彻底解决这个问题。我自己也在不断学习、不断变好。我觉得自己现在比大概今年 2 月的时候要好很多。那时候我几乎已经完全沉迷 agent 了。现在我稍微——但我的重点是,当团队里有人跟我说那句话时,那对我是个非常强烈的时刻,我会意识到:哇,你说得没错。而且我在团队其他人的工作里也会看到同样的问题。这并不只是我一个人的情况。它确实把很多东西都彻底打乱了。
我希望最终能走到一个世界——虽然我们现在还没到,而且我也不知道能不能到——在那里,如果你提一个 PR,而且它已经全绿了,那么它被合并的概率应该非常高。因为那意味着:你已经对大多数真正重要的东西做了自动化验证。
我们在自己的项目里已经有不少这样的东西,而且一直在努力增加,我觉得这很有帮助。尤其是在 TY 里,我们有大量 benchmark,会在每一个 PR 上通过 Valgrind 和 CodSpeed 运行。所以我们会在每个 PR 上测试内存占用、模拟时间和 wall time。除此之外,我们还有一个非常大的生态测试套件。基本上每次你提一个 PR,我们都会在一堆生态项目上跑改动前和改动后的结果,然后生成一份 diff 报告,把所有新增或消失的诊断信息、错误等都列出来。
所以这些年我们一直在做越来越多这样的事情。说实话,这些东西的重要性甚至早在我们开始用 agent 之前就已经很高了。没有这些东西,我们本来也很难继续构建这些项目。我的重点是:我希望有更多自动化验证,并不断把这件事做得更好。
这还包括一些事情,比如现在我们基本默认:团队里任何人提 PR 之前,大概率都已经先让 Codex review 跑过几轮了。这基本上已经成了默认前提。
主持人:这个流程本身也可以自动化吧?不过所谓 Codex review,本质上就是让 agent 去 review 一遍代码、做二次检查?
Charlie Marsh:对,就是跑 Codex,然后执行 slash review。
主持人:明白。
Charlie Marsh:就这么简单。它也没多复杂,但因为现在如果有贡献者提了 PR,这是我们做的第一件事。因为它通常确实能发现一些不错的问题。
所以我觉得,一类办法是:你怎么建立更多自动化系统,帮助确保事情是对的。这也包括像持续改进 agents.md 文件。如果有一些东西是你在 review 里反复给反馈、但 agent 总是学不会的,那就尽量想办法把这些经验显式传达给库。甚至可以固化成 skill。我们团队里就有一些共享 skill,诸如此类。顺便说一句,这些其实都不复杂,很朴素。
另一类问题则是:我怎么确保自己真的投入了必要的努力,去保证我提交的是一个高质量 PR?对我来说,这意味着我真的应该理解 PR 里的每一行代码——我知道,这听起来像是一个低到不能再低的要求,但确实如此。
另外,我也会尽量自己在 GitHub UI 里把每个 PR 再审一遍。这其实是我一直都觉得很有帮助的事。你如果真的打开 PR,点进 Files,用一个 reviewer 的视角把它读一遍,通常会发现一些在本地 diff 里很容易漏掉的问题。我一直觉得这价值。
还有一点,是尽量把“技能”编码出来。某种程度上这也属于第一类,但比如把那些我自己总是犯、agent 也总是犯的错误,尽量整理成 skill。最近一个例子是:我发现自己经常收到一种 PR 反馈,形式基本都是:“这里这个条件、这个 if 语句,到底是想捕获什么情况?因为我把它注释掉之后,所有测试都还通过。”
于是我就想:好,那在我提任何 PR 之前,大概应该加一道检查,让 agent 先过一遍:这些条件现在到底还有没有意义,还是说它们只是上一次重构后遗留下来的废代码?
所以,我也还在学习,但这些就是我现在正在做的一些事情。
对,我还是觉得,现在真的是一个很难的软件构建时代。很长一段时间里,我其实一直有个担心:AI 会让我们更高效,但编程本身可能会变得没那么有趣,因为我是真的很喜欢编程。
我当时会想:完了,以后我是不是得把所有时间都花在 review 代码上,天天给这个总是出错的笨 agent 写 prompt?虽然它整体上可能还是更高效,但这种工作听起来一点都不好玩。可现在我的感觉已经比几个月前好多了。我也不完全确定为什么。
我想一方面是 agent 变强了,工具链也变好了,我自己也更适应了。另一方面,是我越来越能体会到:和 agent 一起工作,到底打开了什么样的新可能。现在做实验的成本低得惊人。
有太多我一直想试的事情、一直想回答的问题,现在几乎可以立刻得到答案。因为在软件开发里,最难的事情就是反馈环太长。如果你想验证一个想法,你得先花两周把它写出来,然后部署、收集数据,最后发现:啊,我错了,方向不对。而现在,我可以在一个下午跑五个这样的实验。
举个有点傻的例子:在 UV 里,我们几乎所有东西都是 snapshot test。也就是说,我们的测试方式基本就是运行 UV,然后验证输出。整个程序几乎都是这么测的。
这意味着我们有很多测试,也意味着我们有很多测试输出。而这些测试输出实际上就存放在测试文件里。比如我们有个 Rust 文件,像 lock.rs 这种,用来测试 UV 的各种 lock 场景。它特别特别长,其中一个原因就是,所有 lock 输出的快照都直接放在测试里。
然后我就会想:嗯,如果我们把这些 snapshot 放到单独的文件里,会怎么样?编译会不会因此更快一点?因为现在它们 technically 都是 Rust 代码,那如果这些内容不再在 Rust 文件里,会不会让构建更快之类的。
这件事我一直都想试,但如果让我自己来做,这个实验会非常痛苦,因为你得把所有那些测试都改掉。于是我就让 agent 在后台帮我做了,同时我去做别的事情,然后很快就拿到了很多数据。最后答案是否定的——不过也不完全否定。更准确地说,如果你只是反复修改 snapshot 输出,那么这样做其实是有帮助的。因为这些输出放到别处之后,你就再也不需要为了它们重新编译整个程序了。总之,它影响的是这个环节。
我的重点是:我现在整天都在跑这种实验,尝试那些过去很难做、很贵才能回答的问题。而真正把一个实验结果变成生产级方案,这中间当然还是有真实工作的。但一方面是工具和 agent 在变得更强,另一方面是有许多以前我根本做不到的事情,现在已经能非常轻松地做到了。
第三点是,我越来越意识到:我从“做软件”中获得的很多价值,其实并没有失去。因为其中一部分价值来自于认真思考数据结构该怎么布局;还有很大一部分价值来自于合并一个 PR,真正解决了用户的问题。我从“修好某个东西、把某个东西变得更好”这件事本身获得很大满足感,而不一定只是从“亲手把代码敲出来”这件事里获得满足。
所以我非常能理解那些觉得“和 agent 一起工作,好像失去了什么”的人,因为我自己也确实有这种感觉。但和几个月前比起来,我现在对“作为软件工程师与 agent 共事”这件事,心态已经好很多了。
主持人:你刚才提到性能优化时说过,如果只是把 Codex 放出去,它往往更擅长做这些局部优化,但对一些——至少在今天看来更依赖人类创造力的——系统级优化,它还不太行。这让我想到你发过的一条推文。你说:“一想到如果没有足够的软件工程经验,我会用这些工具产出多少垃圾,我就有点担心。”
Charlie Marsh:我现在依然很担心这个问题。
主持人:对。这让我想到你刚提到的 Mitchell Hashimoto 那条推文——“Codex,你去把这个做了”和“你把 Codex 当成一个工具来驾驭,并不断把它往正确方向推”,这两者之间差别非常大。
我也挺想听听你对这个问题的看法,因为那条推文传播得很广,我觉得很多人都在思考这件事。
Charlie Marsh:成为一名优秀的软件工程师,比以往任何时候都更有用了。现在依然是这样:我觉得我们团队里工程能力最强的那些人,即便是在使用 agent 这件事上,也仍然是最有效率的。
有意思的是,现在大家经常会谈论一个词,叫 token maxing。我不知道你熟不熟这个说法。比如说,有些地方会讨论要不要搞一个排行榜。这事最近在 Twitter 上也很常见——有些公司有 token 排行榜——然后它会制造出非常糟糕的激励,让人为了上榜而拼命多用 token。
好玩的是,token 使用量本身,在内部通常确实是可以查到的。虽然不一定真的有排行榜,但你是能看到谁用了多少 token 的。这件事其实挺值得看一眼。因为我并不在乎团队里谁用的 token 最多。甚至有些同事会刻意提醒自己不要在意自己在 token 排行榜上的位置,我觉得这很好。我真的不在乎。他们只要把工作做好就行,用不用很多 token,我都不在乎。
但去看这个排行榜依然挺有意思,因为团队里一些最有生产力的人,确实也用了很多 token,对吧?这里是有相关性的。我不是说这一定是因果关系,但我确实觉得,很多优秀工程师都能非常有效地使用 agent,并希望借此放大自己的能力。
主持人:你用 Rust 做出了这个概念验证,而且反响很好。但你为什么会决定围绕它创办一家公司?融资过程又是怎么进行的?你的动机是什么?
Charlie Marsh:对。所以我后来离开了 Spring。其实是一个非常亲密的朋友说服我离开的,他差不多同一时间也离开了 Meta,然后跟我说:“我们应该一起创办家公司。” 我当时就说:“好吧,行。”
当然,事情也没有那么简单。但我确实离开了。然后我们就某种程度上进入了所谓的“创意迷宫”,开始思考:我们到底想做什么?我们一开始其实并不知道自己想做什么,于是花了很多时间去探索一个“想法的维恩图”。有些东西他感兴趣、我不感兴趣;有些东西我感兴趣、电我不感兴趣;中间还有一些交集,我们就去探索那些交集中的东西。
但与此同时,我在所有空闲时间里都在做开发工具,因为那才是我真正觉得特别有意思的东西。而那条路跟他并不完全契合。那段时间我正在做 Ruff,也在做 GitHub 上还能看到的另外几个项目,可能没那么有意思,但我当时确实在试很多不同的方向。我对 WebAssembly 很感兴趣,也写过一个某种意义上的 TypeScript CI/CD 工具包,有点像你用 TypeScript 写流水线,然后它会把它们转译成 Docker。我当时会想:“哦,这可能挺酷。” 总之,我当时在做很多东西。
到了那个阶段,我决定开始和投资人建立关系,但我并不打算立刻融资,因为我还不知道自己到底要做什么。我的思路是:先试着认识一些愿意投这类东西的人,等三到六个月之后,我再回头联系他们,说:“嘿,我们之前聊得很愉快。现在我正在做 X。”
这就是我当时的思路。后来我认识了一些投软件基础设施和开发工具的投资人,聊得也不错。问题在于,事情有时候会发展得非常快。所以我其实并没打算这么早开始融资,但最后某种程度上是被“说服”了。与此同时,Ruff 也在快速增长,于是我就逐渐被说服:这里面确实已经有足够多的东西,足以支撑一家公司。这整个过程其实很有意思。
我当时的想法是:我还不太确定这东西怎么变成一家公司,但我后来基本上被说服了——这里面已经有足够的基础,可以围绕它建立一家真正的公司。
主持人:是投资人说服了你?
Charlie Marsh:对。对此我其实很感激。
另外一件让我放下顾虑的事也挺有意思:“如果你发现这并不适合你,你是可以把钱退回去的”。我其实觉得这句话特别高明,因为我九成九本来也不会真的这么做,但它确实让我的心里负担小了很多。
所以,中间有很多绕来绕去的细节,但总之,后来我逐渐开始更全职地做 Ruff。与此同时,我和那些潜在的早期投资人之间也形成了一种协作关系:我们会进行很长时间的交流,讨论我的各种想法。然后慢慢地,这件事就变得很清楚了:这里面已经足够成熟了。我还写了一个大致的产品路线图,说实话,其中很多内容到目前为止都和我们后来真正做出来的东西大体一致。再后来,事情基本上就变成:是的,我们想投你;这是我们愿意给出的条款。我当时就想:哇,原来事情真的要发生了。
所以整个过程挺有意思,因为我以前从来没做过这种事。我之前的整个职业生涯都只是一个 IC 软件工程师,然后突然之间,我要开始创业了。
有趣的是,我其实觉得“全力投入去创办公司”这个决定本身并没有那么大压力。反而很多压力是在后面,公司开始真的运转起来、开始成功之后才来的。因为一开始,我某种程度上没什么可失去的。但随着公司开始变得成功,我会想:哇,公司好像真的在运转了。那接下来会发生什么?而且我还有 20 个人的团队在依赖我。
所以我不知道。至少对我这个创始人来说,我其实挺规避风险的,但我反而觉得,创办公司本身比后面那些需要处理的复杂局面更容易做决定。整个过程发展得非常快,也有些出乎意料,而且某种程度上是和投资人形成了一种共生关系,这些都是我之前没预料到的。
主持人:我还真没想到会是投资人主动——因为我原本以为一般都是创始人特别想融资,到处说“请投我”,但你这里反而是投资人来——
Charlie Marsh:我觉得这件事可能有一百万种不同发生方式。我们后来一共融了三轮:种子轮、A 轮和 B 轮。其实 A 轮和 B 轮我们甚至都没有正式对外宣布。我的意思是,它们最后在收购博客里被提到了,但整体情况有点复杂。简单说,就是我们一直没真正腾出手来做这件事。
主持人:你们为什么不公布?因为这不是对招聘很有帮助的营销吗?
Charlie Marsh:这当然是好的营销。但基本上总会有一些理由让我们觉得:再等等吧。然后对我来说,它看起来也总像是一件挺费劲的事。再加上我会觉得:说实话,它从来都不像是最重要的事情。因为我们当时正在做很多产品,公司发展也不错,我就会想:我们其实不太需要这波营销。
后来我们确实计划过要宣布,但还没来得及,我们就被收购了,所以最后也没发。
不过那三轮融资基本都是抢投式的,都是投资人主动发起的。我们确实很幸运,因为公司发展得不错,大家愿意投。但从我的角度看,这种位置也挺有意思的。
我觉得我一直都偏保守,不是那种会非常积极主动地跑出去大规模融资的人。但我们也确实很幸运,恰好在做一些让大家非常有信心、也非常愿意下注的东西。
主持人:这大概也让你在谈判里占了很大优势,因为最好的位置之一就是:你其实愿意转身走人,你并不非要这笔钱不可。甚至从定义上讲,你一开始都不是为了这件事去的,是他们来找你,说“请收下这笔钱”。
Charlie Marsh:这话倒是没错。虽然我不会说自己是个很会谈判的人,但对,我至少确实算是手里握着一副不错的牌。
不过我们在投资人这件事上真的非常幸运。我们的投资人都特别棒,非常支持我们,而且和我想建公司的方式高度一致。几乎没有冲突,也从来没有什么“你必须做这个”“你必须做那个”的压力,就是纯粹的支持。
也许唯一一类持续性的反馈是:我其实本可以更激进一点,比如扩张得更快、增长得更快、多做一些事情。但我自己其实很喜欢我们当时的运作方式。总之,我们和投资人的合作体验非常好,所以我一直觉得自己挺幸运的,因为这件事完全可能走向另一面。
主持人:我能理解投资人投资是期待未来的收入回报,但你们这家公司到底是怎么赚钱的?因为你们的软件不是免费送的吗?
Charlie Marsh:对。所以我们在去年 8 月推出了一个商业产品。它叫 Py——本质上可以看成是 UV 的托管版配套产品。很多 UV 用户不会使用公共 registry,而是会购买私有 registry 软件,可能是出于安全考虑,也可能是因为他们需要发布自己的私有制品,诸如此类。
于是我们自己做了一个私有 registry,而且对 UV 做了很多一等支持。这样一来,我们可以解决大量在 issue tracker 里看到的、用户使用其他方案时遇到的问题。另一方面,因为客户端和服务端都是我们自己做的,所以可以做一些纵向一体化的优化,让整个体验特别快、特别好用之类的。
我们围绕这个卖了一段时间,收入增长其实还不错。当然,它不是那种——你知道,现在 AI 时代里大家总在讲“史上最快做到一亿美元收入的公司”之类的故事。我们不是那种速度。但我们确实成功卖进了一些大型企业。
有意思的是,因为我们先有开源项目,而且大家都在用我们的开源工具,所以我们的销售漏斗其实质量非常高。我的表述会是:我们的客户数量虽然不算特别多,但客户质量极高。
所以我们想赚钱的总体逻辑一直是:继续做开源,工具本身保持免费、开源、完全非商业化;然后我们围绕这些工具,去做一些“当你开始用我们的工具后,自然接下来就会需要”的软件。比如你在用 UV,那么你大概率就会遇到一些我们可以通过私有 registry 帮你解决的问题。这样一来,漏斗前端就是使用 UV 的人,等他们出现这些问题后,我们再卖 registry 给他们。而 registry 也只是一个更大平台中的一部分。我们原本的目标,是围绕 Python 去做一个平台,卖很多不同的东西,有点像“Python 云”。那是我们当时在构建的方向。
而收购带来的一个很酷的变化是:这个平台中的一些能力,现在我们反而可以直接免费开放给所有人。举个例子,这个平台里有很大一部分工作,是围绕 Python 和 GPU 的生态做的。因为在 Python 社区里,有很大一部分用户会把 Python 用在 GPU 场景里,比如 PyTorch,以及围绕 PyTorch 构建起来的一整套软件生态。
出于各种原因,这一套的使用体验并不算好。很多时候它很难装、很难用,也很难选对版本;还跟你的 GPU 型号、安装的 CUDA 版本等因素强相关。于是我们当时是用一种特定方式去尝试解决它:我们做了自己的发行体系。你可以把它类比成一种 Linux 发行版,因为我们会预先把很多彼此兼容、能协同工作的东西都构建好,然后把它作为能力提供给客户。
而现在,我们实际上可以把这部分东西直接免费开放给所有人,因为我们不再需要作为独立公司去构建一个独立商业模式了。我们现在只需要专注于做出很棒的工具,去带动一个更广泛的生态。所以后面还会有更多动作,但这确实是一个让我很兴奋的点。
主持人:作为第一次创业、而且还是工程背景出身的人,有没有哪类经验让你觉得很意外?如果有工程师也准备走创始人这条路,你会特别想跟他说什么?
Charlie Marsh:创业的方法有太多种了。老实说,我其实不太喜欢别人给创业建议,因为这个行业里太多都是“幸存者偏差”。你完全可能去听两场分享,然后两个都极其成功的创始人给出完全相反的建议。你听完只会想:那我到底该怎么做?
所以我更倾向于认为:你得搞清楚什么是真正适合你的。这里面有大量决策要做,比如要不要线下办公、要不要远程,对吧?关键是你要有自己的原则,并坚持它。两种方式都可以很好。我们整个公司就是远程搭建起来的,而且效果非常好,真的很棒。那我认不认同线下办公也有好处?当然认同。但如果我们必须线下办公,我能不能招到后来那支团队?绝对不可能。
所以这类事情永远都存在取舍。你只能尽量有原则地判断,搞清楚什么和你自己真正契合。像这样的决策有一百万个,你都得认真想清楚。
从很多方面说,我觉得自己很幸运,能够按自己想要的方式来经营公司。比如我会尽量把花在融资和投资人身上的时间压到最低;我会优先选择那些我真正信任的投资人,而不是去和一大堆投资人周旋、互相压价,只为了拿到最好的条款。我的目标一直是:找到一个我真正信任的合作伙伴,拿到一份足够好的条款,然后尽快推进,好把融资这件事本身占用的时间降到最低。
所以,想清楚你真正看重什么,不要太执着于“你觉得自己应该怎么做”,而是多想想“你真正想怎么做”,以及什么样的方式才真正适合你的工作方式。
主持人:你最推荐的一本书是什么?
主持人:不管是技术类的,还是作为创始人对你有帮助的都行。
Charlie Marsh:我其实不怎么看太多技术书。但我确实看过几场对我影响非常大的演讲,这可能算是另一种形式。
主持人:是哪些演讲?
Charlie Marsh:Zig 的作者 Andrew Kelley 有一场关于“面向数据设计(data-oriented design)”的演讲,我从中学到了很多。其实不仅仅是学到具体内容,它甚至在某种程度上启发了我,让我开始用一种完全不同的视角去看软件。所以我到现在还是会经常想到那场演讲。
主持人:挺有意思。
Charlie Marsh:对,像这种就很棒。真的非常好。它大概是讲——不过我有阵子没看了,可能会讲错——很多内容基本上是在讲他们在 Zig 编译器里做的一些设计决策,以及他们如何思考内存、分配等问题。
我是在自己系统编程生涯还比较早期的时候看到那场演讲的,当时就有一种感觉:哇,这些人真的非常在意他们正在做的东西。我当时想,这太酷了。
主持人:对。最后一个问题,如果你可以回到职业生涯最开始的时候,比如大学刚毕业那会儿,以你现在知道的这些事情,你会给那时的自己什么建议?
Charlie Marsh:我也不知道。要在没有强烈“事后诸葛亮偏差”的情况下给自己提建议,真的很难。
不过,我想还是有一些事情,我可能会告诉自己:你做的是对的,不要那么担心。但如果把人生重来一千次,它到底会怎么发展,也很难说。
比如说,我离开学校后去了 Khan Academy。那时候我心里确实有一部分会想:哇,我是不是应该去一家更典型的大科技公司?我以后会不会很后悔,错过那样的经历?再比如,Khan Academy 的薪水其实不错,但大厂薪水肯定更高,我还看着朋友们拿到巨额奖金之类的。我心里会有点不安:我是不是也应该这么选?
但我觉得,从长期来看,那次选择其实回报非常大。我的意思是,也许去大厂也会很好,但我后来得到的那种经历,恰恰是我真正想要的,而我当时做这个决定,本来也是出于我认为正确的理由。
所以我觉得,这里面很多事情都像是——我也不知道——一切自有其因吧。只要你是基于原则做决定,我觉得最终还是有可能走通的。
主持人:我们一开始聊的时候,你说你之所以会走上这条路,是因为你尝试了很多不同的生态,所以才有了更宽的视角。我猜如果你当时去了 Google,只在 Google 那套封闭体系里工作,你可能就不会有同样的洞察。
Charlie Marsh:对。我其实也会想到 Spring,因为我在那里待了四年半。后来我刚离开,他们就决定做战略转向,最后加入了 Genentech。但我们一开始的目标,是想做一家某种意义上非常疯狂的药物发现公司。我们聚焦在衰老方向,而且是用计算机视觉来做。我们当时想做的事情真的非常、非常有野心。
后来我也会想:啊,这家公司最终没有获得那种巨大的退出,或者我们没有实现最初全部的梦想。那我是不是把时间都浪费了?但最终我会觉得:没有。因为作为早期员工,我经历了公司不同阶段,也因此学到了太多关于如何经营自己公司、以及各种技术上的东西,而这些后来我都带进了自己的公司里。
所有事情最后都像是汇聚成了一条线,把我带到真正热爱的事情上——也就是构建工具——并不断为它输送养分。
本文来自微信公众号“AI科技大本营”,编译:王启隆