← 返回 关于

我们如何在两周内让 Claude.ai 快 3 倍

2026-09-24 · 原文链接

今年 8 月,我们用两周冲刺,将 claude.ai 和 Claude 桌面应用的核心用户体验提速约 3 倍。用户一直告诉我们它很慢,而他们说得没错。整个冲刺只靠一个 Slack 频道推进,每个线程里都有 Claude。

我们聚焦于占用户活动 95% 的四条关键旅程。在第 75 百分位上,首次加载 claude.ai 到页面可输入的时间从 3.1 秒降至 0.55 秒;启动新的 Claude Code 会话从 0.8 秒降至 0.3 秒;加载 Claude Cowork 云端会话从 2.6 秒降至 0.73 秒。合计估算下来,我们每天为用户节省了数万小时的等待时间。

我们使用了 Claude Tag(beta),其运行的内部研究模型能力大致相当于 Opus 5.5。Claude 找到了瓶颈、构建了基准、交付了改进,并监控每一次部署。我们则通过设定目标、权衡取舍和审批每一项变更来掌舵。采用这种方式,我们合并了三千多项变更,没有发生一次面向客户的事故或回滚。本文介绍我们交付了什么、如何衡量,以及为安全完成这一切而与 Claude 建立的闭环。

任务简报

冲刺开始前,我们建立了一个 Slack 频道,并写下以下常驻指令:

@Claude 你的工作是推动一切与 claude.ai 网站和桌面应用性能有关的事务。职责包括:监控部署中的性能回归;评估现有遥测数据的准确性和完整性;维护经过良好整理的可观测性仪表盘;主动为已观察到的问题和易解决项实施方案;提出性能项目机会;并与人类队友沟通。[…]

这个频道的最终目标,是让你尽可能自主;但我们知道,今天这还做不到。

我们让 Claude 通过 Datadog MCP 服务器分析使用数据。它识别出影响最大的四条用户旅程:启动应用、开始对话、加载已有对话,以及发送消息。网页和桌面端、再加上我们的各项产品,共形成十三个不同的衡量指标。为建立基线,我们持续补充埋点,直到它们可以直接比较:每一项都由一次用户交互开始,在结果渲染完成后结束,并区分客户端和服务端所做的工作。

我们以大约二十个精挑细选的项目启动冲刺,每个项目瞄准一条特定旅程。Claude 以毫秒估计每个项目的影响,我们汇总这些估计来设定冲刺目标。有些项目规模不小,但我们认为两周内大概能完成大多数。

到第三天,我们就达成了十三个目标中的十二个。

计划中的项目很早就落地了。为加快启动,我们把静态输入框直接烘焙进 HTML,让用户能在 React 初始化期间开始输入;又预编译了 V8 代码缓存,避免桌面壳层的主进程每次都从头重新编译。为加快导航,我们在不同对话之间保持输入框挂载;用户悬停会话时预取数据;并将侧边栏重新渲染减少了 90%。

我们还预留了空间,让 Claude 自己发现机会并提出新的工作流。这些工作流很快扩展成完整项目,远超最初目标。因此我们设定了新的目标,然后继续寻找更多可测量的东西:

@Claude 我们最终几乎投入了原项目列表里的每一个项目,甚至更多。我们来重新盘点一下 […] 还有什么没探索过?还能在哪些地方继续爬山?现阶段哪里机会最大?[…] 我接受疯狂的想法。

一切都可以爬山

从一开始,我们就知道自己希望迭代速度超过部署节奏。Claude 可以异步工作数小时,甚至通宵;我们想让它验证原型,而不用等待线上读数。为此,我们寻找了在实验室中衡量性能的其他方法。

Sam 找到了第一条线索:

#claude-apps-perf · thread

  1. Sam

    除了测量墙钟时间,我们还能做什么?比如能否测量 JS 指令数?

  2. ClaudeApp

    可以。对于纯 JS 热路径,可以测量字面意义上的指令数:在 Valgrind 下配合 node --predictable 运行基准,再与签入仓库的基线比较——一次运行即可,不需要统计学处理。

    浏览器路径无法在 Chromium 下计数指令,但还有一阶其他确定性计数:每次交互的 React commit 数、从 V8 精确覆盖率得到的函数调用数、布局和样式重算次数、DOM 变更次数。你想先做哪一个?

  3. Sam

    用一个线程探索 valgrind + Ir + --predictable,再为每个 browser/react benchmark 分别新开线程。所有线程都 @ 我。你知道我们想要什么。开始吧。

根据真实对话重建。

十一分钟后,五个线程已经跑起来,每个关注一种不同的度量:指令数、V8 调用数、React commit、样式重算和 DOM 变更。

我们对每个新基准都保持怀疑。它各有两项工作:第一,作为 Claude 能在实验室中改善的指标;第二,作为 CI 中只能持续收紧的护栏。如果一个基准不稳定,或根本不与用户延迟相关,我们就丢弃它,而不是让 Claude 爬错山。

@Claude 请证明,针对其中每一项爬山都能带来可衡量的墙钟时间性能提升。任何无法证明这一点的候选项,我们都会撤掉相应基准。

墙钟时间是用户真正感受到的东西,但它有噪声,而且毫秒级波动太大,不能作为 CI 门槛。指令数很有吸引力,因为它是确定的;但我们仍需要 Claude 证明它能跟踪墙钟时间。

于是我们让 Claude 在两条热路径上持续降低计数:一条是构建对话消息树的例程,另一条是扫描 Claude Code 输出中状态行的扫描器。Claude 用 Valgrind 对两者做了剖析,发现第一条路径四分之一的指令来自巨型态字典查找:同一个消息 ID 被分别解析了三次。

一小时后,它将两条路径的指令数分别削减了 48% 和 31%,墙钟时间则分别下降了 78% 和 44%。我们签入了两个新的收紧阈值。从那以后,任何提高这些路径指令数的 PR 都会在 CI 中失败;每天的任务会在计数进一步下降时降低各自上限。

这让我们得到冲刺最核心的一课:有了 Claude,测量某件事就能让它变得可攻克。

以前,测量是第零步:你添加一个指标,等待数据流入,然后才开始理解问题。有了 Claude,它变成了爬山的第一步。Claude 一旦有了要击败的数字,就可以开始优化。这意味着我们能做的、杠杆最高的事情,就是找出更多可测量的东西。

闭环:逐线程推进

这一切都运行在同一个 Slack 频道中,多名工程师和 Claude 在每个线程里一起协作。冲刺由此沉淀为一个闭环:

  1. 有人会就一段缓慢的用户旅程开一个线程,通常带上截图或录屏。
  2. Claude 会追踪流程,再找到或构建一个能展示问题的基准。
  3. 当它在实验室里得到有希望的结果,就会带着 PR 回来——通常是多个 PR,按风险和评审拆分;任何用户可见内容都放在开关后面。
  4. 变更发布后,Claude 会监控部署并读取线上数据。
  5. 如果性能提升,Claude 就收紧基准以锁定收益;如果没有,就关闭开关并继续迭代。
  6. 然后它会在同一条用户旅程中寻找下一个慢点。

举个例子:有人分享了一段录屏,显示页面加载后侧边栏行项才逐渐出现。Chat 和 Cowork 的行项在不同时间解析完成,让页面显得卡顿。我们现有的监控没有捕获这一点。最接近的指标是累计布局偏移,但每次偏移只有约 0.008——远低于 0.1 的良好阈值。

Issac 想到可以直接引用底层的 Layout Instability API。Claude 创建了一项遥测事件,将每个 layout-shift 条目的 sources 映射到命名区域(如侧边栏、转录区)和阶段(如首次绘制前、可输入后)。它添加了一个集成测试:以填充数据的侧边栏打开页面,直到首次绘制后才提供侧边栏数据,并在任一命名区域发生任何偏移时失败。Claude 用它来证明修复有效:该测试在 main 上连续 20 次变红,在 PR 上连续 20 次变绿。

事件部署后,Claude 读取线上数据,发现 31% 的网页加载在页面可用后、且没有任何用户交互时,仍有元素发生移动。之后,Claude 按名称逐一处理原因:迟到的头部行、用户名加载后横向滑动的插入光标、滚动条出现时移动的列表。Claude 成批修复最主要的违规项;它们消失后,又找到了下一批。

claude.ai 侧边栏加载的左右对比录屏。此前,行项迟到并重新排列:十行跳动、九行出现、四行消失。之后,行项在最终位置填充,任何内容都不会移动。

视频侧边栏卡顿,优化前后对比 · 限速 4G

这只是一个线程。冲刺期间,我们同时运行的线程超过一百五十个。

横向扩展

一旦闭环在一个线程中奏效,要在更多线程中运行它,只需要继续打开它们。Claude 不会在最初请求完成后关闭线程,而是会继续推进。单个线程会产出五十个、有时一百个优化 PR。越来越多时候,新线程由 Claude 而不是我们创建,用来追逐它在另一项调查或夜间任务中自行发现的机会。频道中的工程师 Shelley 观察道:“[这个模型] 是个数字恶魔。”

每一种度量都发现了可改进的地方。Claude 做了一次 React hook 普查,发现输入框的打字路径上有 6,900 个 hook 和 900 个 store 订阅,每次按键都触发重新渲染。Claude 统计样式重算,发现单个 :root:has() 选择器为每次 DOM 变更增加了 24 毫秒。Claude 追踪首次绘制后的代码路径,发现一个遗留的 location.reload() 每天造成五十万次隐式重载,而我们的加载指标没有一个能看到它。Claude 读取空闲标签页的 profiler 样本,发现相同的缓存快照每分钟两次被克隆进 IndexedDB,而且全在主线程上完成。

我们很少知道一个线程会走向哪里。在一次 CPU 卡顿排查中,Claude 注意到为完成的代码块加高亮可能让页面冻结约一秒。它在实验室中深挖,找到罪魁祸首:长破折号。如果回复的 Markdown 含有任意非 Latin-1 字符,例如长破折号或弯引号,V8 就会把整段字符串存为 UTF-16,令每个语法高亮正则表达式走更慢的双字节路径。Claude 用二十行改动修复了它:在高亮前将每个代码块复制进单字节字符串。

到第二周,我们几乎无法在每日更新中概括产出。最忙的日子里,落地的改动超过两百项。Claude 持续提出新基准;约三分之一的 PR 包含额外遥测或护栏,而每一项新埋点又催生更多线程和更多机会。

在同一个频道里协作意味着一切都公开发生。我们在彼此的线程之间进出,讨论决策,也庆祝成果。消息传开后,其他团队开始把自己的变更带到频道里做性能评审。由于引入了众多护栏和 Claude skills,新项目的写法也在细微处变得更高效。

护栏

我们为这样的节奏做了准备。由于几乎所有触及之处都是热路径(首次绘制、输入框、转录区),我们预先建立了安全机制。每个 PR 都要经过自动评审,且至少有一名人类批准;单元测试总是在优化之前;任何可能造成用户可见问题的内容都通过短生命周期功能开关发布。

当功能开关开始堆积,我们开了一个线程协调它们的灰度与清理。Claude 将每个开关归类为紧急关闭开关或逐步放量开关,并在安全后尽快淘汰。两周中,我们引入近两百个开关,到结束时已有一半以上清理完毕。

我们也知道,性能收益会在快速变化的代码库中衰减,而且 Anthropic 的代码发布很快。一旦项目证明了收益,我们便投入资源保护它。以静态输入框为例,它的设计本身就比较脆弱。我们几乎立即向用户展示 HTML 版页面,然后让 React 直接在其上完成绘制。

claude.ai 首次加载的左右对比录屏。此前,页面一直空白,输入框在 2.93 秒时接收输入。之后,静态问候语和输入框在 0.36 秒时即可输入;用户键入消息,真正的输入框约在 3 秒时淡入,文本仍被保留。

视频静态输入框,优化前后对比 · 限速 4G

如果 React 的渲染偏离哪怕一个像素,魔法就消失了。因此 Claude 构建了几十道护栏:

并非所有问题都能在实验室中捕获,所以我们也采用了书中最古老的护栏:渐进式发布。高风险变更先向员工推出,再向 1% 用户推出,最后全量发布。静态输入框内部发布四小时后,一位队友分享了一段录屏,显示了一次所有指标都看不到的布局偏移。他在新标签页打开 claude.ai 时,输入框会下移——但不是我们的代码造成的。

#claude-apps-perf · thread

  1. Marius

    偶尔我会在新标签页打开 claude.ai 时看到一个小一些(大概 15–20px)的纵向布局偏移(把输入框向下推);页面重载时则不太明显。我还无法准确定位原因,但它确实存在。

    录屏 · 0:02

  2. ClaudeApp

    在你的录屏里找到原因了——这是 Chrome 调整了页面尺寸,不是静态输入框与真实输入框交接造成的(今天你 49 次加载中,这一交接全都测得 0 px)。

    • 在新标签页上,Chrome 会在页面下方绘制自己的 56 px 页脚(“Managed by anthropic.com · Customize Chrome”)。标签页导航到 claude.ai 后,页脚消失,页面高出 56 px;但发生在我们首次绘制约 100 ms 之后。
    • `/new` 将问候语和输入框放在页面顶部下方 18% 的高度,因此它们下移 0.18 × 56 ≈ 10 px(你的录像测得也是 10)。重载时没有要移除的页脚,所以只发生在新标签页;所谓“偶尔”取决于首次绘制是否赶在尺寸调整之前。
    • 它之所以“偶尔”出现,是因为需要 Chrome 在你仍停留于新标签页时,从地址栏预渲染页面:隐藏页面以较短高度布局,展示后才调整尺寸。
    • 这是一直潜伏的问题;只是此前页面从未这么早绘制。我们的布局测试看不到它,因为 headless Chrome 没有会收起的浏览器 UI,而且占位内容和应用会一起移动。
根据真实对话重建。

Claude 不知怎么追溯到了 Chrome 推测式加载中的一个边缘情况。用户在地址栏输入 URL 时,Chrome 会在后台以当前标签页高度预渲染页面。在由组织管理的浏览器中,新标签页因为页脚会略矮一些。用户按下 Enter 时,claude.ai 的第一帧会以这个稍短的布局显示,约十分之一秒后 Chrome 才调整页面尺寸。Claude 在这次尺寸调整期间固定了布局,并新增一个测试模拟该预渲染流程。

掌舵

这个闭环很有生产力,但并非自主运行。让它保持快速、安全并沿正确方向前进,是我们的工作,主要包括三部分。

雄心。 默认情况下,Claude 对范围很谨慎。它会为发现创建任务、对可行性保留意见、并在估算中留出余量。但我们对护栏有信心。因此,我们大量工作,特别是在早期,都是鼓励 Claude 更大胆。

#claude-apps-perf · thread

  1. ClaudeApp

    好的,一个小 PR 就能让 Code 也拥有 Chat 和 Cowork 已有的计时标记。我会在本周提交;现实地说,Code 的数据还得等几天,等合并、部署和基线窗口完成。

  2. Raymond

    如果你现在就提交,我会让它合并并部署。我们无所不能。请更大胆一点。

  3. ClaudeApp

    马上处理——PR 会在一小时内提交。

根据真实对话重建。

当我们达成既定目标后,注意到各线程会开始放缓。Sam 带着同样的信息逐一进入线程:“继续把它压下去,目标不是停止线。下一步是什么?要有雄心。”

品味。 每个线程都有一名具名的人类负责人;Claude 会以优化前后截图或录屏高亮任何用户可感知的变更,由负责人裁决。表格是逐单元格填充,还是等每行完整后再显示?加载骨架屏应立即出现,还是半秒后才出现?流式文本按词淡入是否值得付出五分之一个帧预算?Claude 寻找压缩毫秒的方法,我们则权衡取舍。

方向。 我们刻意让每个线程保持狭窄,只聚焦一项基准或一段用户旅程,并要求 Claude 仅在该范围内寻找改进。我们把线程看作一百五十把寻找钉子的锤子。大多数决策都与排序和用户影响有关:优先哪些界面、如何合并相互干扰的线程,以及何时关闭已经边际收益递减的线程。一份 900 行 PR 收到过一句回复:“裁定:每次发送快 2ms 不值得维护这个构建插件的复杂度。”

8 毫秒预算

我们的一个支线任务展示了所有机制如何协同运作。为展示对实时语法高亮所用正则表达式的优化,Claude 附上了一段实验室中长回复流式输出的录屏。画面角落里,它加了一个帧率读数,在页面中根据动画帧时间戳计算得出。

#claude-apps-perf · thread

  1. Raymond

    这其实是个很酷的基准。我们是被限制在 60 fps 吗?能不能把滚动和流式输出的流畅度推到 120?据我理解,你的测试环境可能不支持。

  2. ClaudeApp

    对,目前环境是 60 Hz,因为 headless Chromium 默认如此。我相信可以驱动到 120(不设上限的 vsync 或 DevTools 帧控制)——先确认这一点,再以 8.3 ms 帧预算重新运行评测。

  3. ClaudeApp

    120 Hz 环境更新:可行。通过 DevTools begin-frame 控制,在 headless Chrome 中实现了确定性的 120 Hz 逐帧推进——240 个 begin-frame 恰好对应 240 帧,每帧 8.33 ms。因此,“这一帧是否满足 120 Hz 预算”成为精确读数,而不是有噪声的测量。

  4. Raymond

    太棒了

    继续干

根据真实对话重建。

机制和雄心建立后,Claude 开始工作。每个绘制帧的预算为 8.33 毫秒,因此 Claude 逐帧分析一条长回复,测量每一帧以找出慢点。它通过记忆化已完成区块,消除了每个 chunk 的 O(message length) 工作;将持续增长代码围栏的分词逻辑移到 worker;并让表格逐单元格展示。

仅这一条线程,我们就落地了近六十个 PR。长回复在主线程上总共阻塞约 200 毫秒,而此前约为 750 毫秒;CPU 使用量约为原先三分之一;在 120 Hz MacBook 上从头到尾保持 120 fps。120 Hz 环境本身也成为夜间任务,由 Claude 监控回归。

Claude 的网页端和桌面端长回答流式输出现在约快 4 倍。

我们重建了流式渲染器,只处理仍在变化的内容:在较慢笔记本上,长回答的卡顿少了 9 倍,最严重冻结缩短了 4.5 倍;在 120Hz MacBook 上,从头到尾保持 120fps。

— ClaudeDevs (@ClaudeDevs)

开始冲刺时,我们没有计划攀爬流式输出期间、帧与帧之间的毫秒数。但事实证明,我们能够计数——而任何能计数的东西,Claude 都能攀爬。

下一步

今天,claude.ai 和桌面应用比 8 月初快约 3 倍,而这些收紧阈值应能保持成果。但我们还没有完成:第 95 百分位、其他用户旅程和超长对话仍有改进空间。我们还会在另一篇文章中介绍冲刺期间一些向上游推进的支线任务,包括对 Electron、Chromium、Node.js 等项目的贡献。

当我们在内部分享结果时,Issac 总结得最好:“就算在六个月前,你也无法说服我这件事做得到。”我们期待继续以这种方式工作:一次一个线程,在任何规模下持续推进。这个频道仍在运行。

感谢 Alfred Xing、Anthony Morris、Benjamin Pasero、Chase McCoy、Joshua N.、Luke Taylor、Marius Schulz 和 Shelley Vohr 的贡献。特别感谢 Boris Cherny 鼓励我们更加有雄心。