跳到主要内容LangChain4j 实战:在 Java 项目中接入大模型的完整记录 | 极客日志JavaAIjava
LangChain4j 实战:在 Java 项目中接入大模型的完整记录
记录用 LangChain4j 在 Java 中集成大模型的完整过程:从多模块工程搭建、接入通义千问与 DeepSeek,到 Spring BootStarter 自动装配、低阶/高阶 API、模型参数调优、多模态视觉、流式输出、对话记忆、提示词工程、Redis 持久化,最后利用 Function Calling 调用天气服务。所有代码可运行,关键步骤均附截图。
指针猎手1 浏览 LangChain4j 是想在 Java 应用里用上大模型时一个比较务实的选择。它统一了不同提供商的调用方式,底层的抽象也做得不错。
官网:https://docs.langchain4j.dev/

文档在 docs.langchain4j.dev 维护,更新还算勤快。下面是我实际搭一遍的完整过程。
第一个能跑的 demo
新建一个多模块 Maven 父工程,统一控制 Spring Boot 和 LangChain4j 的版本。
父 pom 核心片段:
<properties>
<spring-boot.version>3.5.0</spring-boot.version>
<langchain4j.version>1.0.1</langchain4j.version>
<langchain4j-community.version>1.0.1-beta6</langchain4j-community.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<>import
dev.langchain4j
langchain4j-bom
${langchain4j.version}
pom
import
dev.langchain4j
langchain4j-community-bom
${langchain4j-community.version}
pom
import
scope
</scope>
</dependency>
<dependency>
<groupId>
</groupId>
<artifactId>
</artifactId>
<version>
</version>
<type>
</type>
<scope>
</scope>
</dependency>
<dependency>
<groupId>
</groupId>
<artifactId>
</artifactId>
<version>
</version>
<type>
</type>
<scope>
</scope>
</dependency>
</dependencies>
</dependencyManagement>
第一个模块用通义千问 qwen-plus,接入方式走 OpenAI 兼容接口。先去阿里云百炼申请 Key,baseUrl 固定为 https://dashscope.aliyuncs.com/compatible-mode/v1。
新建子模块 langChain4j-01study,pom 加上 langchain4j-open-ai 和 langchain4j。
配置类直接创建 OpenAiChatModel Bean:
@Configuration
public class LLMConfig {
@Bean
public ChatModel chatModel() {
return OpenAiChatModel.builder()
.apiKey(System.getenv("aliQwen-api"))
.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
.modelName("qwen-plus")
.build();
}
}
控制器里调用 chatModel.chat(msg) 就能得到回复。
多模型共存:加一个 DeepSeek
同一个工程里用不同模型也很简单。DeepSeek 的 baseUrl 是 https://api.deepseek.com/v1,模型名 deepseek-chat,去官网拿 Key。
@Bean("qwen")
public ChatModel qwenChatModel() {
return OpenAiChatModel.builder()
.apiKey(System.getenv("aliQwen-api"))
.baseUrl("https://dashscope.aliyuncs.com/compatible-mode/v1")
.modelName("qwen-plus")
.build();
}
@Bean("deepseek")
public ChatModel deepseekChatModel() {
return OpenAiChatModel.builder()
.apiKey("sk-4af********")
.baseUrl("https://api.deepseek.com/v1")
.modelName("deepseek-chat")
.build();
}
和 Spring Boot 深度整合
LangChain4j 提供了 Spring Boot Starter,在配置文件里写几行就能自动装配。
新建模块 langChain4j-02boot,pom 引入 langchain4j-open-ai-spring-boot-starter 和 langchain4j-spring-boot-starter。
langchain4j.open-ai.chat-model.api-key=${aliQwen-api}
langchain4j.open-ai.chat-model.model-name=qwen-plus
langchain4j.open-ai.chat-model.base-url=https://dashscope.aliyuncs.com/compatible-mode/v1
ChatModel Bean 会自动创建。也可以使用 @AiService 声明式接口,框架生成代理。
低阶 API vs 高阶 API
LangChain4j 有两层抽象,可以根据场景选。
- 低阶 API:直接操作
ChatModel、UserMessage、ChatResponse 等,能拿到 token 消耗,控制力强。
- 高阶 API:通过
AiServices.create(interface, model) 代理接口,代码更少。
新建模块 langChain4j-03lowhigh。
@GetMapping("/chat1")
public String chat1(@RequestParam String msg) {
return chatModel.chat(msg);
}
ChatResponse chatResponse = chatModel.chat(UserMessage.from(msg));
TokenUsage tokenUsage = chatResponse.tokenUsage();
System.out.println("本次 token 消耗:" + tokenUsage);
public interface ChatAssistant {
String chat(String prompt);
}
@Bean
public ChatAssistant chatAssistant(ChatModel chatModel) {
return AiServices.create(ChatAssistant.class, chatModel);
}
调整模型参数
日志:.logRequests(true).logResponses(true),同时 logging.level.dev.langchain4j=DEBUG。
监听器:实现 ChatModelListener 可以在请求/响应时插入逻辑,比如透传 TraceID。
重试:.maxRetries(2),网络异常时会自动重试。断开网络后访问,可以看到重试请求:
超时:.timeout(Duration.ofSeconds(1)),避免长时间卡住。
多模态:看图说话
视觉模型(如 qwen-vl-max)能同时理解文本和图片。新建模块 langChain4j-04chatimage。
配合 UserMessage 里的 ImageContent 可以把 base64 图片传进去。
byte[] imageBytes = resource.getContentAsByteArray();
String base64 = Base64.getEncoder().encodeToString(imageBytes);
UserMessage userMessage = UserMessage.from(
TextContent.from("描述图片内容"),
ImageContent.from(base64, "image/jpg")
);
阿里的文生图模型 wanx2.1-t2i-turbo 也能通过 langchain4j-community-dashscope 接入。新建模块 langChain4j-05chatimage。
Response<Image> generate = wanxImageModel.generate("一只猫");
String url = generate.content().url().toString();
ImageSynthesisParam build = ImageSynthesisParam.builder()
.apiKey(System.getenv("aliQwen-api"))
.model("wanx2.1-t2i-turbo")
.prompt(prompt)
.n(2)
.size("1024*1024")
.build();
ImageSynthesisResult result = new ImageSynthesis().call(build);
流式输出
服务端生成一段,客户端就收到一段,体验会好很多。引入 langchain4j-reactor 就行。
新建模块 langChain4j-06chatstream。
StreamingChatModel 配合 Flux 使用:
@GetMapping("/chat1")
public Flux<String> chat1(String msg) {
return Flux.create(fluxSink -> {
streamingChatModel.chat(msg, new StreamingChatResponseHandler() {
@Override
public void onPartialResponse(String s) {
fluxSink.next(s);
}
@Override
public void onCompleteResponse(ChatResponse cr) {
fluxSink.complete();
}
});
});
}
对话记忆
为了让模型记住上下文,LangChain4j 提供了 MessageWindowChatMemory 和 TokenWindowChatMemory。
新建模块 langChain4j-07chatmemory。
在 AiService 里用 @MemoryId 标注,框架会按 id 保存历史。
public interface ChatMemoryAssistant {
String chat(@MemoryId Long userId, @UserMessage String prompt);
}
@Bean
public ChatMemoryAssistant assistant(ChatModel model) {
return AiServices.builder(ChatMemoryAssistant.class)
.chatModel(model)
.chatMemoryProvider(memoryId -> MessageWindowChatMemory.withMaxMessages(20))
.build();
}
测试:user 1 说自己叫 Java,再问'我叫什么',回答正确;user 3 的记忆互不干扰。
提示词工程
新建模块 langChain4j-08chatprompt。
@SystemMessage("你是一位专业的中国法律顾问,只回答与中国法律相关的问题")
@UserMessage("请回答:{{question}},字数控制在{{length}}以内")
String chat(@V("question") String question, @V("length") int length);
结构化模板:用 @StructuredPrompt 注解 POJO。
@StructuredPrompt("根据中国{{legal}}法律解答:{{question}}")
public class LawPrompt {
private String legal;
private String question;
}
PromptTemplate template = PromptTemplate.from("你是一个{{it}}助手,{{question}}怎么办");
Prompt prompt = template.apply(Map.of("it", "财务", "question", "人民币大写"));
ChatResponse response = chatModel.chat(prompt.toUserMessage());
持久化到 Redis
默认的 ChatMemory 在内存中,重启就没了。实现 ChatMemoryStore 接口可以把消息存进 Redis。
新建模块 langChain4j-09chatPersistence。
@Component
public class RedisChatMemoryStore implements ChatMemoryStore {
@Override
public List<ChatMessage> getMessages(Object memoryId) {
String json = redisTemplate.opsForValue().get("chat_memory:" + memoryId);
return ChatMessageDeserializer.messagesFromJson(json);
}
@Override
public void updateMessages(Object memoryId, List<ChatMessage> list) {
redisTemplate.opsForValue().set("chat_memory:" + memoryId, ChatMessageSerializer.messagesToJson(list));
}
@Override
public void deleteMessages(Object memoryId) {
redisTemplate.delete("chat_memory:" + memoryId);
}
}
然后在 ChatMemoryProvider 里使用这个 Store。测试后可以看到 Redis 里有了对应 key。
工具调用 (Function Calling)
大模型通过 Function Calling 告诉我们需要调哪个函数、传什么参数。LangChain4j 支持低阶和高阶两种方式。
新建模块 langChain4j-10chatFunctionCalling。
低阶 API 手工构建 ToolSpecification 和 ToolExecutor:
ToolSpecification tool = ToolSpecification.builder()
.name("开具发票助手")
.description("根据信息开具发票")
.parameters(JsonObjectSchema.builder()
.addStringProperty("companyName", "公司名称")
.addStringProperty("dutyNumber", "税号")
.addStringProperty("amount", "金额")
.build())
.build();
高阶 API 只需要把带有 @Tool 注解的类交给 AiServices。这里以查询天气为例,借助和风天气 API。
@Service
public class WeatherService {
private static final String URL = "https://api.qweather.com/v7/weather/now?location=%s&key=%s";
public JsonNode getWeather(String location) {
String url = String.format(URL, location, "你的KEY");
return new ObjectMapper().readTree(new RestTemplate().getForObject(url, String.class));
}
}
public class InvoiceHandler {
@Tool("获取天气信息")
public String handle(@P("天气状况") String text, @P("风向") String windDir) throws Exception {
System.out.println("天气状况:" + text + " 风向:" + windDir);
return new WeatherService().getWeather("101010100").toString();
}
}
@Bean("highfunction")
public FunctionAssistant highFunctionAssistant(ChatModel chatModel) {
return AiServices.builder(FunctionAssistant.class)
.chatModel(chatModel)
.tools(new InvoiceHandler())
.build();
}
待补充
向量数据库、RAG 和 MCP 模型上下文切换的部分正在整理,后续会持续更新。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- RSA密钥对生成器
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
- Mermaid 预览与可视化编辑
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online