响应流式输出
本页介绍使用底层 LLM API 进行响应流式输出。 高层 LLM API 请参见 AI Services。
LLM 一次生成一个 token,因此许多 LLM 提供商支持按 token 流式返回响应, 而无需等待整段文本生成完毕。 这能显著改善用户体验:用户不必等待未知时长,几乎可以立即开始阅读回复。
与 ChatModel 和 LanguageModel 接口对应,还有
StreamingChatModel 和 StreamingLanguageModel 接口。
它们的 API 类似,但支持流式响应。
它们接受 StreamingChatResponseHandler 接口的实现作为参数。
public interface StreamingChatResponseHandler {
default void onPartialResponse(String partialResponse) {}
default void onPartialResponse(PartialResponse partialResponse, PartialResponseContext context) {}
default void onPartialThinking(PartialThinking partialThinking) {}
default void onPartialThinking(PartialThinking partialThinking, PartialThinkingContext context) {}
default void onPartialToolCall(PartialToolCall partialToolCall) {}
default void onPartialToolCall(PartialToolCall partialToolCall, PartialToolCallContext context) {}
default void onCompleteToolCall(CompleteToolCall completeToolCall) {}
default void onUnmappedRawEvent(Object rawEvent) {}
void onCompleteResponse(ChatResponse completeResponse);
void onError(Throwable error);
}
通过实现 StreamingChatResponseHandler,可为以下事件定义处理逻辑:
- 当下一段部分文本响应生成时:会调用
onPartialResponse(String)或onPartialResponse(PartialResponse, PartialResponseContext)(实现其中任一 即可)。 取决于 LLM 提供商,部分响应文本可能包含一个或多个 token。 例如,token 一可用即可直接发送到 UI。 - 当下一段部分思考/推理文本生成时:会调用
onPartialThinking(PartialThinking)或onPartialThinking(PartialThinking, PartialThinkingContext)(实现其中任一即可)。 取决于 LLM 提供商,部分思考文本可能包含一个或多个 token。 - 当下一个部分工具调用生成时:会调用
onPartialToolCall(PartialToolCall)或onPartialToolCall(PartialToolCall, PartialToolCallContext)(实现其中任一即可)。 - 当 LLM 完成单个工具调用的流式输出时:会调用
onCompleteToolCall(CompleteToolCall)。 - 当提供商发出尚未通过上述任一类型化回调暴露的原始流式事件时:
会调用
onUnmappedRawEvent(Object)。参见下方的未映射原始事件。 - 当 LLM 完成生成时:会调用
onCompleteResponse(ChatResponse)。ChatResponse对象包含完整响应(AiMessage)以及ChatResponseMetadata。 - 发生错误时:会调用
onError(Throwable error)。
以下是使用 StreamingChatModel 实现流式输出的示例:
StreamingChatModel model = OpenAiStreamingChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName(GPT_4_O_MINI)
.build();
String userMessage = "Tell me a joke";
model.chat(userMessage, new StreamingChatResponseHandler() {
@Override
public void onPartialResponse(String partialResponse) {
System.out.println("onPartialResponse: " + partialResponse);
}
@Override
public void onPartialThinking(PartialThinking partialThinking) {
System.out.println("onPartialThinking: " + partialThinking);
}
@Override
public void onPartialToolCall(PartialToolCall partialToolCall) {
System.out.println("onPartialToolCall: " + partialToolCall);
}
@Override
public void onCompleteToolCall(CompleteToolCall completeToolCall) {
System.out.println("onCompleteToolCall: " + completeToolCall);
}
@Override
public void onCompleteResponse(ChatResponse completeResponse) {
System.out.println("onCompleteResponse: " + completeResponse);
}
@Override
public void onError(Throwable error) {
error.printStackTrace();
}
});
更简洁的流式方式 是使用 LambdaStreamingResponseHandler 类。
该工具类提供静态方法,可用 lambda 表达式创建 StreamingChatResponseHandler。
用 lambda 进行流式输出非常简单。
只需调用 onPartialResponse() 静态方法,并传入定义如何处理部分响应的 lambda:
import static dev.langchain4j.model.LambdaStreamingResponseHandler.onPartialResponse;
model.chat("Tell me a joke", onPartialResponse(System.out::print));
onPartialResponseAndError() 方法可同时为
onPartialResponse() 和 onError() 事件定义处理逻辑:
import static dev.langchain4j.model.LambdaStreamingResponseHandler.onPartialResponseAndError;
model.chat("Tell me a joke", onPartialResponseAndError(System.out::print, Throwable::printStackTrace));
未映射原始事件
这是面向高级场景的实验性功能。API 未来可能会变更。
大多数应用只需使用上述类型化回调。不过,某些 LLM 提供商会发出
LangChain4j 尚(未)映射到专用回调的额外流式事件——例如,
OpenAI 服务端工具(如 web_search)的生命周期事件
(response.web_search_call.in_progress、response.web_search_call.searching、
response.web_search_call.completed)。
onUnmappedRawEvent(Object rawEvent) 回调可让你访问此类事件。它仅对
尚未通过任一类型化回调
(onPartialResponse、onPartialThinking、onPartialToolCall、onCompleteToolCall、onCompleteResponse)
暴露的事件调用。换言之,部分响应、思考和工具调用不会作为未映射原始事件重复出现,
因此你可以同时消费两者而不会重复。
rawEvent 的具体类型取决于提供商实现:
| 提供商 | 原始事件类型 |
|---|---|
| OpenAI、Anthropic、Google AI Gemini、Mistral、Ollama | dev.langchain4j.http.client.sse.ServerSentEvent |
| OpenAI(官方)- Responses API | com.openai.models.responses.ResponseStreamEvent |
| OpenAI(官方)- Chat Completions API | com.openai.models.chat.completions.ChatCompletionChunk |
| Amazon Bedrock | software.amazon.awssdk.services.bedrockruntime.model.ConverseStreamOutput |
| Google GenAI | com.google.genai.types.GenerateContentResponse |
由于事件类型与提供商相关,通常用 instanceof 检查并转型:
model.chat(userMessage, new StreamingChatResponseHandler() {
@Override
public void onPartialResponse(String partialResponse) {
System.out.println("onPartialResponse: " + partialResponse);
}
@Override
public void onUnmappedRawEvent(Object rawEvent) {
if (rawEvent instanceof ServerSentEvent sse) {
System.out.println("Raw SSE event: " + sse.event() + " -> " + sse.data());
}
}
@Override
public void onCompleteResponse(ChatResponse completeResponse) {
System.out.println("onCompleteResponse: " + completeResponse);
}
@Override
public void onError(Throwable error) {
error.printStackTrace();
}
});
使用 AI Services 时,可通过
TokenStream.onUnmappedRawEvent(Consumer<Object>) 回调获取相同事件。
取消流式输出
若要取消流式输出,可在以下任一方法中进行:
onPartialResponse(PartialResponse, PartialResponseContext)onPartialThinking(PartialThinking, PartialThinkingContext)onPartialToolCall(PartialToolCall, PartialToolCallContext)
上下文对象包含 StreamingHandle,可用于取消流式 输出:
model.chat(userMessage, new StreamingChatResponseHandler() {
@Override
public void onPartialResponse(PartialResponse partialResponse, PartialResponseContext context) {
process(partialResponse);
if (shouldCancel()) {
context.streamingHandle().cancel();
}
}
@Override
public void onCompleteResponse(ChatResponse completeResponse) {
System.out.println("onCompleteResponse: " + completeResponse);
}
@Override
public void onError(Throwable error) {
error.printStackTrace();
}
});
调用 StreamingHandle.cancel() 后,LangChain4j 将关闭连接并停止流式输出。
一旦调用了 StreamingHandle.cancel(),StreamingChatResponseHandler 将不再收到后续回调。