最近在学习 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 chatModel
Spring 容器中实际创建的对象一般是对应模型的具体实现,例如:
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 UserTool
Spring 默认会把它注册成:
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
让模型真正查询数据库中的用户信息。