最近在折腾 Claude 桌面客户端的时候,遇到了一个让人相当抓狂的小问题。虽然我已经通过自定义网关(比如 cc-switch 这类工具)成功接入了模型,平时聊天推理一切正常,但那个客户端隔三差五就会弹出一个报错提示,说是网关无法访问,操作超时。

看着满屏的英文报错信息,强迫症真的很难受:

Inference — Gateway was unreachable: The operation was aborted due to timeout

明明刚才还能聊得好好的,怎么突然就失联了?难道是网关挂了?

问题根源:诡异的“点号”测活

Claude Desktop 报错提示

Claude Desktop 频繁弹出的网关超时报错提示

为了搞清楚状况,我特意去翻看了一下网关的日志记录。这一看不要紧,原来罪魁祸首不是网关不稳定,而是 Claude Desktop 自身的一套“测活”机制。

简单来说,Claude Desktop 为了确认后端是否在线,会时不时发送一个特殊的请求。这个请求非常简单,内容仅仅是一个点号 .。它的逻辑大概是:

  1. 发送一个极短的请求,要求 AI 回复。
  2. 如果后端正常响应,说明网关通畅。
  3. 如果没反应或超时,就报错提醒用户。

然而,有些中转网关(尤其是为了节省 token 或者做了安全清洗的网关)在处理这种“空内容”或“无意义字符”时,可能会直接过滤掉,不返回任何内容,或者因为内容太短而不做处理。结果就是:网关收到了,但啥也没回。

cURL 命令示例

Claude Desktop 发送的测活请求 cURL 还原命令

在 Claude Desktop 看来,我发了消息没回音,那就是超时!于是乎,那个烦人的报错弹窗就又出现了。

抓包还原:它到底发了啥?

为了让大家更直观地理解,我把 Claude Desktop 发送的这个测活请求还原成了 cURL 命令,你们感受一下这个精简的程度:

curl 'http://127.0.0.1:8787/v1/chat/completions' \
  -H 'Accept: application/json' \
  -H 'Anthropic-Beta: claude-code-20250219' \
  -H 'Anthropic-Version: 2023-06-01' \
  -H 'Authorization: <your-token>' \
  -H 'Content-Type: application/json' \
  -H 'User-Agent: axonhub/1.0' \
  -d '{"messages":[{"role":"user","content":"."}],"model":"sonnet","max_tokens":1}'

核心就在这个 -d 参数里:content 只是一个点 .。对于这种请求,我们作为管理员其实没必要真的调起模型去回复它,既浪费钱又浪费资源。

解决方案:给客户端一个“爱的 Ping”

既然知道了原因,解决办法就很简单了。我们不需要真的去回答这个点号,只需要骗过客户端的检测机制即可。

我做了如下调整:在网关侧增加一条规则,专门针对这种测活请求。

逻辑如下:

  • 检测条件:当请求的 body 中包含类似 {"content":"."} 这种特征,或者 max_tokens 极小且内容仅为单字符时。
  • 响应动作:直接在网关层拦截,不让请求转发给上游 API,而是直接返回一个字符串 pong(或者任何符合格式的 JSON 响应)。

配置后的效果: 修改完网关配置后,Claude Desktop 再发出测活请求,网关瞬间返回 pong,客户端立刻确认“连接在线”,从此那个红色的超时报错再也没有出现过,界面清爽多了。

写在最后

这个 bug 说大不大,说小不小,但确实极其影响使用体验。很多朋友在搭建自己的 AI 网关集群时,很容易忽略客户端侧的这些“小动作”。如果你也在使用 Claude Desktop 接入自建网关,不妨检查一下日志,看看是不是被这个“点点点”给坑了。

通过这样一个小小的网关层优化,不仅解决了误报问题,还能拦截无效请求,节省转发资源,一举两得。希望这个小技巧能帮到正在折腾的你!

标签: none

AI Skills Smart Station on Nick Launches

评论已关闭