1、概述
前面我们学习了LangChain4j 的基本使用方法,并掌握了如何将其与 Spring Boot 集成,实现 AI 大模型在工程化场景中的落地应用。今天我们继续学习 Langchain4j 相关的内容,我们先从上次的AI智能助手的案例开始,结合AI智能体应用的特点 ,基于 Spring Boot 与 LangChain4j 构建一个具备实际功能的 AI 智能体应用。帮助大家进一步掌握AI智能体应用开发相关的技术。
2、项目环境搭建
本次工程涉及的软件版本信息如下:
Java: 17
langchain4j: 1.0.0-beta3
SpringBoot: 3.2.6
2.1、新建工程
首先我们创建一个项目,项目的结构如下
2.2、添加依赖
父工程版本管理
<dependencyManagement><dependencies><!--引入SpringBoot依赖管理清单--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>${spring-boot.version}</version><type>pom</type><scope>import</scope></dependency><!--引入百炼依赖管理清单--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-community-bom</artifactId><version>${langchain4j.version}</version><type>pom</type><scope>import</scope></dependency><!--引入langchain4j依赖管理清单--><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-bom</artifactId><version>${langchain4j.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>
依赖信息
<dependencies><!-- web应用程序核心依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!-- 编写和运行测试用例 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId></dependency><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId><version>${knife4j.version}</version></dependency><dependency><groupId>dev.langchain4j</groupId><artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId></dependency></dependencies>
2.3、配置信息
其中 api-key 需要自己申请,本文使用的是 阿里的百炼平台
server.port=9010#langchain4j测试模型
langchain4j.open-ai.chat-model.base-url= https://dashscope.aliyuncs.com/compatible-mode/v1
langchain4j.community.dashscope.chat-model.api-key=XXXX
langchain4j.community.dashscope.chat-model.model-name=qwen-max#请求和响应日志
langchain4j.open-ai.chat-model.log-requests=true
langchain4j.open-ai.chat-model.log-responses=true
#启用日志debug级别
logging.level.root=debugspring.data.mongodb.uri=mongodb://192.168.93.34:5030/qwen-max_memory_dbspring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML
spring.thymeleaf.encoding=UTF-8
2.4、编写启动类
/*** @Description* @Author wcan* @Date 2025/5/8 下午 13:42* @Version 1.0*/
@SpringBootApplication
public class Lc4jApplication {public static void main(String[] args) {SpringApplication.run(Lc4jApplication.class,args);}
}
3、快速实现AI智能助手
3.1、创建CheeseAgent
CheeseAgent 就是一个AI服务代理,也是本案例最核心的一个接口,相关代码如下:
@AiService(wiringMode = EXPLICIT,chatModel = "qwenChatModel",chatMemoryProvider = "chatMemoryProviderCheese")
public interface CheeseAgent {@SystemMessage(fromResource = "java-prompt-template.txt")String chat(@MemoryId Long memoryId, @UserMessage String userMessage);
}
其中 chatModel 指定了使用 阿里的 qwen 大模型。 ChatMemoryProvider 指定聊天记忆的实现。
@SystemMessage 注解的意思是定义系统提示词,这里使用的是模板,我们在 resource 目录下新建一个文件,文件名是 java-prompt-template.txt ,文件内容如下
你的名字是“cheese”,你是一个工作了10年的Java软件工程师,你的知识面很广并且各方面技术都有很深的理解。1、请仅在用户发起第一次会话时,和用户打个招呼,并介绍你是谁。2、作为一个项目经验丰富的架构师:
精通互联网金融、传统银行金融、电商、零售等各行业的软件架构设计,能够提供清晰的设计思路、详细的设计流程。你可以帮助别人设计出可落地的项目架构,并合理的引入各类中间件,同时对于项目管理你也具备丰富的经验,很多时候能给出合理的意见,帮助团队leader3、你还需要包含以下功能:
快速的解决问题:根据用户提供的报错信息,能快速准确的定位到问题的原因。
知识储备丰富,技术面广:能够指导用户学习,帮助用户快速学习Java生态相关的技术4、你具备丰富的分布式、高并发、高可用系统设计相关的经验5、生成的内容以HTML格式返回6、今天是 {{current_date}}。
3.2、聊天记忆实现
这里我们依然采用 MongoDB 作为持久化存储方案,具体设计思路可以参考上一篇文章,这里直接给出代码。
package org.wcan.lc4j.store;import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.ChatMessageDeserializer;
import dev.langchain4j.data.message.ChatMessageSerializer;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.stereotype.Component;
import org.wcan.lc4j.entity.ChatMessages;import java.util.LinkedList;
import java.util.List;@Component
public class MongoChatMemoryStore implements ChatMemoryStore {@Autowiredprivate MongoTemplate mongoTemplate;@Overridepublic List<ChatMessage> getMessages(Object memoryId) {Criteria criteria = Criteria.where("memoryId").is(memoryId);Query query = new Query(criteria);ChatMessages chatMessages = mongoTemplate.findOne(query, ChatMessages.class);if(chatMessages == null)return new LinkedList<>();return ChatMessageDeserializer.messagesFromJson(chatMessages.getContent());}@Overridepublic void updateMessages(Object memoryId, List<ChatMessage> messages) {Criteria criteria = Criteria.where("memoryId").is(memoryId);Query query = new Query(criteria);Update update = new Update();update.set("content", ChatMessageSerializer.messagesToJson(messages));//根据query条件能查询出文档,则修改文档;否则新增文档mongoTemplate.upsert(query, update, ChatMessages.class);}@Overridepublic void deleteMessages(Object memoryId) {Criteria criteria = Criteria.where("memoryId").is(memoryId);Query query = new Query(criteria);mongoTemplate.remove(query, ChatMessages.class);}
}
3.3、Agent 核心配置
这里依然使用配置类来实现,具体代码如下
package org.wcan.lc4j.config;import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.wcan.lc4j.store.MongoChatMemoryStore;@Configuration
public class CheeseAgentConfig {@Autowiredprivate MongoChatMemoryStore mongoChatMemoryStore;@BeanChatMemoryProvider chatMemoryProviderCheese() {return memoryId -> MessageWindowChatMemory.builder().id(memoryId).maxMessages(20).chatMemoryStore(mongoChatMemoryStore).build();}
}
3.4、实体类设计
本次案例中需要设计一个聊天对话的实体类 ChatForm,他的作用是封装用户输入的数据。此外还需要一个聊天记忆的实体类 ChatMessages,它的作用是封装持久化的内容。
package org.wcan.lc4j.entity;import lombok.Data;@Data
public class ChatForm {private Long memoryId;//对话idprivate String message;//用户问题
}
package org.wcan.lc4j.entity;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;@Data
@AllArgsConstructor
@NoArgsConstructor
@Document("gpt_messages")
public class ChatMessages {@Idprivate ObjectId messageId;private String memoryId;private String content;}
3.5、业务Controller 实现
最后我们实现一个 controller 提供用户和 qwen 大模型交互的界面
package org.wcan.lc4j.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.wcan.lc4j.assistant.CheeseAgent;
import org.wcan.lc4j.entity.ChatForm;/*** @Description* @Author wcan* @Date 2025/5/8 下午 14:12* @Version 1.0*/
@RestController
public class CheeseAgentController {@Autowiredprivate CheeseAgent cheeseAgent;@PostMapping("/cheese/chat")public String chat(@RequestBody ChatForm chatForm){return cheeseAgent.chat(chatForm.getMemoryId(),chatForm.getMessage());}
}
3.6、功能测试
我们启动项目。使用 apipost 工具测试
由于在前面的提示词中我们要求大模型以 HTML 的格式返回生成的内容,所以我么看到的是是一段HTML。到这里我们已经完成了一个聊天助手的应用了。
4、什么是AI智能体应用
简单来说 AI智能体应用 就是一种能够自主地感知环境、做出决策并执行的软件系统。它们通常被设计成能够在特定环境中完成特定任务,而无需人类直接干预。
前面我们开发出来的智能助手也具备类似的功能,但是并不能称为智能体应用,很显然它仅仅只能完成问答形式的聊天而已,并不具备执行某类任务的功能或者是做出某类决策的功能。
下面是我总结的一张图
其中 AI Agent 至少有4个维度的功能,分别是内容记忆(Memory)、干活(Action)、工具集合(Tools)以及执行计划(Planning)。内容记忆主要有短期记忆和永久记忆 也就是我们前面实现的内存记忆和记忆持久化。
Action 主要是我们的系统怎么和大模型进行沟通和信息交换.planning 是大模型本身具备的思考能力。至于Tools相当于是大模型的一个挂,我们可以自己实现某种功能,让大模型去执行特定的代码实现。
5、Tools (Function Calling)
5.1、Agent Function Calling
我们接下来研究一下如何设计 Agent 的 Tools。我们还是先来看文档上的描述
https://docs.langchain4j.dev/tutorials/tools
一些大模型(LLM)除了生成文本之外,还可以触发操作。有一种概念叫做 工具或者 函数调用(function calling)。他是由开发人员封装的特定的功能,在合适的场景下可以被 LLM 调用。我们先来看官方文档上的案例
假设我们需要计算 475695037565的平方根是多少, LLM 肯定是能计算的,但是 计算的不是那么精确,所以我们需要自己开发一个数学计算的工具,在必要的时候提供给 LLM 调用。
5.2、集成一个计算工具
参照文档 我们来实现一个数学计算的工具,相关代码如下
package org.wcan.lc4j.tools;import dev.langchain4j.agent.tool.Tool;
import org.springframework.stereotype.Component;@Component
public class CalculatorTools {@Tooldouble sum(double a, double b) {System.out.println("Sums 2 given numbers");return a + b;}@Tooldouble squareRoot(double x) {System.out.println("Returns a square root of a given number");return Math.sqrt(x);}
}
我们为了证明这段代码执行过,可以加个输出语句。
然后我们将工具配置在 @AiService 注解中
@AiService(wiringMode = EXPLICIT,chatModel = "qwenChatModel",chatMemoryProvider = "chatMemoryProviderCheese",tools = "calculatorTools" )
public interface CheeseAgent {@SystemMessage(fromResource = "java-prompt-template.txt")String chat(@MemoryId Long memoryId, @UserMessage String userMessage);
}
然后可以使用工具测试就能看到效果了
并且控制台 输出了 埋点的内容
5.3、High Level Tool API
按照官方的说法,Langchain4j 提供了2种方式支持 Tools ,低阶的高阶的,
前面的案例中 我们使用的是 @AiService 和 @Tool 注解实现的,算是高阶的。
前面我们知道 @Tool 注解 是用来注释方法的,它可以告诉大模型 这个方法是干嘛的,那么 假如方法有多个参数,怎么告诉大模型 每个参数是干什么的呢,这里肯定也有对应的注解,我么回到文档上
这里有个@P注解 用来标识方法参数的语义。除了这里两个注解之外 还有 @ToolMemoryId 、@Description 等等,大家可以自行去查阅。
6、智能体集成邮件服务
6.1、功能概述
前面我们学习了 Funnction Calling 相关内容,并且参照官网实现了一个计数器的工具,接下来我们开发一个邮件服务,并且集成到我们的智能体应用中。
具体的功能描述是: 用户在页面上输入自己的问题,让我们的智能体 cheese处理完用户的问题后,将结果发送到用户的邮箱中。
6.2、页面开发
这里我们使用 Thymeleaf 快速的实现一个简单的页面,相关代码如下
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>AI 智能助手 - Chat with Cheese</title><style>body { font-family: Arial; padding: 20px; max-width: 600px; margin: auto; }input, textarea, button { width: 100%; margin-bottom: 10px; padding: 10px; font-size: 1em; }.result { margin-top: 20px; background-color: #f5f5f5; padding: 15px; border-radius: 5px; }</style>
</head>
<body><h2>🧀 Cheese AI 助手</h2><form th:action="@{/chat}" method="post"><div><label for="memoryId">记忆 ID:</label><input type="text" id="memoryId" name="memoryId" placeholder="例如:user_123" required /></div><div><label for="message">输入内容:</label><textarea id="message" name="message" rows="4" placeholder="向 AI 提问..."></textarea></div><button type="submit">发送</button>
</form><div class="result" th:if="${response}"><h3>对话记录:</h3><p><strong>你问:</strong> <span th:text="${message}"></span></p>
<!-- <p><strong>AI 回答:</strong> <span th:text="${response}"></span></p>--><p><strong>AI 回答:</strong> <span th:utext="${response}"></span></p>
</div></body>
</html>
在此之前我们需要引入 Thymeleaf 的依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency>
接着我们重新写一个Controller
package org.wcan.lc4j.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.wcan.lc4j.assistant.CheeseAgent;@Controller
public class ChatController {@Autowiredprivate CheeseAgent cheeseAgent;@GetMapping("/chat")public String showChatPage() {return "chat-page";}@PostMapping("/chat")public String handleChat(@RequestParam("memoryId") Long memoryId,@RequestParam("message") String message,Model model) {String response = cheeseAgent.chat(memoryId, message);model.addAttribute("memoryId", memoryId);model.addAttribute("message", message);model.addAttribute("response", response);return "chat-page";}
}
完成之后我们需要添加 Thymeleaf 的配置,关于Thymeleaf 和SpringBoot 的 Web 开发 这里默认大家都会哈。
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML
spring.thymeleaf.encoding=UTF-8
最后重新启动项目,浏览器访问 http://localhost:9010/chat
6.3、邮件服务开发
我们这里使用SpringBoot 中的 JavaMailSender 快速的实现一个邮件服务。步骤还是加依赖,写配置、撸代码三连。
首先添加依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-mail</artifactId></dependency>
接着添加邮箱的配置
# 以qq邮箱为例
spring.mail.host=smtp.qq.com
spring.mail.port=587
# 你的发信邮箱 默认是作为系统的发信邮箱
spring.mail.username=XXXXXX@qq.com
# 邮箱的协议授权码 需要注意 不是邮箱密码 需要 在你的邮箱设置中开启 IMAP/SMTP服务
spring.mail.password=XXXX
# 默认协议
spring.mail.protocol=smtp
# 开启授权
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
然后我们来编写代码,新建一个邮箱服务
package org.wcan.lc4j.service;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Service;@Service
public class EmailService {@Autowiredprivate JavaMailSender javaMailSender;public void sendSimpleEmail(String to, String subject, String text) {SimpleMailMessage message = new SimpleMailMessage();//需要 和 配置文件中指定的发信邮箱对应message.setFrom("XXXX@qq.com");message.setTo(to);message.setSubject(subject);message.setText(text);javaMailSender.send(message);}
}
至此发送邮件的功能已经开发完成了。
6.4、集成邮件服务
下面我们来集成 前面开发的邮件服务了,同样的我们需要创建一个 EmailTools ,然后将这个Tools 配置到 我们的智能体的 AiService 注解中,相关代码如下
package org.wcan.lc4j.tools;import dev.langchain4j.agent.tool.P;
import dev.langchain4j.agent.tool.Tool;
import org.springframework.stereotype.Component;
import org.wcan.lc4j.service.EmailService;/*** @Description* @Author wcan* @Date 2025/5/11 上午 8:59* @Version 1.0*/
@Component
public class EmailTools {private final EmailService emailService;public EmailTools(EmailService emailService) {this.emailService = emailService;}@Tool(name = "发送邮件", value="根据收件人 to,邮件主题 subject 和邮件内容 text 发送邮件")public String sendEmail(@P(value = "收件人") String to, @P(value = "邮件主题")String subject,@P(value = "邮件内容")String text) {emailService.sendSimpleEmail(to, subject, text);System.out.println("邮件已发送");return "邮件已发送";}}
上述代码中 使用了 @Tool 注解标注了 这是一个发送邮件的工具,使用 @P 注解 告诉大模型 相关的参数的含义。接着我们需要将这个工具配到 Aiservice 中
package org.wcan.lc4j.assistant;import dev.langchain4j.service.*;
import dev.langchain4j.service.spring.AiService;import static dev.langchain4j.service.spring.AiServiceWiringMode.EXPLICIT;@AiService(wiringMode = EXPLICIT,chatModel = "qwenChatModel",chatMemoryProvider = "chatMemoryProviderCheese",tools = {"calculatorTools","emailTools"} )
public interface CheeseAgent {@SystemMessage(fromResource = "java-prompt-template.txt")String chat(@MemoryId Long memoryId, @UserMessage String userMessage);
}
@AiService 注解 的 tools 属性是一个数组,因此我们可以配置多个 Tool
6.5、功能测试
我们重启动服务,这次通过浏览器的页面来访问。我们输入准备好的问题
接着我们查看自己的邮箱中的邮件
可以发现我们的 EmailTool 已经被大模型 调用了。
我们配置了两个Tool,还有一个数值计算的 Tool ,我们再来测试一下 能否让大模型 同时调用两个 Tool 来完成用户的需求呢
再来看看我们的邮件
7、总结
本篇文章主要介绍了AI智能体应用相关的概念以及如何 开发出一款 AI智能体应用,我们基于Langchain4j 和 SpringBoot 快速的实现了Cheee 助手。接着 使用了 MongoDB 作为聊天记忆持久化的解决方案,让我们的Cheese助手 具备了记忆功能,然后通过 Function Calling 的方式 集成了 计算服务和邮件服务两个功能,让Cheese 具备了精确计算能力和邮件服务的能力。
希望本文对大家进一步掌握AI智能体应用开发相关的技术有所帮助。