讲透 SSE:从流式响应到 LangChain model.stream()

大模型回答问题时,如果等整段内容全部生成完再返回,用户会经历明显的等待。

因此聊天应用通常采用流式输出

复制代码
模型生成一点
→ 返回一点
→ 页面显示一点

现在使用 LangChain,实现已经非常简单:

arduino 复制代码
const stream = await model.stream(prompt);

for await (const chunk of stream) {
  process.stdout.write(chunk.content);
}

但要真正理解这几行代码,还得知道底层发生了什么。


一、流式输出最底层是什么

以前直接使用 fetch 请求模型接口时,开启:

vbnet 复制代码
stream: true

不能再简单地:

csharp 复制代码
await response.json();

而是读取:

vbscript 复制代码
response.body

大概过程是:

ini 复制代码
const reader = response.body.getReader();
const decoder = new TextDecoder();

while (true) {
  const { value, done } = await reader.read();

  if (done) break;

  console.log(decoder.decode(value));
}

数据经历:

javascript 复制代码
HTTP Response
    ↓
ReadableStream
    ↓
Uint8Array
    ↓
TextDecoder
    ↓
字符串

这里要明确一点:

read() 每次得到的是一个数据块 chunk,不保证刚好是一条完整消息。

例如:

css 复制代码
data: {"content":"hello"}

可能被拆成:

css 复制代码
data: {"cont

和:

css 复制代码
ent":"hello"}

所以以前还需要 buffer 保存残缺内容,再和下一块数据拼起来。

这也是为什么自己处理模型原始流会比较麻烦。


二、SSE 到底是什么

SSE,全称:

arduino 复制代码
Server-Sent Events

它是一种基于 HTTP 的服务器单向推送机制。

最小 Node 服务:

javascript 复制代码
const http = require('http');

const server = http.createServer((req, res) => {
  if (req.url === '/stream') {
    res.writeHead(200, {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      Connection: 'keep-alive'
    });

    res.write('data: 你\n\n');

    setTimeout(() => {
      res.write('data: 好\n\n');
    }, 1000);

    setTimeout(() => {
      res.write('data: SSE\n\n');
      res.end();
    }, 2000);
  }
});

server.listen(3000);

SSE 最重要的是两个东西。

响应头:

vbnet 复制代码
Content-Type: text/event-stream

数据格式:

kotlin 复制代码
data: 你

data: 好

其中:

kotlin 复制代码
data:

表示事件的数据。

而:

复制代码
\n\n

表示一个事件结束。


三、浏览器如何接收 SSE

浏览器原生提供了:

复制代码
EventSource

使用方式:

ini 复制代码
const eventSource =
  new EventSource('/stream');

eventSource.onmessage = event => {
  console.log(event.data);
};

服务器:

lua 复制代码
res.write('data: hello\n\n');

浏览器最终得到:

ini 复制代码
event.data === 'hello';

所以完整过程是:

vbnet 复制代码
Browser
   ↓
GET /stream
   ↓
Node Server
   ↓
Content-Type: text/event-stream
   ↓
data: hello\n\n
   ↓
EventSource
   ↓
onmessage

注意,SSE 并不是服务器返回很多个 HTTP Response。

仍然是:

vbscript 复制代码
1 个 Request
+
1 个 Response

只不过这个 Response 没有立即结束,而是不断:

ini 复制代码
res.write();

向其中追加内容。


四、Stream 和 SSE 不是一回事

Node 中读取文件也可以:

scss 复制代码
fs.createReadStream('./index.html')
  .pipe(res);

这同样叫 Stream。

所以:

arduino 复制代码
Stream

是一个更大的概念:

数据可以边产生、边读取、边处理。

而 SSE 是:

建立在 HTTP 流式响应之上的一种事件通信格式。

可以这样记:

arduino 复制代码
Stream
→ 数据怎么持续流动

SSE
→ 流动的数据怎么组织成事件

因此:

arduino 复制代码
Stream ≠ SSE

但 SSE 会利用 Stream。


五、为什么后来又需要 BFF

浏览器直接调用大模型接口会出现几个问题:

vbnet 复制代码
API Key 暴露
跨域
不同模型接口格式不同
流式解析逻辑堆在前端

因此真实项目通常变成:

复制代码
Browser
   ↓
Node / BFF
   ↓
LLM

BFF 负责:

vbnet 复制代码
保存 API Key
请求大模型
处理跨域
统一模型接口
转发流式响应

本质还是:

markdown 复制代码
上游模型流
    ↓
Node
    ↓
下游浏览器流

六、现在为什么只需要 model.stream()

理解完底层,再回来看 LangChain:

arduino 复制代码
const stream =
  await model.stream('介绍一下莫扎特');

for await (const chunk of stream) {
  process.stdout.write(chunk.content);
}

以前自己处理的是:

javascript 复制代码
HTTP Response
↓
ReadableStream
↓
Uint8Array
↓
TextDecoder
↓
buffer
↓
SSE / JSON
↓
模型增量内容

现在 LangChain 给我们的已经是:

复制代码
AIMessageChunk

所以只需要:

复制代码
chunk.content

这里的 chunk 和以前网络层的 chunk 已经不是一个层级。

以前:

ini 复制代码
chunk = 字节数据块

现在:

ini 复制代码
chunk = AI 消息增量

LangChain 把不同模型供应商底层的流式协议进行了统一封装。


七、最终只需要记住三层

markdown 复制代码
HTTP Stream
    ↓
数据可以持续传输

SSE
    ↓
在 HTTP Stream 上组织事件

LangChain
    ↓
model.stream()
    ↓
AIMessageChunk

真正写业务时:

arduino 复制代码
const stream = await model.stream(prompt);

for await (const chunk of stream) {
  process.stdout.write(chunk.content);
}

就够了。

但理解 SSE 和 Stream 的意义在于:当以后遇到代理缓存、流式中断、BFF 转发等问题时,不会把 model.stream() 当成一个完全无法解释的黑盒。

相关推荐
β添砖java2 小时前
深度学习31注意力机制、注意力分数、使用注意力机制的seq2seq、自注意力
人工智能·深度学习
ZGIAI3 小时前
销售团队最缺的不是另一个 AI,而是有人把跟进这件事一直做下去
人工智能·架构
隔窗听雨眠3 小时前
MCP会成为Agentic AI的标准吗?技术演进、生态博弈与标准之路的深度分析
人工智能
2601_967659883 小时前
2026年多个网页快速提取重点、自动汇总、对比并整理成表格的AI工具清单
人工智能
IT古董3 小时前
AI资讯日报|2026年9月5日:GPT-6 Astra全量推送却遭“翻车“,奥特曼紧急致歉一天12条大新闻:OpenAI翻车、英伟达收购、最狠AI法案出台
人工智能
chen_zn953 小时前
《WAM 系列》Zero-WAM | 人类视频上下文学习 | 未来片段预测 | 零样本跨任务泛化
人工智能·具身智能·vla
airank3 小时前
2026年GEO服务商选型指南:系统梳理服务商分类框架、主流服务商能力横评、企业级选型六大维度及避坑要点,帮助企业在AI搜索时代做出科学决策。
大数据·人工智能·数据分析·aigc
Delite8023 小时前
摆脱实验室束缚:便携式卡尔费休微量水分检测技术与现场应用解析
大数据·网络·人工智能
Jialu.3 小时前
模型压缩实战:BERT 量化从 390MB 到 146MB 的实践
人工智能·深度学习·bert