揭秘LLM流式输出:从原理到代码实现

205 阅读4分钟

引言

在当今的AI浪潮中,LLM(大型语言模型)如日中天。而流式输出作为提升用户体验的关键技术,也越来越受到开发者的关注。今天,我们就来深入探讨一下LLM流式输出的原理,并结合具体的代码实现,详细介绍前后端的连接方式。

屏幕录制 2025-06-26 160940.gif

为什么需要LLM流式输出

在正式介绍代码之前,我们先了解一下为什么需要流式输出。在与LLM交互时,如果等待模型生成完整的回答后再一次性输出,用户可能需要长时间等待,这会极大地影响用户体验。而流式输出则可以边生成边输出,让用户更快地看到响应,就像聊天一样自然流畅。

流式输出不仅能提升用户体验,还与成本有关。因为LLM通常是按token(令牌)计费的,流式输出可以让用户在等待的过程中就开始获取信息,而不需要等到所有token都生成完毕。

前端与后端的职责

在流式输出的实现中,前端和后端都有各自的职责。前端主要负责提供良好的用户体验,尽快返回结果给用户。而后端则负责与LLM交互,生成内容并通过合适的方式推送给前端。

代码实现:前后端流式输出连接方式

后端代码(index.js)

首先,我们来看后端代码,它是基于Node.js和Express框架实现的。以下是具体的代码及注释解释:

// 启动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({
        // steam 文本流, 事件
        '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('服务器启动成功');
})

代码解释

  1. 引入Express框架:使用require('express')引入Express框架,并创建一个Express应用实例app
  2. 根路由:当用户访问根路径/时,服务器会返回index.html文件。
  3. SSE路由:当用户访问/sse路径时,服务器会设置响应头,支持服务器端推送(SSE)。Content-Type设置为text/event-stream,表示这是一个事件流;Cache-Control设置为no-cache,禁止前端使用缓存;Connection设置为keep-alive,保持连接。
  4. 定时推送消息:使用setInterval函数每隔1秒向客户端推送当前时间。
  5. 启动服务器:使用http.listen方法启动服务器,监听端口1314。

前端代码(index.html中的JavaScript部分)

接下来,我们看前端代码,它主要负责与后端建立连接并处理接收到的消息。以下是具体的代码及注释解释:

document.getElementById('streamButton').addEventListener('click', function() {
    const outputDiv = document.getElementById('output');
    outputDiv.textContent = '连接中...';

    // 创建EventSource连接到服务器端的流式接口
    // html5 事件流 给他支持SSE 的地址
    // SSE Server Sent Events 服务器端推送事件
    const eventSource = new EventSource('/sse');
    
    // 接收消息事件
    eventSource.onmessage = function(event) {
        // 将接收到的内容追加显示
        outputDiv.textContent += '\n';
        outputDiv.textContent += event.data + '\n';
    };

    // 错误处理
    eventSource.onerror = function(err) {
        console.error('EventSource failed:', err);
        outputDiv.textContent += '\n\n发生错误,请刷新页面重试。';
        eventSource.close();
    };
});

代码解释

  1. 点击事件监听:当用户点击开始流式输出按钮时,会触发点击事件。
  2. 显示连接中信息:将输出区域的文本内容设置为连接中...
  3. 创建EventSource连接:使用EventSource对象创建一个与服务器端/sse路径的连接。
  4. 接收消息事件:当接收到服务器端推送的消息时,会触发onmessage事件,将接收到的消息追加到输出区域。
  5. 错误处理:当连接发生错误时,会触发onerror事件,在输出区域显示错误信息,并关闭连接。

完成展示:

屏幕录制 2025-06-26 161537.gif

总结

通过以上的代码实现,我们可以看到,前后端流式输出的连接方式主要是通过服务器端推送(SSE)来实现的。后端设置响应头,支持服务器端推送,前端使用EventSource对象与后端建立连接,接收服务器端推送的消息。这种方式可以让用户更快地看到响应,提升用户体验。

希望本文能帮助你更好地理解LLM流式输出的原理和实现方式。如果你有任何问题或建议,欢迎在评论区留言。

以上就是关于LLM流式输出的详细介绍,希望对你有所帮助!