1. 从一次深夜调试说起:Spinner 卡住到底卡在哪
凌晨一点半,终端里那个小小的 Spinner 还在转,光标一闪一闪,像极了在嘲笑我。这大概是每个用 Claude Code 的人都经历过的场景:你敲下一段需求,回车,然后那个旋转的指示符开始转,转了三秒、五秒、十秒……你开始怀疑是不是网络断了,是不是模型挂了,是不是自己写错了什么配置。等你终于忍不住按了 Ctrl+C,它又突然吐出一大段结果,仿佛刚才只是在发呆。
这个现象在社区里被反复讨论,关键词无非就是 Claude Code、Spinner、卡顿、排查方案这几个。但真正把这件事讲清楚的文章不多,大部分停留在“重启试试”“换个网络”这种层面。我前后在 Windows、Ubuntu、VS Code 插件三种环境下都踩过这个坑,也帮同事排查过好几次,慢慢摸出了一套相对系统的判断逻辑。这篇就把 Spinner 这个状态标识到底代表什么、卡顿的根源分几类、每一类怎么定位怎么解,一次性讲透。
先说清楚这篇文章适合谁看:如果你刚开始用 Claude Code,遇到转圈就慌,那第一部分和排查表能帮你快速止血;如果你已经用了一段时间,想搞清楚为什么有时候快有时候慢,那中间关于请求链路和状态机的部分会对你有用;如果你是团队里负责给大家配环境的人,那配置和资源占用那几节可以直接拿去当 checklist。全文不讲虚的,都是我自己实测和帮人排查时攒下来的东西。
需要提前说明一点,Claude Code 的版本迭代很快,UI 上的 Spinner 样式、日志路径、配置项名称在不同版本里可能有差异。我下面提到的具体路径和参数,是基于我写这篇时手头几个环境的实际情况,你对照自己版本的时候如果发现对不上,以你本地为准,思路是通用的。
2. Spinner 到底是什么:状态标识背后的请求链路
2.1 Spinner 不是“加载中”这么简单
很多人把 Spinner 理解成浏览器的加载圈,觉得它转就是在下载东西。Claude Code 里的 Spinner 其实是一个复合状态指示器,它至少覆盖了四个阶段:本地请求组装、网络传输、服务端推理、结果流式回传。这四个阶段里任何一个卡住,Spinner 都会继续转,因为从 UI 的角度它只知道“我发出去了,还没收到完整结果”。
这就解释了为什么有时候你看到 Spinner 转很久,但其实服务端早就开始返回了,只是前端在等一个完整的响应块。流式输出(streaming)的设计本来是为了让用户早点看到内容,但如果某个环节的缓冲策略不对,流式就退化成了“憋大招”,你看到的就一直是转圈。
我实测过一个很典型的对比:同样的 prompt,在网络状况好的时候,Spinner 大概转 1 到 2 秒就开始出字;网络抖动的时候,Spinner 会先转 5 秒左右,然后突然一次性吐出前 200 个字,再继续流式。这个“先憋后吐”的模式,基本可以判定是传输层或者缓冲层的问题,而不是模型本身慢。
2.2 一次请求的完整生命周期
要把卡顿讲清楚,得先把一次请求从按下回车到看到结果的全过程拆开。我按自己的理解画了一条链路,虽然不能用图,但用文字描述一样清楚:
- 你在输入框敲完内容,按下回车,客户端开始组装请求体,包括你的 prompt、当前会话上下文、可能还有项目里的文件引用。
- 组装完成后,客户端发起网络请求,这一步涉及 DNS 解析、连接建立、TLS 握手。
- 请求到达服务端,服务端开始推理,这一步的时间取决于模型负载和你的上下文长度。
- 服务端开始流式返回 token,客户端边收边渲染。
- 全部返回完毕,Spinner 停止,光标恢复。
这五步里,第 1 步和第 5 步是纯本地的,通常很快;第 2 步和第 4 步跟网络强相关;第 3 步是服务端的事,你控制不了,但可以通过减少上下文来间接影响。Spinner 卡住,本质上就是这五步里某一步的耗时超出了你的预期。
提示:判断卡在哪一步,最直接的办法是看日志。Claude Code 一般会在本地留请求日志,里面有时间戳,能看到请求发出和首个 token 返回之间的间隔。这个间隔如果很长,问题在传输或服务端;如果间隔很短但整体很慢,问题在流式渲染或本地处理。
2.3 为什么 Spinner 会“假死”
有一种情况特别迷惑人:Spinner 在转,但日志显示请求早就完成了。这就是所谓的“假死”,UI 状态和实际请求状态不同步。造成假死的原因通常有三个:一是前端渲染线程被阻塞,比如你同时开了很大的项目,文件监听占满了主线程;二是流式回调没有正确触发 UI 更新;三是某些版本的 bug,请求完成事件丢了。
我在 VS Code 插件里遇到过一次典型的假死,后来发现是插件和某个文件监听扩展冲突,导致主线程一直在处理文件变更事件,Spinner 的更新被排到了队列后面。关掉那个扩展之后,Spinner 立刻恢复正常。所以遇到“明明很快但就是不出结果”的情况,先怀疑本地资源竞争,而不是网络。
3. 卡顿根源分类:从网络到本地的五类问题
3.1 网络层:最容易被误判的一类
网络问题是最常见的背锅侠,但真正是网络问题的比例其实没那么高。我统计过自己遇到的二十多次卡顿,纯网络原因的只有五六次。网络层的问题又分几种:DNS 解析慢、连接建立慢、传输过程中丢包重传、以及带宽被其他应用占满。
DNS 解析慢这个很隐蔽,因为第一次解析之后会有缓存,你可能只在特定时候遇到。判断方法是看日志里请求发出到连接建立的时间,如果这个时间超过 1 秒,基本就是 DNS 或者连接建立的问题。解决办法也简单,换一个响应快的 DNS,或者在本地 hosts 里把常用域名固定下来。
带宽占用这个更常见,尤其是你在下载东西或者开视频会议的时候。Claude Code 的流式输出对带宽要求不高,但对延迟敏感,一旦有别的应用在抢带宽,延迟就会上去,Spinner 就会转得久。我一般会在排查的时候先关掉下载工具和同步盘,再看效果。
3.2 服务端层:你控制不了但能规避
服务端的推理时间取决于模型负载和你的输入长度。输入越长,推理越慢,这是物理规律。我做过一个粗略的测试,同样的任务,上下文从 2K token 增加到 8K token,首个 token 的返回时间大概会翻倍。所以如果你发现 Spinner 转得久,先看看自己是不是塞了太多上下文进去。
另一个服务端因素是并发限制。免费额度或者低档订阅通常有并发上限,如果你同时开了多个会话,后面的请求会排队。这个在日志里表现为请求发出后长时间没有响应,但连接是正常的。解决办法就是减少同时进行的会话数,或者错峰使用。
注意:有些卡顿是服务端在“思考”,尤其是涉及复杂推理的任务。这种情况下 Spinner 转得久是正常的,你强行中断反而会丢失已经生成的内容。判断方法是看任务复杂度,如果只是简单问答却转很久,那才是异常。
3.3 客户端层:本地资源竞争是隐形杀手
客户端层的问题最容易被忽略,因为它跟“网络”和“模型”都没关系。Claude Code 作为一个本地应用,要占用 CPU、内存、磁盘 IO,还要跟编辑器或其他工具交互。任何一个资源紧张,都会表现为 Spinner 卡顿。
我遇到过的客户端层问题包括:内存不足导致频繁 GC、磁盘 IO 被其他进程占满、CPU 被编译任务吃满、以及前面提到的文件监听冲突。这些问题在任务管理器里都能看到端倪,排查的时候开着资源监视器,一边复现卡顿一边看哪个指标飙高,基本就能定位。
还有一个容易被忽略的点是杀毒软件。有些杀毒软件会对每个网络请求做深度检测,这会显著增加请求延迟。如果你发现所有请求都慢,而且关了杀毒软件就正常,那就是它的问题。把 Claude Code 的进程加入白名单通常能解决。
3.4 配置层:错误的参数会放大卡顿
配置问题属于“自己给自己挖坑”的类型。常见的错误配置包括:超时时间设得太短导致频繁重试、代理配置不对导致请求绕路、模型选择不当导致用了一个很慢的模型、以及上下文窗口设得过大。
超时时间这个特别典型。有些人为了“不让它卡”,把超时设得很短,结果请求还没完成就被中断,客户端重试,又中断,又重试,Spinner 就一直转。正确的做法是把超时设得比正常响应时间略长,给服务端留足推理时间。
代理配置的问题在于,如果代理本身不稳定,所有请求都会受影响。我建议在排查阶段先直连,确认直连正常之后再考虑代理。如果必须用代理,选一个延迟低的,并且确保代理本身没有做额外的内容检测。
3.5 版本与兼容层:升级不一定解决问题
版本问题比较尴尬,因为有时候升级能解决卡顿,有时候升级反而引入新问题。我遇到过某个版本在 Windows 上 Spinner 渲染有 bug,转是转了但不出结果,降级一个版本就好了。也遇到过旧版本不支持新的流式协议,导致卡顿,升级之后解决。
兼容性问题主要出现在编辑器插件和独立客户端混用的时候。比如你在 VS Code 里用插件,同时又开了桌面版,两者可能争抢同一个配置文件或者端口。我建议同一时间只用一个入口,避免这种冲突。
4. 排查方案实操:从五分钟止血到深度定位
4.1 五分钟快速止血清单
当你正卡着,不想做深度排查,只想赶紧恢复,可以按这个顺序试:
- 按一次 Esc 或 Ctrl+C,看是否能中断当前请求。如果能中断,说明客户端还活着,问题在请求本身。
- 检查网络,打开一个网页看能不能正常加载。如果网页也慢,那是整体网络问题。
- 看任务管理器,CPU、内存、磁盘哪个飙高。如果某个指标接近 100%,先关掉占用高的其他程序。
- 重启 Claude Code。这一步能解决大部分临时性的状态错乱。
- 如果重启无效,换一个最简单的 prompt 试,比如“你好”。如果简单 prompt 也卡,那是环境问题;如果简单 prompt 正常,那是你之前的输入太重。
这个清单我帮同事排查时用了很多次,大概七成的情况在前三步就能定位。剩下的三成需要往下走。
4.2 日志定位法:找到卡住的那一步
深度排查的核心是看日志。Claude Code 的日志一般在用户目录下的配置文件夹里,Windows 在%APPDATA%附近,Linux 和 macOS 在~/.config或~/.claude附近。具体路径随版本变化,你可以用文件搜索找最近修改的 log 文件。
日志里重点看几个时间戳:请求发出时间、连接建立时间、首个响应字节时间、响应完成时间。这四个时间点把一次请求切成三段:连接耗时、首字节耗时、传输耗时。哪一段异常,问题就在哪一层。
我整理了一个对照表,方便你快速判断:
| 异常段 | 可能原因 | 优先排查方向 |
|---|---|---|
| 连接耗时过长 | DNS 慢、网络不通、代理问题 | 换 DNS、检查代理、直连测试 |
| 首字节耗时过长 | 服务端推理慢、上下文过长、并发排队 | 减少上下文、错峰、检查额度 |
| 传输耗时过长 | 带宽不足、丢包、流式缓冲问题 | 关下载、检查网络质量、看版本 |
| 全程都慢但日志正常 | 本地渲染阻塞、资源竞争 | 看 CPU 内存、关冲突扩展 |
4.3 资源监视法:抓出隐形占用
资源监视法适合那种“日志看起来正常但就是卡”的情况。操作很简单:打开系统自带的资源监视器,一边复现卡顿,一边观察。
重点看三个指标:CPU 的单核占用、内存的可用量、磁盘的活动时间。如果 CPU 某个核跑满,说明有计算密集任务在抢;如果内存可用量很低,说明在频繁换页;如果磁盘活动时间接近 100%,说明 IO 是瓶颈。
我在 Windows 上遇到过一次磁盘 IO 导致的卡顿,原因是同步盘在后台扫描大量小文件。把同步盘暂停之后,Spinner 立刻顺畅了。这种问题日志里完全看不出来,只有看资源监视器才能发现。
4.4 隔离测试法:二分定位问题源
隔离测试的思路是不断缩小范围。具体做法:
- 换一个干净的环境,比如新建一个系统用户,只装 Claude Code,看是否还卡。如果不卡,说明是你原环境里的某个东西在干扰。
- 在原环境里,逐个关闭可能冲突的程序,每关一个测一次,直到找到罪魁祸首。
- 如果怀疑是配置问题,把配置文件备份后重置为默认,看是否恢复。
这个方法比较费时间,但定位最准。我一般只在其他方法都无效的时候用,因为它能给出确定性的结论。
5. 分场景实战:Windows、Ubuntu、VS Code 各自的坑
5.1 Windows 环境:路径、权限与杀毒软件
Windows 上的卡顿,很大一部分跟路径和权限有关。Claude Code 如果装在带空格的路径下,或者路径里有中文,某些版本会出问题。我建议装在纯英文、无空格的路径下,比如C:\Tools\ClaudeCode。
权限问题表现为请求发出后没有任何响应,日志里也看不到错误。这通常是防火墙或者杀毒软件拦截了。解决办法是把 Claude Code 的可执行文件加入防火墙白名单,同时在杀毒软件里排除它的进程和配置目录。
还有一个 Windows 特有的坑是终端编码。如果终端编码不是 UTF-8,流式输出里的特殊字符可能导致渲染异常,表现为 Spinner 卡住。把终端编码改成 UTF-8 通常能解决。
5.2 Ubuntu 环境:依赖、权限与 systemd
Ubuntu 上的问题多半跟依赖和权限有关。Claude Code 依赖一些系统库,如果库版本不对,可能表现为启动正常但请求异常。用ldd检查一下可执行文件的依赖,看有没有 missing 的。
权限问题在 Linux 上更常见,因为普通用户对某些目录没有写权限。如果配置目录不可写,客户端可能无法保存状态,导致每次请求都像第一次一样。检查配置目录的权限,确保当前用户可读写。
如果你是用 systemd 管理 Claude Code 的服务,注意服务的资源限制。默认的 systemd 服务可能有内存和文件描述符的限制,请求量大时会触发限制导致卡顿。适当调高LimitNOFILE和MemoryMax能缓解。
5.3 VS Code 插件:扩展冲突与配置同步
VS Code 插件版的卡顿,八成跟扩展冲突有关。VS Code 本身是个扩展宿主,装了几十个扩展之后,主线程很容易被占满。排查方法是打开扩展宿主进程的 CPU 占用,看是不是某个扩展在狂吃 CPU。
我遇到过的冲突源包括:文件图标扩展、Git 增强扩展、以及某些 AI 补全扩展。这些扩展会在你打字时频繁触发,跟 Claude Code 抢资源。临时禁用它们,看卡顿是否消失。
配置同步也是个坑。如果你开了 VS Code 的设置同步,Claude Code 的配置可能在不同机器之间来回覆盖,导致行为不一致。建议把 Claude Code 相关配置排除在同步之外。
6. 常见问题速查与避坑心得
6.1 高频问题速查表
| 现象 | 最可能原因 | 快速处理 |
|---|---|---|
| Spinner 转很久但最终有结果 | 上下文过长或服务端负载高 | 精简输入,错峰使用 |
| Spinner 转但永远没结果 | 网络中断或客户端假死 | 中断重试,重启客户端 |
| 简单问题也卡 | 本地资源竞争或配置错误 | 看资源监视器,重置配置 |
| 时快时慢无规律 | 网络抖动或并发排队 | 检查网络质量,减少并发 |
| 升级后开始卡 | 新版本 bug 或兼容问题 | 回退版本,看更新日志 |
| 只有特定项目卡 | 项目文件过多触发监听 | 排除大目录,关文件监听 |
6.2 我踩过的三个坑
第一个坑是盲目调大超时。我一开始以为超时越长越好,结果设了 300 秒,卡的时候要等五分钟才报错,反而更难受。后来改成 60 秒,配合重试,体验好很多。超时不是越长越好,要跟正常响应时间匹配。
第二个坑是忽略日志轮转。有段时间日志文件涨到几个 G,客户端写日志都变慢了。后来配了日志轮转,限制单个文件大小和保留数量,卡顿明显减少。日志是好东西,但不管它也会变成负担。
第三个坑是在低配机器上开太多会话。我有一台老笔记本,同时开三个 Claude Code 会话,内存直接吃满,Spinner 转得跟幻灯片一样。后来改成一次只开一个,用完就关,问题解决。资源有限的时候,克制比优化更有效。
6.3 几个提升流畅度的小技巧
把常用的大目录排除在文件监听之外,能显著减少后台 IO。具体做法是在项目配置里加排除规则,把node_modules、dist、.git这类目录排除掉。
定期清理会话历史。会话历史太长不仅占内存,还会拖慢上下文组装。我一般每周清理一次,只保留最近几天的。
如果经常处理长文本,考虑分段处理。把一个大任务拆成几个小任务,每个任务的上下文都短,整体反而更快。这跟“一口吃不成胖子”是一个道理。
提示:如果你在团队里推广 Claude Code,建议统一配置模板,把超时、日志、排除规则这些一次性配好。个人各自摸索的话,每个人都会踩一遍同样的坑,浪费的时间加起来很可观。
7. 关于本地模型接入的一点补充
有些朋友会问能不能接本地模型来避免网络卡顿。这个思路是对的,本地模型确实没有网络延迟,但换来了本地推理的延迟。如果你的机器性能够强,本地模型可以很流畅;如果机器一般,本地推理可能比网络请求还慢。
接入本地模型的关键是接口兼容。Claude Code 通常支持配置自定义的 API 端点,你把端点指向本地模型的 HTTP 服务就行。但要注意,本地模型的流式协议要跟客户端兼容,否则会出现“请求成功但 Spinner 不停”的情况。我建议先用一个简单的 curl 测试本地模型的流式输出,确认协议对得上再接入。
本地模型的另一个好处是隐私,所有数据不出本机。如果你处理的是敏感内容,这个优势很重要。但代价是你要自己维护模型和硬件,长期成本不一定比用云端低。这个取舍看你的具体需求。
8. 最后分享一个判断卡顿性质的小方法
我平时判断卡顿是“真卡”还是“假卡”,用一个小技巧:在 Spinner 转的时候,轻轻敲一下键盘上的任意键(不要按回车)。如果界面有反应,比如光标闪了一下,说明 UI 线程还活着,卡的是请求;如果完全没反应,说明 UI 线程也被阻塞了,问题在本地。
这个方法的原理很简单,UI 线程和请求线程通常是分开的。UI 有反应说明只是请求慢,等一等或者中断重试就行;UI 没反应说明本地资源出了问题,得从资源竞争入手排查。我用这个方法快速区分过很多次,比看日志还快。
另外,养成一个习惯:遇到卡顿先别急着中断,等十秒。很多时候服务端只是慢了一点,你中断了反而要重来。十秒还没动静,再考虑中断和排查。这个耐心能帮你省下不少重复劳动。