长连接无处不在:Agent 架构背后的通信真相

一句话概括:Agent 架构里几乎每一条链路——UI ↔ Agent、Agent ↔ LLM、Agent ↔ MCP——都是长连接。这不是工程师故意”炫技”,而是被 LLM 的流式生成这一底层特性逼出来的必然结果。一旦生成是”边想边说”的,请求-响应就塌了,整条链路都得跟着流起来。


一个你可能忽略的细节

打开 ChatGPT、Claude、Cursor 这些 AI 应用,你都会看到同一个现象:

回复是一个字一个字”蹦”出来的,不是整段刷新出来的。

看起来只是个动画效果?不是。

它背后是整个 Agent 架构区别于传统 Web 应用的底层通信范式——没有任何一个字是等全部生成完才发过来的,每一个 token 都是实时推送的

再往深看一层,你会发现:

  • 浏览器和 Agent 服务之间,连接一直没断
  • Agent 和 LLM 厂商(OpenAI / Anthropic)之间,连接也没断
  • Agent 和它调用的 MCP Server 之间,连接还是没断

三段链路,全是长连接

这就很反常识了。传统 Web 时代我们被教育的是”HTTP 是短连接,一问一答,请求完就断开”。为什么到了 Agent 时代,这条金科玉律突然不管用了?

这篇文章就来把这件事讲透。


先说答案:所有长连接的源头只有一个

整条链路里那么多长连接,它们的根因是同一个

LLM 的生成是流式的(Streaming)——一次一个 token,按时间顺序往外吐。

这是 LLM 架构决定的物理事实,不是工程选择:

  • LLM 是 autoregressive(自回归)模型,生成第 N 个 token 必须依赖前 N-1 个 token
  • 每生成一个 token 需要一次完整的前向推理,耗时通常在 10~50ms
  • 生成 1000 个 token 就得等 10~50 秒

如果走传统”请求-响应”模式,用户按下回车后要盯着空白屏幕等半分钟,才能一次性看到完整回复。这种体验没人能忍。

所以 LLM 的输出天然就是流式的。这个”流式”特性像涟漪一样,从最底层的 LLM 一路往上传导,把每一段链路都逼成了长连接:

1
2
3
4
5
6
7
8
9
10
LLM 流式生成(源头)


Agent ↔ LLM 链路必须流式


UI ↔ Agent 链路必须流式


(顺便)Agent ↔ MCP 也用了长连接

下面我们一段一段拆开看。


第一段:Agent ↔ LLM(源头)

这是整条链路最关键的一段——它决定了所有上游的形态

先澄清一个概念:HTTP “长连接”到底指什么?

在展开之前,必须先厘清一个被严重滥用的词。我们口头说的 “HTTP 长连接”,在技术上其实对应三种完全不同的东西

名字 本质 关键 Header 谁能主动发
① Keep-Alive 多次请求复用同一条 TCP 连接 Connection: keep-alive 客户端发、服务端回
② 长响应 / Chunked / SSE 一次请求,响应体被拉长、分块推送 Transfer-Encoding: chunked 服务端单向推
③ WebSocket 协议升级后变成双向消息管道 Upgrade: websocket 双方都能主动发

严格说,HTTP 协议本身没有”长连接”的概念,只有”连接复用”和”分块传输”这两种基础能力。我们日常所说的”长连接”,是这些底层能力组合出来的应用层现象

本文后面讲的 Agent ↔ LLM 链路,属于 ②——一次 HTTP 请求,响应以 SSE 格式流式输出,底层 TCP 连接顺便被 Keep-Alive 复用。而 UI ↔ Agent 多数也是 ②,只有语音等双向场景才会用到 ③。搞清楚这点,后面的内容就不会混淆。

传统 HTTP 的模型

先回忆一下传统 HTTP 请求:

1
2
3
Client → POST /api/search → Server
Client ← 完整 JSON 响应 ← Server
[连接关闭]

一次请求、一次响应、连接关闭。服务器必须把所有数据准备齐了,才能一次性发出来

为什么 LLM API 不能这么玩?

假设 OpenAI 的接口设计成传统 HTTP:

1
2
3
4
POST /v1/chat/completions
→ 服务器开始推理
→ 推理 30 秒...
→ 一次性返回完整回复

问题大了:

  1. 用户体验灾难:盯着 loading 转 30 秒
  2. 超时风险极高:很多网关默认 30s 超时,长回复直接断
  3. 无法中断:用户看到前两句就觉得跑偏了,也只能等它跑完才能改

LLM API 实际的样子:SSE 流式响应

所以所有主流 LLM API(OpenAI、Anthropic、Gemini)都用了 SSE(Server-Sent Events)

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /v1/chat/completions
Accept: text/event-stream

HTTP/1.1 200 OK
Content-Type: text/event-stream

data: {"choices":[{"delta":{"content":"长"}}]}

data: {"choices":[{"delta":{"content":"连"}}]}

data: {"choices":[{"delta":{"content":"接"}}]}

data: [DONE]

注意几个关键点:

① 这是一个 HTTP 请求,但响应是”分段流式”的

HTTP 响应头发回来后,连接不关闭。服务器每生成一个 token(或几个 token 一批),就通过同一个连接推一条 data: 事件过来。直到 [DONE] 标记才关闭。

② 本质上是”长响应”,而非传统意义的”长连接”

严格说,它仍然是一次 HTTP 请求,只是响应体被拉长到整个生成周期。这种模式有几个专有名词:

  • Chunked Transfer Encoding(分块传输编码,HTTP/1.1 的能力)
  • Server-Sent Events(基于分块传输的标准化事件流格式)

③ 为什么选 SSE 而不是 WebSocket?

这是个好问题,很多人会直接反应”WebSocket 不是更适合长连接吗?”

但 LLM 生成的通信有个关键特征:数据流是单向的(服务器推、客户端只管收)。SSE 在这种场景下有几个碾压性优势:

对比点 SSE WebSocket
协议复杂度 就是 HTTP,无新协议 需要 Upgrade 握手、独立帧格式
浏览器支持 EventSource 原生支持 原生支持但 API 复杂
穿透代理 就是 HTTP,CDN / 网关全透明 很多中间设备不认
重连机制 Last-Event-ID 自动重连 需要应用层自己实现
鉴权 和普通 HTTP 一样(Header / Cookie) 握手阶段鉴权比较绕
场景匹配 单向推送 ✅ 双向通信(但 LLM 不需要)

一句话:LLM 场景下服务器只管”吐”、客户端只管”听”,SSE 用最小成本解决了问题,没必要上 WebSocket。


第二段:UI ↔ Agent

搞清楚了底层,上一层就很自然了。

问题:Agent 收到 LLM 的流式 token,怎么交给前端?

Agent 作为中间层,面临一个选择:

  • 方案 A:把 LLM 的流全部收集完,整理好一次性返回给前端 —— 传统 HTTP 模式
  • 方案 B:LLM 吐一个 token,Agent 就往前端推一个 token —— 流式透传

如果选 A,那前面 LLM 流式设计的所有好处都白费了——用户依然要盯着 loading 等半分钟

所以几乎所有 AI 应用都选了 B:前端到 Agent 也用流式推送

具体怎么实现?主流就两种

方式一:SSE(最常见)

ChatGPT、Claude.ai、Cursor 都是这个方案:

1
2
3
4
5
6
7
8
9
10
11
前端:
const es = new EventSource('/api/chat?q=...');
es.onmessage = (e) => {
appendToken(JSON.parse(e.data));
};

Agent 服务端(伪代码):
response.setHeader('Content-Type', 'text/event-stream');
for await (const chunk of callLLM(prompt)) {
response.write(`data: ${JSON.stringify(chunk)}\n\n`);
}

好处:上下游协议一致(SSE→SSE),Agent 只需要做”管道”式透传,逻辑极简。

方式二:WebSocket(少数场景)

当 Agent 不是”问一句答一句”,而是需要双向实时交互时,才考虑 WebSocket。典型场景:

  • 语音对话 Agent:前端要持续上传音频流,后端要持续下发音频流 —— 双向都是流
  • 协作式 Agent:用户可以随时打断、插话,Agent 要实时响应
  • 多 Agent 协同的可视化调试:后端状态频繁变化,前端要实时展示

除了这些场景,纯文本聊天的 Agent 用 SSE 就够了

为什么不能用普通 HTTP 轮询?

可能有人想:前端每 500ms 轮询一次 /api/poll?session=xxx 不就行了?

别。三个硬伤:

  1. 延迟高:token 已经生成好了,却要等下一次轮询才能拿到,体验卡顿
  2. 带宽浪费:绝大多数轮询拿回来的是”还没有新内容”,纯浪费
  3. 服务端压力大:QPS 被无意义地放大几十倍

这就是为什么流式推送(Push)比轮询(Pull)在 LLM 场景里碾压式胜出


第三段:Agent ↔ MCP

这一段有点意思——它用长连接,原因和上面两段完全不一样

MCP 的长连接不是因为流式,而是因为”会话”

LLM 的流式是数据特性导致的长连接。MCP 的长连接则是协议特性导致的:

MCP 是一个有状态的会话协议,不是无状态的 RPC。

回想一下 MCP 的完整握手流程(这段在我 [[Agent如何通过MCP调用工具:完整链路详解|上一篇文章]] 里讲过):

1
2
3
4
5
6
1. 建立连接
2. initialize(协商协议版本、交换能力)
3. notifications/initialized(确认就绪)
4. tools/list(拉取工具列表)
5. 后续可能有 N 次 tools/call
6. 期间 Server 可能反向推送 notifications/tools/list_changed

关键点:步骤 2-6 必须发生在同一个会话上下文里。

如果每次 tools/call 都重新走一遍 1→2→3→4,那:

  • 握手开销巨大(几百毫秒级)
  • 服务端无法推送动态事件(工具列表变了、资源更新了)
  • 状态管理会变成一场灾难

所以 MCP 从设计之初就是”建立连接 → 多轮对话 → 关闭连接“的模式,天然就是长连接。

MCP 的三种传输方式

MCP 规范定义了两大类传输方式(三种实际形态):

① stdio(本地进程)

1
2
3
4
5
6
7
Host 进程
│ fork/exec

MCP Server 子进程

├─ stdin(Host 写,Server 读)
└─ stdout(Server 写,Host 读)

用标准输入输出作为双向管道,进程存活期间管道一直开着。这就是”长连接”的进程间等价物

适用场景:本地工具、文件系统、开发辅助工具(你本地跑的绝大多数 Claude Desktop / Cursor 的 MCP 都是这种)。

② HTTP + SSE(旧方案,已废弃)

2024 年规范的远程传输方案:

1
2
Client → GET /sse → Server(建立 SSE 长连接,接收推送)
Client → POST /endpoint → Server(发送请求)

两个端点配合:一个 SSE 长连接专门收服务端推送,一个 POST 端点专门发请求。

问题:双端点架构复杂、断线重连语义不清、水平扩展困难。

③ Streamable HTTP(2025-03 新方案,当前主流)

这是当前 MCP 的主推远程传输方式,只用一个 /mcp 端点

1
2
3
4
5
6
7
Client → POST /mcp
Accept: application/json, text/event-stream
Body: JSON-RPC 请求

Server 决定返回什么:
├── 简单调用 → 返回单个 JSON(一次性响应)
└── 复杂调用 → 升级为 SSE 流(流式推送中间结果、进度)

精妙之处

  • 同一个端点既能做传统 RPC,也能做流式推送
  • Server 根据请求类型动态决定返回格式
  • 配合 Mcp-Session-Id 响应头维护会话状态,天然支持水平扩展和断线重连

这个方案事实上打通了”无状态 RPC”和”有状态长连接”的边界——需要短连接时它就是短连接,需要长连接时它就是长连接

一张表看懂 MCP 传输的演化

方案 推出时间 端点数 传输形态 当前状态
stdio 2024-11 - 进程管道 ✅ 本地场景主流
HTTP + SSE 2024-11 2 个 双通道长连接 ⚠️ 已废弃
Streamable HTTP 2025-03 1 个 按需流式 ✅ 远程场景主流

整体链路全景图

把三段链路拼起来,就是一个典型 Agent 应用的完整通信拓扑:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌──────────────┐
│ 浏览器 UI │
└──────┬───────┘
│ ① SSE 长连接(流式推送 token)
│ Content-Type: text/event-stream

┌──────────────────────────────────┐
│ Agent 服务 │
│ ┌────────────┐ ┌────────────┐ │
│ │ LLM Client │ │ MCP Client │ │
│ └─────┬──────┘ └─────┬──────┘ │
└────────┼───────────────┼─────────┘
│ │
│ ② HTTP 长响应 │ ③ stdio / Streamable HTTP
│ SSE 流式 │ 长连接会话
▼ ▼
┌────────────┐ ┌────────────┐
│ LLM 厂商 │ │ MCP Server │
│ OpenAI等 │ │ 各种工具 │
└────────────┘ └────────────┘

每一条箭头下面都藏着一个长连接。这不是巧合,而是三个独立原因共同作用的结果

链路 是长连接吗? 真正原因
UI ↔ Agent ✅ SSE LLM 生成是流式的,必须端到端透传
Agent ↔ LLM ✅ HTTP 长响应 + SSE LLM autoregressive 生成,耗时长
Agent ↔ MCP ✅ stdio / Streamable HTTP MCP 是有状态会话协议

注意第二列的措辞差别——Agent ↔ LLM 严格说是”长响应”,不是”长连接”。它仍然是一次 HTTP 请求,只不过响应体被拉长到整个生成过程。这个区别在架构设计时很重要(下面会讲为什么)。


长连接带来的新问题

“长连接无处不在”听起来很美好,但工程上是一堆新坑。Agent 开发绕不开这些问题:

① 超时怎么办?

传统后端的 Nginx / 网关配置,默认 proxy_read_timeout 60s。LLM 生成经常超过这个时间,连接直接被中间层砍掉。

解决方案

  • 网关层把超时调到 300s+(或无限)
  • 应用层周期性发心跳事件(比如 SSE 里每 15s 发一条 : heartbeat 注释行),让中间层知道”我还活着”

② 断线重连怎么办?

SSE 有原生的 Last-Event-ID 机制,客户端重连时会带上最后收到的事件 ID,服务端可以从那之后续传。

:LLM 推理不是可重放的——你中断再恢复,state 已经丢了。所以实际工程里经常是:

  • 短时间断线(几秒)→ 丢弃当前生成,重新发起
  • 会话级断线 → 靠 session_id 恢复对话历史,而不是恢复”正在进行的生成”

MCP 的 Streamable HTTP 用 Mcp-Session-Id 解决会话恢复,但”正在流式生成中”的恢复仍然是难题。

③ 水平扩展怎么办?

长连接的天然问题——同一个会话必须粘在同一台机器上(sticky session)。负载均衡不能轮询分发,否则 MCP 会话状态就对不上了。

主流做法

  • session_id 做一致性哈希路由
  • 或者把会话状态外置到 Redis,让任何实例都能接手

④ 并发和资源占用

每一个活跃用户都占用:

  • 1 条 UI ↔ Agent 的 SSE 连接
  • 1 条 Agent ↔ LLM 的 HTTP 长响应
  • N 条 Agent ↔ MCP 的会话连接

这些连接在”用户在思考、没发消息”的时候也开着。所以 Agent 服务的并发连接数瓶颈比传统 API 服务严重得多。

应对

  • 必须用异步非阻塞 IO(Python 选 asyncio,Go 天然适合,Node 也行)
  • 连接复用(HTTP/2 多路复用、MCP 会话池)
  • 空闲连接及时回收

⑤ 可观测性难度陡增

传统 API 的 trace 是一次请求-响应闭环,清晰明了。Agent 链路的一次”用户提问”可能跨越:

  • 1 次 UI→Agent 的 SSE
  • N 次 Agent→LLM 的流式调用
  • M 次 Agent→MCP 的工具调用
  • 每次调用内部又是 token 级的流式事件

做好 tracing 需要把这些关联到同一个 trace_id,并且能在时间轴上展示 token 级事件。这是一门新学问。


回到开头的问题

为什么 Agent 时代长连接无处不在?

现在可以给一个完整的答案:

  1. LLM 的流式生成是物理源头——autoregressive 架构决定了生成必须逐 token 进行
  2. 这个特性向上游传导——Agent 不能”等齐了再发”,UI 不能”等齐了再显示”
  3. SSE 成为事实标准——单向推送场景下它的综合成本最低
  4. MCP 顺势而为——会话型协议本来就适合长连接,正好和上面的生态匹配

短连接是”写信”——写好、寄出、等回信,每次都要重新建立联系。
长连接是”打电话”——接通一次,边想边说,实时互动

Agent 需要的从来不是”写信”——它需要的是一通永不挂断的电话。


一句话总结

范式 传统 Web Agent 架构
通信形态 请求-响应(短连接) 流式推送(长连接)
设计前提 服务器能快速准备好完整响应 服务器边生成边输出
核心协议 REST / HTTP SSE / Streamable HTTP / stdio
状态管理 无状态,幂等友好 有会话,需要粘性
基础设施挑战 负载均衡、缓存 连接管理、流式 tracing、会话路由

一整个软件架构范式的迁移,正在发生。长连接不是 Agent 架构的”实现细节”,而是它区别于传统 Web 时代的本质特征

下一次当你看到屏幕上那一个个蹦出来的字,你知道了——它们每一个,都是从一条没有挂断的电话线里,实时推过来的