Michael Truell · Cursor(Anysphere)联合创始人兼 CEO

Cursor 团队:AI 编程的未来(Lex Fridman 播客 #447)

2024-10-06 · Lex Fridman Podcast (Lex Fridman) · 2h29m · 原文链接

→ 在 AI 访谈库中阅读(可切换中英、记录进度)
四位联合创始人与 Lex Fridman 长谈代码编辑器的本质、Cursor 的技术架构与 AI 辅助编程时代的人机协作。看点:他们坦言创业起点是 2020 年读到 OpenAI 缩放定律论文后的判断——编辑器本质只是'加强版文字处理器',大模型将给它留出巨大的重构空间。

以下是与 Cursor 团队创始成员的对话,他们是 Michael Truell、Sualeh Asif、Arvid Lunnemark 和 Aman Sanger。Cursor 是一款基于 VS Code 的代码编辑器,它为 AI 辅助编程添加了许多强大的功能。它吸引了编程和 AI 社区的关注与热情。所以我想这是一个绝佳的机会,可以深入探讨 AI 在编程中的角色。这是一场超级技术性的对话,其意义远不止于一款代码编辑器——它关乎编程的未来,以及更广泛地说,关乎人类与 AI 在设计和构建复杂而强大的系统时协作的未来。这里是 Lex Fridman 播客。想支持本节目,请查看简介中的赞助商。现在,亲爱的朋友们,有请 Michael、Sualeh、Arvid 和 Aman。好,这太棒了。今天我们请到了来自 Cursor 团队的 Michael、Aman、Sualeh 和 Arvid。第一个问题,一个大而荒谬的问题:代码编辑器的意义是什么?代码编辑器在很大程度上就是你构建软件的地方。今天——或者说很长时间以来——它指的是你对一种形式化编程语言进行文本编辑的地方。对于不是程序员的人来说,可以把代码编辑器想象成程序员专用的、经过强力改装的文字处理器。之所以说它是"强力改装"的,是因为代码有很多结构,因此这个所谓的"文字处理器"——代码编辑器——实际上能为你做很多写作领域的文字处理器一直做不到的事情。这包括方方面面:从给代码中的实际词元(token)提供视觉区分,让你能快速扫读;到让你在代码库中导航,有点像用超链接在互联网上冲浪,跳转到你所使用的东西的定义;再到错误检查,帮你捕捉低级的 bug。传统上,这就是代码编辑器的含义。而我认为,随着构建软件的含义开始变得不同,代码编辑器是什么,在未来 10 年里将发生巨大的变化。我还觉得,代码编辑器就应该好玩。对,这一点非常重要,非常重要。而且这其实是我们决定做什么功能时一个被低估的考量。我们构建的很多东西,做出来之后我们会试用、做实验,然后实际上把它们扔掉——因为它们不好玩。而"好玩"的很大一部分,就是"快"。很多时候,快就是好玩。对,快就是……对,这句话应该印在 T 恤上。从根本上说,我认为吸引很多人在计算机上构建东西的原因之一,就是这种疯狂的迭代速度。在其他学科里,你可能会被资源卡住,甚至被"能不能召集起一大群人"卡住。而编程是一件了不起的事情:只有你和计算机,仅凭这一点,你就能非常快地构建出很酷的东西。对于不了解的人来说,Cursor 是一款超酷的新编辑器,它是 VS Code 的一个分支(fork)。很有意思的一个话题是,听听你们各自与编辑器的渊源。我想你们都曾是 VS Code 加 Copilot 的忠实用户,你们是怎么走到 VS Code 的?这又如何引出了你们与 Cursor 的旅程?对。我想我们中的很多人——其实是所有人——最初都是 Vim 用户。纯粹的 Vim。纯 Vim,对。不是 Neovim,就是终端里的纯 Vim。至少对我自己来说,大约是在 Copilot 发布的时候,也就是 2021 年,我特别想试试它。于是我进入了 VS Code——那是它唯一可用的平台、唯一支持它的代码编辑器。尽管我真的很喜欢用 Vim,但 Copilot 配合 VS Code 的体验,已经好到足以说服我转换阵营。在我们开始做 Cursor 之前,那基本上就是我的默认配置。也许我们该解释一下 Copilot 是做什么的。它是一个非常好用的自动补全。当你开始写一个东西时,它会建议一到两三行代码来帮你补全。这里面有一种有趣的体验,就像你和好朋友关系很铁的时候,朋友会接上你的话。当它做得好的时候,有一种亲密感。也许有比"亲密"更恰当的词,但那是一种很酷的感觉,就像"我靠,它懂我"。然后当它不懂你的时候,就有一种不愉快的感觉。所以存在那种摩擦。但我要说,对很多人来说,"它懂我"的感觉压过了"它不懂我"。我认为 GitHub Copilot 有一个被低估的方面:即使它错了,也只是有点烦人,但并不那么糟糕,因为你只需再敲一个字符,也许它就懂你了;或者你再敲一个字符,然后它就懂你了。所以即使它错了,也不算太糟。对,你可以不断迭代、修正它。对我来说,Copilot 另一个被低估的地方在于,它是第一个真正的 AI 产品,第一个面向消费者的语言模型产品。所以 Copilot 可以说是大语言模型(LLM)的第一个杀手级应用。对。而且它的 beta 版是 2021 年就发布了,对吧?对。那么 Cursor 的起源故事是什么?大约在 2020 年,OpenAI 的缩放定律(scaling laws)论文发表了。那是一个时刻:这个领域看起来出现了清晰的、可预测的进步——即使我们没有任何新想法,只要有更多计算和更多数据,看起来就能让这些模型变得好得多。顺便说一句,关于缩放定律这个话题我们大概能聊上三四个小时,但简单总结一下:那是一篇论文、一组论文和一组想法,说的是在机器学习领域,模型规模和数据规模越大可能越好。是越大越好,而且是可预测地更好。好,这是另一个话题了,不过继续说。对,大约在那个时候,我们中的一些人进行了很多概念性的讨论:这一切会是什么样子?对于所有这些不同的知识工作者领域来说,随着这项技术变得更好,它们将如何被改善?然后我想有那么几个时刻,那篇论文里预言的理论收益开始让人感觉非常具体,让人感觉到:如果你想在 AI 领域做有用的工作,你实际上可以直接去做,而不必去读个博士。感觉当时真的出现了一整类可以构建的、真正有用的系统。我想第一个时刻我们已经稍微谈过了,就是玩早期的 Copilot——那太棒了,很神奇。我想下一个让一切"咔哒"一声对上的重大时刻,是拿到 GPT-4 的早期访问权限。大约 2022 年底,我们开始摆弄那个模型,能力的跃升让人感觉巨大。在那之前,我们在做几个不同的项目。因为 Copilot、因为缩放定律、因为我们此前对这项技术的兴趣,我们一直在捣鼓面向程序员的工具,但都是非常具体的东西。比如,我们在为必须在 Jupyter Notebook 里工作的金融专业人士构建工具,或者在试验能不能用这些模型做静态分析。然后 GPT-4 的能力跃升让人感觉:看,这真正坐实了我们此前预测的理论收益。感觉在那个时间点上,你立刻就能构建多得多的东西。而且,如果我们保持逻辑一致,那感觉真的不只是一个单点解决方案的问题——所有的编程都将流经这些模型。感觉这需要一种不同类型的编程环境、不同类型的编程方式。于是我们出发去构建那个更宏大的愿景。有一件事我记得特别清楚。我的室友是 IMO(国际数学奥林匹克)金牌得主,美国还有一个叫 Putnam 的竞赛,相当于大学生的 IMO,是一个数学竞赛,他也特别强。Shengtong 和 Aman,我记得大概是 2022 年 6 月,打了一个赌:到 2024 年 6 月或 7 月,模型能不能在 IMO 上拿金牌。

IMO 就是国际数学奥林匹克。对,IMO 是国际数学奥林匹克。Arvid 和我也都参加过,所以这事有点切身。我记得当时想:这根本不可能发生。虽然我在某种程度上相信进步,但我当时觉得,Aman 就是在痴人说梦。说实话,我确实完全错了,但那可能是这群人里最有先见之明的一次打赌。后来 DeepMind 的新结果证明你是对的。嗯,技术上讲——

差一分,技术上不算错。

Aman 那时候就对这些东西特别热情。Aman 之前有一件缩放定律 T 恤,他会穿着到处走,上面印着图表和公式。所以你是"感受到了 AI",或者说感受到了缩放。对。我清楚地记得和 Michael 的一次谈话。在那之前,我没有特别深入、批判性地思考过缩放定律。他提出了一个问题:为什么"缩放"不是你所需要的一切?或者说,为什么缩放不会带来巨大的进步?我想我经历了类似"悲伤的几个阶段":愤怒、否认,最后,认真思考之后——接受。我想从那以后,我对进步一直相当充满希望和乐观。我要补充一点说明:我认为这也取决于你会在哪些领域看到进步。数学就是一个绝佳的领域,尤其是形式化定理证明,因为你能获得极好的信号——可以真正验证结果是否正确。这意味着像强化学习(RL)这样的方法可以在这里非常非常有效。我认为你可能会拥有在数学上远超人类的系统,但严格来说仍然算不上 AGI。好,那我们把话题一路推进到 Cursor。Cursor 是什么?它是 VS Code 的一个分支。而 VS Code 长期以来是最受欢迎的编辑器之一,所有人都爱上了它,所有人都离开了 Vim——我也为它离开了 Emacs,抱歉。它在某种根本的方式上统一了开发者社区。然后你们审视这个领域,看到缩放定律,看到 AI 正变得惊人,于是你们决定:只给 VS Code 写一个扩展是不够的,因为那有很多限制。如果 AI 要不断变得越来越好,我们需要真正重新思考 AI 如何成为编辑过程的一部分。于是你们决定分叉 VS Code,开始构建许多了不起的功能,我们后面会聊到。但那个决定是什么样的?因为 VS Code 有很多扩展,包括 Copilot,都在做 AI 相关的事情。直接分叉 VS Code 的决定是怎么做出的?做一个编辑器这个决定,对我们来说——至少就我们想做和想实现的目标而言——似乎是不言自明的。因为当我们开始做这个编辑器时,我们的想法是:这些模型会变得好得多,它们的能力会不断提升,而这将彻底改变你构建软件的方式——一方面你会获得巨大的生产力提升,另一方面也是根本性的:构建软件这一行为本身将发生巨大变化。如果你只是现有编程环境的一个插件,你对代码编辑器的控制是非常有限的。我们不想被那些限制锁死,我们想能够构建最有用的东西。好,那么很自然的问题是:VS Code 加 Copilot 在某种意义上是你们的竞争对手,你们怎么赢?基本上就靠速度和功能的质量吗?对。我认为这个领域相当有趣,也许相当独特。如果你看以往的技术浪潮,也许每次只有一件大事发生,然后解锁一波新公司。但现在,每一年、每一次模型能力的跃升,你都会解锁一波新的功能、新的可能性,尤其是在编程领域。所以我认为在 AI 编程领域,哪怕只领先几个月——更不用说领先一年——都会让你的产品有用得多得多。我认为一年后的 Cursor,需要让今天的 Cursor 显得过时。我认为 Microsoft 做了许多非常出色的事情,但我不认为他们所处的位置能像一家创业公司那样,真正持续地在这个方向上创新和推进,快速实现功能并推进。对,还要做那些真正推高天花板所必需的研究和实验。我不太用"功能"来思考这件事,我更多是用"程序员获得的能力"来思考。比如你知道,新的 o1 模型出来了,而且我确信还会有更多不同类型的模型,比如更长的上下文,也许更快。有各种疯狂的想法可以尝试,希望其中 10% 的疯狂想法最终能变成某种又酷又有用的东西,而我们希望人们能更早用上这些。换个说法:一个被低估的事实是,我们是在为自己做这个产品。我们创办 Cursor 的时候,你能真切地感到这种挫败感:你看得到模型在变好,但 Copilot 的体验却没有变化。就像是,"哥们儿,天花板在越升越高,你们为什么不做新东西?你们应该做新东西啊。那些 alpha 功能都在哪儿呢?"——根本没有 alpha 功能。我相信它卖得很好,我相信那是一门好生意,但它给人的感觉不对。我是那种真的很想尝试、使用新东西的人,而在很长一段时间里,就是没有新东西。对,这很有意思。我不知道该怎么用语言表达,但当你把 Cursor 和 Copilot 比较时,Copilot 很快就开始让人觉得有点陈旧了,不知为何。对。我认为对我们有帮助的一点是,我们是把所有环节放在一起做的:我们在开发用户体验(UX)和你与模型交互的方式的同时,也在开发我们如何让模型给出更好的答案——比如你如何构建提示词(prompt),如何找到上下文,对于 Cursor Tab 来说,如何训练模型。我认为让同一批人负责从头到尾的整个体验,这一点对我们很有帮助。对,就像做 UI 的人和训练模型的人坐在相距 18 英尺的地方,甚至常常就是同一个人。对,常常甚至是同一个人。这样你就能创造出一些东西——如果你们彼此不交流、不一起实验,那些东西是不可能诞生的。而且如你所说,你们在用 Cursor 来写 Cursor。当然,是啊。那我们来聊聊其中一些功能吧。先聊那个全知全能的、"赞美 Tab"的功能——基本上就是打了激素的自动补全。Tab 是怎么工作的?Tab 是什么?从高层次上概括,我要说 Cursor 目前有两件事做得相当好。它还能做其他事情,但它主要在两方面帮助程序员。第一是这样一个理念:像一个非常敏捷的同事一样在你身后看着你,能抢在你前面打字,弄清你接下来要做什么。这就是最初的想法——一个好的自动补全的核心理念就是预测你接下来要做什么。你可以把这个概念做得更有野心:不只是预测光标后面的字符,而是真正预测你要做的下一个完整修改、下一个 diff、你要跳转到的下一个位置。Cursor 目前做得相当好的第二件事,是帮助你有时抢在 AI 前面,告诉它该做什么,实现从指令到代码。在这两方面,我们都做了大量工作,让这些操作的编辑体验符合人体工学,同时也让它们既聪明又快。我们特别想要的一样东西,是希望模型能替我们编辑代码。那算是一个心愿。在拥有一个真正能替你编辑代码的好模型之前,我们做了多次尝试。有了好模型之后,为了获得好的体验,我们又投入了大量精力让推理变得快。我们也开始整合——Michael 刚才提到的——跳转到不同位置的能力。这种跳转,我想源于这样一种感觉:一旦你接受了一个编辑,接下来该去哪里应该是显而易见的。就像"我做了这个修改,模型应该知道下一个要去的位置是往下 18 行"。如果你是 Vim 用户,你可以按 18JJ 之类的,但我为什么要做这个?模型应该直接知道。所以我们的想法是:你只需按 Tab,它就跳到 18 行之下,然后把下一个编辑展示给你,你再按 Tab。只要你能一直按 Tab,一切就这么进行下去。所以我们内部的竞赛是:我们能让人连续按多少次 Tab?一旦你有了这个想法,更抽象地说,值得思考的是:这些编辑在什么意义上是"零熵"的?一旦你表达了你的意图,这个编辑本身就不再包含新的信息比特来完成你的想法,但你仍然必须敲一些字符,才能让计算机明白你真正在想什么。那么,也许模型应该直接读懂你的心思,所有零熵的比特都应该被一个 Tab 键消除掉。这就是那个抽象层面的想法。这里有一个有意思的现象:如果你看语言模型在不同领域上的损失,我记得代码的每字节比特数(bits per byte,一种按字符归一化的损失度量)比自然语言更低,这意味着代码中总体上有大量高度可预测的词元、大量高度可预测的字符。而当你不只是想自动补全代码,而是要预测用户在编辑现有代码时接下来会做什么时,这种可预测性我认为会进一步放大。所以 Cursor Tab 的目标是:当意图实际上已经确定时,消除你在编辑器内做出的所有低熵操作,直接把你在时间上向前跳、向前快进。那么,"下一个光标位置预测"的直觉和技术细节是什么?那种跳转,我想对很多人来说并不那么直观。我可以讲讲让这些东西运转起来的一些细节。它们的延迟必须极低,所以你需要针对这个任务训练小模型。特别是,它们极度消耗预填充(pre-fill)词元——意思是它们的提示词非常非常长,模型要看你的大量代码,但实际生成的词元并不多。与之完美匹配的方案是使用稀疏模型,也就是 MoE(混合专家)模型。这是我们取得的一个突破,它大幅改善了模型在较长上下文下的性能。另一个突破是我们构建的一种投机解码(speculative decoding)的变体,我们称之为投机编辑(speculative edits)。我认为这两点是让它质量相当高、速度非常快的重要因素。好,所以是混合专家模型(mixture of experts),输入巨大,输出很小。对。好,那关于怎么把它做好,还有什么可说的?比如缓存在其中扮演什么角色?缓存扮演着巨大的角色。因为你要处理这么多输入词元,如果你在某一行里敲的每一次键,都要把所有传入的词元重新跑一遍模型,那你第一会大幅恶化延迟,第二会用负载压垮你的 GPU。所以你需要把喂给模型的提示词本身设计成"缓存友好"的,然后你需要跨请求复用 KV 缓存,这样你花费的工作量、计算量才会更少。再追问一下:Tab 在近期应该能做到哪些事情?生成代码、填补空白、跨多行编辑代码?对,然后是跳转到同一文件内的不同位置。然后,希望还能跳转到不同的文件。如果你在一个文件里做了一个编辑,也许你必须去另一个文件完成你的想法,它就应该带你去第二个文件。对,而完整的泛化形式是"下一个动作预测"。有时候你需要在终端里运行一个命令,它应该能根据你写的代码建议那个命令。还有些时候,它给了你一个建议,但你很难判断建议对不对,因为你实际上还需要更多信息才能判断——比如你需要知道类型才能验证它是否正确。这时它也许应该先把你带到某个东西的定义处,再把你带回来,让你掌握接受下一个补全所需的全部知识。同时也把知识提供给人。对,没错。能不能集成一些别的?我最近认识了一个叫 ThePrimeagen 的人,他可以通过 SSH 点咖啡。哦对,我们干过那个,我们干过。那模型也能做那个吗?喂饱你,给你提供咖啡因?好,所以那就是总体框架。对。而神奇时刻会是这样的:编程是一门奇怪的学科,有时候——不总是,但有时候——你接下来五分钟要做的事情,其实可以根据你最近做的事情预测出来。那么,能不能达到这样一个世界:接下来的五分钟,要么在你放手的情况下由它带着你完成,要么更接近于你只是不断看到它的下一步,然后说"嗯,不错,不错,不错",就这样啪嗒啪嗒地按过这些大的修改。既然聊到这个,我应该提一下:Cursor 特别酷、特别显眼的一点是有一整套 diff 界面。模型用红色和绿色显示"我们将这样修改代码",你在聊天窗口里可以点 apply,它会把 diff 展示给你,你可以接受这个 diff。能不能讲讲这个方向的方方面面?我们大概会有四五种不同类型的 diff。我们为自动补全优化了一种 diff,它的界面与你审查较大代码块时的 diff 不同。然后我们还在为跨多个文件的场景优化另一种 diff。从高层次看,区别在于:自动补全的 diff 必须读起来非常非常快——其实所有场景下都应该读得快,但自动补全时你的眼睛聚焦在一个区域,人类没法同时看太多不同的地方。你是说在界面层面?在界面层面。它目前在旁边有一个框。我们现在的方案是:如果它想删掉某处代码、加上别的代码,它会在旁边显示一个框。也许可以在 cursor.com 上打开给大家看,就是我们说的这个。做出这个东西之前,我们大概试了三四种方案。最早的尝试是那种蓝色的删除线。在变成旁边的框之前,它会用类似 Google Docs 的风格给要删除的代码画删除线,然后你会看到新代码——那太让人分心了。然后我们尝试了很多方案:有各种删除样式,有红色高亮。之后的一版迭代还挺好笑的:在 Mac 上你要按住 Option 键,它会高亮一块代码区域,提示你这里可能有东西要来。比如在那个例子里,"input"和"value"会整个变蓝,蓝色是在提示 AI 对你有一个建议。它不直接把内容展示给你,而只是暗示 AI 有一个建议。如果你真想看,就按住 Option 键,你就会看到新的建议;松开 Option 键,你又会看到原始代码。顺便说,那还挺不错的,但你得知道要按住 Option 键。对。顺便说一句,我不是 Mac 用户,但我明白了。那是个按键,你们那些人的东西。对,它就是不直观。我认为这是关键问题。而且这有可能也不是它的最终版本。我个人非常期待在这个领域做大量改进。我们常把它称为"验证问题"(verification problem):这些 diff 对小的编辑很好用,但对于大的编辑,或者涉及多个文件的时候,审查这些 diff 实际上有点让人望而却步。这里有几个不同的想法。我们的一个想法是:diff 的某些部分很重要,包含大量信息;而 diff 的另一些部分熵很低,就是同样的东西反反复复。所以也许可以高亮重要的部分,把不那么重要的部分置灰。或者可以有一个模型来看这个 diff,发现"这里很可能有个 bug",就画上一条红色波浪线,提示"你应该重点审查 diff 的这一部分"。这一类的想法我觉得很令人兴奋。对,这真是一个非常迷人的 UX 设计工程领域。你们基本上是在引导人类程序员读完他们需要读的所有东西,一点不多读。对,要做到最优。而且你希望由一个智能模型来做这件事。目前的 diff 算法就只是普通的算法,没有智能。设计算法的过程里有智能,但算法本身并不关心"这是关于这个还是关于那个",所以你希望让一个模型来做这件事。我认为更一般的问题是:这些模型会变得聪明得多。随着模型变得更聪明,它们能提出的修改也会大得多。而随着修改越来越大,人类要做的验证工作就越来越多、越来越难。你需要帮他们一把。我可不想把所有时间都花在审查代码上。能不能再多讲讲跨多文件的 diff?

GitHub 试图用代码评审(code review)解决这个问题,对吧?当你做代码评审时,你是在跨多个文件审查多个 diff。但就像 Arvid 之前说的,我认为你可以做得比代码评审好得多。代码评审其实挺糟糕的:你花大量时间去啃那些对你来说往往很陌生的代码,而且它常常连多少 bug 都抓不到。我认为用语言模型可以大幅改善这种评审体验,比如用 Arvid 描述的那类技巧,把你指向真正重要的区域。另外我认为,如果代码是由这些语言模型生成的,而不是由别人写的,那么情况就不同了:代码评审的体验本来是同时为评审者和代码作者设计的,而当代码作者是一个语言模型时,你就不必那么在意"作者"的体验,你可以完全围绕评审者来设计整个流程,让评审者的工作尽可能有趣、轻松、高效。我觉得,如果只是天真地把这些东西做得像传统代码评审,问题就出在这里。我认为你可以更有创造性地推进这个边界。在"什么是可能的"这个方向上,有一个想法是:我认为顺序很重要。通常你评审一个 PR 时,你有一个文件列表,从上到下逐个审查。但实际上,你其实想先理解某一部分,因为它在逻辑上是先出现的,然后再理解下一部分。而且你不想自己去琢磨这个顺序,你希望有一个模型引导你走完整个过程。那么创造这一步会不会越来越走向自然语言?目标是不是用自然语言取代实际的(代码)?我觉得有时候……我不认为所有编程都会变成自然语言。原因是这样的:假如我和 Sualeh 结对编程,Sualeh 坐在电脑和键盘前,有时我作为"主驾"会对 Sualeh 说"嘿,实现这个函数",那样是行得通的。但有时候,向 Sualeh 解释我想让他做什么实在太烦人了,于是我干脆接过键盘,写出一部分示例给他看,他就明白了——那才是最容易的沟通方式。我认为对 AI 也一样:有时与 AI 沟通的最容易方式,就是给它看一个例子,然后它把同样的事情应用到其他所有地方。又比如你在做一个网站,向 AI 展示你想要什么,最容易的方式不是用语言告诉它,而是拖拽东西、画东西。也许最终我们会有脑机接口之类的东西,能理解你在想什么。所以我认为自然语言会有一席之地,但它绝对不会是大多数人在大多数时候的编程方式。用这个编辑器我真的"感受到了 AGI"。感觉底层有大量机器学习在运转。给我讲讲让这一切运转起来的机器学习吧。

Cursor 实际上是靠一组我们自己训练的定制模型,与那些在推理密集型任务上表现出色的前沿模型协同工作的。Cursor Tab 就是一个很好的例子:你可以把这个模型专门化,在我们设定的任务上,甚至比前沿模型还要好(如果你看评测的话)。另一个领域说来有点出人意料也需要定制模型、而且效果很好,是 apply(应用修改)。前沿模型很擅长勾勒代码计划、生成修改的粗略草图,但真正生成 diff 对前沿模型来说相当困难。你用 Sonnet、用 o1、用任何前沿模型试试,它们都会在一些愚蠢的事情上出错,比如数行号,尤其是在超大的文件里。为了缓解这个问题,我们的做法是:让(前沿)模型先勾勒出一个粗略的代码块来表明修改是什么,然后我们训练一个模型把这个修改真正应用到文件上。我们应该说明一下:apply 就是模型看了你的代码,给了你一个非常好的关于要做什么新东西的建议,而那个对人类来说看似微不足道的"把两者合并"的步骤,你们说其实并不简单。与普遍的看法相反,它不是一个确定性算法。对。你会在别处看到 apply 的浅层仿制品,它们大多数时候都会出问题,因为你以为可以做某种确定性的匹配,但它至少 40% 的情况下会失败,结果就是极其糟糕的产品体验。总的来说,我认为这种"模型会越来越聪明"的格局下……apply 让你能做的另一件事是:让最智能的模型使用更少的词元。生成所有这些词元既在延迟上代价高昂,也在成本上代价高昂。所以你可以给出一个非常非常粗略的草图,然后让你的小模型去实现它,因为把已经勾勒好的代码实现出来是容易得多的任务。我认为这种格局会持续下去:用越来越聪明的模型做规划,而实现细节也许可以交给不那么智能的模型处理。也许将来会有 o1 或者能力更强的模型给出一个更高层的计划,由 Sonnet 递归地展开执行,最后再交给 apply 模型。也许我们该聊聊怎么把它做快。对,我总觉得"快"是个有意思的细节。快就是好。对,怎么做到快?让它快的一个重要组成部分是投机编辑。投机编辑是投机解码的一个变体,也许有必要先简单介绍一下投机解码。投机解码利用的是这样一个事实:大多数时候——我要加个限定,是在语言模型生成处于内存受限(memory bound)状态时——一次处理多个词元比一次生成一个词元更快。这和你观察"提示词元与生成词元的每秒词元数"时看到的现象是同一个原因:提示词元的处理速度要快得多得多。通常的投机解码是用一个非常小的模型来预测草稿词元,再由大模型去验证。而在代码编辑场景中,我们对"现有代码长什么样"有非常强的先验——这个先验就是一模一样的原始代码本身。所以你可以直接把原始代码的片段喂回给模型,模型在大多数时候都会"同意":好,我就把这段代码原样吐回来。这样你就可以并行处理所有这些行。你用足够多的代码块反复这么做,最终会到达一个分歧点:模型此时预测出与原始代码不同的文本。它会生成那些词元,然后当足够多的词元与原始代码重新吻合后,我们再重新开始按代码块投机。这最终呈现出来的效果,就是一个快得多的常规代码编辑版本——看起来就像模型在以快得多的速度重写全部代码。我们可以沿用与 diff 相同的界面,只是内容会以快得多的速度流式呈现。而且优势在于,在流式输出的同时,你就可以开始审查代码了,不必等它完成,没有漫长的加载画面。所以这也是优势的一部分:人可以在它完成之前就开始阅读。我觉得这里有意思的一点是:投机(speculation)如今是一个相当普遍的思想,不只在语言模型里。CPU 里显然有投机执行,数据库里也有投机,到处都有投机。让我问一个有点荒谬的问题:哪个 LLM 更擅长编程?GPT 还是 Claude?在编程语境下谁赢?我相信答案要微妙得多,因为听起来这里的每一个环节都用着不同的模型。对。我认为没有哪个模型帕累托支配(Pareto dominate)其他模型,也就是说,在我们认为重要的所有维度上都更好。这些维度包括:速度、编辑代码的能力、处理大量代码的能力、长上下文,还有其他一些,以及编程能力本身。目前我要说综合最好的就是 Sonnet。我想这是共识。o1 非常有意思,它的推理能力真的很强,如果你给它非常难的、编程面试风格的题目,或者 LeetCode 题目,它能做得相当好。但它给人的感觉是,不像 Sonnet 那样能理解你粗略的意图。看其他许多前沿模型时,我有一个不满:并不是说它们在基准上刷题训练,但相对于其他方面,它们在基准测试上的表现实在太好了。你在这些基准、以及基准分布之内的任务上测它们,它们表现很好;但当你把它们稍微推到基准之外时……我认为 Sonnet 是在保持能力一致性上做得最好的:它在基准测试中的能力,和你让它做任何编程相关的事情时的能力,大体是一样的。另一个荒谬的问题:正常的编程体验与基准测试所代表的东西之间有什么区别?在我们评估这些模型时,你认为基准测试在哪些地方力有不逮?顺便说,这是一个非常非常难、但极其重要的细节:基准测试与真实编程有多大差别。真实编程不是面试式编程。真实编程里,人类会说着半通不通的英语,有时候你会说"哦,照我之前做的那样做",有时候你会说"去加上这个东西,然后再帮我做那个另外的事情,然后做这个 UI 元素"。很多事情是依赖上下文的。你真正想要的是理解这个人,然后做这个人想要的事情。抽象一点说也许可以这样表述:面试题是规格说明非常明确的,它们高度依赖规格说明;而人类的真实需求,规格说明程度要低得多。这个问题既被 Sualeh 刚才说的复杂化了,也和 Aman 谈到的有关:存在"基准里能建模的东西"与"真实编程"之间的偏差问题,而这有时很难概括,因为真实编程非常混乱,有时候什么算对、什么算错都没有被很好地定义。而且这个问题还因为公开基准的问题而难上加难。一方面公开基准有时会被"爬山式"地刷分,另一方面,要把公开基准的数据从模型(训练数据)里剔除也非常非常难。举例来说,最流行的智能体基准之一 SWE-bench,就已经严重污染了这些基础模型的训练数据。如果你让这些基础模型做一道 SWE-bench 题目,甚至不给它们代码库的上下文,它们也能"幻觉"出正确的文件路径、幻觉出正确的函数名。所以这些东西的公开属性本身就很棘手。对,那种情况下,它可能就是在那些 issue 或 pull request 本身上训练过的。也许实验室会开始做得更好——或者他们已经在去污染方面做得不错了——但他们不会剔除代码仓库本身的实际训练数据。这些都是最流行的 Python 仓库,比如 SymPy 就是一个例子。我不认为他们会为了在这些基准上获得真实的评估分数,而在 SymPy 和所有这些流行的 Python 仓库上削弱自己的模型。对。鉴于基准的这些缺陷,那些用这些模型构建系统、或者构建这些模型本身的机构,实际上在用一些有意思的"拐杖"来判断方向对不对。在很多地方,人们实际上就是让人类来试用这些东西,给出定性的反馈。有一两家基础模型公司,就有专门的人把这作为工作的重要部分。在我们内部,我们同样会定性地评估这些模型,而且除了我们的私有评测之外,我们实际上非常依赖这种方式。就是"氛围"。对,"氛围"。氛围基准,人类基准。你们把人拉进来做"氛围检查"(vibe check)。对。我基本上也是这么干的,就是读网络论坛、Reddit 和 X。不过我不知道该怎么恰当地采信人们的意见,因为他们会说"我感觉 Claude 或 GPT 变笨了"之类的话。他们会说"我感觉"——我自己有时也会有那种感觉,但我总怀疑是模型的问题还是我的问题。关于 Claude 有一个有意思的说法。AWS 有不同的芯片,我怀疑它们的数值特性与 Nvidia GPU 略有不同。有人猜测 Claude 表现变差,可能与用了 AWS Bedrock 上的量化版本有关,而不是运行在 Anthropic GPU 上的那个版本。我采访过一堆持各种阴谋论的人,很高兴你聊到了这个"阴谋论"。这倒不算什么阴谋论,更多是……人就是人,存在这些细节。你在做数量疯狂的浮点运算,芯片是混乱的东西,然后你完全可能就是碰上了 bug。bug 有多难避免,怎么强调都不为过。那么,好的提示词在这一切中扮演什么角色?你提到基准测试的提示词是高度结构化、表述规范的。人应该做什么来最大化成功率?提示词的重要性是什么?你们写过一篇博客,你们称之为"提示设计"(prompt design)。对。我认为这取决于你在用哪个模型,它们各不相同,对不同的提示词反应也不同。最初的 GPT-4,以及去年那一批早期模型,对提示词相当敏感,而且它们的上下文窗口非常小。我们手头有代码库周边的所有这些信息,可能都与提示词相关:你有文档,有你添加的文件,有对话历史。于是问题来了:当空间有限时,你怎么决定实际往提示词里放什么?即使对今天的模型、即使你有长上下文,填满整个上下文窗口也意味着更慢,意味着模型有时反而会犯糊涂——有些模型比其他模型更容易糊涂。我们内部有一个系统,叫 Preempt,它在这方面帮了我们一点忙。我想它是为上下文窗口只有 8,000 词元的时代构建的。这有点像做网站:你希望它在手机上能用,在桌面屏幕上也能用。你面对的是动态的信息,这和设计印刷杂志不同——那种情况下你确切知道能把东西放在哪里。但当你做网站、或者构建提示词时,你有这些输入,你需要把它们格式化成无论输入多大都始终能用的形式;输入太大时,你可能就得裁剪掉一些东西。所以想法是:好,我们借鉴一下——设计网站的最佳方式是什么?我们非常喜欢 React 和声明式的方式:你在 JavaScript 里用 JSX 声明"这是我想要的",并且"这个东西的优先级更高",或者"这个的 z-index 比那个高"。然后在网页设计里,有一个渲染引擎,比如 Chrome;在我们这里则是一个提示词渲染器,它把所有东西排布到"页面"上。你以声明的方式说明你想要什么,它来想办法实现。我们发现这非常有帮助。而且它的角色随时间发生了变化:起初是为了适配小上下文窗口,而现在它的用处在于,它帮我们把"进入提示词的数据"与"提示词的实际渲染"分离开。这样调试就更容易了:你可以修改提示词的渲染方式,然后在旧的提示词上重放——因为你保存了进入提示词的原始数据——然后你可以看到,我的修改是否真的在整个评测集上带来了改进。所以你们真的在用 JSX 写提示词?对,对。它看起来就像 React。有各种组件。比如我们有一个文件组件,它接收光标信息——通常你文件里光标所在的那一行大概是最重要的一行,因为那是你正在看的。于是你可以设定优先级:那一行优先级最高,然后每远离一行就减一分。最终渲染时,它会计算实际能放下多少行,并以那一行为中心呈现。太妙了。对,你还可以做别的花活:如果你有来自整个代码库的大量代码块,你可以用检索,用嵌入(embedding)和重排序(reranking)分数,来为这些组件设定优先级。那么人类在提问时,是不是也应该用类似的方式?在提示里写 JSX 会有好处吗?还是说整个理念就是应该松散、随意?我认为我们的目标是:你就做对你来说最自然的事情,然后我们的工作是弄清如何检索出相关的东西,让你的表达真正说得通。这正是我和 Perplexity 的 Aravind 讨论过的话题。他的整个理念是:应该让人想多懒就多懒。那当然是很美好的,但我觉得,对程序员你是可以提出更多要求的,对吧?如果你只说"随便你怎么写"——人是懒惰的。这里存在一种张力:是纵容懒惰,还是提供更多信息?系统能不能"施压"或者说"激励"你变得更善于表达——不是语法层面,而是你在提示中传达的思考深度层面?我认为,即使系统逼近某种完美水平,当你向模型要求某样东西时,往往你传达的意图仍然不足以让它知道该做什么。解决意图缺失有几种方式。一种简单的做法是让模型直接问你:"根据你的请求,这些部分我不确定该怎么做,你能澄清一下吗?"另一种做法也许是:既然你的请求存在不确定性,可能对应五六种不同的生成结果,那不如把这些结果全部展示给你,让你挑。让模型选择"反问"而不是直接生成,有多难?就是如何处理不确定性:我要不要选择索取更多信息来消除歧义?我们做的一件事——是最近新增的——是尝试建议你可以添加的文件。在你打字的时候,系统可以猜测不确定性在哪里,并给出建议。比如也许你在写你的 API,我们可以根据你之前在同一个文件里的提交猜出,客户端和服务器端的代码会非常有用。这里还有一个很难的技术问题,就是如何……

……如何跨所有提交解决这个问题:给定你当前的提示词,哪些文件最重要?我们目前推出的还只是初始版本,我相信我们可以把它做得准确得多。这还非常实验性。然后我们的想法是给你展示:你要不要把这个文件、这个文件、还有这个文件也加进来?并告诉模型替你编辑那些文件。因为如果你在写 API,你或许也应该编辑使用这个 API 的客户端和服务器端代码,以及解析这个 API 的那一端。这样会很酷:在你写提示词的阶段、甚至在你按下回车之前,也许我们就能帮你消解一部分不确定性。你们在多大程度上使用智能体(agent)方法?智能体有多大用处?我们觉得智能体真的非常酷。我认为智能体有点像一个人。你能感觉到你在接近 AGI,因为你看到一个演示,它表现得就像一个人会做的那样,真的非常酷。我认为智能体目前对很多事情还不是特别有用,不过我们正在接近它们真正有用的阶段。有某些类型的任务,有一个智能体会非常好。比如,我很想有一个这样的智能体:我们有一个 bug,在我们的聊天输入框里有时无法 Command+C 和 Command+V。这个任务的规格说明非常明确,我只想用两句话说"这个不能用,请修好它",然后我想要一个智能体自己去干,一天后我回来审查结果。你的意思是它会自己找到正确的文件?对,它找到正确的文件,尝试复现这个 bug,修复它,然后验证修复是正确的。这可能是一个耗时很长的过程。我很想拥有那样的东西。不过,关于编程,常常有一种信念,认为智能体将接管所有的编程。我们不这么认为。因为编程的很大一部分价值在于迭代:你其实并不想事先把东西完全指定清楚,因为在看到初始版本之前,你并不真的知道自己想要什么,然后你想在那个基础上迭代,再提供更多信息。所以对于很多编程工作,我认为你真正想要的是一个即时的系统:立刻把初始版本还给你,然后你可以超级快速地迭代。那像最近发布的 Replit Agent 那样的东西呢?它还能搭建开发环境、安装软件包、配置一切、配置数据库,并真正部署应用。这也在你们梦想的清单里吗?我想是的。对某些类型的编程来说,那会非常酷。那在 Cursor 的范围之内吗?在。我们目前没有在积极做这个,但可以肯定的是,我们想让程序员的生活更轻松、更有趣。有些事情就是特别乏味,你需要走一堆步骤,你想把它委托给一个智能体。还有些事情,你可以在自己工作的同时让一个智能体在后台运行。比如你有一个前后端都涉及的 PR,你正在做前端部分,你可以让一个后台智能体去弄清你在做什么,等你做到 PR 的后端部分时,你就已经有了一些可以迭代的初始代码。那也会非常酷。我们已经谈过速度这个话题了,但我想再多聊一会儿,聊聊让这个东西变得非常快所涉及的各种技术细节。Cursor 的每一个方面——绝大多数方面——都感觉非常快。我提过 apply 大概是最慢的环节。抱歉,这个痛点——

我知道,这是个痛点。这是我们自己也在感受的痛,我们正在修。对。这本身就说明了一些问题:一个感觉起来一两秒的东西会让人觉得慢,说明其他一切真的非常非常快。那么,关于如何让这些模型快起来——如何让聊天快、让 diff 快——有没有什么立刻能想到的技术细节?有,我们可以过一遍我们用的很多策略。一个有意思的东西是缓存预热(cache warming)。你可以这么做:在用户打字的过程中,你就大概知道会用到某些上下文,而且在用户打完之前你就能知道。如我们之前讨论的,跨请求复用 KV 缓存能带来更低的延迟和更低的成本。所以当用户开始输入时,你就可以立即用——比如当前文件的内容——预热缓存。当他们按下回车时,实际需要预填充和计算的词元就非常少了,然后才开始生成。这会显著降低首词元时间(TTFT)。你能解释一下 KV 缓存是怎么工作的吗?好。Transformer 的工作方式……我挺喜欢这个话题。让 Transformer 不只是独立地看每个词元、而是能看到之前词元的机制,是注意力(attention)的键(keys)和值(values)。一般来说,注意力的工作方式是:在你当前的词元处有一个查询(query),然后你拥有之前所有词元的键和值——它们是模型内部存储的、对提示中所有先前词元的某种表征。默认情况下,做聊天时,模型必须为每一个词元做一次贯穿整个模型的前向传播,那是大量的矩阵乘法,非常非常慢。而如果你已经做过这些计算,把键和值存了下来,并保留在 GPU 里,那么——比如说我已经为前 n 个词元存好了——如果我现在要计算第 n+1 个词元的输出,我就不需要把前 n 个词元再过一遍整个模型,因为我已经有了它们所有的键和值。你只需对最后那个词元做前向传播,然后在做注意力时复用那些已经算好的键和值。这(注意力)是 Transformer 中唯一的顺序部分,或者说顺序依赖的部分。有没有更高层次的缓存,比如缓存提示词之类的,也能有帮助?有,还有其他类型的缓存可以做。对 Cursor Tab 来说,你可以做一件有意思的事:你基本上可以提前预测——假装用户已经接受了当前建议——然后触发下一个请求。这样你就缓存了……这是投机与缓存的结合:你在投机"如果他们接受了会发生什么",然后你手里就有了这个缓存好的下一个建议。当他们按下 Tab 时,下一个建议立刻就在等着他们了。这是一种聪明的启发式技巧,利用了更高层次的缓存。尽管模型本身没有任何变化,它却让人感觉很快。而如果你能把 KV 缓存做小,你获得的一个好处是也许可以投机得更多。也许你可以真的预测 10 个可能有用的候选——预测接下来的 10 个——然后用户命中这 10 个之一的概率,要比命中你展示给他们的那一个精确候选的概率高得多。也许他们再敲一个字符,就命中了缓存里的另一个候选。对,这里有各种各样的技巧。这里的普遍现象——我认为它对强化学习也非常有用——是:也许模型的单次采样并不很好,但如果你预测 10 个不同的结果,事实证明这 10 个里有一个是对的概率会高得多。这就是 pass@k 曲线。强化学习所做的一部分,就是利用这种 pass@k 现象,做出许多不同的预测。可以这样理解:模型内部其实"知道"、或者说对"哪一个候选是对的、人类想要的是哪一个"有某种不确定性。当我们对 Cursor Tab 模型做强化学习时,我们做的事情之一,就是预测模型产生的一百个不同建议中,哪一个对人类更合意——人类更喜欢其中哪些。也许模型能预测很远,也许只能预测一点点,也许介于两者之间。你可以给人类更喜欢的结果以奖励,惩罚人类不喜欢的结果,然后训练模型输出人类更喜欢的建议。你就有了这些非常有用的强化学习循环,它们利用了这些 pass@k 曲线。Aman 也许可以讲得更细。对,这和速度略有不同——不过技术上你可以把它绕回来,因为如果你对小模型做强化学习,让它达到与大模型相同的性能,那你就可以用更小的模型了。Sualeh 刚才提到了关于 KV、关于缩小 KV 缓存尺寸的一些东西,这方面还有其他一些技术也非常……

……对速度非常有帮助。回到过去,大概两年前,人们主要用多头注意力(multi-head attention)。我认为后来出现了向更高效注意力方案迁移的趋势,比如分组查询注意力(grouped-query attention)或多查询注意力(multi-query attention)。在更大的批量(batch size)下,这对更快地生成词元非常有帮助。有意思的是,这对首词元时间、预填充速度没有影响;它影响的是生成词元的速度。为什么?因为在生成词元时,你的瓶颈不再是对所有词元做那种高度可并行的矩阵乘法,而是——对于长上下文、大批量的情况——你读取那些缓存的键和值的速度。这是内存带宽问题。那怎么让它更快?我们可以尝试压缩这些键和值的大小。多查询注意力是其中最激进的:通常在多头注意力里,你有若干个注意力头、若干个查询头,多查询注意力只保留查询头,去掉所有的键值头,只剩一个键值头,其余全是查询头。分组查询注意力则保留全部查询头,键和值的头数变少,但不会减到只有一个。总之,这里的核心就是减小 KV 缓存的大小。然后还有 MLA。对,多头潜在注意力(multi-latent attention)。这个稍微复杂一点。它的工作方式是把你所有头上的全部键和值,转换成一个潜在向量(latent vector),在推理时再把它展开。

MLA 来自一家叫 DeepSeek 的公司。这是一个相当有意思的算法。也许关键的想法是:在 MQA 和其他方案里,你做的是减少 KV 头的数量,好处是头变少了。但理论上,你其实希望各个键和值真的彼此不同。所以另一种压缩尺寸的方式是:为所有键值保留一个大的共享向量,同时为每个词元保留较小的向量。这样你存储的就只是那个较小的东西,相当于某种低秩压缩(low-rank reduction)。而到最后你要计算最终结果时——记住你是内存受限的,也就是说你还有剩余的算力可用——你可以把潜在向量重新展开。这样效率高得多,因为比如说,你把要保存的向量的大小缩小了 32 倍之类的。也许,让键、值和查询各自成套、两两对应地匹配,与把它们全压缩成一个之间,前者在交互上有某种丰富性。好。所有这些处理的都是内存受限问题。那么归根到底,这如何映射到用户体验上?它映射到两点。第一,你现在可以把缓存做得大得多,因为分配给 KV 缓存的空间变小了,你也许可以更激进地缓存更多东西,缓存命中率就更高,而这有助于降低首词元时间,原因前面已经讲过。第二,当你开始用越来越多的请求、越来越大的批量做推理时,生成词元的速度不会出现明显的下降。它也让你可以把提示词做得更大,对吧?对。KV 缓存的大小基本上等于所有提示词的大小乘以并行处理的提示词数量。所以你可以增加任一维度——批量大小或者提示词的大小——而不会恶化生成词元的延迟。

Arvid,你写过一篇博客《Shadow Workspace: 在后台迭代代码》。那是怎么回事?先说明一下:我们希望有大量的事情在后台发生,我们也在实验很多东西。目前,除了缓存预热、或者为你的 Command+K 之类找出合适的上下文之外,后台发生的事情还不多。但想法是:如果你能真正在后台消耗计算,你就能在比"预测你接下来要写的几行"更长一点的时间尺度上帮助用户——真正去预测你接下来 10 分钟要做什么。而在后台做,你就可以花更多计算来做这件事。我们实现了 Shadow Workspace 这个想法,并在内部用于实验。要真正从后台工作中获益,你需要某种反馈信号回馈给模型,否则的话,你只能靠让模型思考更久来获得更高的性能——o1 就是那种方式的好例子。但另一种提升性能的方式,是让模型迭代并获得反馈。对程序员来说,一个非常重要的反馈来源是语言服务器(language server)。绝大多数语言都有语言服务器,每种语言有一个单独的。它能告诉你"你这里用错了类型"并给出错误,或者让你跳转到定义,它在某种程度上理解你代码的结构。语言服务器是各方开发的扩展:TypeScript 的语言服务器由 TypeScript 团队开发,Rust 的语言服务器由 Rust 团队开发,然后它们都通过语言服务器协议(Language Server Protocol)与 VS Code 对接。这样 VS Code 就不必内置所有语言,而是可以复用现有的编译器基础设施。它是用来做什么的?用于代码检查(linting)、跳转到定义,还有查看你所使用的正确类型。所以它也做类型检查?对,类型检查,还有跳转到引用。当你在一个大项目里工作时,这些是刚需。没有它,在大项目里写代码就非常困难。能再说说它在 Cursor 内部是怎么被使用的吗?就是那个语言服务器协议通信。在 Cursor 里,它一方面像在 VS Code 里一样用来向程序员展示信息,但另一方面,我们的想法是把同样的信息展示给 AI 模型,而且要以不影响用户的方式来做,因为你想在后台做。Shadow Workspace 背后的想法就是:我们可以生成一个隐藏的 Cursor 独立窗口——Electron 里你可以设置这个标志,窗口存在但你看不见。在这个窗口里,AI 智能体可以随意修改代码——只要它们不保存,因为那还是同一个文件夹——然后它们可以从代码检查器获得反馈、跳转到定义,并在自己的代码上迭代。也就是真的在后台运行一切,就好像……

对,甚至也许运行代码。那是最终版本,那是我们想要的。博客的很大篇幅其实是在讲如何实现它,因为这有点棘手。你希望它在用户的机器上运行,让它与用户的环境完全一致。在 Linux 上你可以做一件很酷的事情:镜像文件系统,让 AI 对文件做修改——它以为自己在文件层面操作,但实际上那些修改被存在内存里。你可以写一个内核扩展让它工作。而在 Mac 和 Windows 上就更困难一点,不过这是个有趣的技术问题。还有一个也许有点 hacky 但我很喜欢的有意思的想法,是对"保存"持有一把锁。你可以让语言模型持有"保存到磁盘"的锁。这样你就不是在操作已保存到磁盘的文件的真实版本,而是在操作之前所说的 Shadow Workspace——那些只存在于内存中的未保存内容。你仍然能获得 lint 错误,能在里面写代码。当你想运行代码时,会有一个小提示说有一把锁存在,然后如果你要并发操作,你可以从语言服务器或 Shadow Workspace 那里把锁拿回来。顺便说,那真是个令人兴奋的功能。稍微跑个题:允许模型修改文件,这让人们害怕,但它真的很酷——让智能体去做一组任务,你第二天回来看看结果,就像对待一位同事那样。对,对。而且我认为"可运行性"可能会有不同的版本。对于简单的事情——你在用户编程的几分钟内代表他们做一些事情——让它在用户本地机器上运行是合理的。而对于更激进的事情——你要做耗时更长的大改动——

……你大概会想在某种沙箱化的远程环境里做。而那又是一个极其棘手的问题:如何精确复现——或者大体复现到运行代码时实际等价的程度——用户的环境?在远程的沙箱里。我很好奇你想要什么样的编程智能体。你想让它们找 bug?还是实现新功能?你想要什么样的智能体?顺便说,当我想到智能体时,我想的不只是编程。就拿这档播客来说,有视频剪辑。你去看 Adobe 的东西,背后有很多代码——文档写得很烂的代码——但你可以用代码与 Premiere 交互。基本上我在 YouTube 上传的所有内容、我做的一切,你大概能想到的,我都是通过代码完成的,包括翻译和配音,所有这些。所以我设想的是那一类任务:把许多与剪辑本身没有直接关系的任务自动化。这是我想的方向。但就编程而言,我最根本想到的会是找 bug——很多层次的 bug 查找,而且是找逻辑 bug,不是"字面"的,而是那种"精神层面"的 bug,比如实现的大方向弄错了之类的。关于找 bug 的问题——

对,很有意思的是这些模型在找 bug 上如此糟糕。只是朴素地用提示让它们找 bug 时,它们的校准差得惊人,即使是最聪明的模型。正是,即使是 o1。你怎么解释这一点?有没有好的直觉?我认为这些模型是预训练分布的强烈反映。我确实认为随着损失越来越低,它们会泛化,但我不认为现在的损失和规模已经低到让它们在代码上完全泛化。我们使用前沿模型的用途——它们非常擅长的——主要是代码生成和问答。这些东西在预训练里数量巨大:GitHub 上数万亿词元规模的代码,Stack Overflow 和 GitHub issues 之类的问题与回答。而当你把它们推向那些在网上很少存在的任务——比如 Cursor Tab 的目标:根据此前的编辑预测下一个编辑——这种脆弱性就显现出来了。bug 检测是另一个好例子:真正"检测出真实 bug 并提出修复"的样例其实并不多,模型在这上面就特别挣扎。但我认为这是一个模型迁移的问题。就像从通用代码预训练模型迁移到 Cursor Tab 目标能获得极好的迁移一样,从一个非常擅长代码的通用模型迁移到 bug 检测,也会非常类似,只是需要朝那个方向轻推一把。要说清楚的是,我认为它们其实非常理解代码。在预训练过程中构建起来的表征几乎可以肯定——在网络流的某个地方——模型知道这里可能有些可疑(sketchy)的东西。它感觉到了某种可疑。但真正把这种"可疑感"引出来……问题的一部分在于,人类对"哪些 bug 真正重要"是非常有校准的。这不只是说"这里有点可疑",而是要分清:这是无关紧要的可疑,还是"会把服务器搞垮"的可疑。这里面也许包含某种文化知识:为什么资深工程师(staff engineer)是资深工程师?他之所以厉害,是因为他知道三年前有人写过一段很可疑的代码,把服务器搞垮了。相对地,也许这个东西只是个实验,那么有几个 bug 没关系——你只是想试验一下、找找感觉。所以如果你在写实验代码时模型变得很烦人,那非常糟糕。但如果你在写非常面向生产的东西,比如你在写数据库、在给 Postgres 或 Linux 之类提交代码——你是 Linus Torvalds——那么哪怕一个边界情况出问题都是不可接受的。要校准的就是用户的"偏执程度"。但即便如此,即使你把偏执程度调到最大,它还是不太能领会。对,对。不过这对人类来说也很难:理解哪一行代码重要、哪一行不重要。我记得你们网站上有一条原则说:如果一段代码可能造成很大破坏,就应该加一条注释,写"这行代码很危险"。而且全大写、重复十遍。不,你们说的是:函数里的每一行代码都要这么写。这挺深刻的,它说明了人类的某种特点:工程师会离开,甚至同一个人也可能忘记单个函数如何"击沉泰坦尼克号"。只看一段代码,你未必能看得那么清楚。对。而且我认为那条原则对今天的 AI 模型也部分适用:如果你真的在每一行都写上"危险、危险、危险",模型会更关注它,也更有可能在那个区域找到 bug。这实际上就是一个非常好的实践:标注代码可能造成多大的破坏。对。不过这有争议。有些人觉得它很丑。Sualeh 就——

我其实认为——事实上,这是我从 Arvid 那里学到的东西之一——从审美上我不喜欢它,但确实有它的道理:它对模型有用,而且人类真的很健忘,很容易犯一个小错就把服务器搞垮。当然我们会做很多测试之类的,但总有一些事情你必须非常小心。对。像普通的文档字符串(docstring),人们改代码时往往只是扫一眼,心想"哦这个我会",你真的需要把危险点明确指出来,才能防止它溜过去。对,你必须被提醒"你可能造成很大的破坏"。我们平时不太会这么想。你想的是"我怎么弄明白它是怎么工作的,好去改进它",你不会去想相反的方向。直到我们对一切都有了形式化验证(formal verification),到那时你想做什么都行,只要证明通过,你就能确定自己没有引入 bug。那具体来说,你觉得那个未来会是什么样子?我认为人们将不再写测试。模型会——你写一个函数,模型给你建议一个规格(spec),你审阅这个规格。与此同时,一个聪明的推理模型计算出一个证明,证明实现符合规格。我认为对大多数函数来说都会是这样。你不觉得这有点触及你之前谈的问题吗——为软件指定意图的困难。有时候意图真的很难指定,那么要证明实现确实符合你的意图也会非常难。你觉得那个规格很难生成吗?对,或者说,对于一个给定的规格——

我认为存在这样的问题:你真的能做形式化验证吗?那到底可不可能?我觉得这里有更多值得深挖的东西。而且,即使你有了这个规格——

规格是用自然语言写的吗?规格会是形式化的。但那能有多容易呢……

所以我认为,你会关心那些在规格语言里不容易被良好指定的东西。我明白了,明白了。对,这也许可以作为反驳"形式化验证就是一切"的一个论据。担心的是会出现一份庞大的文档,去替代——

替代类似单元测试的东西。当然,对。不过我认为规格语言大概也可以演进,去覆盖一些它们目前覆盖不了的东西。我说不好,我觉得这非常令人兴奋。而且你说的不只是单个函数,你说的是整个代码库。我认为整个代码库更难,但那正是我想要的,而且我认为应该是可能的。因为最近有很多工作表明,你可以把形式化验证一路做到硬件层面:你形式化验证 C 代码,然后穿过 GCC 编译器做验证,再穿过 Verilog 一直验证到硬件。那是一个无比庞大的系统,但它真的成立。我认为大型代码库在某种意义上是类似的,它们也是多层系统。如果你能把它分解开、对每一部分做形式化验证,我认为应该是可能的。我认为规格问题是个真实的问题。那副作用怎么处理?或者说外部依赖怎么处理?比如调用 Stripe 的 API。也许 Stripe 会为他们的 API 写一个规格。但你不可能对所有东西都这么做啊。能对你用到的一切都这么做吗?比如如果里面用了语言模型呢?也许……

……也许人们会把语言模型当作原语(primitive)用在他们写的程序里,程序就依赖于它,那你怎么把这个也纳入进来?我认为你也许仍然能证明。证明什么?关于语言模型?我认为,比如说,证明一个语言模型是对齐的,或者证明它确实给出正确答案,感觉是有可能做到的。那可是梦想啊。对,那就是——如果可能的话。这是你的"我有一个梦想"演讲。如果可能,那肯定会有助于确保你的代码没有 bug,也有助于确保 AI 不会毁灭全部人类文明。所以从 AI 安全的整个光谱一直到找 bug。你说模型在找 bug 上很挣扎,那希望在哪里?我的最初希望是——Michael 可以补充——它应该首先帮忙抓那些蠢 bug。它应该非常快地抓住蠢 bug,比如差一错误(off-by-one error)。有时你在注释里写了一种意思,代码却写反了。这非常常见,我就会这样:注释里写"小于",代码里也许写成了"大于"之类的。模型应该会说:"呃,这看着可疑,你确定要这么写吗?"但最终它也应该能抓到更严重的 bug。对。我还想指出很重要的一点:拥有好的找 bug 模型,对于达到"让 AI 为你做越来越多编程"的最高境界来说是必需的。如果 AI 在为你构建越来越多的系统,你就不仅需要生成,还需要验证。没有验证,我们之前谈过的那些用这些模型编程的问题就会变得难以为继。所以这不只是为了人类——"你写了个 bug、我写了个 bug,帮我找出来"——它还关乎能够验证 AI 写的代码、检查它。这非常重要。对。那具体怎么做呢?我们吃饭时为"到底怎么训练一个找 bug 模型"吵过很多次。一个很流行的想法是:引入一个 bug 可能比找出一个 bug 容易。所以你可以训练一个模型往现有代码里引入 bug,然后训练一个反向的找 bug 模型,用这些合成数据去找 bug。这是一个例子。此外还有很多想法。你也可以完全不在模型层面下功夫,而是拿最大的模型,给它们大量代码之外的信息。因为"盯着一个文件问 bug 在哪"是个很难的问题——对人类来说往往也很难,对吧?所以你常常需要运行代码,需要能看到运行轨迹(trace)、能在调试器里单步执行。有另一整个方向是朝那边走的。而且这里可能存在两种不同的产品形态。一种是有一个非常专门化、非常快的模型,在后台运行,试图发现 bug。另一种可能是——回到 Arvid 之前那个"输入框有恶性 bug"的例子——有时候你明确知道有一个 bug,你不是在无假设地随便检查,而是"这是个问题,我真的想解决它",于是你砸上大量大量的计算,你愿意为解决那个 bug 花 50 美元,甚至更多。你们有没有想过把钱整合进整个产品里?如果它找到了一个 bug,或者生成了一段让我特别欣赏的代码,我大概会愿意付相当多的钱。几天前我有过一个时刻:我开始用 Cursor 时,它生成了完美的——完美的三个函数,用来与 YouTube API 交互,更新字幕、做不同语言的本地化。那个 API 的文档写得不怎么样,而且我 Google 了半天也找不到确切的做法,到处是让人迷惑的信息,而 Cursor 完美地生成了。我往后一靠,读了代码,心想:这是对的。我测试了,确实对。我当时就想:我想给小费。想要一个按钮,点一下"给你 5 美元"。一方面是实实在在地支持公司、支持这个产品;另一方面,这大概能传递一个很强的信号,"干得好",对吧?比仅仅接受代码强得多的信号。你实际上发出了一个强烈的"干得好"。而对于找 bug,显然有很多人愿意为一个 bug 付一大笔钱——就像 bug 赏金(bug bounty)那样,对吧?你们想过这个吗?想过,这在公司内部是个有争议的想法。我觉得这在某种程度上取决于你有多相信人性。我觉得这样会很酷:你尝试找 bug 不花钱,如果它没找到 bug,你花 0 元;如果它找到了一个 bug,你点了接受,界面上会在括号里显示"1 美元",于是你花 1 美元接受了一个 bug。当然会有担心:我们花了很多计算,也许人们会只是复制粘贴(而不点接受)。我觉得那是个隐忧。还有一个担心是,把钱引入产品会让它变得……不再那么好玩了。你不得不去想钱的事,而你只想想代码的事。所以也许更合理的做法是把它剥离出去:你每个月付一笔费用,然后所有这些功能免费用。但仍然可以有一个打赏组件。它还是会带上那个美元符号。我觉得那没问题。但我也理解"也许你根本不想引入它"的观点。对,我本来想说,人们真正会这么做的时刻,是他们分享的时候:他们有了一个绝妙的例子,就分享给朋友们。还有一种可能的世界,是对这个"荣誉制度"问题有技术解法。如果我们能达到更理解系统输出的程度——比如我们之前谈的用 LSP 做错误检查、再加上运行代码——如果你能达到真正以某种方式验证"哦,我确实修好了这个 bug"的程度,那么赏金系统也许就不必依赖荣誉制度了。终端和代码之间有多少交互?如果你在终端里运行代码,能获得多少信息?能不能做一个循环:它运行代码,如果代码在运行时报错,它就建议如何修改代码?现在它们是完全分开的两个世界吗?我知道你可以在终端里用 Ctrl+K 来帮你写命令。你也可以在 Command+K 之类的场景里使用终端上下文,基本上都可以。不过循环的部分我们还没有。我们猜想那样的东西可能非常有意义。还有一个问题是:它是在前台发生,还是像我们讨论过的那样在后台发生。当然,后台挺酷的。我们在用不同的方式运行代码。而且这里还有数据库这一侧的问题:怎么保护它不去修改数据库?嗯,这方面确实有很酷的解决方案。有一个正在开发的新 API——不在 AWS 上,我记得是在 PlanetScale,不知道 PlanetScale 是不是第一家加的——是给数据库加"分支"的能力。如果你在开发一个功能,想针对生产数据库做测试,但又不想真的动生产数据库,你可以给数据库加一个分支。实现方式是给预写日志(write-ahead log)加分支。要正确地实现这个显然有很多技术复杂性。我猜数据库公司需要新事情可做,因为他们现在已经有很好的数据库了。我们在用的数据库之一 Turbopuffer,可能也会给预写日志加上分支。所以也许 AI 智能体会用分支:它们针对某个分支做测试。到时候"支持分支"可能会成为对数据库的一种硬性要求。如果能给文件系统加分支就太有意思了,对吧?对。我感觉一切都需要分支。就像——

对,就像多重宇宙的问题,对吧?如果你对一切都分支,那就太多了。我是说,显然有那些超级聪明的算法,能确保分支时不会真的占用大量空间或 CPU 之类的。好,这是个问基础设施的好时机。你们主要用 AWS。有什么有趣的细节?有什么有趣的挑战?为什么选 AWS?为什么 AWS 依然赢?

AWS 就是真的非常非常好。真的很好。每当你用一个 AWS 产品,你就知道它一定能用。虽然把它设置起来的过程可能是十足的地狱。为什么它的界面那么糟糕?因为它就是太好了,不需要好界面。这就是赢家的姿态。我觉得正是,这就是赢家的本性。对。但 AWS 你永远可以信任——它总是能用。如果出了问题,那多半是你自己的问题。好。有没有什么有趣的……

……挑战吗?你们是一家相当新的创业公司,要扩展到服务这么多人。对,我认为这是一段有趣的旅程。每秒请求数每加一个零,你就会撞上各种问题:你用的缓存、数据库这些通用组件,随着规模变大都会出问题。我们现在的规模已经大到会在数据表里遇到整数溢出之类的事情了。此外还有一些我们自己构建的定制系统,比如我们的检索系统——为你的代码库计算语义索引、回答关于代码库的问题——我一直觉得它是较难扩展的东西之一。我有几个朋友是非常非常资深的工程师,他们的一句口头禅是:系统在扩展时会在哪里崩,是非常难预测的。你可以试着提前预测,但每当你加上那个额外的零,总会有些奇怪的事情发生。你以为你把一切都想过了,但其实并没有。就那个特定系统而言,具体细节是这样的:我们显然会上传——我们把你的所有代码切块,然后把代码发上去做嵌入,把嵌入向量存进数据库,但我们不存任何代码本身。这里还有一些考虑是为了确保不引入客户端 bug——我们对客户端 bug 极其偏执——我们把大部分细节放在服务器端,一切都是加密的。所以技术挑战之一,始终是确保本地索引、本地代码库状态与服务器上的状态一致。我们技术上最终的做法是:对每一个文件,你可以保存一个哈希;对每一个文件夹,你可以保存一个哈希——它是其所有子节点哈希的哈希——递归地一直做到顶层。为什么要做这么复杂的东西?一种做法是给每个文件保存一个哈希,然后每分钟尝试下载服务器上的哈希,找出服务器上不存在的文件——也许你刚创建了一个新文件,也许你刚删了一个文件,也许你切到了新分支——然后调和客户端与服务器的状态。但那会带来极其巨大的网络开销。一方面在客户端——用 Cursor 的人可没人希望我们一直轰炸他们的 Wi-Fi;另一方面也会给数据库带来巨大的开销,相当于每秒都在读一个几十 TB、接近 20 TB 的数据库,那太疯狂了,你绝对不想那么干。所以做法是:你只尝试调和项目根部的那一个哈希。如果出现不匹配,你就去找分歧在哪里:看子节点的哈希是否匹配,不匹配再看它们的子节点,如此下去。但只有在不匹配的场景下才这么做,而对大多数人、大多数时候,哈希都是匹配的。有点像分层的调和。对,类似那样。它叫 Merkle 树。对,Merkle 树。能看到你们不得不把这些问题都想一遍,挺酷的。之所以变难,就是因为用的人多了。而且如果你的一些客户拥有非常非常大的代码库……我们当初重新梳理过自己的代码库,它挺大的,但完全比不上某些存在了 20 年的公司,有海量的文件,而你要把这个能力扩展到全部程序员。这里有各种细节:做出简单版本很容易,但把它扩展到很多人、很多公司,显然是个难题。这与……除了扩展我们目前的方案,还要提出新想法(我们显然也在做),然后在过去几周里把所有这些也扩展上去。这个索引系统里还有很多聪明的附加设计。比如,成本的瓶颈不在于把东西存进向量数据库,而在于对代码做嵌入。你不想为一家公司里使用完全相同代码的每一个人都重新做一遍嵌入——也许他们只是在不同分支上、有几个不同的文件,或者做了一些本地修改。因为嵌入才是瓶颈,你可以用一个聪明的技巧,而不必操心处理分支和其他数据库的复杂性:就是对根据给定代码块哈希计算出的向量做缓存。这意味着当公司里第 n 个人进入他们的代码库时,速度会非常非常快。而且这一切都不需要在我们的服务器上存任何代码——不存代码数据,我们只在向量数据库和向量缓存里存向量。目前从代码库索引中获得的最大收益是什么?纯粹好奇,用户能得到什么好处?看起来长期收益会越来越多,但短期就是对代码库提问?那个的用处是什么?我认为最明显的用处是:你想在你的大代码库里找到某件事发生的位置,你只有一个模糊的记忆——"我想找到我们做 X 的那个地方"——但你并不确切知道在普通文本搜索里该搜什么。这时你按 Command+Enter 用代码库聊天来提问,很多时候它就能找到你心里想的那个正确位置。而且像你提到的,未来这只会越来越强大。我们正在大力提升检索质量。我认为它的天花板远比人们以为的高得多。这里有个值得问的问题:你们有没有考虑过——以及为什么没怎么做——本地化?我们刚才讨论的一切放到云端都极其困难:你得考虑缓存的所有这些问题、大量程序员共用同一个大代码库的问题,得解开那个谜题。而大多数软件就是把这类繁重的计算放在本地做的。你们考虑过在本地做嵌入吗?考虑过。我觉得在本地做会很酷,只是真的很难。要记住一点:我们的一些用户用的是最新的 MacBook Pro,但我们超过 80% 的用户用的是 Windows 机器,其中很多性能并不强。本地模型实际上只在最新的电脑上才跑得动,而且把它做进产品也是很大的开销。所以即使我们想做,目前也不是我们能够聚焦的事情。有些人在做本地方案,我觉得那很好。但随着模型越来越大、你想用更大的模型做更花哨的事情,在本地做就更难了。对,而且这不是"电脑弱"的问题。比如你是一家大公司,你有大公司的代码库,即使在最强悍的 MacBook Pro 上,处理大公司代码库也真的非常难。这甚至不是"你是不是学生"的问题。即使你是大公司里最好的程序员,如果一切都在本地做,你的体验也会很糟糕。你可以勉强凑合,但那又一次不好玩了。对。在那种海量代码库上做近似最近邻检索,会吃光你的内存和 CPU。这还只是检索。再说说模型这一侧:本地模型面对巨大的逆风。其一,趋势似乎是走向 MoE。MoE 的一个好处也许是它们更受内存带宽约束,这一点相对于用 GPU(或者说 Nvidia GPU)来说对本地是有利的;但坏处是这些模型总体上更大了,它们往往甚至不能装进单个节点,而需要多个节点。哪怕是很好的 MacBook 也绝无可能装下。而且我认为特别是对编程来说,问题并不是"模型是否跨过了'够好'的门槛,然后我们就满足了"——那也许适用于其他问题、也许正是本地模型的用武之地——但人们永远想要最好的、最智能的、能力最强的东西,而那对几乎所有人来说都非常非常难在本地运行。你不是也想要能力最强的模型吗?你想要 Sonnet。对,还有 o1——

——听你刚才推销 o1 的样子,你会满足于一个更差的模型吗?听着,是的,我是那种人。但确实有些人喜欢在本地做事情,尤其是——真的有一整个开源运动在抵制(中心化),而他们的存在其实是件好事,因为你需要抵制正在壮大的权力中心。其实有一个替代本地模型的方案,是我特别喜欢的。我认为它还很大程度上处于研究阶段,但你可以设想为语言模型推理做同态加密(homomorphic encryption):你在本地机器上加密你的输入,然后发送上去,服务器可以在这些加密数据上使用大量计算、运行你在本地跑不动的模型,但他们看不到数据是什么。然后他们把答案发回来,你解密答案,只有你能看到答案。这仍然非常处于研究阶段,所有的工作都是在设法降低开销,因为现在的开销真的很大。但如果能实现,我认为那会非常非常酷,也会非常有影响力。因为有一件事其实挺令人担忧的:随着这些模型越来越好,它们会在经济上越来越有用,于是世界上越来越多的信息和数据将流经一两个中心化的主体。这里既有传统的黑客攻击的担忧,也产生了一种可怕的局面:如果全世界的信息都以明文流经一个节点,就可能出现非常糟糕的监控。有时这会以"好的理由"开始:人们想防止坏人用 AI 模型做坏事,于是加入一些监控代码;然后另一些人进来了,你就走上了滑坡,开始用世界上的大量数据做坏事。所以我非常希望我们能解决同态加密,实现隐私保护的机器学习。我要说,这是我们如今所有软件共同面临的挑战:云端能提供太多功能,我们所有人越来越依赖它,它让我们的生活很美好,但也有坏处。这就是为什么你要依靠真正好的安全防护来抵御基本的攻击。但同时,控制这些数据的公司只有一小撮,他们显然握有筹码,而且可能被各种方式渗透。这就是我们生活的世界。对。我真正相当担心的是这样一种局面:Anthropic 有负责任扩展政策(responsible scaling policy),现在我们处于低 ASL(Anthropic 安全等级)的模型阶段。但当我们到了那种非常强大的 ASL-3、ASL-4 模型时,出于大体合理的安全理由,你会想监控所有的提示词。我认为那是合理的、可以理解的,每一方的出发点都能理解。但天哪,如果全世界的信息都被那样重度监控,那太可怕了,太中心化了。你走在一条非常细的线上:一边是你不想让模型失控,另一边是——老实说,我不知道我是否信任让全世界的信息都流经三家模型提供商。为什么你觉得这和云服务商不同?因为很多这类数据本来根本不会上云。你往往想给 AI 模型更多的数据——你会把那些你本来绝不会放到网上的个人数据交给这些公司或这些模型。而且它还使控制更加中心化:现在用云,你通常可以用自己的加密密钥,云厂商能做的其实有限;而在这里,是中心化的主体看到一切的确切明文。说到上下文,这其实是我写代码时的一个摩擦点。比如在 Python 里,导入了一堆东西,你大概能直觉到我想放进上下文里的那类东西。自动弄清上下文有多难?这挺棘手的。我认为未来我们可以在自动计算上下文方面做得好得多。有一点很重要:包含自动上下文是有权衡的。你为这些模型包含的上下文越多,第一它们越慢,请求成本越高,这意味着你能做的模型调用就更少、后台能做的花哨事情就更少。而且对很多模型来说,提示词里信息太多它们会犯糊涂。所以你纳入的上下文在准确性和相关性上的门槛应该相当高。我们已经在产品的某些地方做了一些自动上下文,这绝对是我们想大幅提升的方向。我认为这里有很多很酷的想法可以尝试,既包括学习更好的检索系统——更好的嵌入模型、更好的重排序器——也包括一些很酷的学术想法,有些我们内部也试过。而且整个领域都在广泛探索:能不能让语言模型达到这样一种程度——模型本身就能理解一个新的信息语料库?最热门的版本是:能不能把上下文窗口做成无限的?如果窗口无限了,能不能让模型真正关注这无限的上下文?然后,在它能关注之后,为了让这在实践上可行,能不能对这无限上下文做缓存,不必反复重新计算?还有一些正在尝试的很酷的想法,更类似于微调,把这些信息真正学进模型的权重里。有可能,在权重层面学习,会得到与在上下文内学习(in-context learning)在质上不同的理解。我认为这一切最终会怎么走,现在还没有定论。在此期间,作为一家公司,我们对更好的检索系统、对挑出与你手头工作最相关的代码库部分,感到非常兴奋——我们在这上面能做得比现在好得多。关于"把知识直接学进权重",有一个有意思的概念验证是 VS Code。我们是 VS Code 的分支,而 VS Code 的代码全是公开的。这些模型在预训练时看过所有这些代码,可能也看过关于它的问答,然后又经过微调和 RLHF,能够回答一般性的代码问题。所以当你问它关于 VS Code 的问题时,它有时会幻觉,但有时确实答得不错。它只是碰巧还行。但如果你能专门训练或后训练(post-train)一个模型,让它真正被打造成理解这个代码库的模型呢?这是一个开放的研究问题,我们相当感兴趣。此外还有一个不确定性:你是想让模型端到端地做完一切——也就是在其内部完成检索,然后回答你的问题、生成代码;还是想把检索与前沿模型分离开——也许几个月内你会得到一些比最好的开源模型强得多的超强模型,那时你会想单独训练一个非常好的开源模型来充当检索器,充当把上下文喂给这些更大模型的角色。能不能再多讲讲"后训练一个模型来理解代码库"?你指的是什么?是合成数据的方向吗?有很多可能的尝试路径,肯定不缺想法,问题只在于动手把它们都试一遍,用经验判断哪个效果最好。一个非常朴素的做法是复刻 VS Code 和这些前沿模型身上发生的事情:我们继续做某种持续预训练(continued pre-training),既包含通用代码数据,也大量掺入你关心的某个特定仓库的数据。然后在后训练阶段——先从指令微调(instruction fine-tuning)说起——你有一个常规的代码指令微调数据集,然后再掺入大量关于那个仓库代码的问题。你要么获取真实的问答(可能比较难),要么就像你暗示的那样用合成数据:让模型对代码的各个片段提问,也就是取出代码片段,提示模型为这段代码提出一个问题,然后把这些作为指令微调的数据点加进去。理论上,这可能会解锁模型回答关于那个代码库问题的能力。让我问问 OpenAI 的 o1。你认为那种测试时计算(test-time compute)系统在编程中扮演什么角色?我认为测试时计算……

我认为测试时计算真的非常有意思。此前一直是预训练范式:随着你扩大数据量和模型规模,你会在损失和下游基准上获得越来越好的性能——在我们用它编程或做其他任务时,总体表现也越来越好。而我们开始撞上一堵"数据墙",也就是说,继续在这个范式里扩展会变得困难。所以扩大测试时计算是一条有意思的路:增加推理时使用的浮点运算量,而随着推理时算力的增加,模型性能也获得相应的提升。传统上,我们只能真的去训练一个更大的、始终使用那么多算力的模型;而现在,我们也许可以用同样规模的模型,让它运行更长时间,从而得到一个相当于大得多的模型质量的答案。我特别喜欢的一点是:有些问题也许需要"用 100 万亿词元训练出的 100 万亿参数模型"级别的智能,但那也许只占全部查询的 1%,甚至 0.1%。那你会花费所有这些精力和算力去训练一个成本那么高的模型,却极少运行它吗?感觉完全是浪费。不如训练一个能处理 99.9% 查询的模型,然后对于那些真的真的需要最高智能的少数人,你有办法在推理时把它跑得更久。那怎么弄清哪个问题需要哪种水平的智能?有没有可能动态判断:什么时候用 GPT-4,什么时候用小模型,什么时候需要 o1?对,那当然是一个开放的研究问题。我不认为有谁真正很好地破解了这个模型路由问题。我们对 Cursor Tab 之类的场景有一些初步的实现,但在 GPT-4o、Sonnet、o1 之间切换这个层面上,就更棘手一些。而且还有一个问题:判断"这个东西对 4 级模型来说是不是太难了",本身需要什么水平的智能?也许你需要 o1 级别的模型才能判断。这真的还不清楚。你提到了预训练过程,然后是后训练,再然后是测试时计算,对吧?分开来看,最大的收益在哪里?这里很微妙。因为测试时计算——要让测试时计算奏效,需要一整套训练策略。而这件事真正奇怪的另一点是,除了大实验室——甚至也许只有 OpenAI——没有人真正知道它是怎么工作的。有一些非常有意思的论文透露了他们可能在做什么的蛛丝马迹,也许他们在用过程奖励模型(process reward model)做搜索。但问题是,我们并不确切知道它长什么样,所以很难评论它归在哪一块。我会把它放在后训练里,但也许最终,为让测试时计算奏效而花费的算力将使预训练相形见绌。所以我们甚至不知道 o1 是不是只用了思维链加强化学习,我们不知道他们怎么用这些,我们什么都不知道。不妨推测一下:如果你要构建一个与之竞争的模型,你会怎么做?对,一件要做的事是——我认为你大概需要训练一个过程奖励模型。也许我们可以讲讲奖励模型:结果奖励模型(outcome reward model)与过程奖励模型的区别。结果奖励模型是人们为语言模型训练的传统奖励模型,它只看最终结果。如果你在做一道数学题,那就看你做完的最终结果,给它打个分:我们认为这个结果的奖励是多少。过程奖励模型则尝试给思维链打分。OpenAI 去年夏天有一篇这方面的初步论文,他们用人工标注员构建了一个相当大的、几十万条的思维链打分数据集。总体来看,除了把过程奖励模型用作"在一堆样本之间做选择"的手段之外,我还没见过人们用它的其他有意思的方式。那些论文里做的都是:从语言模型采样一堆输出,然后用过程奖励模型给所有这些生成结果打分——也许再配合一些其他启发式方法——然后据此选出最佳答案。人们认为可能奏效、也希望奏效的真正有意思的方向,是用这些过程奖励模型做树搜索。因为如果你真的能给思维链的每一步打分,那么你就可以分叉、探索思维链的多条路径,然后用过程奖励模型评估你所走的这条分支有多好。对,前提是分支的质量与最终结果的质量有很强的相关性——也就是说你有一个好的模型能知道该走哪条分支,不只是短期的,而是长期的。对。我认为已经开源、人们在讨论的有意思的工作,是如何以更自动化的方式训练过程奖励模型。我可能说得不对,可能漏了一些论文,但我还没有看到任何"创造性地用过程奖励模型在代码上做树搜索"并且效果特别好的东西。这算是一个 AI 安全问题,也许还有点哲学意味:OpenAI 说他们对用户隐藏思维链,而且他们说那是一个艰难的决定。他们不展示思维链,而是让模型对思维链做摘要。他们还说会在后台监控思维链,以确保模型没有试图操纵用户——这是一个很引人入胜的可能性。总之,你怎么看隐藏思维链这件事?对 OpenAI 来说,一个考虑——这完全是猜测——可能是他们想让人们难以从他们的模型中蒸馏出这些能力。如果你能接触到那个隐藏的思维链,复制这项技术可能实际上会更容易,因为那是非常重要的数据:看到模型达到最终结果所走的步骤,你大概可以在那上面训练。而且这有一个镜像的先例,发生在一些大语言模型提供商身上——这也是猜测——这些 API 中有一些曾经提供对生成词元的对数概率(log probabilities)的便捷访问,也提供提示词元上的对数概率,后来其中一些 API 把这些拿掉了。再说一次,纯属猜测,但一种想法是:拿掉它们的原因是,如果你能拿到对数概率,就像那个隐藏思维链一样,它能给你更多信息,用来把这些能力从 API、从这些最大的模型里蒸馏到你自己控制的模型里。给我们之前关于集成 o1 的讨论加一个星标补充:我认为我们仍在学习如何使用这个模型。我们在 Cursor 里提供了 o1,因为我们拿到模型时真的很想试试,我想很多程序员也会想试。但 o1 并不是 Cursor 默认体验的一部分,我们至今还没有找到一种把它整合进编辑器的方式,使它成为那种你每小时、甚至每天都会伸手去用的东西。所以关于如何使用这个模型,现在还没有定论。我们也还没有看到有人发布的东西能让人清楚地感到"哦,这就是它的用例了"。显而易见值得尝试的方向,也许是让它更容易支撑那些后台运行的东西、让这些模型跑在循环里、让这些模型智能体化。但我们仍在探索。要说清楚的是,我们有想法,我们只是需要在把东西放出去之前,先尝试做出真正极其有用的东西。但它有一些显著的限制。哪怕撇开能力不谈,它不支持流式输出。这意味着当你想监督输出的时候,用它真的非常痛苦,你只能干等着一大段文字整个出现。而且这确实感觉像是测试时计算与搜索的开局阶段,非常像一个 v0 版本,有太多东西感觉不太对劲。我猜想,在人们不断增加预训练数据量与模型规模、在预训练上寻找技巧的同时,现在会有另一条线:让搜索工作得越来越好。那让我问问"Strawberry 的明天"。看起来 GitHub Copilot 可能会以某种方式集成 o1,有些评论说"这意味着 Cursor 完了"。我想我看到一条评论说"是时候关掉 Cursor 了"。是时候关掉 Cursor 了(笑)。那么,是时候关掉 Cursor 了吗?我认为这个领域与以往有点不同……

……不同于 2010 年代以往的软件领域。我认为这里的天花板真的、真的、真的高得难以置信。所以我认为三四年后最好的产品,会比今天最好的产品有用得多得多。你可以大谈护城河、品牌之类的,说这是我们的优势,但我认为归根到底,如果你停止在产品上创新,你就会输。这对创业公司来说也是好事,对想进入这个市场的人来说也是好事,因为这意味着你有机会靠做出更好的东西,去战胜那些已经拥有大量用户的人。所以我认为接下来几年,关键就是构建最好的产品、构建最好的系统。这既取决于建模引擎这一侧,也取决于编辑体验这一侧。对。我认为 Cursor 相对于市面上其他产品的额外价值,大多并不只是快速集成 o1 这样的新模型,而是来自那些你意识不到、却在产品的每一个层面为你工作的定制模型的深度,以及每一个功能里那种深思熟虑的用户体验。好,从这个意味深长的回答,我们再降回技术层面。你提到你们有一个合成数据的分类学。哦对。能解释一下吗?好。我认为合成数据主要有三类。先说什么是合成数据。有普通数据,也就是非合成数据,就是自然产生的数据——通常来自人类做过的事情,从某个人类过程中你获得这些数据。合成数据的第一类是蒸馏(distillation):让一个语言模型输出词元或词元上的概率分布,然后你可以在这上面训练某个能力较弱的模型。这种方法不会让你得到一个比产生这些词元的原模型更强的模型,但如果你想从某个非常昂贵、高延迟的模型中引出某种能力,把它蒸馏进某个更小的、面向特定任务的模型,它就非常有用。第二类是当问题的一个方向比反方向更容易的时候。一个绝佳的例子就是我们之前提到的 bug 检测:引入看起来合理的 bug,比真正检测出它们容易得多——这对人类来说大概也是如此。所以你可以用一个没在多少数据上训练过、不那么聪明的模型往代码里引入一堆 bug,然后用这些合成数据去训练一个非常擅长检测 bug 的模型。最后一类,我想是大实验室在合成数据上主要在做的:用语言模型生成可以被轻松验证的文本。一个极端的例子是:如果你有一个能检测文本是否达到 Shakespeare 水准的验证系统,然后你让一群猴子在打字机上乱敲,最终你就能攒够训练数据,训练出一个 Shakespeare 水准的语言模型。而这非常符合数学的情形:对形式语言而言,验证真的非常非常容易。你可以让一个还凑合的模型生成大量的推理轨迹(rollouts),然后挑出那些确实证明了目标定理的,进一步训练。对代码你也可以做类似的事情,比如 LeetCode 式的问题:如果你有一组测试,你知道通过这些测试就等于真正解决了问题,你就可以验证它通过了测试,然后用通过测试的输出去训练模型。不过我认为要在所有领域、或者一般性地把这个做通会有点棘手:对于那些开放式的杂七杂八的任务、或者更长时程的任务——即使在编程里——拥有完美的验证器感觉真的非常难。这是因为你不像 Arvid 那么乐观。对。所以第三类需要有一个验证器。验证感觉在你确凿知道结果正确的时候是最好的。那种时候就不会是用语言模型去验证,而是用测试、用形式化系统。或者运行程序,甚至做人类形式的验证,也就是人工质量把关。对,对。但那种"语言模型版本"的验证,比如运行程序、模型真的理解了——

对,那介于两者之间。对。我认为那一类是最有可能带来巨大收益的。那 RLHF 与 RLAIF 呢?它们在提升模型性能中扮演什么角色?

RLHF 是指你用的奖励模型是从人类给出的反馈标注中训练出来的。如果对你关心的这类任务你能获得大量人类反馈,我认为这条路是行得通的。RLAIF 则有意思在于,它依赖于这样一个约束:验证确实比生成容易不少。因为乍看之下,你在干什么?你在用这个语言模型去看语言模型的输出,然后改进语言模型——但不,如果语言模型验证某个解比生成它容易得多,那它其实可能行得通,你或许能获得某种递归式的提升。但我不认为它最终会长得和这个完全一样。另一件你可以做的事——我们某种程度上在做——是 RLAIF 和 RLHF 的混合:通常模型其实已经相当正确了——在 Cursor Tab 的场景里,就是在两个可能的生成结果之间挑出更好的那个——它只需要一点点人类的推动,只需要大约 50 到 100 个样例,就能把模型的先验与你想要的对齐。这和普通的 RLHF 看起来不一样,通常人们是在海量样例上训练这些奖励模型。当你比较生成与验证、或者生成与排序时,你的直觉是什么?排序比生成容易得多吗?我的直觉会说:是的,应该如此。这又回到——如果你相信 P 不等于 NP,那么就存在一大类问题:给定证明去验证,比真正给出证明要容易得多得多。我在想,会不会有一天同样是 AI 证明了 P 不等于 NP,或者 P 等于 NP。那真的会很酷。那会是……某个由 AI 获得的 Fields 奖。谁得奖呢?又是一个开放的哲学问题。我其实特别好奇一个好的赌注会是什么:AI 什么时候能拿 Fields 奖?这不是 Aman 的专长吗?我不知道 Aman 在这上面押的是什么。哦抱歉,是 Nobel 奖还是 Fields 奖先来?

Fields 奖先。

Fields 奖级别的成果。我认为 Fields 奖先来。你当然会这么说了。不过那(数学)也是个孤立的系统,可以验证——

当然。我甚至不知道我还需不需要多说……我当时觉得通往 IMO 的路径更清晰一些,因为那时它已经能做出几道 IMO 题了,而且根据当时的文献,有一堆唾手可得的策略可以采用。我认为我现在在定理证明这个领域投入得少多了,对我们离解决那些非常非常难的开放问题有多近,直觉也弱了。所以你觉得会是 Fields 奖先来,而不是物理或其他——

哦,百分之百。我认为那大概率更可能。是的是的是的。我觉得像 BSD 猜想(Birch and Swinnerton-Dyer 猜想)、Riemann 猜想,或者任何一个这种硬核数学难题——它们是真的非常难,连通往一个解的路径长什么样都不清楚。我们甚至不知道路径长什么样,更不用说……

那你不买账"这是个孤立系统、你有很好的奖励系统、感觉更容易训练"这个说法吗?我认为我们可能会在 AGI 之前拿到 Fields 奖。我是说,我会非常高兴的。但我不知道……我想也许 2028、2030 年吧。

Fields 奖?

Fields 奖。好吧。考虑到最近事情进展的速度,那感觉像是永远之后了。说到进展速度,我们聊聊缩放定律吧。对于不了解的人,也许可以先讲讲缩放定律这整个思想:它们是什么?现状如何?你认为未来会怎样?有意思的是,OpenAI 最初那篇缩放定律论文其实略有错误,因为他们在学习率调度上有些问题。后来 Chinchilla 展示了一个更正确的版本。再之后,人们又偏离了"计算最优"的做法,因为人们现在更多是在给定推理预算的前提下优化模型的实际效果。而且我认为这些曲线的维度比我们最初使用的"计算量、参数数量和数据"要多得多……

……推理计算量是显而易见的一个维度,我认为上下文长度是另一个明显的维度。比如说,如果你最关心推理计算量和上下文窗口这两件事,那你也许想训练的是某种 SSM(状态空间模型),因为它们在超长上下文下便宜得多、快得多。即使它们在训练时的缩放特性可能差 10 倍——也就是说你得多花 10 倍的计算量才能训练出同等能力的模型——也是值得的,因为你最在乎的是超长上下文窗口下的推理预算。所以看人们如何在这些维度上腾挪,会很有意思。对,你说到了多个维度。最初的概念显然只是看两个变量:以参数数量衡量的模型规模,和以词元数量衡量的数据规模,并研究两者的比例。存在一个数字——或者至少一个下限——这个想法挺有吸引力的,而且当时似乎正有一个数字浮现出来。你还相信"越大越好"吗?我认为,就纯粹性能和纯粹智能而言,更大肯定更好。我认为人们可能走的路径是——我个人特别看好蒸馏。可以拧的旋钮有多少?比如我们花一大笔钱做训练,得到能力最强的便宜模型。真正尽你所能去在乎推理时计算——这件事的朴素版本人们已经做过了,就是 Llama 那些模型:把 7B 模型在远超 Chinchilla 最优的词元量上狠狠地过度训练。但如果你真的在乎,也许该做的是 Gemma 的做法:不只是在词元上训练,而是直接以最小化"与 Gemma 27B 的分布之间的 KL 散度"为目标来训练,也就是知识蒸馏。你花费的是让这个 270 亿参数模型跑遍所有这些词元的计算量,只为了得到那个更小的模型。而蒸馏给你的是一个更快的模型——更小意味着更快。对。蒸馏在理论上是从你训练的数据中榨取更多信号。它也许是跨越——不是完全跨越,而是部分缓解——数据墙的另一种方式:你可用于训练的数据就那么多,那就让这个非常非常大的模型在所有这些词元上训练,然后把它蒸馏进一个更小的模型,也许对于这个小得多的模型,每个词元能得到比直接训练它更多的信号。如果我给你 10 万亿美元,你会怎么花?当然,你不能买岛什么的。在"改进大模型"与"比如花钱做 RLHF 里的 HF"之间,你会怎么分配?我认为训练这些大模型有很多秘密和细节我就是不知道,只有大实验室的人才掌握。问题是,即使我尝试去做,也会浪费掉很多钱,因为我不知道那些东西。先大幅悬置怀疑,假设你拥有那些专门知识——或者如果你是说必须用你现在有限的信息来操作——

不不不,我的意思是你直接空降进去,拿到所有信息、所有小的特性、所有定义训练方式的小参数。我们要看的是:在未来 5 年里,为了最大化你所说的"纯粹智能",钱应该怎么投?答案难道不是特别简单吗?你就是尽可能多地搞算力。归根到底,你要买的只是 GPU,然后研究人员就可以找到……你可以自己调,在大模型和小模型之间权衡。这就引出一个问题:你到底是被算力和钱限制,还是被别的东西限制?我更倾向于 Arvid 的看法:我们某种程度上是被想法限制的。但也总有那个"但是"——

如果你有很多算力,你就能跑很多实验。所以你会跑很多实验,而不是拿那些算力去训练一个巨型模型?我会(跑实验)。但我确实相信我们在想法上是受限的。我觉得,对。因为即便有所有这些算力、能收集到世界上的所有数据,你最终受限的甚至不是想法,而是真正优秀的工程。即使拥有世界上所有的资本,你真能召集起来吗?世界上真正能在这里带来改变的人并不多。研究里有大量的工作,就是纯粹的、非常非常难的工程工作。举一个比较随手的例子:你去看最初的 Transformer 论文,有多少工作是把文献中蕴含的一堆非常有意思的概念联结在一起,又有多少工作是进去写代码——比如 CUDA 内核,或者别的什么(我不知道它最初是跑在 GPU 还是 TPU 上)——把 GPU 性能真正打满?让 Noam Shazeer 进去做所有这些代码,对吧?Noam 大概是世界上最好的工程师之一。或者更进一步,看下一代模型:让模型并行(model parallelism)工作起来,把它扩展到几千块、也许几万块 V100 上——我记得 GPT-3 大概就是那样。要让这一切运转起来,必须投入太多的工程努力。如果你能把那个成本降下来——也许降不到零,但降低 10 倍,让拥有绝妙想法的人能超级容易地立刻做出他们梦想的新架构的版本,在 GPU 上拿到 50%、40% 的利用率——我认为那会把研究加速一大截。我认为,如果你看到一条清晰的改进路径,你总应该先摘低垂的果实,对吧?OpenAI 和其他所有实验室摘低垂果实的做法大概是对的。低垂的果实就是:你可以一路扩展到 GPT-4.25 的规模,你就继续扩展,东西就不断变好。当一切都在奏效时,就没有必要去实验新想法,你应该死磕下去,尽可能榨出更多的汁来。然后,也许当你真的需要新想法时……我认为如果你在花 10 万亿美元,你大概会想花一些钱去真正重新评估你的想法——那个时候你大概就是被想法限制的了。我想我们所有人都相信,要一路走到 AGI,可能需要新想法。而且我们大概也都相信,存在在更小规模上测试这些想法的方法,并且能够相当有信心地判断它们能不能成。只是对实验室来说,在他们目前的位置上,很难把非常有限的研究和工程人才投入到探索所有这些其他想法上——因为眼前有那条核心路线,在相当长一段时间内大概率还能继续提升性能。对,而且这些大实验室正在赢,所以他们就一路狂奔。好。那么一个大问题,展望未来:你们现在处于编程世界的中心。你认为在接下来的几个月、一年、两年、五年、十年里,编程——编程的本质——会如何改变?我们真正兴奋的,是一个程序员长期坐在驾驶座上的未来。你们已经听我们聊过一点这个了:一个强调程序员的速度与能动性(agency)、强调控制权的未来——你能修改任何你想修改的东西,能对你正在构建的东西进行非常快速的迭代。我认为这与这个领域里某些人正跳向的方向有点不同。有一个吸引了很多人的想法是:你能不能对你的计算机说话,让它替你构建软件,就像你在 Slack 上跟一个工程部门或一位工程师交流那样?能不能就是一个孤立的文本框?我们对那个方向不感兴趣的一部分原因,是我们谈过的延迟问题;但更大的一部分原因是,那意味着放弃大量的控制权。在文本框里说话时,你很难做到真正具体。而且如果你注定只能像与一个工程部门沟通那样与这个东西沟通,你实际上是把大量大量真正重要的决策拱手让给了这个机器人。这触及了工程的本质。我认为一些离工程稍远的人可能会以为:规格说明已经完整写好了,工程师们只是过来实现,只是把它变成代码、让它存在。但我认为,很多最好的工程——我们享受的那种工程——包含大量微小的决策:关于你到底在构建什么,以及在速度、成本和其他各种因素之间非常艰难的权衡……

……这些都牵涉在一个系统之中。只要真正设计软件的还是人类、指定要构建什么的还是人类——而不是一家全由 AI 运营的公司——我们认为你会真正希望人坐在驾驶座上,主导这些决策。至于那具体长什么样,现在还没有定论。我认为一个有点奇特的设想是:你可以控制你查看代码库的抽象层级。你可以指向代码库的特定部分,也许你以伪代码的形式消化一个代码库,而且你还可以直接编辑那些伪代码,让修改在下面形式化编程的层面落地。你可以纵览软件中任何一段逻辑的整体面貌(gestalt),你保留编程中文本编辑的心流成分,你保留控制权——你甚至可以下沉到代码层,也可以在更高的抽象层级上操作——同时获得巨大的生产力提升。如果能在抽象栈上自由上下移动就太好了。对。那里还有很多细节要解决,这还是个模糊的想法,时间会证明它是否行得通。但这些原则——控制、速度、人在驾驶座上——我们认为真的非常重要。对某些事情,就像 Arvid 之前提到的,对某些编程风格,你可以用类似 ChatGPT 的方式整个交出去——比如你有一个规格说明得非常清楚的 bug。但那不是编程的大部分,也不是我们认为很多人所珍视的那部分编程。那编程这项基本技能呢?现在有很多人——尤其是年轻人——有点害怕。他们热爱编程,但担心"如果我走这条职业道路,我还会有未来吗"。你们认为编程这项技能本身会发生根本性的变化吗?我实际上认为,现在是构建软件的一个非常非常令人兴奋的时代。我们还记得 2013 年、2012 年左右编程是什么样子:那时有多得多的糟粕和样板代码,要去查一些特别烦人的东西。那些东西今天仍然存在,肯定没有归零,但今天的编程比那时好玩多了。我们正在真正逼近"乐趣的浓缩":所有真正吸引人们编程的东西——比如能非常快速地构建东西、速度、个人控制——所有这些都被大幅调高了。所以我认为,对构建软件的人来说,这将是一段非常非常有趣的时光。我认为技能大概也会改变。我认为人们的品味和创造性想法会被放大,而样板式的文本编辑会变少,甚至"谨小慎微"也会变少一点——那在今天对程序员来说非常重要。我认为它会有趣得多。你们怎么看?我同意。我非常兴奋于能够修改……最近发生的一件事是:我们想对代码库做一次相对大的迁移。我们之前在 Node.js 里用 async local storage,众所周知它性能不太好,我们想迁移到我们的 context 对象。这是一次影响整个代码库的大迁移。Sualeh 和我花了大概五天时间来做这件事——即使有今天的 AI 工具。我非常期待这样的未来:我只要展示几个例子,AI 就把同样的改动应用到所有相关位置,然后它高亮出"哦,这是一个新情况,该怎么处理?"我再精确演示那里该怎么做。然后这件事 10 分钟就能完成。这样你就可以快得多地迭代。你不必事先想那么多、站在黑板前苦思"我们究竟该怎么做"——因为试错成本太高。你可以先随便试一下,发现"哦,这其实不完全是我想要的",然后马上再改。所以,是的,我认为未来做程序员会非常有趣。对,我很喜欢这一点。编程很多时候感觉有两种路子:一种是你事先非常认真仔细地思考最佳方案,然后用你有限的工程时间去实现它;而我更喜欢直接扎进代码里,先试着写一把,看看它大致长什么样,然后在那个基础上快速迭代。那样感觉更有趣。对。就说生成样板代码这一点就很棒,你可以专注于那些微妙的、困难的设计决策。迁移——我觉得这个很酷——大语言模型基本上能够从一种编程语言翻译到另一种编程语言,或者在"迁移"这个词的一般意义上做迁移。但这是当下的情况。人们的恐惧在于:随着模型越来越好,你做的创造性决策就越来越少,会不会走到这样一个境地——你在自然语言的设计空间里操作,自然语言成为主要的编程语言?也许我可以借"给建议"的方式来问:如果有人现在对编程感兴趣,你们觉得他们该学什么?比如你们是从 Java 起步的,还有——我忘了——

一些 PHP。

PHP。

Objective-C。

Objective-C,没错。反正到最后我们都知道 JavaScript 会赢,而且不是 TypeScript,就是原版 JavaScript。它会吞噬世界,也许还有一点 PHP。这也引出另一个问题。我记得 Don Knuth 有一个说法,人口中某个百分比是"极客",编程需要一种特定的心理特质。感觉现在,能做出色编程的人的类型会不会越来越扩大?我认为不同的人编程有不同的理由。但我认为,真正的、也许最好的程序员,是那些真的、发自内心热爱编程的人。比如我们团队里有些人,下班回家之后,会打开 Cursor,开始给自己的业余项目写代码,写一整晚,写到凌晨三点。他们难过的时候会说:"我就是真的需要写代码。"我认为存在那种层次的程序员——那种对编程的痴迷与热爱,造就了真正最好的程序员。我认为这类人会真正钻研事物运作的细节。我想我问的正是那种程序员。当那个超级 Tab——那个超棒的、"赞美 Tab"——成功的时候,你会不停地按 Tab。你们团队里那个人比谁都热爱 Cursor Tab,对吧?对。而且这不只是按 Tab。"按 Tab"只是顺口的说法,是宣传语。你按 Tab 时实际在做的,是在持续不断地注入意图。有时你拒绝建议,有时你多敲几个字符。你就是在以这种方式塑造正在被创造的东西。我认为编程会大大地变成"你到底想要创造什么"。这是一种更高带宽的方式——你与计算机的沟通带宽会越来越高,而单纯打字的带宽,比起传达意图来说是低得多的。这正好呼应你们题为《Engineering Genius》的宣言:"我们是一个应用研究实验室,构建非凡的、高产出的人机(human-AI)系统。"这说的正是这种混合的元素。"首先,我们在打造未来的工程师:一个人机混合程序员,其效能比任何单个工程师高出一个数量级。这位混合工程师将毫不费力地掌控他们的代码库,不敲任何低熵的键。他们将以自己判断力的速度进行迭代,即使在最复杂的系统中也是如此。通过结合 AI 与人类的巧思,他们将比最好的纯 AI 系统更聪明、更高明。我们是一群研究者和工程师。我们构建软件和模型,在'有用'与'可能'的边缘发明创造。我们的工作已经改善了数十万程序员的生活。"而在通往那个目标的路上,至少会让编程更有趣。那么,感谢你们今天的对话。谢谢。谢谢你邀请我们。谢谢。感谢收听这场与 Michael、Sualeh、Arvid 和 Aman 的对话。想支持本播客,请查看简介中的赞助商。最后,让我用我在 Reddit 上看到的一句随机、有趣、也许还很深刻的编程格言来结束:"没有什么比一个能用的临时解决方案更持久。"感谢收听,希望下次再见。