Anthropic 如何在两周内让 Claude 的常用操作快约 3 倍
Anthropic 报告:两周内 13 项常用操作的提速倍数几何平均为 3.1 倍;网页首次可输入快 5.6 倍,Cowork 发送消息的客户端处理快 19 倍。
打开 Claude,等输入框能打字;切进旧对话,等内容出现;点发送,等页面响应。这些等待通常只有几百毫秒到几秒,却会在每天反复操作时累积。用户向 Anthropic 反映 claude.ai 太慢,工程团队也承认这个问题。
为此,Anthropic 做了一次为期两周的性能冲刺。团队报告,claude.ai 和桌面端的核心交互整体快了约 3 倍:仅网页版首次打开到可输入的耗时,就从约 3.1 秒缩短到 0.55 秒。打开应用、开始对话、切换旧会话和发送消息,都有了不同程度的改善。
这次冲刺中,Claude 参与寻找瓶颈、建立测试和提交修改,工程师负责目标、取舍与发布。团队称,两周内合并了 3000 多项变更,没有发生面向用户的故障或回滚。
核心结果:四类常用操作快在哪里
团队跟踪的是占用户日常行为 95% 的四类核心操作:启动应用、开始新会话、加载已有会话与发送消息。结合网页端和桌面端各产品,这四类操作被细化为 13 项具体测量。各项都取第 75 百分位耗时(p75,即约 75% 的观测操作耗时不超过这个数值),再将提速倍数取几何平均,得到 3.1 倍。具体到用户手上的操作,变化是这样的:
变化最明显的是首次打开网页和 Cowork 发送消息:前者从约 3.1 秒降到 0.55 秒,后者的客户端处理从 928 毫秒降到 48 毫秒。这里的发送耗时只计客户端处理,不含模型生成回复。新建和切换会话的等待也普遍缩短。以下将全部 13 项结果按操作展开,前后数字可以直接逐行比较。
四类操作,前后对照
8 月 13 日 → 8 月 27 日 · p75 耗时 · 单位:毫秒
灰条为优化前,绿条为优化后;每一行灰条均为 100%,看绿条即可比较剩余等待比例。
打开应用
| 产品 / 平台 | 优化前 → 优化后 | 耗时减少 |
|---|---|---|
| claude.ai网页 · 首次加载 | 3,085→550 | −82% |
| 桌面应用冷启动 | 6,310→3,328 | −47% |
开始对话
| 产品 / 平台 | 优化前 → 优化后 | 耗时减少 |
|---|---|---|
| Chat网页 | 416→273 | −34% |
| Chat桌面 | 460→224 | −51% |
| Claude Code桌面 | 837→347 | −59% |
加载旧会话
| 产品 / 平台 | 优化前 → 优化后 | 耗时减少 |
|---|---|---|
| Chat网页 | 1,557→646 | −59% |
| Chat桌面 | 1,353→488 | −64% |
| Cowork桌面 · 云会话 | 2,566→728 | −72% |
| Claude Code桌面 | 545→262 | −52% |
发送消息 仅客户端处理
| 产品 / 平台 | 优化前 → 优化后 | 耗时减少 |
|---|---|---|
| Chat网页 | 180→59 | −67% |
| Chat桌面 | 140→64 | −54% |
| Cowork桌面 · 云会话 | 928→48 | −95% |
| Claude Code桌面 | 250→52 | −79% |
发送消息不包含模型生成回复的等待。各行条形独立归一化,绝对耗时请看数字;降幅按原始毫秒值计算并取整。数据来自 Anthropic 工程博客。
团队据此估计,这些优化每天可为用户累计节省数万小时的等待时间。
方法:把一条慢操作变成可重复的优化循环
冲刺开始时,团队建了一个专门的 Slack 频道,让使用内部研究模型的 Claude Tag(beta;Anthropic 称其能力大致相当于 Opus 5.5)参与讨论。
团队先让 Claude 通过 Datadog MCP 分析使用数据,再补齐埋点:每项测量从用户发起操作开始,到对应结果在界面上渲染结束,同时拆分客户端与服务端工作。这样,团队才有了可以横向比较、也能追查慢在哪里的基线。
工程师先选出约 20 个针对具体用户操作的项目并设目标;到第三天,13 个冲刺目标中已有 12 个达成。完成预定项目后,他们继续寻找新的可测量慢点。
有人在 Slack 线程里指出某个慢片段,常附录屏或截图;Claude 追踪代码并建立能复现问题的基准,随后提交便于审阅的 PR。部署后,它按版本和平台查看实际使用数据。有效就调低基准上限,无效就关掉开关继续迭代,再寻找这条操作中的下一个慢点。高峰期同时有 150 多条线程;最忙的一天落地 200 多项变更,约三分之一的 PR 还增加了新的遥测或防回退检查。一条线程完成最初的问题后还会继续追查,累计提出五十个、甚至一百个优化 PR;Claude 也会根据其他调查或夜间任务发现的问题主动开启新线程。新增测量持续暴露下一处瓶颈,让这轮冲刺超出了最初的项目清单。
测量:从实验室基准到 CI 棘轮
团队希望每轮尝试都能快于部署节奏。Claude 可以连续工作数小时,甚至整夜运行;如果每次改动都必须先上线、再等真实用户数据,优化就会不断停下来。实验室基准提供了一个能立即检验想法的环境:先在本地反复试,找到有效候选,再上线验证用户是否真的少等了。
在性能优化中,用户最终感知到的是墙钟时间(Wall-clock time,即实际耗时毫秒数)。但毫秒读数受系统调度和运行环境波动影响较大,若直接作为 CI 门禁容易引起频繁误报。
团队采取的做法是结合“实验室指标推动优化”与“线上真实数据验证”的双层测量:
- 在纯 JavaScript 路径上,使用 Valgrind 工具配合 Node.js 的确定性模式(
node --predictable)统计 CPU 指令数;在浏览器端则关注 React 提交次数、V8 调用次数、样式重算与 DOM 变动等相对确定的计数。 - 团队并未把所有指标直接设为门禁,而是要求先在实验中证明这些计数下降确实与实际耗时降低相关,再把有效的基准设为 CI 门禁。如果某项基准不稳定或无法对应实际耗时改善,就将其废弃。
例如在两个具体的密集计算路径实验中:
- 组装会话消息树时,原本解析逻辑将同一消息 ID 在字典中查询了三次;优化为单次查询后,指令数减少了 48%,JIT 预热后的实际运行耗时下降了 78%;
- Claude Code 状态行扫描通过先做廉价首字符判断再执行正则,指令数下降 31%,实际耗时下降 44%。
在验证两者的对应关系后,团队才在 CI 中加入“棘轮”(Ratchet):只要相关代码导致指令数上升,CI 就不予通过;若后续改动让计数进一步降低,脚本便自动下调上限,锁定优化成果。这里被锁住的是两个具体计算路径的指令数;整项用户操作是否更快,还要回到上线后的真实数据里看。
发布:特性开关与分级灰度
在快速迭代过程中,每个 PR 都经过自动化审查并至少获得一名人类工程师批准;团队先建立单元测试,再做优化。
对于可能影响用户可见表现的风险改动,团队会将其置于短期特性开关(Feature Flags)之后。高风险改动通常遵循分级发布策略:先推给内部员工,再扩大到 1% 的用户,最终全量开放。在两周冲刺期间,团队引入了近 200 个特性开关;同时专门设立讨论线程跟踪开关的生命周期,一旦确认改动稳定,便及时清理分支代码,冲刺结束时已有超过一半的开关被安全移除。
案例:静态输入框让用户提前打字
在单页 Web 应用中,浏览器加载 HTML 后还要执行 JavaScript、挂载组件树与状态。在这段初始化窗口期内,页面可能已经可见,输入框却还不能使用。
团队采取的方案是将一个静态输入框(static composer)直接烘焙进初始 HTML 中,让用户在 React 初始化完成前就能直接键入。
原页录屏对比清晰呈现了这一机制的作用:
- 提前获得可输入能力:原本需要等待 2.93 秒页面才能输入,加入静态输入框后,加载约 0.36 秒即可开始打字。这并不意味着整个 React 应用在 0.36 秒就完成了全部加载与初始化,而是把“能够输入”的时间点大幅提前。
- 平滑交接状态:用户键入过程中,React 继续在后台完成初始化;约 2.96 至 3.16 秒时,真实的 React 输入框接替静态元素,用户已输入的文本得到保留。
这种静态占位与动态组件交接的设计本身容易引发视觉瑕疵。如果静态 HTML 与后续 React 渲染在尺寸或位置上有哪怕 1 像素偏差,或者在接替瞬间漏掉按键,用户就会看到明显的跳动或丢字。
为了保证交接的一致性,团队建立了多项防护机制:
- 静态 HTML 标记由真实 React 组件在 jsdom 环境中生成,并用测试防止两者的呈现逐渐偏离;
- 在 14 种视口尺寸下进行自动比对,限制对齐偏差在 1 像素以内;
- 通过自动化测试模拟在交接过程中的连续按键,确认输入不会丢失;
- 在线上监控十分之一像素级别的位移事件。
内部灰度时,同事仍录到了新标签页打开后的下落。但静态输入框与 React 的交接记录显示没有位移。Claude 最后追到了浏览器窗口高度的变化:
- 预渲染时,页面先矮了一截。 Chrome 在用户输入网址时就提前渲染页面。受组织管理的新标签页底部有一个 56 像素高的页脚,预渲染采用了较矮的可用高度。
- 页脚消失,输入框跟着下移。 导航后,页面约在首屏出现 100 毫秒后增高 56 像素;输入框按页面高度的 18% 定位,因此下移约
56 × 18% ≈ 10像素。 - 原测试看不到这次变化。 无头 Chrome 没有这块浏览器界面;静态输入框和 React 界面又一起移动,测量两者交接偏差也抓不到它。
团队随后固定了这次高度调整期间的布局,并补上模拟预渲染流程的测试。这个问题此前就存在,只是页面现在显示得更早,才被看见。
在 Electron 桌面端,团队则利用 V8 引擎的代码缓存(Code Cache)预先编译主进程代码,避免冷启动时反复重新编译,使桌面端冷启动耗时大幅缩减。
切换对话也有更朴素的优化:输入框在不同对话之间保持挂载,鼠标悬停会话时预取数据,侧边栏的重复渲染减少了 90%。这些改动分别节省了重新创建界面、等待数据与无谓重绘的时间。
案例:三个隐藏在代码和指标里的慢点
在性能分析中,许多耗时并非来自明显的复杂算法,而是隐藏在细微的底层机制与代码调用路径中。
1. 字符编码对语法高亮性能的影响
在排查客户端 CPU 偶发停顿时,团队注意到一个现象:部分回复中的代码块在完成渲染后,语法高亮有时会导致页面停顿近 1 秒。
在实验室中分析调用栈后,团队发现这与非 Latin-1 字符相关:例如破折号(em dash —)或弯引号(curly quotes)。
在 V8 引擎内部,若字符串中仅包含 Latin-1 字符(每字符占 1 字节),会采用紧凑的单字节形式存储;一旦 Markdown 文本中包含破折号或弯引号等非 Latin-1 字符,V8 会将整段字符串内部存储转为双字节的 UTF-16 编码。这会导致后续正则表达式在执行高亮匹配时,走入相对较慢的双字节路径。
针对这一问题,团队提交了约 20 行代码:在提取出只含单字节字符的代码块后,将其复制为单字节字符串,再传入语法高亮处理。在原文图表给出的实验条件下(4 vCPU 容器、无头 Chrome,每个数值运行 2—3 次),页面上首个 TypeScript 代码块的主线程阻塞时间从 1.0 秒下降到 0.35 秒,后续每次高亮从 100 毫秒降至 40 毫秒。
2. 标准指标未能反映的侧边栏位移
在早期版本中,工程师通过录屏发现:页面加载时,左侧侧边栏列表容易出现条目跳动。普通对话与 Cowork 云会话数据返回时机不同,导致列表前后错动。
但在常规前端监控中,累积布局位移(CLS)并没有报警。原页记录显示,这些跳动单次位移的评分大约仅为 0.008,远低于通常视为良好的 0.1 阈值,导致问题在综合指标中被忽略。
团队选择直接基于底层的 Layout Instability API 建立遥测,把每次位移条目的来源区域映射到命名分区(如侧边栏、正文区),并区分发生阶段(如首屏渲染前、可输入之后)。这让工程师能回答“用户已经开始操作后,究竟是哪一块还在动”,从总体评分追到具体原因。
接着,Claude 写了一个刻意制造问题的集成测试:先打开带有会话列表的页面,把侧边栏数据扣住,等首屏绘制后再放行;只要指定区域发生位移,就判失败。同一个测试在主分支上运行 20 次全部失败,在修复 PR 上运行 20 次全部通过。原本只能靠录屏描述的“看着抖”,由此变成了可以反复检验的修复目标。
线上数据显示:约 31% 的网页访问在页面已可输入且用户没有任何交互时,仍然存在元素位移。结合录屏看,修复前存在 10 行跳动、9 行出现、4 行消失的明显错动。定位到具体来源后,团队批量处理了后发的头部行、用户名加载后光标横向移动、滚动条出现引起的宽度变动等问题,修复后侧边栏条目在固定位置填充,消除了这部分多余位移。
3. 排查中发现并清理的其他瓶颈
随着测量深入,团队还在多个关键路径上排查并修复了一批隐藏问题:
- 输入路径上的依赖项过多:对输入组件进行梳理时,发现其输入路径上有 6900 个 React Hooks 和 900 个状态订阅(store subscriptions),都处在每次敲键时可能重新渲染的路径上。这个计数让原本难以注意到的开销有了明确检查对象。
- 全局选择器的重算开销:全局样式中包含一个
:root:has()伪类选择器,使得每次 DOM 变动额外增加了 24 毫秒的样式重算时间。 - 遗留的隐藏重载:首屏绘制后的一处历史遗留逻辑中存在
location.reload(),导致线上每天产生约 50 万次未被常规加载指标捕获的隐藏重载。 - 空闲标签页的主线程读写:在无操作的闲置标签页中,每分钟有两次将完全相同的缓存快照复制到 IndexedDB,持续占用了主线程资源。
案例:让长回复在 120Hz 屏幕上流畅出现
大模型产品在交互上的一个显著特征是流式输出(Streaming):回复内容随着数据块持续推进,页面文本也在持续增长。
在长回复场景下,文本累积往往会加重前端渲染负担。在原始逻辑中,每个数据块的处理常伴随与整条消息长度成正比的工作量。消息越长,每次新增内容需要重复处理的部分越多,容易导致界面掉帧。
在 120Hz 高刷新率屏幕上(如支持 ProMotion 的 MacBook Pro),每帧的渲染预算约为 8.33 毫秒。为了优化高刷体验,团队通过 DevTools 的 begin-frame 控制,在实验室搭建了 120Hz 的逐帧评测环境,逐帧测量长回复生成时的耗时分布,并进行了针对性重构:
- 缓存已完成块(Memoization):对已经输出完毕的内容块做缓存,消除每个数据块中与整条消息长度成正比的重复计算。
- 分词逻辑移入 Worker:仅将处于增长状态的代码围栏(growing code fences)的 tokenization 处理移至 Web Worker,避免占用渲染主线程。
- 表格呈现方式的选择:团队选择在流式输出时逐个单元格显示。这样可以分散原本集中在一帧里的工作量,但逐格出现是否比整行出现更好看,仍由工程师判断。
在这一专项线程中,团队合并了近 60 个 PR。在团队的测试实验中,一段长回复在主线程的累计阻塞时间从约 750 毫秒降到约 200 毫秒,CPU 占用降至约原本的三分之一;在一台 120Hz MacBook 上,演示长回复从头至尾保持了 120fps。该测试环境随后也被设为夜间回归监控任务。
人类工程师负责目标、取舍和关停
每条线程都有一名明确的人类负责人。凡是用户能感知的变化,Claude 都会附上修改前后的截图或录屏,让负责人判断体验是否值得。每条线程的范围也被限定在一个基准或一类操作上,避免不断向外扩张。工程师主要负责以下三个维度的裁定:
- 提高目标(Ambition):在默认设定下,模型对改动范围通常偏向谨慎,容易在评估可行性时留出缓冲,并在达成初始目标后放慢节奏。工程师的作用是鼓励其继续探索更深层的瓶颈,设定更高的优化预期。
- 权衡体验与复杂度(Taste):模型在寻找毫秒级优化时,需要人类对视觉呈现与工程代价进行取舍。例如在流式文本渲染中,是否为了文字逐字淡入效果牺牲五分之一的单帧渲染预算;或者当一个构建插件 PR 增加了 900 行代码却只能让单次发送节省 2 毫秒时,工程师会因维护复杂度过高而予以否决。
- 把控全局节奏(Direction):冲刺高峰期同时有超过 150 个讨论线程在运行。工程师需要聚焦各个线程的边界,协调不同线程间的改动优先级,合并相互影响的代码,并在某些方向出现边际收益递减时及时终止探索。
写在最后
这次冲刺最值得借鉴的,是把一次性能修复接成了持续运转的循环:先找到用户真实经历的等待,再建立能快速试验的基准;候选改动上线后,用真实用户数据确认效果;确认有效,就用自动化检查守住成果。
测量工具本身也留下了长期价值。新增的遥测会暴露下一处慢点,CI 检查会拦住性能回退,120Hz 测试则在夜间继续运行。团队观察到,其他项目开始主动来做性能审查,新代码也受到这些检查和 Claude 技能的影响。两周积累下来的,既有更快的页面,也有下一次优化可继续使用的工具和工作方式。
Anthropic 仍列出了尚待改善的第 95 百分位数(p95,即较慢一端的体验)、其他操作路径和超长会话。冲刺还产生了对 Electron、Chromium、Node.js 等上游项目的贡献,团队计划另文展开。本文能展示的是这套方法如何处理已被找到、能够度量的瓶颈;新的慢点仍需继续被发现和解释。
