go 无法直接通过 websocket 连接 deepseek 官方 api,因其仅支持 restful sse 流式响应(/v1/chat/completions?stream=true),未开放 wss:// 端点;所谓“deepseek websocket”实为 siliconflow 等第三方兼容代理或自建 vllm/ollama 桥接服务。

直接说结论:Go 用 net/http + gorilla/websocket 能跑通 DeepSeek 的流式 WebSocket 接口,但官方不提供原生 WebSocket API;你实际连的是兼容 OpenAI 协议的代理端点(比如 SiliconFlow、Cloud.SiliconFlow.cn 或自建 vLLM/Ollama 代理),不是 DeepSeek 官方 wss://api.deepseek.com ——后者目前未开放公开 WebSocket 支持。
为什么 Go 原生 WebSocket 连不上 deepseek.com?
DeepSeek 官方 API 文档(截至 2026 年 5 月)只明确支持 RESTful /v1/chat/completions,且仅允许 stream=true 返回 SSE(Server-Sent Events)格式。它没有公开 wss:// 端点,也没有在 OpenAPI spec 中声明 WebSocket upgrade 路径。你在网上看到的 “DeepSeek WebSocket 示例”,基本都指向第三方中转服务(如 wss://cloud.siliconflow.cn/v1/chat/completions)或本地部署的协议桥接层。
常见错误现象:
- 连接
wss://api.deepseek.com直接返回404或426 Upgrade Required但无有效 upgrade header - 用
gorilla/websocketDial 成功后,发消息立刻被 close,日志显示invalid request format - 误把 REST 流式响应(chunked + data: 字段)当成 WebSocket 消息,导致解析失败
如何用 Go 正确对接流式响应?选 REST+EventSource 还是 WebSocket?
优先走 REST + text/event-stream。它更轻量、调试方便、Go 标准库开箱即用,且与 DeepSeek 官方行为完全对齐。WebSocket 只应在以下情况考虑:前端强依赖 ws://(如某些低版本浏览器)、已有成熟 WS 消息总线、或后端需双向实时控制(如中断生成、动态插入选项)。
关键差异:
-
http.Client发起POST,Header 设Accept: text/event-stream,Body 含"stream": true→ 收到逐行data: {...}块 -
gorilla/websocketDial 第三方 WSS 地址(如wss://cloud.siliconflow.cn/v1/chat/completions),发送 JSON 消息体,接收二进制或文本帧 → 格式取决于代理实现,非标准 - REST 流式天然支持超时、重试、HTTP 代理穿透;WebSocket 需手动处理 ping/pong、连接保活、断线重连逻辑
示例片段(REST 流式):
req, _ := http.NewRequest("POST", "https://api.deepseek.com/v1/chat/completions", bytes.NewReader(payload))
req.Header.Set("Authorization", "Bearer "+apiKey)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "text/event-stream")
resp, _ := http.DefaultClient.Do(req)
scanner := bufio.NewScanner(resp.Body)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if strings.HasPrefix(line, "data:") {
data := strings.TrimPrefix(line, "data:")
var chunk map[string]interface{}
json.Unmarshal([]byte(data), &chunk)
if content, ok := deepSeekGetContent(chunk); ok {
fmt.Print(content) // 逐 token 输出
}
}
}
如果坚持用 WebSocket,要注意哪些坑?
你连的不是 DeepSeek,而是兼容层。必须确认该端点是否真正透传 token 级流式输出,而不是把整个 response 打包成单条 WebSocket message 发送——后者就失去了流式意义。
容易踩的坑:
- 第三方 WSS 地址返回的 JSON 结构和 OpenAI 不一致(例如用
delta.text而非delta.content),json.Unmarshal直接 panic - 未设置
websocket.Upgrader.CheckOrigin = func(r *http.Request) bool { return true },导致本地开发时跨域拒绝 - 忽略
websocket.WriteMessage的并发安全,多个 goroutine 同时写导致write tcp: use of closed network connection - 没处理
websocket.CloseMessage,连接异常关闭时无法触发 cleanup,goroutine 泄漏
真实可用的 WSS 端点示例(2026 年实测):
-
wss://cloud.siliconflow.cn/v1/chat/completions(需在请求头带Authorization) -
wss://api.siliconflow.cn/v1/chat/completions(部分 region 已切为 HTTPS-only) - 自建 vLLM +
--enable-reasoning+ OpenAI 兼容 API 开启后,可反向代理为 WSS
最常被忽略的一点:DeepSeek-V4-Pro 虽然支持 1M 上下文和 tool call,但它在流式模式下默认不返回 tool_calls 字段的中间 delta ——只有完整生成完毕后才在最后一个 chunk 里吐出全部 tool_calls 数组。如果你依赖流式解析工具调用,得自己缓存并合并 delta,不能假设每个 chunk 都含完整结构。











