一、LLMs流式输出介绍
流式输出(Streaming Output)是大语言模型(LLMs)中一种重要的交互方式,它允许模型将生成的文本逐步返回给用户,而不是等待整个响应完成后再一次性返回。
二、流式输出的工作原理
-
分块生成:模型不是一次性生成完整回答,而是逐词或逐句生成
-
即时传输:每生成一部分内容就立即发送给客户端
-
持续处理:客户端可以即时显示或处理接收到的部分结果
三、流式输出的优势
1、用户体验提升
-
降低等待焦虑:用户不需要长时间等待就能看到部分结果
-
更自然的交互:模拟人类对话的逐步响应方式
-
响应感知:用户可以早期判断响应方向是否正确
2、技术优势
-
降低延迟:首个令牌(Token)的响应时间(TTFB)大幅缩短
-
内存效率:不需要缓存完整响应再发送
-
中断能力:用户可以在中途停止不想要的响应。
四、流式输出的应用场景
-
聊天应用:模拟真人对话体验
-
代码生成:逐步显示生成的代码
-
长文写作:分段显示生成内容
-
实时翻译:边听边翻译的场景
-
教育应用:逐步解释复杂概念
五、流式输出的技术挑战与解决方案
挑战 | 解决方案 |
---|---|
连接稳定性 | 心跳机制、自动重连 |
部分响应处理 | 客户端状态管理 |
错误处理 | 错误边界设计、重试机制 |
前后端协调 | 定义明确的数据协议 |
六、流式与一次性输出的对比
特性 | 流式输出 | 一次性输出 |
---|---|---|
响应时间 | 快(首个令牌) | 慢(完整响应) |
内存占用 | 低 | 高 |
用户体验 | 更优 | 一般 |
实现复杂度 | 较高 | 较低 |
适用场景 | 交互式应用 | 批处理任务 |
流式输出已成为现代LLM应用的标准功能,它能显著提升用户体验,特别是在需要长时间生成内容的场景中。
七、流式输出的实现
下面使用Langchain4j+阿里千问来实现:
1、引入依赖:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>langchain4j</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>langchain4j_springboot</name>
<description>langchain4j_springboot</description>
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<spring-boot.version>3.4.3</spring-boot.version>
<langchain4j.version>1.0.0-beta1</langchain4j.version>
</properties>
<dependencies>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-community-bom</artifactId>
<version>${langchain4j.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
这里重点说一下需要引入webflux实现流式响应输出:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
2、在application.properties配置流式输出所需要的信息
langchain4j.community.dashscope.streaming-chat-model.api-key=配置key
langchain4j.community.dashscope.streaming-chat-model.model-name=qwq-32b
3、编码实现:
package com.example.langchain4j.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.langchain4j.community.model.dashscope.QwenChatModel;
import dev.langchain4j.community.model.dashscope.QwenStreamingChatModel;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
/**
* @author:
* @Desc:
* @create: 2025-07-13 12:40
**/
@RestController
@RequestMapping("/ai")
public class ChatController {
@Autowired
private QwenStreamingChatModel streamingChatModel;
//须指定produces为stream输出,编码为utf-8,否则会乱码
@RequestMapping(value = "/stream", produces = "text/stream;charset=UTF-8")
public Flux<String> stream(@RequestParam(value="message",defaultValue = "你是谁") String message){
Flux<String> flux = Flux.create(sink -> {
streamingChatModel.chat(message, new StreamingChatResponseHandler() {
@Override
public void onPartialResponse(String partialResponse) {
sink.next(partialResponse);
}
@Override
public void onCompleteResponse(ChatResponse chatResponse) {
sink.complete();
}
@Override
public void onError(Throwable throwable) {
sink.error(throwable);
}
});
});
return flux;
}
}