LangChain4j全集-15-Response Streaming

📅 2026/7/22 12:45:18
LangChain4j全集-15-Response Streaming
Response Streaming梳理下面我按这篇文档的结构结合你是 Spring Boot 开发者的视角把LangChain4j Response Streaming讲清楚。这篇文档讲的是如何让大模型的回答像 ChatGPT 那样一个字、一段一段地流式返回而不是等整段回答生成完再一次性返回。1. Response Streaming 是什么大模型生成文本时本质上不是一次性生成完整答案而是一个 token 一个 token 地生成。这里的token可以简单理解为一个字一个词一个词的一部分一个标点一小段文本不同模型、不同服务商对 token 的切分方式不同。普通非流式调用是这样用户提问 ↓ 等待模型完整生成 ↓ 一次性返回完整答案流式调用是这样用户提问 ↓ 模型生成一点就返回一点 ↓ 前端马上显示 ↓ 继续生成继续显示类似 ChatGPT 的打字机效果。2. 为什么要用 Response Streaming文档里说它可以显著改善用户体验。原因很简单如果用户问请帮我写一篇 1000 字的文章非流式模式下用户可能要等 5 秒、10 秒甚至更久页面上什么都没有。而流式模式下可能 0.5 秒后就开始看到内容当然可以下面是一篇关于...这样用户会觉得系统响应更快页面没有卡住更像真实 AI 助手适合聊天机器人、客服、写作助手等场景3. 这篇文档讲的是 Low-level LLM API文档开头有一句说明This page describes response streaming with a low-level LLM API.See AI Services for a high-level LLM API.意思是这篇文档讲的是底层 API的流式响应。LangChain4j 里大致有两种使用方式方式一Low-level API你直接操作模型对象比如StreamingChatModelChatModelLanguageModel这种方式比较底层、灵活但是需要自己处理消息回调token错误流式事件工具调用等方式二AI Services这是更高级的封装。类似这样interfaceAssistant{Stringchat(StringuserMessage);}或者流式interfaceAssistant{TokenStreamchat(StringuserMessage);}AI Services 更适合业务开发因为它可以帮你自动处理很多东西比如Prompt 模板Chat MemoryToolsRAGStructured Output如果你是 Spring Boot 开发者刚开始学习我建议先理解 Low-level API 的原理再在项目里优先考虑 AI Services。4. ChatModel 和 StreamingChatModel 的关系文档里提到For the ChatModel and LanguageModel interfaces, there are corresponding StreamingChatModel and StreamingLanguageModel interfaces.意思是 LangChain4j 有普通模型接口也有对应的流式模型接口。4.1 ChatModelChatModel是普通的聊天模型接口。它的特点是等模型完整生成后一次性返回完整结果。大概类似这样ChatModelmodel...;ChatResponseresponsemodel.chat(你好介绍一下 LangChain4j);System.out.println(response.aiMessage().text());结果是完整返回LangChain4j 是一个用于 Java 应用集成大语言模型的框架...4.2 StreamingChatModelStreamingChatModel是流式聊天模型接口。它的特点是模型生成一部分就回调一部分。类似这样StreamingChatModelmodel...;model.chat(你好介绍一下 LangChain4j,newStreamingChatResponseHandler(){OverridepublicvoidonPartialResponse(StringpartialResponse){System.out.print(partialResponse);}OverridepublicvoidonCompleteResponse(ChatResponsecompleteResponse){System.out.println(\n生成完成);}OverridepublicvoidonError(Throwableerror){error.printStackTrace();}});这时候控制台可能会逐步输出LangChain4j 是 一个用于 Java 应用集成大语言模型 的框架...5. LanguageModel 和 StreamingLanguageModel文档还提到LanguageModel StreamingLanguageModel这两个和ChatModel类似但概念上有些区别。5.1 LanguageModel更偏向传统文本生成。例如输入一个 prompt输出一段文本适合这类场景请续写下面这段话 从前有一座山...5.2 ChatModel更偏向聊天场景。它通常支持多轮消息例如UserMessageAiMessageSystemMessageToolExecutionResultMessage也就是说它知道哪些话是用户说的哪些话是 AI 说的哪些话是系统指令哪些话是工具调用结果现在大多数大模型应用尤其是聊天机器人、AI 助手都会优先使用ChatModel或StreamingChatModel。6. StreamingChatResponseHandler 是核心文档中的核心接口是publicinterfaceStreamingChatResponseHandler{defaultvoidonPartialResponse(StringpartialResponse){}defaultvoidonPartialResponse(PartialResponsepartialResponse,PartialResponseContextcontext){}defaultvoidonPartialThinking(PartialThinkingpartialThinking){}defaultvoidonPartialThinking(PartialThinkingpartialThinking,PartialThinkingContextcontext){}defaultvoidonPartialToolCall(PartialToolCallpartialToolCall){}defaultvoidonPartialToolCall(PartialToolCallpartialToolCall,PartialToolCallContextcontext){}defaultvoidonCompleteToolCall(CompleteToolCallcompleteToolCall){}defaultvoidonUnmappedRawEvent(ObjectrawEvent){}voidonCompleteResponse(ChatResponsecompleteResponse);voidonError(Throwableerror);}这个接口的作用是你告诉 LangChain4j当模型流式返回不同类型的数据时应该怎么处理。它类似 Spring 里的回调接口或者事件监听器。你可以理解成模型开始生成 ↓ 每生成一点文本调用 onPartialResponse ↓ 如果生成思考过程调用 onPartialThinking ↓ 如果生成工具调用调用 onPartialToolCall ↓ 如果工具调用完整了调用 onCompleteToolCall ↓ 最终完整回答生成完调用 onCompleteResponse ↓ 如果出错调用 onError7. onPartialResponse(String partialResponse)这是最常用的方法。defaultvoidonPartialResponse(StringpartialResponse){}它表示当模型生成了一小段文本时就会调用这个方法。例如用户问介绍一下 Spring Boot模型可能分多次返回SpringBoot是一个用于简化Spring 应用开发每次返回一小段就调用一次OverridepublicvoidonPartialResponse(StringpartialResponse){System.out.print(partialResponse);}在 Spring Boot 里这个方法通常用来推送给前端 SSE推送给 WebSocket写入响应流实现打字机效果作用总结方法作用onPartialResponse(String partialResponse)接收模型生成的文本片段常见用途实时推送到前端类似场景ChatGPT 边生成边显示8. onPartialResponse(PartialResponse, PartialResponseContext)文档里还提到另一个重载方法defaultvoidonPartialResponse(PartialResponsepartialResponse,PartialResponseContextcontext){}它和onPartialResponse(String)的区别是String版本只给你文本内容。而这个版本给你的信息更多。可以简单理解为PartialResponsepartialResponse代表这次流式返回的内容对象。PartialResponseContextcontext代表这次返回时的一些上下文信息。可能包括服务商相关信息、响应上下文、事件信息等。如果你只是做普通聊天页面通常用这个就够了onPartialResponse(StringpartialResponse)如果你需要更精细控制比如区分模型响应的不同事件获取更完整的上下文兼容不同模型服务商的特殊返回做日志、监控、调试可以使用带context的版本。9. partial response 不一定是一个 token文档强调Depending on the LLM provider, partial response text can consist of a single or more tokens.意思是每次回调返回的内容不一定刚好是一个 token。有些服务商一次返回一个 token你 好 我 是 AI有些服务商一次返回一小段你好 我是 AI 助手。所以开发时不要假设一次回调就是一个字一次回调就是一个完整词一次回调就是一句话正确做法是收到什么就追加什么。例如StringBuilderbuildernewStringBuilder();OverridepublicvoidonPartialResponse(StringpartialResponse){builder.append(partialResponse);}10. onPartialThinking接收模型的思考过程文档里提到defaultvoidonPartialThinking(PartialThinkingpartialThinking){}defaultvoidonPartialThinking(PartialThinkingpartialThinking,PartialThinkingContextcontext){}这个方法表示当模型流式输出 reasoning / thinking 内容时会调用这个方法。现在有些模型支持“推理过程”或者“思考过程”。比如用户问小明有 3 个苹果又买了 5 个一共有几个模型可能内部会有思考用户问的是加法问题3 5 8...然后最终回答一共有 8 个苹果。部分模型可能把这类“思考过程”也作为流式事件返回。普通回答 vs Thinking可以这样理解类型说明是否展示给用户PartialResponse最终回答的一部分一般展示PartialThinking模型思考/推理过程看业务决定是否应该展示 Thinking这个要看你的业务。如果你做的是编程助手数学解题推理演示AI 调试工具可以考虑展示部分 reasoning。如果你做的是客服机器人普通问答助手企业内部助手一般不建议直接展示模型思考过程。因为它可能让用户困惑暴露不必要的中间内容包含不稳定推理不同模型支持程度不同11. onPartialToolCall接收工具调用片段文档里提到defaultvoidonPartialToolCall(PartialToolCallpartialToolCall){}defaultvoidonPartialToolCall(PartialToolCallpartialToolCall,PartialToolCallContextcontext){}这个和 LangChain4j 的 Tools / Function Calling 有关。11.1 什么是 Tool Call大模型本身不会真正查数据库、查天气、调用接口。但是它可以决定我需要调用某个工具来完成任务。比如你定义了一个工具publicclassWeatherTool{ToolpublicStringgetWeather(Stringcity){return北京今天晴25 度;}}用户问北京今天天气怎么样模型可能不会直接回答而是生成一个工具调用{name:getWeather,arguments:{city:北京}}这就是 tool call。11.2 为什么 Tool Call 也需要流式因为工具调用的参数可能也是一点一点生成的。比如模型生成这个 JSON{name:getWeather,arguments:{city:北京}}流式过程中可能分几段返回{name:getWeather,arguments:{city:北京}所以 LangChain4j 提供了onPartialToolCall(...)用来接收尚未完整的工具调用片段。11.3 实际开发中怎么用如果你是初学者大多数情况下不用自己处理onPartialToolCall。因为使用 AI Services 时LangChain4j 可以帮你处理 tools你更常关心最终结果手动处理 tool call 复杂度较高但如果你在做底层框架、调试工具调用、实现自定义 agent那么这个方法就很重要。12. onCompleteToolCall工具调用完整生成文档里提到defaultvoidonCompleteToolCall(CompleteToolCallcompleteToolCall){}它表示当模型完整生成了一个工具调用时会调用这个方法。也就是说onPartialToolCall是片段。onCompleteToolCall是完整结果。比如最终完整的工具调用是{name:getWeather,arguments:{city:北京}}这时你就可以在onCompleteToolCall里拿到完整工具名和参数。简单理解方法时机用途onPartialToolCall工具调用生成中看中间片段、做调试onCompleteToolCall工具调用生成完可以执行工具调用13. onUnmappedRawEvent未映射的原始事件文档里提到defaultvoidonUnmappedRawEvent(ObjectrawEvent){}这个方法的意思是如果模型服务商返回了某些 LangChain4j 还没有标准化映射的原始流式事件会调用这个方法。不同大模型服务商的流式协议不完全一样。比如OpenAIAnthropic ClaudeGoogle GeminiOllamaDashScopeAzure OpenAI它们返回的流式事件格式可能不同。LangChain4j 会尽量把它们统一抽象成PartialResponsePartialThinkingPartialToolCallCompleteToolCallCompleteResponse但是有时候服务商会返回一些特殊事件LangChain4j 没有对应的统一对象。这时就会进入onUnmappedRawEvent(ObjectrawEvent)这个方法有什么用主要用于调试日志记录兼容某个服务商的特殊功能排查为什么某些事件没有被标准处理例如OverridepublicvoidonUnmappedRawEvent(ObjectrawEvent){log.info(收到未映射的原始事件: {},rawEvent);}对于普通业务开发这个不是必须实现。14. onCompleteResponse完整响应结束文档中的必实现方法之一voidonCompleteResponse(ChatResponsecompleteResponse);它表示模型本次完整回答已经生成结束。这个方法很重要。因为在流式过程中你会不断收到片段onPartialResponse(Spring)onPartialResponse( Boot)onPartialResponse( 是一个)onPartialResponse(框架)等全部结束后会调用onCompleteResponse(...)这里可以拿到完整的ChatResponse。completeResponse 里面通常有什么一般可以包含AI 最终消息完整文本token 使用情况finish reasonmetadata工具调用信息具体字段取决于 LangChain4j 版本和模型服务商。常见用途OverridepublicvoidonCompleteResponse(ChatResponsecompleteResponse){log.info(AI 完整响应: {},completeResponse.aiMessage().text());}可以在这里做保存聊天记录统计 token 用量记录日志关闭 SSE 连接通知前端回答结束做后续业务处理15. onError异常处理另一个必实现方法voidonError(Throwableerror);它表示流式调用过程中发生异常。可能的原因包括API Key 错误网络超时模型服务不可用请求参数错误触发模型服务商限流响应解析失败用户中途断开连接实际开发中一定要实现这个方法。例如OverridepublicvoidonError(Throwableerror){log.error(AI 流式响应失败,error);}如果你使用 SSE需要在这里通知前端emitter.completeWithError(error);16. 完整执行流程你可以把整个流式响应过程理解成这样用户发送问题 ↓ 调用 StreamingChatModel.chat(...) ↓ LangChain4j 请求大模型 ↓ 大模型开始生成 ↓ onPartialResponse收到文本片段 ↓ onPartialThinking收到思考片段可选 ↓ onPartialToolCall收到工具调用片段可选 ↓ onCompleteToolCall工具调用完整可选 ↓ onCompleteResponse完整响应结束 ↓ 结束如果中途失败用户发送问题 ↓ 调用模型 ↓ 发生异常 ↓ onError17. Spring Boot 中怎么理解这个东西如果你是 Spring Boot 开发者可以把它类比成普通接口GetMapping(/chat)publicStringchat(Stringmessage){returnaiService.chat(message);}特点后端等 AI 完整生成一次性返回给前端简单但用户体验一般流式接口GetMapping(value/chat/stream,producesMediaType.TEXT_EVENT_STREAM_VALUE)publicSseEmitterchatStream(Stringmessage){SseEmitteremitternewSseEmitter();streamingChatModel.chat(message,newStreamingChatResponseHandler(){OverridepublicvoidonPartialResponse(StringpartialResponse){try{emitter.send(partialResponse);}catch(IOExceptione){emitter.completeWithError(e);}}OverridepublicvoidonCompleteResponse(ChatResponsecompleteResponse){emitter.complete();}OverridepublicvoidonError(Throwableerror){emitter.completeWithError(error);}});returnemitter;}这样前端就能不断收到后端推过来的内容。18. 一个更贴近 Spring Boot 的示例假设你用SseEmitter实现流式返回RestControllerRequestMapping(/ai)publicclassAiController{privatefinalStreamingChatModelstreamingChatModel;publicAiController(StreamingChatModelstreamingChatModel){this.streamingChatModelstreamingChatModel;}GetMapping(value/chat-stream,producesMediaType.TEXT_EVENT_STREAM_VALUE)publicSseEmitterchatStream(RequestParamStringmessage){SseEmitteremitternewSseEmitter(0L);streamingChatModel.chat(message,newStreamingChatResponseHandler(){OverridepublicvoidonPartialResponse(StringpartialResponse){try{emitter.send(SseEmitter.event().name(message).data(partialResponse));}catch(IOExceptione){emitter.completeWithError(e);}}OverridepublicvoidonCompleteResponse(ChatResponsecompleteResponse){try{emitter.send(SseEmitter.event().name(done).data([DONE]));}catch(IOExceptione){emitter.completeWithError(e);return;}emitter.complete();}OverridepublicvoidonError(Throwableerror){emitter.completeWithError(error);}});returnemitter;}}前端可以用consteventSourcenewEventSource(/ai/chat-stream?message介绍一下Spring Boot);eventSource.addEventListener(message,event{console.log(收到片段,event.data);});eventSource.addEventListener(done,event{console.log(完成);eventSource.close();});19. 什么时候用 Response Streaming推荐使用流式响应的场景场景是否推荐AI 聊天机器人强烈推荐AI 客服强烈推荐文章生成推荐代码生成推荐长文本总结推荐简短分类任务不一定需要后台批处理不一定需要JSON 结构化输出视情况而定20. 流式响应和普通响应的对比对比项普通响应流式响应返回方式一次性返回分段返回用户等待时间较长较短体验像普通接口像 ChatGPT开发复杂度低稍高前端处理简单需要 SSE/WebSocket后端处理普通 Controller需要流式推送适合场景短任务长文本、聊天、生成类任务21. 初学者最该掌握哪些点如果你刚开始学 LangChain4j 的流式响应优先掌握这些第一知道为什么要流式因为用户不想一直等。第二知道核心接口StreamingChatModel负责发起流式调用。StreamingChatResponseHandler负责接收流式事件。第三重点掌握三个方法onPartialResponse onCompleteResponse onError这三个最常用。第四Spring Boot 中一般配合 SSE 或 WebSocket常见方式SSE简单适合服务端单向推送WebSocket适合双向实时通信AI 聊天场景下SSE 用得非常多。22. 这篇文档的知识点总表知识点是干什么的作用Response Streaming流式响应边生成边返回token模型生成的最小片段流式输出的基础Low-level LLM API底层模型 API灵活但需要自己处理回调AI Services高级封装 API更适合业务开发ChatModel普通聊天模型一次性返回完整回答StreamingChatModel流式聊天模型分段返回回答LanguageModel普通文本模型文本生成StreamingLanguageModel流式文本模型流式文本生成StreamingChatResponseHandler流式响应处理器接收各种流式事件onPartialResponse文本片段回调实时展示给用户PartialResponseContext文本片段上下文获取更详细信息onPartialThinking思考过程回调接收 reasoning 内容onPartialToolCall工具调用片段处理 function calling 流式片段onCompleteToolCall完整工具调用工具调用生成完成onUnmappedRawEvent未映射原始事件调试服务商特殊事件onCompleteResponse完整响应结束保存结果、关闭流onError异常回调错误处理23. 你可以这样记最简单的理解方式StreamingChatModel 是流式模型 StreamingChatResponseHandler 是监听器 onPartialResponse 是每次收到一点内容 onCompleteResponse 是全部结束 onError 是出错如果你只是做一个 Spring Boot LangChain4j 的 AI 聊天接口最开始只需要关心onPartialResponse(StringpartialResponse)onCompleteResponse(ChatResponsecompleteResponse)onError(Throwableerror)其他的onPartialThinking onPartialToolCall onCompleteToolCall onUnmappedRawEvent可以等你学习 Tools、Agent、Reasoning 模型时再深入。