前言
流式输出是一种增量式的数据传输方式,它允许大模型在生成内容的同时,将已生成的部分立即发送给客户端,而不必等待整个响应完成。这种方式的核心特点是:
- 实时性:模型生成一小段内容就立即传输,用户几乎可以实时看到生成过程
- 增量传输:通过 SSE(Server-Sent Events)或 WebSocket 协议实现服务器到客户端的持续数据流
- 低感知延迟:用户通常在 100ms 内就能看到首批内容,大幅降低等待感
流式输出的原理剖析
什么是流式输出?
简单来说,流式输出就是数据不是一次性全部返回,而是分批次逐步传输给客户端。在 AI 生成式内容的场景中,模型会一个 token 一个 token 地生成内容,每生成一个 token 就立即发送给客户端,客户端再实时显示给用户。
为什么需要流式输出?
从后端角度来看,LLM API 通常是以流式的方式提供内容,这是因为模型生成内容是逐步进行的,每个 token 的生成都有一定的开销,并且可能需要付费。流式输出可以让用户更快地看到响应,同时也能减少不必要的等待时间。从前端角度来看,流式输出能够提供更好的用户体验,让用户感受到内容是实时生成的,增强了交互的流畅性。
前端如何实现流式输出?
基于 setInterval 和事件机制
在前端,我们可以使用 setInterval 函数结合事件机制来模拟流式输出。 setInterval 可以定时触发函数,模拟数据的逐步生成。而事件机制则可以在数据到达时触发相应的处理函数,更新页面内容。以下是一个简单的示例代码:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>流式输出示例</title>
</head>
<body>
<div id="output"></div>
<script>
const outputElement = document.getElementById('output');
const data = '这是一个流式输出的示例内容,我们将逐步显示它。';
let index = 0;
const intervalId = setInterval(() => {
if (index < data.length) {
outputElement.textContent += data[index];
index++;
} else {
clearInterval(intervalId);
}
}, 100);
</script>
</body>
</html>
在这个示例中,我们使用 setInterval 每隔 100 毫秒向页面添加一个字符,模拟流式输出的效果。
后端如何实现流式输出?
基于 Socket 的长连接
Socket 是一种长连接技术,它允许客户端和服务器之间保持持久的连接,双方可以随时发送和接收数据。在后端实现流式输出时,可以使用 Socket 来实现数据的实时传输。以下是一个简单的 Node.js 示例:
// 启动http server
// 引入express 框架
// import express from 'express'
// node 最初的commonjs 的模块化方案
const express = require('express')
// console.log(express)
const app =express();// 后端应用 App
// 路由
app.get('/',(req,res)=>{
// 返回index.html
// res.send("hello");// str
// response 响应对象 可以返回给前端的内容 有请求req 用户 ,用户通过浏览器(proxy) url localhost:1314 + get +path /
// http 足够简单 高并发 用户赶快走 断开联系
// __dirname 项目的根路径
console.log(__dirname)
res.sendFile(__dirname+'/index.html')
})
// 添加一个支持server push 的路由
app.get('/sse',(req,res)=>{
// 支持server push 不断地服务器端推送 少量的
// 设置响应头
res.set({
// stream 文本流,事件
'Content-Type':'text/event-stream',// 服务器推送的内容类型
'Cache-Control':'no-cache',// 不缓存 禁止前端使用缓存
'Connection':'keep-alive'// 保持连接
})
res.flushHeaders()// 刷新响应头
setInterval(()=>{
const message = `Current Time is ${new Date().toLocaleTimeString()}`
// server push
res.write(`data:${message}\n\n`)
},1000)
})
// node 内置模块
const http=require('http').Server(app);// server 基本能力 B/S 架构
// 监听? 伺服状态 伺候用户
http.listen(1314,()=>{
console.log('http server is running at 1314')
})
如何理解http.listen()这个函数,顾名思义listen是监听的意思,启动一个HTTP服务器,并让它持续监听指定端口(1314)的传入请求。当服务器成功启动后,会执行回调函数,控制台会打印:http server is running at 1314
总结
流式输出作为提升用户体验的重要技术,在 AI 产品和其他需要实时数据展示的场景中具有广泛的应用前景。无论是前端还是后端,都有多种实现流式输出的方式。在全栈开发中,我们可以结合前端和后端的技术,实现一个完整的流式输出系统。随着技术的不断发展,流式输出将会变得更加成熟和普及,成为前端开发中不可或缺的一部分。希望通过本文的介绍,大家对流式输出有了更深入的理解,能够在实际项目中灵活运用这一技术。