Anthropic 复盘如何让 claude.ai 和桌面应用快 3 倍

Hacker News 热门(buzzing.cc 中文翻译)·2026-09-24 05:34·38分钟前·matthieu_bl
AI 导读

Anthropic 团队在两周冲刺中把 claude.ai 和 Claude 桌面应用的核心体验提速约 3 倍,75 分位下 claude.ai 首次可输入时间从 3.1 秒降到 0.55 秒,Claude Code 新会话启动从 0.8 秒降到 0.3 秒,Claude Cowork 云会话加载从 2.6 秒降到 0.73 秒。

Hacker News 热门(buzzing.cc 中文翻译)
同事件
79AI 编辑部评分,满分 100

Anthropic 复盘如何让 claude.ai 和桌面应用快 3 倍

2026-09-24 05:34· 38分钟前· matthieu_bl
AI 导读

Anthropic 团队在两周冲刺中把 claude.ai 和 Claude 桌面应用的核心体验提速约 3 倍,75 分位下 claude.ai 首次可输入时间从 3.1 秒降到 0.55 秒,Claude Code 新会话启动从 0.8 秒降到 0.3 秒,Claude Cowork 云会话加载从 2.6 秒降到 0.73 秒。

正文 · AI 翻译

今年八月,我们用两周的冲刺让 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 server 分析使用数据。它识别出了四条影响最大的用户旅程:启动应用、开始对话、加载已有对话,以及发送消息。在 Web 端和桌面端之间,以及我们的各个产品中,这些旅程共对应十三项不同的测量指标。为了建立基线,我们不断添加埋点,直到它们可以直接比较:每一项都从用户交互开始,在结果渲染完成后结束,并区分客户端和服务端的工作。

我们以一份约二十个精心挑选的项目清单启动了这次冲刺,每个项目都针对一条特定的用户旅程。Claude 以毫秒为单位估算了每个项目的影响,我们将这些估算汇总起来,以此设定本次冲刺的目标。有些项目相当大,但我们认为很可能在两周内完成其中大部分。

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

计划中的项目提前落地了。为了实现更快的启动,我们在 HTML 中内置了一个静态输入框,让用户可以在 React 初始化期间就开始输入,并预编译了 V8 代码缓存,这样桌面端外壳的主进程就不必从头重新编译。为了实现更快的导航,我们让输入框在对话切换之间保持挂载,在用户悬停时预取会话,并将侧边栏的重渲染减少了 90%。

我们还留出了空间,让 Claude 去识别机会并提出新的工作方向。这些工作方向迅速成长为各自独立的完整项目,远远超出了我们最初的目标。于是我们设定了新的目标,然后寻找更多可以衡量的东西:

@Claude 我们最终几乎资助了原始项目清单上的每一个项目,甚至更多。让我们重新梳理一下 […] 我们还有哪些没有探索过,我们可以在哪些方面持续爬坡,现阶段最大的机会在哪里?[…] 我对各种疯狂的想法都持开放态度

任何东西都可以持续爬坡

从一开始,我们就知道自己希望以比部署节奏更快的速度进行迭代。Claude 可以异步工作很多个小时,甚至通宵,我们希望让它无需等待实地数据反馈就能验证自己的原型。为了实现这一点,我们寻找了其他在实验室中衡量性能的方法。

Sam 找到了第一条线索:

媒体内容 · 前往原文查看
#claude-apps-perf · 讨论串
  1. Sam 下午 12:34

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

  2. ClaudeApp 下午12:42

    可以。对于纯 JS 的热点路径,可以统计字面指令数:在 Valgrind 下用 node --predictable 运行基准测试,并与已检入的基线进行比较——一次运行,无需统计。

    对于浏览器路径,Chromium 下没有指令计数,但有一系列其他确定性计数:每次交互的 React 提交次数、V8 精确覆盖率给出的函数调用次数、布局和样式重算次数、DOM 变更次数。你想先要哪个?

  3. Sam 下午12:49

    我们开一个线程探索 valgrind + Ir + --predictable,再开新线程分别跑每个浏览器/React 基准测试。在所有线程里都 @ 我。你知道我们想要什么。开始吧

根据一段真实对话重现。

十一分钟后,五个线程已在运行,每个都聚焦于不同的测量:指令数、V8 调用次数、React 提交次数、样式重算次数和 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. 一旦在实验室里得到有希望的结果,Claude 就会带着一个 PR 回来——往往是好几个,按风险和审查需求来划分大小,任何用户可见的内容都放在功能开关之后。
  4. 上线之后,Claude 会观察部署情况并读取线上数据。
  5. 如果性能有所提升,Claude 就通过把基准测试的阈值收紧来锁定这一成果;如果没有提升,它就关掉功能开关并继续迭代。
  6. 然后它又去寻找同一段旅程中的下一个缓慢之处。

举个例子:有人分享了一段屏幕录制,显示页面加载后侧边栏的行才陆续弹出来。Chat 和 Cowork 这两行在不同时间才渲染完成,让页面显得卡顿。我们现有的监控手段没有一个检测到这个问题。最接近的是 累积布局偏移,但每次偏移的得分只有大约 0.008——远在 0.1 的良好阈值之内。

Issac 想到一个主意,直接引用底层的Layout Instability APIClaude 创建了一个遥测事件,将每个sourceslayout-shift条目映射到命名区域(例如侧边栏、对话记录)和阶段(例如首次绘制之前、可输入之后)。它添加了一个集成测试,在侧边栏已填充数据的情况下打开页面,将侧边栏的数据保持到首次绘制之后,并在任何命名区域出现任何偏移时判定失败。Claude 以此作为基准来证明修复有效:该测试在 main 分支上 20 次运行 20 次失败,在 PR 上 20 次运行 20 次通过。

事件部署后,Claude 读取了现场数据,发现 31% 的网页加载在页面可用之后、没有任何用户交互的情况下移动了某些东西。由此,Claude 逐一排查了各项原因:一行迟到的表头、用户姓名加载完成后横向滑动的光标、滚动条弹出时移动的列表。Claude 批量修复了最主要的几处问题,当这些问题解决后,它又发现了下一批。

视频侧边栏卡顿,前后对比 · 限速 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 注意到高亮一个已完成的代码块会让页面冻结大约一秒。它在实验室里深挖,找到了元凶:em dash(长破折号)。如果一条回复的 markdown 中包含任何非 Latin-1 字符,比如 em dash 或弯引号,V8 就会把整个字符串存为 UTF-16,这会让每一个语法高亮正则都走上更慢的双字节路径。

Claude 用一个二十行的改动修复了它:在 highlight 之前把每个代码块复制成一字节字符串。

到第二周时,我们几乎无法把产出汇总成每日更新。最忙的日子里,落地的改动超过两百项。Claude 不断提出新的基准测试;大约三分之一的 PR 包含了额外的遥测或防护栏,而每新增一个监测工具,就会产生更多讨论串,也带来更多机会。

在同一个频道里工作意味着一切都公开进行。我们随时跳进彼此的讨论串,争论决策、庆祝胜利。消息传开了:其他团队开始把他们的改动带进这个频道,让它们接受性能审查。由于引入了所有这些防护栏和 Claude 技能,新项目在写法上都有了微妙但更注重性能的改进。

防护栏

我们为这样的节奏做好了准备。因为我们几乎触碰到的每一处都是热路径(首次绘制、输入框、对话记录),所以我们一开始就建立了安全机制。每个 PR 都要经过自动化审查,并至少有一名人工批准;单元测试永远先于优化;任何可能引发用户可见问题的改动,都要通过一个短期功能开关来发布。

当开关开始堆积时,我们开了一个讨论串来协调它们的上线和清理。Claude 把每个开关归类为终止开关或渐进放量,并在安全后立即将其退役。在这两周里,我们引入了近两百个开关,其中超过一半在结束时已经被清理掉。

我们也知道,性能优势在快速迭代的代码库中会逐渐衰减,而 Anthropic 的代码发布节奏很快。一旦某个项目被证明带来了收益,我们就会投入精力去保护它。例如,静态合成器在设计上就是脆弱的。我们几乎立即向用户展示页面的 HTML 副本,然后让 React 直接在其上渲染。

视频静态合成器,前后对比 · 限速 4G

如果 React 渲染偏差哪怕一个像素,魔法就会失效。因此 Claude 构建了数十道防护措施:

  • 静态标记通过在 jsdom 中渲染真实的 React 组件来生成,并有测试保证两者永远不会偏离。
  • 一套集成测试套件在十四个视口尺寸下将静态页面与 React 渲染结果进行对比,并断言对齐误差在 1 px 以内。
  • 一个按键测试会直接输入贯穿整个交接过程,任何按键丢失或顺序错乱都会导致测试失败。
  • 在实际运行中,每次交接都会报告精确到十分之一像素的偏移。Claude 会为任何非零移动的事件开启一个讨论线程。

并非所有问题都能在实验室里被发现,因此我们还用上了最古老的防护手段:增量发布。高风险变更先向员工发布,然后向 1% 的用户发布,最后才向所有人发布。我们在内部发布静态输入框四小时后,一位同事分享了一段屏幕录制,展示了一个我们的任何指标都看不到的布局偏移。当他在新标签页中打开 claude.ai 时,输入框会下移——但那并不是我们的代码造成的。

媒体内容 · 前往原文查看
#claude-apps-perf · 讨论串
  1. Marius下午6:15

    我偶尔会看到,在新标签页中打开 claude.ai 时出现一个较小的(大概 15–20px)垂直布局偏移(把输入框向下推)(重新加载页面时则不太明显)。我不太能确定到底是什么导致的,但它确实存在

    屏幕录制 · 0:02
  2. ClaudeApp 下午7:05

    在你的录制中找到了——是 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 在你仍停留在新建标签页时就从地址栏预渲染该页面:这个隐藏页面以较矮的高度完成布局,只有在显示之后才会被调整尺寸。
    • 这个问题一直潜伏着;只是页面以前从未那么早进行绘制。我们的布局测试看不到它,因为无头 Chrome 没有可收回的浏览器 UI,而且占位符和应用是一起移动的。
根据一段真实对话重现。

不知怎么的,Claude 把它追溯到了 Chrome 推测性加载中的一个边缘情况。当用户在地址栏中输入 URL 时,Chrome 会在后台以当前标签页的高度预渲染该页面。在由组织管理的浏览器上,新标签页会因为一个页脚而略矮一些。当用户按下回车键时,claude.ai 的第一帧显示的是那个略矮的布局,而 Chrome 在大约十分之一秒后对其进行了调整大小。Claude 在调整大小过程中固定了布局,我们还添加了一个测试来模拟预渲染流程。

引导

这个循环很有成效,但它并不是自主运行的。让它保持快速、安全且不偏离正轨是我们的工作,而这分为三个部分。

野心。 默认情况下,Claude 对范围很谨慎。它会把发现的问题记成工单,对可行性含糊其辞,还会在估算上留出余量。但我们对自己的护栏很有信心。我们做的很多事情,尤其是在早期,就是鼓励 Claude 更大胆一些。

媒体内容 · 前往原文查看
#claude-apps-perf · 讨论串
  1. ClaudeApp 下午3:32

    是的——一个小 PR,就能让 Code 拥有 Chat 和 Cowork 已经具备的同样的计时标记。我这周就提上去;现实地说,Code 的数字还要等几天,等合并、部署和一个基线窗口。

  2. Raymond下午3:32

    如果你现在就提上去,我会把它合并并部署。我们有能力做任何事。请更大胆一些

  3. ClaudeApp 下午3:33

    马上办——PR 会在一小时内提上去。

根据一段真实对话重现。

当我们开始达到自己设定的目标时,我们注意到讨论串会慢下来。Sam 一个讨论串接一个讨论串地发同样的消息:“让我们继续把它压下去,目标不是终点。下一步是什么?要有野心。”

品味。每个线程都有一个具名的真人负责人,Claude 会用前后对比截图或录屏标出任何用户可感知的改动,交由他们裁定。表格应该逐格填充,还是等整行完成后再显示?加载骨架屏应该立即出现,还是半秒后才出现?流式文本逐词淡入的效果,是否值得花掉帧预算的五分之一?Claude 寻找各种办法来削减毫秒数,而我们则权衡其中的取舍。

方向。我们把每个线程都刻意收窄,聚焦于某一个基准或某一段旅程,并要求 Claude 只在这个范围内寻找改进。我们把这些线程看作一百五十把寻找钉子的锤子。我们的大多数决策都围绕排序和用户影响:优先处理哪些界面,如何合并那些相互冲突的线程,以及何时关闭一个已经收益递减的线程。有一个 900 行的 PR 只得到了一行回复:“我打算拍板否决——每次发送省 2ms,不值得为此维护这个构建插件的复杂度。”

8 毫秒的预算

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

媒体内容 · 前往原文查看
#claude-apps-perf · 线程
  1. Raymond下午 2:33

    这其实算是个挺狠的基准测试。我们是被限制在 60 fps 吗?你能试着把滚动和流式渲染的流畅度拉到 120 吗?据我所知你的机器可能不支持这个

  2. ClaudeApp 下午2:34

    对,现在的机器跑在 60 Hz,因为 headless Chromium 默认就是这样。我相信它可以被驱动到 120(不限制 vsync 或用 DevTools 帧控制)——先确认这一点,然后我会针对 8.3 ms 的帧预算重新跑一遍评测。

  3. ClaudeApp 下午2:59

    关于 120 Hz 机器的进展:成功了。在 headless Chrome 中通过 DevTools 的 begin-frame 控制实现确定性的 120 Hz 帧步进——240 个 begin-frame 正好对应 240 帧,每帧 8.33 ms,所以“这一帧是否满足 120 Hz 的预算”变成了一个精确读数,而不是有噪声的读数。

  4. Raymond下午3:05

    太厉害了

    绝了

根据一段真实对话重现。

机制和目标确定后,Claude 就开始动手了。每个绘制帧的预算只有 8.33 毫秒,于是 Claude 逐帧梳理一段长回复一帧一帧地,为每一帧计时,找出慢的部分。它通过记忆化已完成的块,消除了每个分块中的 O(message length) 工作,把不断增长的代码围栏的 tokenization 逻辑移到了 worker 中,并逐格渲染表格。

在那一个线程中,我们合并了近六十个 PR。长回复对主线程的阻塞总时长从原来的约 750 毫秒降到了约 200 毫秒,CPU 占用降到约三分之一,并且在 120 Hz 的 MacBook 上从头到尾保持 120 fps。这台 120 Hz 的设备本身也变成了一项每夜运行的任务,由 Claude 负责监控性能回退。

现在,Claude 在网页端和桌面端上的长回答流式输出流畅度提升了约 4 倍。

我们重建了流式渲染器,使其只处理仍在变化的内容,因此在较慢的笔记本上,长回复的卡顿减少了 9 倍,最严重的一次冻结缩短了 4.5 倍,而在 120Hz 的 MacBook 上,它从头到尾都能保持 120fps。

— ClaudeDevs(@ClaudeDevs)2026 年 8 月 24 日

当我们开始这轮冲刺时,我们并没有计划去针对流式输出时帧与帧之间的毫秒数做爬山优化。但结果发现我们确实可以把它们数出来——而任何我们能数出来的东西,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 鼓励我们更加雄心勃勃。