LangChain4j 核心 API:从消息到流式与结构化输出
LangChain4j 的低层 API 适合需要精确控制消息、请求参数和响应处理方式的 Java 应用。它把模型调用拆成几个稳定的抽象:模型接口负责发送请求,消息对象描述上下文,ChatRequest 组合一次调用,ChatResponse 携带模型回复和元数据。
1. 模型抽象
依赖通常由核心包和具体模型适配器组成。应用代码依赖 ChatModel(部分版本也称 ChatLanguageModel)接口,而不是把厂商 SDK 类型散落在业务代码中。下面以 OpenAI 适配器为例:
ChatModel model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o-mini")
.build();更换为其他适配器时,通常只需替换模型构建部分,消息和响应处理仍可复用。具体 artifact 和模型名称应以官方模型集成文档为准。
2. 消息与 ChatRequest
一次对话由不同角色的 ChatMessage 组成:
SystemMessage:行为和角色约束。UserMessage:用户输入,可由字符串或TextContent创建。AiMessage:模型生成的消息。ToolExecutionResultMessage:工具执行后的结果(在启用工具调用时使用)。
只传一条文本时可以直接调用:
String answer = model.chat("用一句话解释 RAG");需要显式控制上下文时,使用消息列表和 ChatRequest:
List<ChatMessage> messages = List.of(
SystemMessage.from("你是严谨的中文技术助手。"),
UserMessage.from(TextContent.from("比较同步调用和流式调用的差异。"))
);
ChatRequest request = ChatRequest.builder()
.messages(messages)
.build();
ChatResponse response = model.chat(request);
AiMessage aiMessage = response.aiMessage();
System.out.println(aiMessage.text());ChatRequest 还可以集中放置温度、最大输出 token、响应格式等参数,便于将一次调用的协议记录完整。
3. 读取 ChatResponse
ChatResponse 不只是字符串容器。常用信息包括:
aiMessage().text():最终文本。aiMessage().thinking():模型或适配器提供的思考字段(可能为空,不能假定所有模型都支持)。metadata().tokenUsage():输入、输出及总 token 使用量(取决于模型是否返回)。
建议在边界层统一处理空响应和可选字段:
ChatResponse response = model.chat(request);
if (response == null || response.aiMessage() == null) {
throw new IllegalStateException("模型未返回有效响应");
}
String text = response.aiMessage().text();
var usage = response.metadata().tokenUsage();
System.out.printf("%s%nusage=%s%n", text, usage);4. 流式响应
流式模型通过 StreamingChatModel 和 StreamingChatResponseHandler 回调分片结果:
StreamingChatModel streamingModel = OpenAiStreamingChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o-mini")
.build();
streamingModel.chat(
List.of(UserMessage.from("分三点说明向量检索的作用。")),
new StreamingChatResponseHandler() {
@Override
public void onPartialResponse(String partialResponse) {
System.out.print(partialResponse);
}
@Override
public void onCompleteResponse(ChatResponse completeResponse) {
System.out.println("\n完成");
}
@Override
public void onError(Throwable error) {
error.printStackTrace();
}
});回调通常需要桥接到 WebSocket、SSE 或 Reactor 流。务必在完成和异常回调中结束下游,否则客户端可能一直等待连接关闭;同时为缓冲区、超时和取消操作设置明确策略。
5. 结构化 JSON 输出
可以在 ChatRequest 中声明响应格式:
ChatRequest request = ChatRequest.builder()
.messages(UserMessage.from("返回包含 answer 和 confidence 两个字段的 JSON"))
.responseFormat(ResponseFormat.JSON)
.build();
String json = model.chat(request).aiMessage().text();ResponseFormat.JSON 是协议层约束,不等于业务对象校验。生产代码仍应使用 Jackson 等库解析,并处理字段缺失、类型错误和模型返回额外文本的情况:
record Answer(String answer, double confidence) {}
Answer result = objectMapper.readValue(json, Answer.class);如果模型或供应商不支持 JSON 模式,应在适配器文档确认兼容行为,并保留解析失败的降级路径。
6. 何时选择低层 API
低层 API 适合以下场景:需要记录完整 token 用量、动态拼装多角色消息、对流式生命周期做精细控制,或要把模型调用封装成自己的领域服务。若只需要把一个接口方法映射为提示词并自动完成对象转换,可以进一步评估 AiServices 等高层 API。
常见注意事项:
- 不要把供应商特有类型泄漏到业务层,优先依赖通用模型接口。
- 将 system message、用户消息和历史消息明确分层,避免把未经信任的文本拼进系统约束。
- token 用量、思考字段和部分元数据都是可选能力,代码应允许为空。
- 流式输出必须处理完成、异常和取消三种终态。
- JSON 输出仍需反序列化和业务校验,不能只依赖提示词。
总结
LangChain4j 核心调用链可以概括为:选择模型实现,构造 ChatMessage,用 ChatRequest 描述一次请求,再从 ChatResponse 或流式回调中消费结果。掌握这组 API 后,可以在不改变业务层协议的前提下替换模型供应商,并逐步接入记忆、工具调用和 RAG 等更高层能力。
