LangChain4j 流式输出

流式输出

流式输出 ,指AI模型不是等生成完完整答案再一次性发送,而是边生成文本,边以"数据流"的形式,将当前已生成的部分实时推送给用户

接口变更:

  • ChatModel -> StreamingChatModel
  • LanguageModel -> StreamingLanguageModel

StreamingXX 系列的接口都具有相似的 API,它们都接收一个 StreamingChatResponseHandler 对象。

通过实现 StreamingChatResponseHandler 接口,可以自定义如下事件:

  • 生成下一个部分文本响应时:

    将调用 onPartialResponse(String)onPartialResponse(PartialResponse, PartialResponseContext) 方法(您可以选择实现其中任一方法)。根据不同的 LLM 提供商,部分响应文本可能包含单个或多个词元。例如,您可以在词元生成后立即将其直接发送至用户界面。

  • 生成下一个部分推理/思考文本时:

    将调用 onPartialThinking(PartialThinking)onPartialThinking(PartialThinking, PartialThinkingContext) 方法(您可以选择实现其中任一方法)。根据不同的 LLM 提供商,部分思考文本可能包含单个或多个词元。

  • 生成下一个部分工具调用时:

    将调用 onPartialToolCall(PartialToolCall)onPartialToolCall(PartialToolCall, PartialToolCallContext) 方法(您可以选择实现其中任一方法)。

  • **当LLM完成单个工具调用的流式传输时:**将调用 onCompleteToolCall(CompleteToolCall) 方法。

  • **当LLM完成生成时:**将调用 onCompleteResponse(ChatResponse) 方法。ChatResponse对象包含完整响应(AiMessage)以及ChatResponseMetadata。

  • **发生错误时:**将调用 onError(Throwable error) 方法。

Low-level API

java 复制代码
@Configuration
public class LLMConfig {
    @Bean
    public StreamingChatModel streamingChatModel() {
        return OpenAiStreamingChatModel.builder()
                .apiKey(System.getenv("ALI_QWEN_API_KEY"))
                .modelName("qwen-plus")
                .baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
                .build();
    }
}

StreamController

java 复制代码
@RestController
@RequestMapping("stream")
@Slf4j
public class StreamController {
    @Resource
    private StreamingChatModel streamingChatModel;

    @GetMapping("/qwen/chat1")
    public void chat1(@RequestParam(value = "question", defaultValue = "你是谁?") String question) {
        streamingChatModel.chat(question, new StreamingChatResponseHandler() {
            @Override
            public void onPartialResponse(String s) {
                System.out.println(s);
            }

            @Override
            public void onCompleteResponse(ChatResponse chatResponse) {
                System.out.println(chatResponse);
            }

            @Override
            public void onError(Throwable throwable) {
                System.out.println(throwable.getMessage());
            }
        });
    }
}

High-level API

定义 AI 服务接口

java 复制代码
public interface Assistant {
    TokenStream chatTokenStream(String message);
}

LLM 配置

java 复制代码
@Configuration
public class LLMConfig {
    @Bean
    public Assistant assistant(StreamingChatModel streamingChatModel) {
        return AiServices.create(Assistant.class, streamingChatModel);
    }
}

StreamController

java 复制代码
@RestController
@RequestMapping("stream")
@Slf4j
public class StreamController {
    @Resource
    private Assistant assistant;
    
    @GetMapping("/qwen/chat4")
    public void chat4(@RequestParam(value = "question", defaultValue = "你是谁?") String question) {
        TokenStream tokenStream = assistant.chatTokenStream(question);
        CompletableFuture<ChatResponse> futureResponse = new CompletableFuture<>();
        tokenStream
                .onPartialResponse((String s) -> log.info(s))
                .onCompleteResponse((ChatResponse response) -> futureResponse.complete(response))
                .onError((Throwable error) -> futureResponse.completeExceptionally(error))
                .start();
        futureResponse.join(); // 阻塞主线程,直到流式传输进程(在另一个线程中运行)完成
    }
}

Flux

可以直接使用 Flux<String> 向客户端响应。为此,请导入 langchain4j-reactor 依赖:

xml 复制代码
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-reactor</artifactId>
    <version>1.11.0-beta19</version>
</dependency>

Low-level API

java 复制代码
@RestController
@RequestMapping("stream")
@Slf4j
public class StreamController {
    @Resource
    private StreamingChatModel streamingChatModel;

    @GetMapping("/qwen/chat2")
    public Flux<String> chat2(@RequestParam(value = "question", defaultValue = "你是谁?") String question) {
        return Flux.create(e -> {
            streamingChatModel.chat(question, new StreamingChatResponseHandler() {
                @Override
                public void onPartialResponse(String s) {
                    e.next(s);
                }

                @Override
                public void onCompleteResponse(ChatResponse chatResponse) {
                    e.complete();
                }

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

High-level API

定义服务接口

java 复制代码
public interface Assistant {
    Flux<String> chatFlux(String message);
}

StreamController

java 复制代码
@RestController
@RequestMapping("stream")
@Slf4j
public class StreamController {
    @Resource
    private Assistant assistant;

    @GetMapping("/qwen/chat3")
    public Flux<String> chat3(@RequestParam(value = "question", defaultValue = "你是谁?") String question) {
        return assistant.chatFlux(question);
    }
}
相关推荐
To_OC3 小时前
手写 AI 编程 Agent 的命令执行工具:我被 child_process 坑出来的实战经验
后端·node.js·agent
逝水无殇6 小时前
C# 异常处理详解
开发语言·后端·c#
考虑考虑8 小时前
Sentinel安装
java·后端·微服务
IT_陈寒8 小时前
SpringBoot自动配置失灵?你可能忘了这个关键注解
前端·人工智能·后端
碎碎念_4928 小时前
SpringBoot + Vue 前后端分离从 0 到 1 完整环境配置流程
vue.js·spring boot·后端
逝水无殇8 小时前
C# 文件的输入与输出详解
开发语言·数据库·后端·c#
YIAN9 小时前
从手写 Agent 工具到 MCP 协议:一文搞懂大模型跨进程跨语言工具调用
后端·agent·mcp
小兔崽子去哪了9 小时前
Docker 删除镜像后磁盘空间没有释放?
后端·docker·容器
高明珠10 小时前
麒麟 V10 SP1 排查实录:ttyS5 每 10 秒莫名收到一批报文,元凶是 eGTouchD
后端
卷无止境10 小时前
Python FFI 技术深度解析:ctypes、cffi 与 pybind11 的性能差异与实践挑战
后端·python