Spring Boot 集成 LangChain4j:从模型调用到 Tool Calling(Demo版) 最近在学习 Java AI 应用开发先用 Spring Boot 搭了一个最小项目完成了大模型调用然后在这个基础上继续接入 AI Service 和 Tool Calling。这篇文章记录整个实现过程也把中间遇到的几个配置问题整理出来。本文最终实现的效果是用户发送自然语言请求 ↓ Spring Boot 接收请求 ↓ 大模型判断是否需要调用工具 ↓ LangChain4j 执行指定的 Java 方法 ↓ 工具返回业务数据 ↓ 大模型整理后回复用户目前使用固定数据模拟查询后续可以把 Tool 接到 Service、MyBatis 和 MySQL。一、项目环境本文使用的环境如下JDK 17 Spring Boot 3.5.x Maven LangChain4j 1.18.0-beta28一开始使用的是JDK 8 Spring Boot 2.6.13项目可以正常启动但无法正常使用当前版本的 LangChain4j Spring Boot Starter自动配置也没有生效。因此学习项目建议直接使用 JDK 17 和 Spring Boot 3。二、创建 Spring Boot 项目创建一个 Maven 类型的 Spring Boot 项目依赖先选择Spring Web项目创建后确认pom.xml中的 Java 版本为 17properties java.version17/java.version /properties完整的核心依赖如下dependencies !-- Spring MVC、Controller、内置 Tomcat -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- OpenAI 兼容模型支持 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-open-ai-spring-boot-starter/artifactId version1.18.0-beta28/version /dependency !-- AI Service、Tool Calling 等 Spring 集成能力 -- dependency groupIddev.langchain4j/groupId artifactIdlangchain4j-spring-boot-starter/artifactId version1.18.0-beta28/version /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies这两个 LangChain4j 依赖的作用不同。langchain4j-open-ai-spring-boot-starter负责读取模型配置并创建ChatModel。langchain4j-spring-boot-starter负责支持AiService、Tool Calling 等更高层能力。添加完成后刷新 Maven 依赖。三、配置大模型在下面位置创建配置文件src/main/resources/application.yml配置内容如下server: port: 8080 langchain4j: open-ai: chat-model: base-url: http://langchain4j.dev/demo/openai/v1 api-key: demo model-name: gpt-4o-mini temperature: 0.3 log-requests: true log-responses: true几个配置项需要分开理解。base-urlbase-url: http://langchain4j.dev/demo/openai/v1这是模型服务的接口地址不是具体模型。api-keyapi-key: demo这是接口调用凭证。当前使用的是演示配置只适合本地学习。model-namemodel-name: gpt-4o-mini这一项才是具体使用的模型名称。整体关系可以理解为base-url请求发到哪个模型服务 api-key用什么凭证调用 model-name具体调用哪个模型四、先完成最简单的模型调用创建 Controllerpackage com.hsw.langchain4jdemo.controller; import dev.langchain4j.model.chat.ChatModel; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; RestController RequestMapping(/api/ai) public class AiController { private final ChatModel chatModel; public AiController(ChatModel chatModel) { this.chatModel chatModel; } GetMapping(/chat) public String chat(RequestParam String message) { return chatModel.chat(message); } }启动项目后访问http://localhost:8080/api/ai/chat?message请介绍一下Java如果可以正常返回内容说明下面这条链路已经打通Controller ↓ ChatModel ↓ LangChain4j ↓ 大模型接口 ↓ 返回文本ChatModel 是什么ChatModel并不是大模型本身而是 LangChain4j 对聊天模型能力定义的统一接口。代码中注入的是ChatModel chatModelSpring 容器中实际创建的对象一般是对应模型的具体实现例如OpenAiChatModel可以类比ListString list new ArrayList();List是接口ArrayList是具体实现。ChatModel的作用就是屏蔽不同模型服务之间的请求细节让业务代码统一使用chatModel.chat(message);五、使用 AI Service直接调用ChatModel适合验证模型连接但后面要做系统提示词、对话记忆、Tool Calling 和 RAG通常会使用 AI Service。创建接口com.hsw.langchain4jdemo.assistant.Assistant代码如下package com.hsw.langchain4jdemo.assistant; import dev.langchain4j.service.SystemMessage; import dev.langchain4j.service.spring.AiService; AiService public interface Assistant { SystemMessage( 你是一名Java学习助手。 回答尽量准确、清晰。 遇到不确定的数据时不要编造。 ) String chat(String message); }这里没有编写AssistantImpl。LangChain4j 会在项目启动时为这个接口创建代理对象并把它注册到 Spring 容器中。调用过程类似Assistant 接口 ↓ LangChain4j 动态代理 ↓ ChatModel ↓ 大模型Controller 可以改成package com.hsw.langchain4jdemo.controller; import com.hsw.langchain4jdemo.assistant.Assistant; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; RestController RequestMapping(/api/ai) public class AiController { private final Assistant assistant; public AiController(Assistant assistant) { this.assistant assistant; } GetMapping(/assistant) public String assistant(RequestParam String message) { return assistant.chat(message); } }测试地址http://localhost:8080/api/ai/assistant?message线程池是什么六、添加第一个 Tool普通模型只能根据已有知识生成内容无法直接查询项目中的数据库或业务接口。例如用户输入帮我查询用户1001的信息模型并不知道项目里的用户数据。如果直接回答很可能会编造。Tool Calling 的作用就是让模型在需要真实数据时调用后端提供的 Java 方法。创建工具类com.hsw.langchain4jdemo.tool.UserTool代码如下package com.hsw.langchain4jdemo.tool; import dev.langchain4j.agent.tool.Tool; import org.springframework.stereotype.Component; Component public class UserTool { Tool(根据用户ID查询用户信息) public String queryUser(Long userId) { // 暂时使用固定数据模拟数据库查询 if (Long.valueOf(1001L).equals(userId)) { return 用户ID1001 用户名张三 年龄25 会员等级黄金会员 ; } return 没有找到对应的用户; } }这里的Tool不是 Controller 接口也不要求它是 HTTP 接口。它就是一个普通 Java 方法只不过通过Tool告诉大模型系统拥有一个根据用户ID查询用户信息的能力真实项目中一般会写成UserTool ↓ UserService ↓ UserMapper ↓ MySQL例如Component public class UserTool { private final UserService userService; public UserTool(UserService userService) { this.userService userService; } Tool(根据用户ID查询用户信息) public UserDTO queryUser(Long userId) { return userService.getById(userId); } }七、把 Tool 注册到 AI Service修改Assistantpackage com.hsw.langchain4jdemo.assistant; import dev.langchain4j.service.SystemMessage; import dev.langchain4j.service.spring.AiService; AiService( tools userTool ) public interface Assistant { SystemMessage( 你是一个用户信息查询助手。 查询用户信息时必须调用工具 不允许自行编造用户数据。 ) String chat(String message); }这里写的是tools userTool而不是tools UserTool.class因为当前版本中tools属性接收的是 Spring Bean 名称。工具类是Component public class UserToolSpring 默认会把它注册成userTool所以 AI Service 中填写tools userTool如果手动指定 Bean 名称Component(userQueryTool) public class UserTool { }对应配置也需要改成AiService( tools userQueryTool )八、测试 Tool Calling启动项目后访问http://localhost:8080/api/ai/assistant?message帮我查询用户1001的信息完整流程如下用户输入 帮我查询用户1001的信息 ↓ Assistant 接收消息 ↓ 大模型分析用户意图 ↓ 模型决定调用 queryUser ↓ LangChain4j 执行 UserTool.queryUser(1001) ↓ Java 方法返回用户数据 ↓ 工具结果再次交给大模型 ↓ 大模型整理后返回最终答案需要注意大模型并没有直接执行 Java 方法。实际分工是大模型决定调用哪个工具以及传递什么参数 LangChain4j解析调用请求并执行 Java 方法 Java 业务代码真正查询数据库或调用业务服务九、wiringMode 是什么在配置 AI Service 时可能会看到下面这种写法AiService( wiringMode AiServiceWiringMode.EXPLICIT, tools userTool )wiringMode控制 AI Service 的依赖如何装配。自动装配默认情况下LangChain4j 会从 Spring 容器中查找合适的模型和其他依赖。AiService( tools userTool )对于当前只有一个ChatModel的学习项目使用自动装配比较简单。显式装配wiringMode AiServiceWiringMode.EXPLICIT表示不再自动选择而是要求开发人员明确指定所使用的模型、工具等 Bean。它适合以下场景项目中存在多个 ChatModel 不同 Agent 使用不同模型 不同 Agent 只能使用指定工具 需要严格控制依赖关系如果写了wiringMode AiServiceWiringMode.EXPLICIT却只指定了 Tool没有指定 ChatModel就可能出现Please specify either chatModel or streamingChatModel对于当前项目直接删除EXPLICIT使用自动装配即可AiService( tools userTool )十、常见问题1. 找不到 ChatModel Bean报错required a bean of type dev.langchain4j.model.chat.ChatModel that could not be found优先检查以下内容。配置文件是否放在src/main/resources/application.yml配置前缀是否正确langchain4j: open-ai: chat-model:依赖是否使用了 StarterartifactIdlangchain4j-open-ai-spring-boot-starter/artifactId而不是只添加普通的模型依赖。还需要检查 JDK、Spring Boot 和 LangChain4j 版本是否匹配。2. tools 属性类型错误错误写法AiService( tools UserTool.class )IDEA 会提示需要String[]实际提供的是ClassUserTool。正确写法AiService( tools userTool )这里填写的是 Spring Bean 名称。3. 提示必须指定 chatModel报错Please specify either chatModel or streamingChatModel通常是因为使用了wiringMode AiServiceWiringMode.EXPLICIT但没有明确指定模型。初学阶段可以先使用自动装配AiService( tools userTool )4. 找不到名为 chatModel 的 Bean报错required a bean named chatModel that could not be found通常是手动写了chatModel chatModel这里要求 Spring 容器里必须存在一个名称正好为chatModel的 Bean。但自动配置生成的模型 Bean 不一定叫这个名字。如果当前只有一个模型直接删除 Bean 名称配置让 LangChain4j 按类型自动装配更合适。十一、当前项目结构完成后项目结构大致如下src/main/java/com/hsw/langchain4jdemo ├── LangChain4jDemoApplication.java ├── assistant │ └── Assistant.java ├── controller │ └── AiController.java └── tool └── UserTool.java src/main/resources └── application.yml当前调用关系AiController ↓ Assistant ↓ ChatModel ↓ 大模型 ↓ 判断是否调用 UserTool ↓ UserTool ↓ 返回业务数据 ↓ 大模型生成最终回答十二、这算不算 Agent只调用chatModel.chat(message);属于普通的大模型接入还不能算 Agent。加入 Tool Calling 后模型已经可以理解用户需求 选择工具 生成工具参数 获取工具执行结果 根据结果继续回答这已经具备了基础 Agent 能力。不过距离完整业务 Agent 还有一些内容多轮对话记忆 多个工具连续调用 数据库真实查询 RAG 知识库 权限校验 高风险操作二次确认 幂等控制 工具调用日志 异常重试和超时处理下一步可以把当前的固定用户数据替换成Spring Boot MyBatis-Plus MySQL UserTool让模型真正查询数据库中的用户信息。