跳到主要内容

响应流式输出

备注

本页介绍使用底层 LLM API 进行响应流式输出。 高层 LLM API 请参见 AI Services

LLM 一次生成一个 token,因此许多 LLM 提供商支持按 token 流式返回响应, 而无需等待整段文本生成完毕。 这能显著改善用户体验:用户不必等待未知时长,几乎可以立即开始阅读回复。

ChatModelLanguageModel 接口对应,还有 StreamingChatModelStreamingLanguageModel 接口。 它们的 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_progressresponse.web_search_call.searchingresponse.web_search_call.completed)。

onUnmappedRawEvent(Object rawEvent) 回调可让你访问此类事件。它尚未通过任一类型化回调 (onPartialResponseonPartialThinkingonPartialToolCallonCompleteToolCallonCompleteResponse) 暴露的事件调用。换言之,部分响应、思考和工具调用不会作为未映射原始事件重复出现, 因此你可以同时消费两者而不会重复。

rawEvent 的具体类型取决于提供商实现:

提供商原始事件类型
OpenAI、Anthropic、Google AI Gemini、Mistral、Ollamadev.langchain4j.http.client.sse.ServerSentEvent
OpenAI(官方)- Responses APIcom.openai.models.responses.ResponseStreamEvent
OpenAI(官方)- Chat Completions APIcom.openai.models.chat.completions.ChatCompletionChunk
Amazon Bedrocksoftware.amazon.awssdk.services.bedrockruntime.model.ConverseStreamOutput
Google GenAIcom.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 将不再收到后续回调。