6. LangChain4j + 流式输出详细说明
@[toc]
流式输出(StreamingOutput)的概念
是一种逐步返回大模型生成结果的技术,生成一点返回一点,允许服务器将响应内容,分批次实时传输给客户端,而不是等待全部内容生成完毕后再一次性返回。这种机制能显著提升用户体验,尤其适用于大模型响应较慢的场景(如生成长文本或复杂推理结果)。
简单的理解就是:服务器接受到一点大模型的信息,就生成发送一点给前端。



LangChain4j + 流式输出实操
-
创建对应的 moudle 模块项目
-
pom.xml 编写相关依赖:最基本的流式输出
langchain4j-open-ai + langchain4j + langchain4j-reactor
这三件必须存在,这里我们不指定版本,而是通过继承的 pom.xml 当中获取。
xml
<!--langchain4j-open-ai + langchain4j + langchain4j-reactor-->
<!-- 低阶 langchain4j-->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
</dependency>
<!-- 高阶 langchain4j-->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
</dependency>
<!-- 流式输出 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-reactor</artifactId>
</dependency>

xml
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.rainbowsea</groupId>
<artifactId>langchain4j-studys</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<groupId>com.rainbowsea.langchain4j07chatstream</groupId>
<artifactId>langchain4j-05chat-stream</artifactId>
<packaging>jar</packaging>
<name>langchain4j-05chat-stream</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--langchain4j-open-ai + langchain4j + langchain4j-reactor-->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-reactor</artifactId>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.22</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
- 配置 applcation.yaml / properties 配置文件,其中指明我们的输出响应的编码格式,因为如果不指定的话,存在返回的中文,就是乱码了。

properties
server.port=9005
spring.application.name=langchain4j-05chat-stream
# 设置响应的字符编码,避免流式返回输出乱码
server.servlet.encoding.charset=utf-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true
- 编写 ChatAssistan 接口,操作交流大模型

java
package com.rainbowsea.langchain4j07chatstream.service;
import reactor.core.publisher.Flux;
/**
* @Date 2025-05-30 16:19
* @Description: TODO
*/
public interface ChatAssistant
{
/**
* 普通输出,不是流式的
* @param prompt
* @return
*/
String chat(String prompt);
/**
* 流式输出,是流式的
* 注意:流式输出返回的要为 Flux<T> 类型的数据类型
* @param prompt
* @return
*/
Flux<String> chatFlux(String prompt);
}
- 编写大模型三件套(大模型 key,大模型 name,大模型 url) 三件套配置类



java
package com.rainbowsea.langchain4j07chatstream.config;
import com.rainbowsea.langchain4j07chatstream.service.ChatAssistant;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
import dev.langchain4j.service.AiServices;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.time.Duration;
import java.util.List;
/**
* @Description: 知识出处,https://docs.langchain4j.dev/tutorials/response-streaming
*/
@Configuration
public class LLMConfig
{
/**
* @Description: 流式对话接口 StreamingChatModel
* @Auther: zzyybs@126.com
*/
@Bean
public StreamingChatModel streamingChatModel(){
return OpenAiStreamingChatModel.builder()
.apiKey(System.getenv("aliQwen-api"))
.modelName("qwen-plus")
.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
.build();
}
@Bean
public ChatAssistant chatAssistant(StreamingChatModel streamingChatModel){
return AiServices.create(ChatAssistant.class, streamingChatModel);
}
/**
* @Description: 普通对话接口 ChatModel
* @Auther: zzyybs@126.com
*/
@Bean(name = "qwen")
public ChatModel chatModelQwen()
{
return OpenAiChatModel.builder()
.apiKey(System.getenv("aliQwen_api")) // 配置为你自己的环境变量当中的值
.modelName("qwen-plus")
.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
.build();
}
}
- 编写流式调用的 cutroller
- 流式输出的编写的 cutroller 有三种方式:
- 第一种方式:Flux.create() 方法(该返回会返回一个 Flux 流式类)返回流式输出,但是 Flux.create() 方法的 StreamingChatLanguageModel 当中的 chat(prompt, new StreamingChatResponseHandler() ) 方法的StreamingChatResponseHandler() 是个接口, 我们需要实现该接口的三个方法。
- 第二种方式:直接重写使用StreamingChatLanguageModel 接口类,直接调用 streamingChatLanguageModel.chat(prompt, new StreamingChatResponseHandler() 方法,没有返回值。不会返回一个 Flux 流式类
- 第三种方式:就是使用自己定义的 ChatAssistant 操作大模型的接口类,让就返回一个 Flux 流式类。推荐使用第三种方式,简单,灵活,方便实现
第一种方式: Flux.create() 方法(该返回会返回一个 Flux 流式类)返回流式输出,但是 Flux.create() 方法的 StreamingChatLanguageModel 当中的 chat(prompt, new StreamingChatResponseHandler() ) 方法的
java
Flux.create(emitter -> {
streamingChatLanguageModel.chat(prompt, new StreamingChatResponseHandler()
{
@Override
public void onPartialResponse(String partialResponse)
{
emitter.next(partialResponse);
}
@Override
public void onCompleteResponse(ChatResponse completeResponse)
{
emitter.complete();
}
@Override
public void onError(Throwable throwable)
{
emitter.error(throwable);
}
});
});

返回的是一个 Flux 流式类
java
import com.rainbowsea.langchain4j07chatstream.service.ChatAssistant;
import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
/**
* @Description: 知识出处,https://docs.langchain4j.dev/tutorials/response-streaming
*/
@RestController
@Slf4j
public class StreamingChatModelController
{
@Resource //直接使用 low-level LLM API
private StreamingChatModel streamingChatLanguageModel;
// http://localhost:9005/chatstream/chat?prompt=天津有什么好吃的
@GetMapping(value = "/chatstream/chat")
public Flux<String> chat(@RequestParam("prompt") String prompt)
{
System.out.println("---come in chat");
return Flux.create(emitter -> {
streamingChatLanguageModel.chat(prompt, new StreamingChatResponseHandler()
{
@Override
public void onPartialResponse(String partialResponse)
{
emitter.next(partialResponse);
}
@Override
public void onCompleteResponse(ChatResponse completeResponse)
{
emitter.complete();
}
@Override
public void onError(Throwable throwable)
{
emitter.error(throwable);
}
});
});
}
}
运行测试:

第二种方式:直接重写使用StreamingChatLanguageModel 接口类,直接调用 streamingChatLanguageModel.chat(prompt, new StreamingChatResponseHandler() 方法,没有返回值。不会返回一个 Flux 流式类

java
import com.rainbowsea.langchain4j07chatstream.service.ChatAssistant;
import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
/**
* @Description: 知识出处,https://docs.langchain4j.dev/tutorials/response-streaming
*/
@RestController
@Slf4j
public class StreamingChatModelController
{
@Resource //直接使用 low-level LLM API
private StreamingChatModel streamingChatLanguageModel;
@GetMapping(value = "/chatstream/chat2")
public void chat2(@RequestParam(value = "prompt", defaultValue = "北京有什么好吃") String prompt)
{
System.out.println("---come in chat2");
streamingChatLanguageModel.chat(prompt, new StreamingChatResponseHandler()
{
@Override
public void onPartialResponse(String partialResponse)
{
System.out.println(partialResponse);
}
@Override
public void onCompleteResponse(ChatResponse completeResponse)
{
System.out.println("---response over: "+completeResponse);
}
@Override
public void onError(Throwable throwable)
{
throwable.printStackTrace();
}
});
}
}
运行测试:

因为没有返回值,只是在控制台当中打印出来。
第三种方式:就是使用自己定义的 ChatAssistant 操作大模型的接口类,让就返回一个 Flux 流式类。

java
import com.rainbowsea.langchain4j07chatstream.service.ChatAssistant;
import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
/**
* @Description: 知识出处,https://docs.langchain4j.dev/tutorials/response-streaming
*/
@RestController
@Slf4j
public class StreamingChatModelController
{
@Resource //自己封装接口使用 high-level LLM API
private ChatAssistant chatAssistant;
@GetMapping(value = "/chatstream/chat3")
public Flux<String> chat3(@RequestParam(value = "prompt", defaultValue = "南京有什么好吃") String prompt)
{
System.out.println("---come in chat3");
return chatAssistant.chatFlux(prompt);
}
}
运行测试:

最后:
"在这个最后的篇章中,我要表达我对每一位读者的感激之情。你们的关注和回复是我创作的动力源泉,我从你们身上吸取了无尽的灵感与勇气。我会将你们的鼓励留在心底,继续在其他的领域奋斗。感谢你们,我们总会在某个时刻再次相遇。"