整体流程
- 后端:NestJS 开启 SSE 接口 → 调用 LangChain 流式 LLM → 逐块推送到前端
- 前端:监听 SSE 流 → 实时拼接文字 → 实现打字机效果
一、后端实现(NestJS + LangChain + SSE)
1. 安装依赖
bash
# Nest 核心
npm install @nestjs/common @nestjs/core @nestjs/platform-express
# LangChain
npm install langchain @langchain/openai
2. 后端 SSE 流式接口(核心代码)
src/ai/ai.controller.ts
typescript
import { Controller, Post, Body, Sse } from '@nestjs/common';
import { Observable } from 'rxjs';
import { ChatOpenAI } from '@langchain/openai';
import { HumanMessage } from '@langchain/core/messages';
@Controller('ai')
export class AiController {
// SSE 流式输出 AI 回答
@Post('stream')
@Sse('sse') // 声明这是 SSE 接口
async streamChat(@Body() body: { message: string }) {
const { message } = body;
// 1. 初始化流式 LLM
const llm = new ChatOpenAI({
model: 'gpt-3.5-turbo',
apiKey: '你的 OPENAI_API_KEY',
configuration: {
baseURL: '你的 OPENAI_BASE_URL',
},
});
// 2. from 接收流式迭代器 → map 包装成 SSE 格式
const stream = await llm.stream([new HumanMessage(message)]);
return from(stream).pipe(
map((chunk) => ({
data: chunk.content,
}))
);
}
}
二、前端实现
- 使用浏览器原生 EventSource 或 fetch + ReadableStream 接收 SSE,从而实现实时拼接内容 → 打字机效果
javascript
async function chat(message) {
const res = await fetch('http://localhost:3000/ai/stream', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ message }),
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
let text = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
// 解析 SSE 流
for (const line of decoder.decode(value).split('\n')) {
if (!line.startsWith('data:')) continue;
const data = line.replace('data:', '').trim();
text += data;
document.getElementById('result').innerText = text; // 打字机效果
}
}
}
三、补充:原生 EventSource
EventSource 只支持 GET,不能 POST。无需 fetch、无需解析流、无需 TextDecoder,因为 EventSource 本身就是 SSE 专用对象,最简洁。
html
<script>
const res = document.getElementById('result');
let text = '';
// 创建 SSE 连接
const es = new EventSource('http://localhost:3000/ai/stream?message=你好');
// 接收流式消息
es.onmessage = e => {
text += e.data;
res.innerText = text;
};
// 结束
es.onend = () => es.close();;
// 错误
es.onerror = es.close;
</script>
四、总结
- 后端:NestJS
@Sse+ LangChainstream()+ RxJS Observable 推流 - 前端:fetch 读取流 → 解析 SSE → 实时渲染打字机效果