🚀 LangChain4j 流式模式实战指南

让 AI 响应"边生成边输出",打造丝滑对话体验


📖 什么是流式模式?

langchain4j 是一个用于构建和运行基于语言模型应用程序的 Java 库。它极大地简化了与各种 AI 模型(如 GPT、Claude 等)的交互过程。

流式模式,正是 langchain4j 提供的一项核心功能——让你以更高效、更实时的方式处理 AI 响应。

💡 核心概念

传统模式 vs 流式模式

对比项 传统模式 流式模式
响应方式 等全部生成完再返回 边生成边返回
用户体验 需要长时间等待 即时看到内容
适用场景 短文本 长文本、对话

🌟 为什么需要流式模式?

1️⃣** 即时反馈**

用户无需等待完整答案生成,就能看到逐步输出的内容。想象一下,你问 AI 一个问题,它像打字一样一个字一个字地蹦出来——这种体验,是不是很像在和真人聊天?

2️⃣** 节省资源**

服务器端不需要缓存全部输出,生成一段发送一段。内存占用更低,系统更轻盈。

3️⃣** 灵活可控**

更容易实现取消正在进行中的请求、实时中断等功能。用户不想看了?随时停止。

🎯 典型应用场景

  • 💬** 聊天机器人** — 让对话更加流畅自然,告别"转圈等待"
  • 📄** 文档摘要生成** — 逐步展示内容概要,用户可随时查看
  • 💻** 代码补全** — IDE 中实时显示补全建议,开发效率翻倍

🏗️ LangChain4j 架构概览

🔧 Low Level 原生方式

底层 API,灵活度最高,适合需要精细控制的场景。

🤖 AI Service 高级方式

封装好的服务层,开箱即用,开发效率更高。


📝 实战示例一:Low Level 原生方式

Step 1:创建模块并引入依赖

新建 Maven 模块,添加必要的依赖包:

<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.whc</groupId>
        <artifactId>langChain4j-whc</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>lanchain4j-whc-stream-memory</artifactId>
    <packaging>jar</packaging>
    <name>lanchain4j-whc-stream-memory</name>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <!-- Spring Boot Web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- LangChain4j 核心 -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-open-ai</artifactId>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j</artifactId>
        </dependency>
        <!-- Reactor 支持(流式响应必备) -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-reactor</artifactId>
        </dependency>
        <!-- 阿里百炼平台 -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-community-dashscope-spring-boot-starter</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>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

💡 提示langchain4j-reactor 是实现流式响应的关键依赖,别忘了加!


Step 2:YAML 配置

application.yml 中配置阿里百炼平台的连接信息:

server:
  port: 9004

spring:
  application:
    name: langchain_whc_stream_memory

ai:
  dashScope:
    apiKey: ${AI_DASHSCOPE_API_KEY}
    modelName: ${AI_DASHSCOPE_MODEL_NAME}
    baseUrl: ${AI_DASHSCOPE_BASE_URL}

🔑 建议将敏感信息(如 API Key)通过环境变量注入,不要硬编码在配置文件中。


Step 3:配置类

将 LLM 配置类交给 Spring 管理,创建 ChatModelStreamingChatModel 的 Bean:

@Slf4j
@Configuration
public class LlmConfig {

    @Value("${ai.dashScope.apiKey}")
    private String dashScopeApiKey;

    @Value("${ai.dashScope.modelName}")
    private String dashScopeModelName;

    @Value("${ai.dashScope.baseUrl}")
    private String dashScopeBaseUrl;

    /**
     * 普通聊天模型(同步响应)
     */
    @Bean
    public ChatModel chatModel() {
        return OpenAiChatModel.builder()
                .apiKey(dashScopeApiKey)
                .modelName(dashScopeModelName)
                .baseUrl(dashScopeBaseUrl)
                .build();
    }

    /**
     * 流式聊天模型(异步分片响应)
     */
    @Bean
    public StreamingChatModel streamingChatModel() {
        return OpenAiStreamingChatModel.builder()
                .apiKey(dashScopeApiKey)
                .modelName(dashScopeModelName)
                .baseUrl(dashScopeBaseUrl)
                .build();
    }
}

Step 4:Controller 实现

编写控制器,提供普通对话和流式对话两个接口:

package com.whc.memory.controller;

import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
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;

/**
 * 流式对话控制器
 * @author 不泽
 * @date 2025/9/2
 */
@Slf4j
@RestController
@RequiredArgsConstructor
public class StreamingController {

    private final ChatModel chatModel;
    private final StreamingChatModel streamingChatModel;

    /**
     * 普通对话接口(同步)
     * @param prompt 用户输入的提示词
     * @return 模型完整回复
     */
    @GetMapping("/chat")
    public String chat(@RequestParam(value = "prompt", 
                       defaultValue = "简单介绍下今年的93阅兵") String prompt) {
        return chatModel.chat(prompt);
    }

    /**
     * 流式对话接口(异步分片)
     * 通过 Flux 返回,防止中文乱码
     * @param prompt 用户输入的提示词
     * @return 流式响应内容
     */
    @GetMapping(value = "/stream-chat")
    public Flux<String> streamChat(
            @RequestParam(value = "prompt", 
            defaultValue = "简单介绍下今年的93阅兵") String prompt) {
        
        return Flux.create(emitter -> {
            streamingChatModel.chat(prompt, new StreamingChatResponseHandler() {
                
                @Override
                public void onPartialResponse(String partialResponse) {
                    log.info("分片回应: {}", partialResponse);
                    emitter.next(partialResponse);
                }

                @Override
                public void onCompleteResponse(ChatResponse completeResponse) {
                    log.info("执行完成!");
                    emitter.complete();
                }

                @Override
                public void onError(Throwable throwable) {
                    emitter.error(throwable);
                }
            });
        });
    }
}

🔑 关键点:使用 Flux.create() 包装流式响应,通过 emitter.next() 逐片发送数据。


Step 5:测试与问题排查

❌ 问题一:出现乱码

初次测试时,返回的中文出现了乱码:

✅ 解决方案:添加编码配置

application.yml 中强制 UTF-8 编码:

server:
  port: 9004
  servlet:
    encoding:
      enabled: true
      force: true
      charset: UTF-8

🌐 问题二:IDE 工具 vs 浏览器体验差异

在 IDEA 的 HTTP 客户端工具中测试,没有明显感觉到实时回复的效果

切换到浏览器访问后,流式输出的效果非常明显!

💡 经验总结:测试流式接口时,建议使用浏览器或支持 SSE 的客户端工具(如 Postman),IDE 内置的 HTTP 客户端可能无法正确展示流式效果。


🤖 实战示例二:AI Service 高级方式

相比原生方式,AI Service 提供了更简洁的编程模型,通过注解和接口定义即可快速搭建 AI 服务。

Step 1:引入依赖

<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-spring-boot-starter</artifactId>
</dependency>


Step 2:定义 Service 接口

使用 @AiService 注解标记接口,LangChain4j 会自动实现:

@AiService
public interface Assistant {

    /**
     * 单次对话方法(同步)
     * @param prompt 用户输入的提示词
     * @return AI 助手的回复内容
     */
    String chat(String prompt);

    /**
     * 流式对话方法(异步)
     * @param prompt 用户输入的提示词
     * @return AI 助手的流式回复内容
     */
    Flux<String> stream(String prompt);
}

亮点:只需定义接口,无需编写实现类!LangChain4j 会在运行时自动生成实现。


Step 3:Controller 调用

@Slf4j
@RestController
@RequestMapping("/ai")
@RequiredArgsConstructor
public class AiServiceController {

    private final Assistant assistant;

    /**
     * 普通对话接口
     * @param prompt 用户输入的提示词
     * @return AI 助手的回复内容
     */
    @GetMapping("/chat")
    public String chat(@RequestParam(value = "prompt", 
                       defaultValue = "介绍下中国登月计划") String prompt) {
        return assistant.chat(prompt);
    }

    /**
     * 流式对话接口(SSE 格式)
     * @param prompt 用户输入的提示词
     * @return AI 助手的流式回复内容
     */
    @GetMapping(value = "/stream-chat", 
                produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> streamChat(
            @RequestParam(value = "prompt", 
            defaultValue = "介绍下中国登月计划") String prompt) {
        return assistant.stream(prompt);
    }
}

Step 4:测试与问题排查

❌ 问题:返回数据带有 data: 前缀

访问流式接口后,发现返回的数据每条都带有 data: 前缀,并且每次返回的数据会分行显示:

🔍 原因分析:这是因为 produces = MediaType.TEXT_EVENT_STREAM_VALUE 使用了 SSE(Server-Sent Events)格式,SSE 规范要求在每条消息前添加 data: 前缀。

✅ 解决方案:改用纯文本格式

新增一个接口,将 produces 设置为 text/plain

/**
 * 流式对话接口(纯文本格式,无 data: 前缀)
 * @param prompt 用户输入的提示词
 * @return AI 助手的流式回复内容(纯文本)
 */
@GetMapping(value = "/stream-chat-plain", 
            produces = MediaType.TEXT_PLAIN_VALUE)
public Flux<String> streamChatPlain(
        @RequestParam(value = "prompt", 
        defaultValue = "介绍下中国登月计划") String prompt) {
    return assistant.stream(prompt);
}

✅ 再次测试:效果完美!

访问新接口,流式输出效果完美呈现,无 data: 前缀,内容连贯流畅:


📋 两种方式对比总结

对比维度 Low Level 原生方式 AI Service 高级方式
开发复杂度 较高,需手动管理 Handler 低,接口定义即可
灵活性 极高,可精细控制每个环节 中等,受框架约束
适用场景 需要自定义逻辑的复杂场景 快速开发、标准对话场景
代码量 较多 极少
学习成本 较高 较低

💡 建议:如果是快速原型开发或标准对话场景,推荐使用 AI Service;如果需要高度定制化的流式处理逻辑,可以选择 Low Level 原生方式


🎯 总结

通过本文的实战演示,我们了解了:

  1. 流式模式的核心价值:即时反馈、节省资源、灵活可控
  2. Low Level 原生方式:灵活但复杂,适合深度定制
  3. AI Service 高级方式:简洁高效,适合快速开发
  4. 常见问题排查:乱码问题、SSE 格式问题及解决方案

🚀 下一步:可以尝试结合 Memory(对话记忆)功能,让 AI 拥有上下文理解能力,打造更智能的对话应用!


📌** 收藏本文**,在需要实现 AI 流式响应时随时查阅!

LangChain4j简述与示例
LangChain4j 与 Spring Boot集成指南
LangChain4j 流式模式实战指南

在这里插入图片描述

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐