在 AI 应用开发中,LangChain4j 是连接大模型与业务系统的重要工具。本文将完整演示如何基于 Spring Boot 整合 LangChain4j,实现通义千问模型调用、MySQL 持久化会话记忆、工具集成等核心功能,提供可直接复用的代码模板。

一、核心依赖配置(pom.xml)

首先需配置 Maven 依赖,核心包含 LangChain4j 相关组件(通义千问接入、Ollama 支持、Spring Boot starter)、Spring Boot 基础组件、MySQL 驱动及 MyBatis-Plus 等。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.nie</groupId>
    <artifactId>langchaini4j-ai</artifactId>
    <version>1.0.0</version>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>17</source>
                    <target>17</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <spring-boot.version>3.5.0</spring-boot.version>
        <knife4j.version>4.3.0</knife4j.version>
        <langchain4j.version>1.0.0-beta3</langchain4j.version>
        <mybatis-plus.version>3.5.11</mybatis-plus.version>
    </properties>

    <dependencies>
        <!-- Spring Boot 基础 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- 接口文档(Knife4j) -->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId>
            <version>${knife4j.version}</version>
        </dependency>

        <!-- LangChain4j 核心 -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>
        </dependency>

        <!-- LangChain4j 模型接入 -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-ollama-spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId>
        </dependency>

        <!-- 数据持久化 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.33</version>
        </dependency>

        <!-- Redis 缓存 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!-- JSON 处理 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.15.2</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.15.2</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
            <version>2.15.2</version>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <!-- Spring Boot 依赖管理 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- LangChain4j 依赖管理 -->
            <dependency>
                <groupId>dev.langchain4j</groupId>
                <artifactId>langchain4j-bom</artifactId>
                <version>1.0.0-beta3</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>
        </dependencies>
    </dependencyManagement>
</project>

二、通义千问模型配置(application.yml)

通过配置文件指定通义千问模型名称,并从本地环境变量读取 API Key(避免硬编码泄露风险)。

langchain4j:
  community:
    dashscope:
      chat-model:
        api-key: ${MY_API_KEY}  # 本地环境变量中配置 API Key
        model-name: qwen-max    # 通义千问模型名称(可替换为 qwen-plus 等)

三、核心业务代码实现

3.1 对话接口定义(Assistant)

定义 AI 对话接口,支持两种对话模式:基础对话和带用户信息的个性化对话,通过注解指定系统提示词。

import dev.langchain4j.agent.tool.P;
import dev.langchain4j.agent.tool.V;
import dev.langchain4j.memory.MemoryId;
import dev.langchain4j.model.input.PromptTemplate;
import dev.langchain4j.model.input.system.SystemMessage;
import dev.langchain4j.model.input.user.UserMessage;

public interface Assistant {

    /**
     * 基础对话(返回 Markdown 格式)
     * @param memoryId 会话 ID(用于隔离不同用户会话)
     * @param question 用户问题
     * @return 模型回答(Markdown 格式)
     */
    @SystemMessage("请以 Markdown 格式返回回答,确保内容清晰、结构合理")
    String chat(@MemoryId int memoryId, @UserMessage String question);

    /**
     * 个性化对话(携带用户信息)
     * @param memoryId 会话 ID
     * @param question 用户问题
     * @param username 用户名(从数据库查询)
     * @param age 用户年龄(从数据库查询)
     * @return 个性化回答
     */
    @SystemMessage(fromResource = "system-prompt1.txt")  // 从资源文件读取系统提示词
    String chatInfo(@MemoryId int memoryId,
                   @UserMessage String question,
                   @V("username") String username,
                   @V("age") int age);
}

system-prompt1.txt:

我是{{username}}
我的年龄是{{age}}
{{current_date}}表示当前日期

3.2 会话记忆配置(AssistantChatMemoryConfig)

配置 AI 服务,指定模型、会话存储方式(MySQL)和工具集成(如计算器),实现会话隔离。

import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.dashscope.QwenChatModel;
import dev.langchain4j.spring.ai.AiServices;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;

@Configuration
public class AssistantChatMemoryConfig {

    @Resource
    private QwenChatModel qwenChatModel;  // 通义千问模型实例

    @Resource
    private MysqlChatMemoryStore mysqlChatMemoryStore;  // MySQL 会话存储

    @Resource
    private CalculatorTools calculatorTools;  // 集成的工具(如计算器)

    @Bean
    public Assistant assistant() {
        return AiServices.builder(Assistant.class)
                .chatLanguageModel(qwenChatModel)  // 指定大模型
                // 会话隔离:为每个会话 ID 创建独立记忆
                .chatMemoryProvider(memoryId -> MessageWindowChatMemory
                        .builder()
                        .id(memoryId)
                        .chatMemoryStore(mysqlChatMemoryStore)  // 自定义 MySQL 存储
                        .maxMessages(20)  // 每个会话最大消息数
                        .build())
                .tools(calculatorTools)  // 集成工具
                .build();
    }
}

3.3 MySQL 会话存储实现(MysqlChatMemoryStore)

自定义 ChatMemoryStore 接口实现,将会话消息序列化为 JSON 存储到 MySQL,支持增删改查。

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import dev.langchain4j.memory.chat.ChatMemoryStore;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.serializer.Json;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.List;

@Slf4j
@Component
public class MysqlChatMemoryStore implements ChatMemoryStore {

    @Resource
    private ChatMemoryStoreMapper chatMemoryStoreMapper;  // MyBatis Mapper 接口

    /**
     * 读取会话消息
     */
    @Override
    public List<ChatMessage> getMessages(Object memoryId) {
        try {
            ChatMessages chatMessages = chatMemoryStoreMapper.getMessages(memoryId.toString());
            if (chatMessages == null || chatMessages.getContent() == null) {
                return List.of();  // 无消息时返回空列表
            }
            // JSON 反序列化为 ChatMessage 列表
            log.info("查询会话 {} 消息:{}", memoryId, chatMessages.getContent());
            return Json.fromJson(chatMessages.getContent(), List.class);
        } catch (Exception e) {
            log.error("读取会话 {} 消息失败", memoryId, e);
            return List.of();
        }
    }

    /**
     * 更新会话消息(新增/修改)
     */
    @Override
    public void updateMessages(Object memoryId, List<ChatMessage> messages) {
        try {
            // ChatMessage 列表序列化为 JSON
            String messagesJson = Json.toJson(messages);
            log.info("会话 {} 待存储消息:{}", memoryId, messagesJson);

            ChatMessages chatMessages = chatMemoryStoreMapper.getMessages(memoryId.toString());
            if (chatMessages == null) {
                // 新增会话记录
                ChatMessages newRecord = new ChatMessages();
                newRecord.setMessageId(memoryId.toString());
                newRecord.setContent(messagesJson);
                chatMemoryStoreMapper.save(newRecord);
            } else {
                // 更新已有会话记录
                chatMessages.setContent(messagesJson);
                chatMemoryStoreMapper.updateData(chatMessages);
            }
        } catch (Exception e) {
            log.error("更新会话 {} 消息失败", memoryId, e);
            throw new RuntimeException("会话消息存储失败", e);
        }
    }

    /**
     * 删除会话消息
     */
    @Override
    public void deleteMessages(Object memoryId) {
        try {
            chatMemoryStoreMapper.deleteByMemoryId(memoryId.toString());
            log.info("删除会话 {} 消息成功", memoryId);
        } catch (Exception e) {
            log.error("删除会话 {} 消息失败", memoryId, e);
            throw new RuntimeException("会话消息删除失败", e);
        }
    }

    // 会话消息实体类(与数据库表映射)
    @Data
    public static class ChatMessages {
        private Integer id;             // 主键
        private String messageId;       // 会话 ID(对应 memoryId)
        private String content;         // 会话消息 JSON
    }
}

3.4 Mapper 接口与 XML(数据访问层)

定义 MyBatis Mapper 接口及 XML 文件,实现 MySQL 数据操作。

3.4.1 ChatMemoryStoreMapper 接口
import com.nie.ai.langchain4j.config.MysqlChatMemoryStore.ChatMessages;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface ChatMemoryStoreMapper {

    // 查询会话消息
    ChatMessages getMessages(String memoryId);

    // 新增会话消息
    void save(ChatMessages chatMessages);

    // 更新会话消息
    void updateData(ChatMessages chatMessages);

    // 删除会话消息
    void deleteByMemoryId(String memoryId);
}
3.4.2 Mapper XML(ChatMemoryStoreMapper.xml)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.nie.ai.langchain4j.mapper.ChatMemoryStoreMapper">

    <!-- 结果映射 -->
    <resultMap id="ChatMessagesResultMap" type="com.nie.ai.langchain4j.config.MysqlChatMemoryStore$ChatMessages">
        <id column="id" property="id" />
        <result column="message_id" property="messageId" />
        <result column="content" property="content" />
    </resultMap>

    <!-- 新增 -->
    <insert id="save">
        INSERT INTO chat_messages (message_id, content)
        VALUES (#{messageId}, #{content})
    </insert>

    <!-- 更新 -->
    <update id="updateData">
        UPDATE chat_messages 
        SET content = #{content}
        WHERE message_id = #{messageId}
    </update>

    <!-- 删除 -->
    <delete id="deleteByMemoryId">
        DELETE FROM chat_messages 
        WHERE message_id = #{memoryId}
    </delete>

    <!-- 查询 -->
    <select id="getMessages" resultMap="ChatMessagesResultMap">
        SELECT id, message_id, content
        FROM chat_messages
        WHERE message_id = #{memoryId}
    </select>
</mapper>

3.5 控制器(ChatController)

提供 HTTP 接口,接收前端请求并调用 Assistant 服务处理对话。

import com.nie.ai.langchain4j.config.Assistant;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@Tag(name = "Agent测试")
@RestController
@Slf4j
public class ChatController {

    @Resource
    private Assistant assistant;

    /**
     * 基础对话接口
     */
    @PostMapping("/chat")
    public String chat(@RequestParam("memoryId") int memoryId, 
                      @RequestParam("question") String question) {
        log.info("接收会话 {} 请求:{}", memoryId, question);
        return assistant.chat(memoryId, question);
    }

    /**
     * 个性化对话接口(接收 JSON 参数)
     */
    @Operation(summary = "个性化对话")
    @PostMapping("/chatInfo")
    public String chatInfo(@RequestBody ChatRequest chatRequest) {
        return assistant.chatInfo(chatRequest.getMemoryId(), 
                                 chatRequest.getQuestion());
    }
}

四、数据库表结构(MySQL)

创建 chat_messages 表,用于存储会话消息 JSON 数据。

CREATE TABLE `chat_messages` (
  `id` int NOT NULL AUTO_INCREMENT,
  `messageId` int DEFAULT NULL,
  `content` text,
  `create_time` datetime DEFAULT NULL,
  `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

五、关键注意事项

  1. API Key 安全:务必通过环境变量 MY_API_KEY 配置,禁止硬编码到代码或配置文件中。
  2. 会话隔离:通过 memoryId 区分不同用户会话,避免消息混淆。
  3. 消息序列化:使用 LangChain4j 自带的 Json 工具类(或 Jackson)实现 ChatMessage 与 JSON 的转换,确保格式正确。
  4. 异常处理:会话存储的增删改查操作需添加异常捕获,避免单个会话错误影响整体服务。
  5. 依赖版本:LangChain4j 处于快速迭代阶段,需注意 langchain4j.version 与 Spring Boot 版本的兼容性(本文使用 1.0.0-beta3 + Spring Boot 3.5.0)。

六、功能测试

  1. 配置环境变量:在本地终端设置 MY_API_KEY(通义千问 API Key,需从阿里云 DashScope 平台申请)。
    # Windows
    set MY_API_KEY=your_api_key
    # Linux/Mac
    export MY_API_KEY=your_api_key
    
  2. 启动服务:运行 Spring Boot 应用,通过 Knife4j 文档(http://localhost:8080/doc.html)测试 /chat 和 /chatInfo 接口。
  3. 验证存储:发送请求后,查看 chat_messages 表,确认会话消息 JSON 已正确存储。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐