版权归属于 LangChat Team
官网:https://langchat.cn
03 - ChatModel 深入理解
版本说明
本文档基于 LangChain4j 1.10.0 版本编写。学习目标
通过本章节学习,你将能够:- 深入理解 ChatModel 接口的所有方法
- 区分 ChatModel 和 ChatModel 的使用场景
- 掌握 StreamingChatModel 流式模型的使用
- 学会选择合适的模型
- 理解异常处理和错误管理
前置知识
- 完成《01 - LangChain4j 简介》章节
- 完成《02 - 第一个 Chat 应用》章节
- 了解基本的 Java 接口和异常处理
核心概念
ChatModel 接口层次结构
LangChain4j 提供了多个层级的模型接口,让我们深入了解它们的关系和使用场景:Copy
// 接口继承关系
LanguageModel (已废弃,不推荐使用)
↓
ChatModel (推荐用于简单场景)
↓
ChatModel (推荐用于需要控制的场景)
↓
StreamingChatModel (用于流式响应场景)
接口对比
| 特性 | LanguageModel | ChatModel | ChatModel | StreamingChatModel |
|---|---|---|---|---|
| 输入类型 | String | String | ChatMessage | ChatMessage |
| 输出类型 | String | String | ChatResponse | TokenStream |
| 控制力 | 低 | 中 | 高 | 高 |
| 流式支持 | ❌ | ❌ | ❌ | ✅ |
| 元数据访问 | ❌ | ❌ | ✅ | ✅ |
| 推荐场景 | 不推荐 | 简单场景 | 进阶场景 | 流式响应 |
ChatModel 详细使用
接口定义
Copy
public interface ChatModel {
/**
* 生成响应(最简单的方法)
* @param userMessage 用户消息
* @return AI 生成的响应
*/
String chat(String userMessage);
}
使用示例
Copy
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
public class ChatModelExample {
public static void main(String[] args) {
// 创建模型实例
ChatModel model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o-mini")
.build();
// 场景 1:简单对话
String greeting = model.chat("你好");
System.out.println(greeting);
// 场景 2:问答
String answer = model.chat("Java 的特点是什么?");
System.out.println(answer);
// 场景 3:文本生成
String story = model.chat("写一个关于 AI 的短故事");
System.out.println(story);
}
}
适用场景
使用 ChatModel 的最佳时机:- 快速原型开发 - 需要快速验证想法
- 简单文本处理 - 只需要文本输入输出
- 学习阶段 - 刚开始学习 LangChain4j
- 脚本任务 - 批处理或自动化脚本
- 需要访问响应元数据(Token 使用、模型信息等)
- 需要使用系统消息或消息历史
- 需要流式输出
- 生产环境中的复杂应用
ChatModel 详细使用
接口定义
Copy
public interface ChatModel {
/**
* 最简单的方法 - 便捷方法
*/
String chat(String userMessage);
/**
* 发送单个消息
*/
ChatResponse chat(ChatMessage message);
/**
* 发送多个消息(可变参数)
*/
ChatResponse chat(ChatMessage... messages);
/**
* 发送多个消息(列表)
*/
ChatResponse chat(List<ChatMessage> messages);
/**
* 完全控制 - 使用 ChatRequest
*/
ChatResponse chat(ChatRequest chatRequest);
}
方法详解
1. chat(String userMessage) - 便捷方法
Copy
ChatModel model = ...;
// 最简单的方式,类似 ChatModel
String response = model.chat("你好");
System.out.println(response);
- 最简单快捷
- 直接返回字符串
- 适合快速测试
2. chat(ChatMessage message) - 单消息
Copy
import dev.langchain4j.data.message.UserMessage;
ChatModel model = ...;
// 使用 UserMessage
UserMessage userMessage = UserMessage.from("你好");
ChatResponse response = model.chat(userMessage);
// 获取响应文本
String text = response.aiMessage().text();
System.out.println(text);
- 可以使用不同类型的消息
- 返回完整的 ChatResponse 对象
- 可以访问元数据
3. chat(ChatMessage… messages) - 多消息
Copy
import dev.langchain4j.data.message.*;
import java.util.List;
ChatModel model = ...;
// 构建多轮对话
List<ChatMessage> messages = List.of(
SystemMessage.from("你是一个友好的助手"),
UserMessage.from("你好"),
AiMessage.from("你好!有什么可以帮助你的吗?"),
UserMessage.from("介绍一下 Java")
);
ChatResponse response = model.chat(messages);
System.out.println(response.aiMessage().text());
- 支持完整对话历史
- 可以包含系统消息
- LLM 可以理解上下文
4. chat(ChatRequest chatRequest) - 完全控制
Copy
import dev.langchain4j.model.chat.request.ChatRequest;
import dev.langchain4j.model.chat.request.ChatRequestParameters;
ChatModel model = ...;
// 构建请求参数
ChatRequestParameters parameters = ChatRequestParameters.builder()
.temperature(0.7)
.maxOutputTokens(500)
.build();
// 构建完整请求
ChatRequest request = ChatRequest.builder()
.messages(
SystemMessage.from("你是一个专业的助手"),
UserMessage.from("什么是机器学习?")
)
.parameters(parameters)
.build();
ChatResponse response = model.chat(request);
System.out.println(response.aiMessage().text());
- 完全控制请求
- 可以设置所有参数
- 最灵活的方式
ChatResponse 详细解析
Copy
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.model.chat.response.ChatResponseMetadata;
import dev.langchain4j.model.data.message.AiMessage;
import dev.langchain4j.model.output.TokenUsage;
import dev.langchain4j.model.output.FinishReason;
ChatResponse response = model.chat("你好");
// 1. 获取 AI 消息
AiMessage aiMessage = response.aiMessage();
String text = aiMessage.text(); // 文本内容
String thinking = aiMessage.thinking(); // 思考内容(如果模型支持)
// 2. 获取元数据
ChatResponseMetadata metadata = response.metadata();
String modelId = metadata.modelName(); // 模型名称
String responseId = metadata.id(); // 响应 ID
TokenUsage tokenUsage = metadata.tokenUsage(); // Token 使用
FinishReason finishReason = metadata.finishReason(); // 完成原因
// 3. Token 使用详情
int inputTokens = tokenUsage.inputTokenCount(); // 输入 Token
int outputTokens = tokenUsage.outputTokenCount(); // 输出 Token
int totalTokens = tokenUsage.totalTokenCount(); // 总 Token
// 4. 完成原因
System.out.println("完成原因: " + finishReason);
StreamingChatModel 流式响应
什么是流式响应?
流式响应(Streaming)允许你在 LLM 生成内容的同时,逐个 Token 地接收输出,而不是等待整个响应生成完成。 类比理解:- 非流式:等待一封信完整写好才送给你
- 流式响应:看着对方写信,每写一个字就能看到一个字
为什么使用流式响应?
- 更好的用户体验 - 用户看到内容逐步出现,感觉更快
- 实时反馈 - 可以提前展示部分内容
- 长时间生成 - 对于生成长文本的场景特别有用
- 取消能力 - 可以中途取消生成
StreamingChatModel 接口定义
Copy
public interface StreamingChatModel {
/**
* 生成流式响应
* @param messages 消息列表
* @param responseHandler 响应处理器
*/
void chat(List<ChatMessage> messages,
StreamingChatResponseHandler responseHandler);
}
基本使用
Copy
import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
import dev.langchain4j.data.message.*;
import java.util.List;
public class StreamingExample {
public static void main(String[] args) {
StreamingChatModel model = OpenAiStreamingChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o-mini")
.build();
List<ChatMessage> messages = List.of(
UserMessage.from("讲一个关于编程的故事")
);
// 定义响应处理器
StreamingChatResponseHandler handler = new StreamingChatResponseHandler() {
@Override
public void onPartialResponse(String partialResponse) {
// 接收部分响应
System.out.print(partialResponse);
}
@Override
public void onCompleteResponse(ChatResponse completeResponse) {
// 响应完成
System.out.println("\n\n生成完成!");
System.out.println("Token 使用: " +
completeResponse.metadata().tokenUsage().totalTokenCount());
}
@Override
public void onError(Throwable error) {
// 错误处理
System.err.println("错误: " + error.getMessage());
}
};
// 生成流式响应
model.chat(messages, handler);
}
}
TokenStream 高级流式响应
使用TokenStream 获得更灵活的控制:
Copy
import dev.langchain4j.model.chat.StreamingChatModel;
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
import dev.langchain4j.data.message.*;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class TokenStreamExample {
public static void main(String[] args) {
StreamingChatModel model = OpenAiStreamingChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o-mini")
.build();
List<ChatMessage> messages = List.of(
UserMessage.from("写一首关于春天的诗")
);
// 获取 TokenStream
TokenStream tokenStream = model.chat(messages);
// 创建 Future 用于等待完成
CompletableFuture<ChatResponse> futureResponse = new CompletableFuture<>();
StringBuilder fullResponse = new StringBuilder();
tokenStream
// 接收部分响应
.onPartialResponse(partialResponse -> {
System.out.print(partialResponse);
fullResponse.append(partialResponse);
})
// 接收部分思考(如果模型支持)
.onPartialThinking(partialThinking -> {
System.out.println("[思考中...] " + partialThinking);
})
// 响应完成
.onCompleteResponse(response -> {
System.out.println("\n\n生成完成!");
futureResponse.complete(response);
})
// 错误处理
.onError(error -> {
System.err.println("错误: " + error.getMessage());
futureResponse.completeExceptionally(error);
})
// 开始流式响应
.start();
// 等待完成
try {
ChatResponse response = futureResponse.join();
System.out.println("完整响应: " + fullResponse.toString());
} catch (Exception e) {
System.err.println("生成失败: " + e.getMessage());
}
}
}
流式响应的回调
Copy
TokenStream tokenStream = model.chat(messages);
tokenStream
// 1. 部分响应
.onPartialResponse(String partialResponse)
// 2. 部分思考(Chain of Thought)
.onPartialThinking(String partialThinking)
// 3. 检索到的内容(RAG)
.onRetrieved(List<Content> contents)
// 4. 中间响应(工具调用后)
.onIntermediateResponse(ChatResponse intermediateResponse)
// 5. 部分工具调用
.onPartialToolCall(PartialToolCall partialToolCall)
// 6. 工具执行前
.beforeToolExecution(BeforeToolExecution beforeToolExecution)
// 7. 工具执行后
.onToolExecuted(ToolExecution toolExecution)
// 8. 完整响应
.onCompleteResponse(ChatResponse response)
// 9. 错误
.onError(Throwable error)
// 开始
.start();
取消流式响应
Copy
TokenStream tokenStream = model.chat(messages);
CompletableFuture<ChatResponse> future = new CompletableFuture<>();
tokenStream
.onPartialResponseWithContext((partialResponse, context) -> {
System.out.print(partialResponse);
// 如果响应超过 100 个字符,取消
if (partialResponse.length() > 100) {
context.streamingHandle().cancel();
future.completeExceptionally(
new RuntimeException("响应过长,已取消"));
}
})
.onCompleteResponse(response -> future.complete(response))
.onError(error -> future.completeExceptionally(error))
.start();
try {
future.join();
} catch (Exception e) {
System.out.println("生成被取消: " + e.getMessage());
}
模型选择策略
OpenAI 模型对比
| 模型 | 上下文 | 速度 | 成本 | 能力 | 适用场景 |
|---|---|---|---|---|---|
| gpt-4o | 128K | 中 | 高 | 最高 | 复杂推理、代码生成 |
| gpt-4o-mini | 128K | 快 | 低 | 高 | 通用场景、平衡性能和成本 |
| gpt-3.5-turbo | 16K | 很快 | 很低 | 中 | 简单任务、批量处理 |
选择建议
选择 gpt-4o:- 需要最高质量和推理能力
- 复杂的代码生成和分析
- 多步推理任务
- 预算充足
- 平衡性能和成本
- 通用聊天应用
- 大多数生产环境
- 性价比最高
- 简单文本处理
- 批量任务
- 成本敏感
- 不需要复杂推理
其他模型提供商
Copy
// Anthropic Claude - 擅长上下文
import dev.langchain4j.model.anthropic.AnthropicChatModel;
ChatModel claude = AnthropicChatModel.builder()
.apiKey(System.getenv("ANTHROPIC_API_KEY"))
.modelName("claude-3-5-sonnet-20241022")
.build();
// Google Gemini - 多模态
import dev.langchain4j.model.google.GeminiChatModel;
ChatModel gemini = GeminiChatModel.builder()
.apiKey(System.getenv("GOOGLE_API_KEY"))
.modelName("gemini-1.5-pro")
.build();
// Ollama - 本地运行
import dev.langchain4j.model.ollama.OllamaChatModel;
ChatModel ollama = OllamaChatModel.builder()
.baseUrl("http://localhost:11434")
.modelName("llama2")
.build();
异常处理
常见异常类型
Copy
import dev.langchain4j.exception.HttpException;
import dev.langchain4j.exception.AuthenticationException;
import dev.langchain4j.exception.RateLimitException;
import dev.langchain4j.exception.TokenException;
import dev.langchain4j.model.chat.ChatModel;
ChatModel model = ...;
try {
String response = model.chat("你好");
System.out.println(response);
} catch (AuthenticationException e) {
// API Key 无效或过期
System.err.println("认证失败: " + e.getMessage());
} catch (RateLimitException e) {
// 超过速率限制
System.err.println("请求过快: " + e.getMessage());
} catch (TokenException e) {
// Token 限制相关错误
System.err.println("Token 错误: " + e.getMessage());
} catch (HttpException e) {
// HTTP 请求错误
System.err.println("HTTP 错误: " + e.getMessage());
} catch (Exception e) {
// 其他错误
System.err.println("未知错误: " + e.getMessage());
}
重试机制
Copy
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import java.time.Duration;
public class RetryExample {
public static void main(String[] args) {
ChatModel model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o-mini")
.maxRetries(3) // 最大重试次数
.timeout(Duration.ofSeconds(30)) // 每次请求的超时时间
.build();
try {
String response = model.chat("你好");
System.out.println(response);
} catch (Exception e) {
System.err.println("重试失败: " + e.getMessage());
}
}
}
自定义异常处理
Copy
public class RobustChatService {
private final ChatModel model;
public RobustChatService(ChatModel model) {
this.model = model;
}
public String generateWithRetry(String message, int maxRetries) {
int attempt = 0;
while (attempt < maxRetries) {
try {
return model.chat(message);
} catch (RateLimitException e) {
attempt++;
if (attempt >= maxRetries) {
throw e;
}
// 指数退避
long waitTime = (long) Math.pow(2, attempt) * 1000;
System.out.println("速率限制,等待 " + waitTime + "ms 后重试...");
try {
Thread.sleep(waitTime);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException("重试被中断", ie);
}
} catch (Exception e) {
// 其他异常不重试
throw e;
}
}
throw new RuntimeException("重试次数耗尽");
}
}
完整示例:智能聊天服务
Copy
import dev.langchain4j.model.chat.*;
import dev.langchain4j.model.openai.*;
import dev.langchain4j.data.message.*;
import dev.langchain4j.exception.*;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
/**
* 智能聊天服务 - 支持异常处理和重试
*/
public class SmartChatService {
private final ChatModel model;
private final List<ChatMessage> conversationHistory;
public SmartChatService() {
this.model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o-mini")
.temperature(0.7)
.maxRetries(3)
.timeout(Duration.ofSeconds(30))
.logRequests(true)
.logResponses(true)
.build();
this.conversationHistory = new ArrayList<>();
// 添加系统消息
conversationHistory.add(
SystemMessage.from("你是一个友好、专业的助手")
);
}
/**
* 发送消息并获取响应(带重试)
*/
public String chat(String userMessage) {
// 添加用户消息到历史
conversationHistory.add(UserMessage.from(userMessage));
try {
// 发送对话历史
ChatResponse response = model.chat(conversationHistory);
// 添加 AI 响应到历史
conversationHistory.add(response.aiMessage());
// 返回响应文本
return response.aiMessage().text();
} catch (RateLimitException e) {
System.err.println("警告: 请求过快,请稍后再试");
return "抱歉,我处理得有点慢,请稍后再试。";
} catch (AuthenticationException e) {
System.err.println("错误: API Key 无效");
return "服务配置错误,请联系管理员。";
} catch (HttpException e) {
System.err.println("错误: 网络请求失败 - " + e.getMessage());
return "网络连接问题,请检查网络后重试。";
} catch (Exception e) {
System.err.println("错误: 未知错误 - " + e.getMessage());
return "抱歉,出现了未知错误。";
}
}
/**
* 清空对话历史
*/
public void clearHistory() {
// 保留系统消息
List<ChatMessage> systemMessages = conversationHistory.stream()
.filter(msg -> msg instanceof SystemMessage)
.toList();
conversationHistory.clear();
conversationHistory.addAll(systemMessages);
}
/**
* 获取 Token 使用统计
*/
public TokenUsage getLastTokenUsage() {
if (!conversationHistory.isEmpty() &&
conversationHistory.get(conversationHistory.size() - 1) instanceof AiMessage) {
AiMessage lastMessage = (AiMessage) conversationHistory.get(
conversationHistory.size() - 1);
// 注意:这里简化了,实际应该从 ChatResponse 获取
return new TokenUsage(0, 0, 0);
}
return new TokenUsage(0, 0, 0);
}
public static void main(String[] args) {
SmartChatService service = new SmartChatService();
// 对话示例
System.out.println("=== 智能聊天服务 ===");
System.out.println();
System.out.println("用户: 你好");
System.out.println("AI: " + service.chat("你好"));
System.out.println();
System.out.println("用户: 什么是 LangChain4j?");
System.out.println("AI: " + service.chat("什么是 LangChain4j?"));
System.out.println();
System.out.println("用户: 清空历史");
service.clearHistory();
System.out.println("对话历史已清空");
System.out.println();
System.out.println("用户: 你好");
System.out.println("AI: " + service.chat("你好"));
}
}
测试代码示例
Copy
import dev.langchain4j.model.chat.*;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.openai.OpenAiStreamingChatModel;
import dev.langchain4j.data.message.*;
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
import java.util.concurrent.CompletableFuture;
class ChatModelAdvancedTest {
private ChatModel chatModel;
private StreamingChatModel streamingModel;
@BeforeEach
void setUp() {
chatModel = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o-mini")
.build();
streamingModel = OpenAiStreamingChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o-mini")
.build();
}
@Test
void should_use_chat_model_with_multiple_messages() {
List<ChatMessage> messages = List.of(
SystemMessage.from("你是一个数学老师"),
UserMessage.from("2 + 2 等于几?")
);
ChatResponse response = chatModel.chat(messages);
assertNotNull(response);
assertTrue(response.aiMessage().text().contains("4"));
System.out.println("多消息响应: " + response.aiMessage().text());
}
@Test
void should_provide_metadata_in_response() {
ChatResponse response = chatModel.chat("你好");
assertNotNull(response.metadata());
assertNotNull(response.metadata().modelName());
assertNotNull(response.metadata().tokenUsage());
assertNotNull(response.metadata().finishReason());
System.out.println("模型: " + response.metadata().modelName());
System.out.println("Token 使用: " +
response.metadata().tokenUsage().totalTokenCount());
}
@Test
void should_stream_response() throws Exception {
List<ChatMessage> messages = List.of(
UserMessage.from("用一句话介绍 Java")
);
CompletableFuture<ChatResponse> future = new CompletableFuture<>();
StringBuilder fullResponse = new StringBuilder();
streamingModel.chat(messages, new StreamingChatResponseHandler() {
@Override
public void onPartialResponse(String partialResponse) {
fullResponse.append(partialResponse);
}
@Override
public void onCompleteResponse(ChatResponse completeResponse) {
future.complete(completeResponse);
}
@Override
public void onError(Throwable error) {
future.completeExceptionally(error);
}
});
ChatResponse response = future.get();
assertNotNull(response);
assertFalse(fullResponse.toString().isEmpty());
System.out.println("流式响应: " + fullResponse.toString());
}
@Test
void should_handle_long_context() {
// 构建长对话上下文
List<ChatMessage> messages = new ArrayList<>();
messages.add(SystemMessage.from("你是一个故事讲述者"));
for (int i = 0; i < 10; i++) {
messages.add(UserMessage.from("第 " + (i + 1) + " 部分"));
ChatResponse response = chatModel.chat(messages);
messages.add(response.aiMessage());
}
// 验证 LLM 记得了上下文
assertTrue(messages.size() > 20);
System.out.println("消息总数: " + messages.size());
}
}
实践练习
练习 1:对比不同接口
创建一个对比程序,比较三种接口的使用:Copy
public class ModelComparison {
public static void main(String[] args) {
String message = "写一首关于春天的诗";
// ChatModel
System.out.println("=== ChatModel ===");
ChatModel languageModel = ...;
String response1 = languageModel.chat(message);
System.out.println(response1);
System.out.println("类型: String");
// ChatModel
System.out.println("\n=== ChatModel ===");
ChatModel chatModel = ...;
ChatResponse response2 = chatModel.chat(message);
System.out.println(response2.aiMessage().text());
System.out.println("类型: ChatResponse");
System.out.println("Token 使用: " +
response2.metadata().tokenUsage().totalTokenCount());
// StreamingChatModel
System.out.println("\n=== StreamingChatModel ===");
StreamingChatModel streamingModel = ...;
streamingModel.chat(
List.of(UserMessage.from(message)),
new StreamingChatResponseHandler() {
@Override
public void onPartialResponse(String partialResponse) {
System.out.print(partialResponse);
}
@Override
public void onCompleteResponse(ChatResponse completeResponse) {
System.out.println("\n类型: TokenStream");
}
@Override
public void onError(Throwable error) {
System.err.println("错误: " + error.getMessage());
}
}
);
}
}
练习 2:实现智能重试
实现一个带指数退避的重试机制:Copy
public class ExponentialBackoffRetry {
private final ChatModel model;
private final int maxRetries;
public ExponentialBackoffRetry(ChatModel model, int maxRetries) {
this.model = model;
this.maxRetries = maxRetries;
}
public String generateWithRetry(String message) {
int attempt = 0;
while (attempt < maxRetries) {
try {
return model.chat(message);
} catch (RateLimitException e) {
attempt++;
if (attempt >= maxRetries) {
throw new RuntimeException("重试次数耗尽", e);
}
// 指数退避:2^attempt 秒
long waitTime = (long) Math.pow(2, attempt) * 1000;
System.out.printf(
"速率限制,第 %d/%d 次重试,等待 %dms...%n",
attempt, maxRetries, waitTime
);
try {
Thread.sleep(waitTime);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException("重试被中断", ie);
}
} catch (Exception e) {
// 其他异常不重试
throw e;
}
}
throw new RuntimeException("重试次数耗尽");
}
public static void main(String[] args) {
ChatModel model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o-mini")
.build();
ExponentialBackoffRetry retry =
new ExponentialBackoffRetry(model, 5);
try {
String response = retry.generateWithRetry("你好");
System.out.println("响应: " + response);
} catch (Exception e) {
System.err.println("失败: " + e.getMessage());
}
}
}
练习 3:流式响应进度条
创建一个带进度条的流式响应示例:Copy
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
public class StreamingProgressBar {
private final StreamingChatModel model;
public StreamingProgressBar(StreamingChatModel model) {
this.model = model;
}
public void chatWithProgress(String message) {
TokenStream tokenStream = model.chat(
List.of(UserMessage.from(message))
);
StringBuilder fullResponse = new StringBuilder();
int progress = 0;
tokenStream
.onPartialResponse(partial -> {
System.out.print(partial);
fullResponse.append(partial);
// 显示进度(假设期望 100 个字符)
progress = Math.min(100, fullResponse.length());
System.out.printf("\n进度: [%-50s] %d%%",
"=".repeat(progress / 2), progress);
})
.onCompleteResponse(response -> {
System.out.println("\n完成!");
System.out.println("总 Token: " +
response.metadata().tokenUsage().totalTokenCount());
})
.onError(error -> {
System.err.println("\n错误: " + error.getMessage());
})
.start();
}
public static void main(String[] args) {
StreamingChatModel model = OpenAiStreamingChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o-mini")
.build();
StreamingProgressBar progress = new StreamingProgressBar(model);
progress.chatWithProgress("讲一个关于 AI 发展的故事");
}
}
总结
本章要点
-
ChatModel 接口层次
- LanguageModel(废弃)
- ChatModel(简单场景)
- ChatModel(进阶场景)
- StreamingChatModel(流式响应)
-
ChatModel
- 最简单的接口
- 只接受 String,返回 String
- 适合快速原型开发
-
ChatModel
- 提供多种 generate 方法
- 返回 ChatResponse 对象
- 可以访问元数据和完整信息
-
StreamingChatModel
- 支持流式输出
- 提供 TokenStream 和响应处理器
- 可以中途取消
-
模型选择
- gpt-4o:最高能力
- gpt-4o-mini:平衡选择(推荐)
- gpt-3.5-turbo:低成本
-
异常处理
- 常见异常类型
- 内置重试机制
- 自定义重试策略
下一步
在下一章节中,我们将学习:- 模型参数的详细配置
- temperature、topP、topK 等参数的含义
- 参数调优技巧
常见问题
Q1:什么时候应该使用 StreamingChatModel? A:当用户需要实时看到生成内容时,比如:- 聊天机器人
- 长文本生成
- 需要提前展示结果的场景
chat(String)- 最简单chat(ChatMessage)- 单消息chat(ChatMessage...)- 多消息chat(ChatRequest)- 完全控制
- 高要求用 gpt-4o
- 平衡用 gpt-4o-mini(推荐)
- 低成本用 gpt-3.5-turbo
参考资料
版权归属于 LangChat Team
官网:https://langchat.cn

