1. 项目概述

本文介绍如何使用 Spring Boot 框架集成 LangChain4j,结合通义千问大模型,构建一个具备对话记忆和工具调用能力的智能对话系统。系统支持普通对话和流式对话两种模式。

2. 项目依赖配置

2.1 Maven Pom.xml 配置

首先,在项目的 pom.xml 文件中添加必要的依赖:

<?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>
    
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.0</version>
        <relativePath/>
    </parent>
    
    <groupId>com.example</groupId>
    <artifactId>lost-system</artifactId>
    <version>1.0.0</version>
    <name>lostSystem</name>
    <description>基于LangChain4j的智能对话系统</description>
    
    <properties>
        <java.version>17</java.version>
        <langchain4j.version>1.0.0-beta3</langchain4j.version>
        <mybatis-plus.version>3.5.11</mybatis-plus.version>
    </properties>
    
    <dependencies>
        <!-- Spring Boot Starters -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        
        <!-- 数据库相关 -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        
        <!-- MyBatis-Plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot-3-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        
        <!-- LangChain4j 核心依赖 -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-spring-boot-starter</artifactId>
            <version>${langchain4j.version}</version>
        </dependency>
        
        <!-- LangChain4j 通义千问集成 -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId>
            <version>${langchain4j.version}</version>
        </dependency>
        
        <!-- LangChain4j Reactor支持 -->
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-reactor</artifactId>
            <version>${langchain4j.version}</version>
        </dependency>
        
        <!-- 工具类 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.16</version>
        </dependency>
        
        <!-- 开发工具 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        
        <!-- 测试依赖 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2.2 配置文件说明

创建 application.yml 配置文件:

spring:
  application:
    name: lostSystem
  
  # 数据源配置
  datasource:
    url: jdbc:mysql://localhost:3306/langchain4j?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
  
  # JPA配置
  jpa:
    show-sql: true
    properties:
      hibernate:
        format_sql: true
        dialect: org.hibernate.dialect.MySQL8Dialect

server:
  port: 8090

# LangChain4j 通义千问配置
langchain4j:
  community:
    dashscope:
      # 流式聊天模型配置
      streaming-chat-model:
        api-key: sk-你的API密钥
        model-name: qwen-max
        temperature: 0.7
        max-tokens: 2000
      
      # 普通聊天模型配置
      chat-model:
        api-key: sk-你的API密钥
        model-name: qwen-max
        temperature: 0.7
        max-tokens: 2000

# 日志配置
logging:
  level:
    org.hibernate.type.descriptor.sql: TRACE
    org.hibernate.SQL: DEBUG
    com.example.lostsystem: DEBUG

# MyBatis-Plus SQL日志配置
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3. 核心组件实现

3.1 AI服务接口定义

创建AI助手接口,支持普通对话和流式对话:

package com.example.lostsystem.service;

import dev.langchain4j.service.AiService;
import dev.langchain4j.service.AiServiceWiringMode;
import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.UserMessage;
import reactor.core.publisher.Flux;

/**
 * AI助手服务接口
 * wiringMode = EXPLICIT: 显式指定依赖注入
 */
@AiService(
    wiringMode = AiServiceWiringMode.EXPLICIT,
    chatModel = "qwenChatModel",           // 普通聊天模型Bean名称
    streamingChatModel = "qwenStreamingChatModel", // 流式聊天模型Bean名称
    chatMemoryProvider = "chatMemoryProvider",     // 对话记忆提供者
    tools = {"testTools"}                         // 工具类列表
)
public interface AiAssistant {
    
    /**
     * 普通对话接口
     * @param id 用户ID,用于对话记忆
     * @param message 用户消息
     * @return AI回复
     */
    String chat(@MemoryId String id, @UserMessage String message);
    
    /**
     * 流式对话接口
     * @param id 用户ID,用于对话记忆
     * @param message 用户消息
     * @return 流式回复
     */
    Flux<String> chatStream(@MemoryId String id, @UserMessage String message);
}

3.2 对话记忆配置

配置对话记忆,支持多轮对话上下文:

package com.example.lostsystem.config;

import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.store.memory.chat.ChatMemoryStore;
import dev.langchain4j.store.memory.chat.InMemoryChatMemoryStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 对话记忆配置类
 */
@Configuration
public class ChatMemoryConfig {

    /**
     * 配置对话记忆提供者
     * @return ChatMemoryProvider
     */
    @Bean
    public ChatMemoryProvider chatMemoryProvider() {
        return memoryId -> MessageWindowChatMemory.builder()
                .id(memoryId)
                .maxMessages(20)  // 保留最近20条消息
                .chatMemoryStore(chatMemoryStore())
                .build();
    }

    /**
     * 配置对话记忆存储(内存存储)
     * @return ChatMemoryStore
     */
    @Bean
    public ChatMemoryStore chatMemoryStore() {
        return new InMemoryChatMemoryStore();
    }
    
    /**
     * 全局对话记忆(可选)
     * @return ChatMemory
     */
    @Bean
    public ChatMemory globalChatMemory() {
        return MessageWindowChatMemory.withMaxMessages(10);
    }
}

3.3 工具类实现

创建AI可以调用的工具函数:

package com.example.lostsystem.tools;

import dev.langchain4j.agent.tool.Tool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

/**
 * 测试工具类
 * AI可以通过这些工具获取外部信息
 */
@Slf4j
@Component
public class TestTools {

    /**
     * 获取用户所在班级
     * @Tool注解让AI知道这是一个可调用的工具
     */
    @Tool("根据用户ID获取用户所在的班级信息")
    public String getUserClass(String userId) {
        log.info("获取用户 {} 所在的班级", userId);
        // 这里可以连接数据库查询真实数据
        // 示例:根据用户ID返回班级
        if ("111".equals(userId)) {
            return "计算机科学与技术1班";
        } else if ("222".equals(userId)) {
            return "软件工程2班";
        }
        return "未分配班级";
    }

    /**
     * 获取今天的天气信息
     */
    @Tool("获取指定城市的今日天气情况")
    public String getCurrentWeather(String city) {
        log.info("获取 {} 的天气信息", city);
        // 这里可以调用天气API
        // 示例返回
        if ("北京".equals(city)) {
            return "北京今天晴,温度15-25℃,空气质量良";
        } else if ("上海".equals(city)) {
            return "上海今天多云,温度18-26℃,空气质量优";
        }
        return city + "今天天气信息暂不可用";
    }
    
    /**
     * 获取当前日期
     */
    @Tool("获取当前系统日期")
    public String getCurrentDate() {
        return LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日"));
    }
    
    /**
     * 计算两个数字的和
     */
    @Tool("计算两个数字的和")
    public String calculateSum(int a, int b) {
        int result = a + b;
        return a + " + " + b + " = " + result;
    }
}

3.4 控制器实现

创建RESTful API接口:

package com.example.lostsystem.controller;

import com.example.lostsystem.service.AiAssistant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;

/**
 * AI对话控制器
 */
@Slf4j
@RestController
@RequestMapping("/api/ai")
public class AiController {

    @Autowired
    private AiAssistant aiAssistant;

    /**
     * 普通对话接口
     * GET /api/ai/chat?userId=123&message=你好
     */
    @GetMapping("/chat")
    public String chat(
            @RequestParam(value = "userId", defaultValue = "default_user") String userId,
            @RequestParam(value = "message", defaultValue = "你好") String message) {
        
        log.info("收到普通对话请求 - 用户ID: {}, 消息: {}", userId, message);
        
        try {
            String response = aiAssistant.chat(userId, message);
            log.info("AI回复: {}", response);
            return response;
        } catch (Exception e) {
            log.error("对话处理失败", e);
            return "抱歉,AI服务暂时不可用,请稍后重试。";
        }
    }

    /**
     * 流式对话接口
     * GET /api/ai/chat/stream?userId=123&message=你好
     */
    @GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> chatStream(
            @RequestParam(value = "userId", defaultValue = "default_user") String userId,
            @RequestParam(value = "message", defaultValue = "你好") String message) {
        
        log.info("收到流式对话请求 - 用户ID: {}, 消息: {}", userId, message);
        
        return aiAssistant.chatStream(userId, message)
                .doOnNext(chunk -> log.debug("流式响应片段: {}", chunk))
                .doOnError(error -> log.error("流式对话失败", error))
                .onErrorReturn("流式对话中断,请重试。");
    }
    
    /**
     * POST方式对话接口
     */
    @PostMapping("/chat")
    public String chatPost(@RequestBody ChatRequest request) {
        return chat(request.getUserId(), request.getMessage());
    }
    
    /**
     * 对话请求DTO
     */
    public static class ChatRequest {
        private String userId;
        private String message;
        
        // getters and setters
        public String getUserId() { return userId; }
        public void setUserId(String userId) { this.userId = userId; }
        public String getMessage() { return message; }
        public void setMessage(String message) { this.message = message; }
    }
}

3.5 应用启动类

package com.example.lostsystem;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

/**
 * 应用启动类
 */
@SpringBootApplication
@EnableJpaAuditing
public class LostSystemApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(LostSystemApplication.class, args);
        System.out.println("""
                ========================================
                LostSystem 智能对话系统启动成功!
                访问地址:http://localhost:8090
                接口文档:
                  普通对话:GET /api/ai/chat?userId=xxx&message=xxx
                  流式对话:GET /api/ai/chat/stream?userId=xxx&message=xxx
                ========================================
                """);
    }
}

4. 使用示例

4.1 启动应用

  1. 确保MySQL数据库已启动,并创建 langchain4j 数据库
  2. application.yml 中配置正确的数据库连接信息和通义千问API密钥
  3. 运行 LostSystemApplication 启动应用

4.2 测试接口

普通对话测试

# 使用curl测试
curl "http://localhost:8090/api/ai/chat?userId=123&message=你好,我是张三"

# 或使用浏览器访问
http://localhost:8090/api/ai/chat?userId=123&message=今天天气怎么样?

流式对话测试

# 使用curl测试流式响应
curl -N "http://localhost:8090/api/ai/chat/stream?userId=123&message=介绍一下Java"

# 或使用支持SSE的客户端

4.3 工具调用示例

AI会自动识别何时需要调用工具:

用户:我在哪个班级?
AI:正在为您查询班级信息...(调用getUserClass工具)
回复:您所在的班级是计算机科学与技术1班。

用户:今天北京天气如何?
AI:正在查询北京天气...(调用getCurrentWeather工具)
回复:北京今天晴,温度15-25℃,空气质量良。

5. 项目结构说明

lost-system/
├── src/main/java/com/example/lostsystem/
│   ├── LostSystemApplication.java      # 启动类
│   ├── config/
│   │   └── ChatMemoryConfig.java       # 对话记忆配置
│   ├── controller/
│   │   └── AiController.java           # 控制器
│   ├── service/
│   │   └── AiAssistant.java            # AI服务接口
│   ├── tools/
│   │   └── TestTools.java              # 工具类
│   └── entity/                         # 实体类(可选)
├── src/main/resources/
│   ├── application.yml                 # 配置文件
│   └── static/                         # 静态资源
└── pom.xml                             # Maven配置

6. 常见问题解决

6.1 API密钥配置

确保在 application.yml 中正确配置通义千问API密钥:

langchain4j:
  community:
    dashscope:
      chat-model:
        api-key: sk-你的真实API密钥

6.2 数据库连接失败

检查MySQL服务是否启动,数据库是否存在:

-- 创建数据库
CREATE DATABASE langchain4j CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐