最近在学习 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 版本为 17:
<properties> <java.version>17</java.version> </properties>完整的核心依赖如下:
<dependencies> <!-- Spring MVC、Controller、内置 Tomcat --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- OpenAI 兼容模型支持 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-open-ai-spring-boot-starter</artifactId> <version>1.18.0-beta28</version> </dependency> <!-- AI Service、Tool Calling 等 Spring 集成能力 --> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-spring-boot-starter</artifactId> <version>1.18.0-beta28</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</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-url
base-url: http://langchain4j.dev/demo/openai/v1这是模型服务的接口地址,不是具体模型。
api-key
api-key: demo这是接口调用凭证。当前使用的是演示配置,只适合本地学习。
model-name
model-name: gpt-4o-mini这一项才是具体使用的模型名称。
整体关系可以理解为:
base-url:请求发到哪个模型服务 api-key:用什么凭证调用 model-name:具体调用哪个模型四、先完成最简单的模型调用
创建 Controller:
package 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可以类比:
List<String> 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 """ 用户ID:1001 用户名:张三 年龄: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
修改Assistant:
package 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:依赖是否使用了 Starter:
<artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>而不是只添加普通的模型依赖。
还需要检查 JDK、Spring Boot 和 LangChain4j 版本是否匹配。
2. tools 属性类型错误
错误写法:
@AiService( tools = UserTool.class )IDEA 会提示需要String[],实际提供的是Class<UserTool>。
正确写法:
@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让模型真正查询数据库中的用户信息。