LangChain4j 集成若依单体应用 | 5 大 AI 功能实战:多轮对话、流式输出、RAG 知识库

用 LangChain4j 开发 AI 应用并集成若依单体 | 从 0 到 1 实战指南

还在手写 HTTP 调用 OpenAI?LangChain4j 让 Java 开发 AI 应用像写普通业务代码一样简单。本文从 LangChain4j 核心概念出发,手把手带你完成若依单体应用集成,实现智能对话、RAG 知识库问答、流式输出、Function Calling 等核心功能,附完整代码


一、为什么需要 LangChain4j?

1.1 没有框架时的痛点

java 复制代码
/**
 * 传统方式:手写 HTTP 调用 OpenAI
 * 问题:代码繁琐、难以维护、缺少上下文管理
 */
public String chat(String message) {
    // 手动构建请求体
    String requestBody = """
        {
            "model": "gpt-4o",
            "messages": [
                {"role": "user", "content": "%s"}
            ]
        }
        """.formatted(message);

    // 手动发 HTTP 请求
    HttpClient client = HttpClient.newHttpClient();
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create("https://api.openai.com/v1/chat/completions"))
        .header("Authorization", "Bearer " + apiKey)
        .header("Content-Type", "application/json")
        .POST(HttpRequest.BodyPublishers.ofString(requestBody))
        .build();

    // 手动解析响应
    HttpResponse<String> response = client.send(request, ...);
    JSONObject json = JSON.parseObject(response.body());
    return json.getJSONArray("choices")
               .getJSONObject(0)
               .getJSONObject("message")
               .getString("content");
    // 还没有:对话历史、RAG、工具调用、流式输出...
}

问题一大堆:

  • 每次调用都要手写 HTTP 请求
  • 没有对话历史管理(多轮对话)
  • 没有 RAG(知识库问答)
  • 没有 Function Calling(工具调用)
  • 切换模型(OpenAI → 通义千问)需要大改代码

1.2 LangChain4j 解决了什么?

java 复制代码
/**
 * 使用 LangChain4j:声明式 AI 服务
 * 一个接口,搞定一切
 */
@AiService
public interface CustomerAssistant {

    @SystemMessage("你是若依系统的智能客服,请用中文回答用户问题")
    String chat(@MemoryId String userId, @UserMessage String message);
}

// 使用
@Autowired
CustomerAssistant assistant;

String reply = assistant.chat("user123", "如何重置密码?");
// 自动管理对话历史、自动调用 LLM、自动返回结果

1.3 LangChain4j 是什么?

LangChain4j 是 Java 版的 LangChain,是目前 Java 生态中最成熟的 AI 应用开发框架。

核心能力:

能力 说明
多模型支持 OpenAI、通义千问、文心一言、Ollama 本地模型等 30+
AI Services 声明式接口,像写 Service 一样开发 AI 功能
对话记忆 自动管理多轮对话历史
RAG 检索增强生成,知识库问答
Function Calling 让 AI 调用你的 Java 方法
流式输出 逐字输出,提升用户体验
Embedding 文本向量化,支持语义搜索

二、LangChain4j 核心概念

2.1 整体架构

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                    LangChain4j 核心架构                              │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                    AI Services(高层 API)                    │   │
│  │  @AiService 声明式接口,自动组装所有组件                      │   │
│  └──────────────────────────┬──────────────────────────────────┘   │
│                             │                                       │
│  ┌──────────────────────────┼──────────────────────────────────┐   │
│  │                          │                                   │   │
│  │  ┌────────────┐  ┌───────┴──────┐  ┌────────────────────┐  │   │
│  │  │  ChatModel  │  │  ChatMemory  │  │  Retriever(RAG)  │  │   │
│  │  │  (LLM 调用) │  │  (对话历史)  │  │  (知识库检索)      │  │   │
│  │  └────────────┘  └──────────────┘  └────────────────────┘  │   │
│  │                                                              │   │
│  │  ┌────────────┐  ┌──────────────┐  ┌────────────────────┐  │   │
│  │  │   Tools    │  │  Embedding   │  │  OutputParser      │  │   │
│  │  │ (工具调用)  │  │  (向量化)    │  │  (结构化输出)      │  │   │
│  │  └────────────┘  └──────────────┘  └────────────────────┘  │   │
│  │                                                              │   │
│  └──────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │                    底层集成(30+ 模型)                       │   │
│  │  OpenAI | 通义千问 | 文心一言 | Ollama | Azure OpenAI | ...  │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

2.2 核心组件说明

组件 作用
ChatModel 与 LLM 交互的核心接口
ChatMemory 管理对话历史(多轮对话)
EmbeddingModel 将文本转为向量
EmbeddingStore 向量数据库(存储和检索向量)
ContentRetriever RAG 检索器,从知识库检索相关内容
Tools 工具定义,让 AI 调用 Java 方法
AI Services 高层声明式 API,自动组装上述组件

三、环境准备

3.1 版本要求

环境 要求
JDK 17+(LangChain4j 1.x 最低要求)
Spring Boot 2.7.x 或 3.x
LangChain4j 1.12.2(本文使用)
若依单体 3.8.x

⚠️ 注意:LangChain4j 1.x 要求 JDK 17+,若依默认是 JDK 8,需要升级或使用 LangChain4j 0.36.x(支持 JDK 8)

3.2 API Key 准备

本文支持两种模型,任选其一:

方案一:OpenAI(需要科学上网)

方案二:通义千问(国内推荐,免费额度)


四、若依单体集成 LangChain4j

4.1 添加依赖

在若依单体的 pom.xml 中添加:

xml 复制代码
<!-- LangChain4j BOM(统一版本管理) -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>dev.langchain4j</groupId>
            <artifactId>langchain4j-bom</artifactId>
            <version>1.12.2</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <!-- LangChain4j 核心 -->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-spring-boot-starter</artifactId>
        <version>1.12.2-beta22</version>
    </dependency>

    <!-- 方案一:OpenAI -->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>
        <version>1.12.2-beta22</version>
    </dependency>

    <!-- 方案二:通义千问(二选一) -->
    <!--
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-community-dashscope-spring-boot-starter</artifactId>
        <version>1.12.2-beta22</version>
    </dependency>
    -->

    <!-- 向量数据库(RAG 用,本文使用内存版) -->
    <dependency>
        <groupId>dev.langchain4j</groupId>
        <artifactId>langchain4j-embeddings-all-minilm-l6-v2</artifactId>
    </dependency>
</dependencies>

4.2 配置文件

application.yml 中添加:

yaml 复制代码
# ===== LangChain4j 配置 =====
langchain4j:
  # 方案一:OpenAI 配置
  open-ai:
    chat-model:
      api-key: ${OPENAI_API_KEY:your-api-key-here}
      model-name: gpt-4o-mini
      temperature: 0.7
      max-tokens: 2048
      log-requests: true
      log-responses: true
    # 流式输出
    streaming-chat-model:
      api-key: ${OPENAI_API_KEY:your-api-key-here}
      model-name: gpt-4o-mini
      temperature: 0.7

  # 方案二:通义千问配置(二选一)
  # community:
  #   dashscope:
  #     chat-model:
  #       api-key: ${DASHSCOPE_API_KEY:your-api-key-here}
  #       model-name: qwen-plus
  #       temperature: 0.7

💡 推荐:API Key 不要写死在配置文件里,使用环境变量或 Nacos 配置中心管理

4.3 创建 AI 模块目录结构

在若依单体中创建 AI 相关目录:

复制代码
ruoyi-admin/
└── src/main/java/com/ruoyi/
    └── ai/
        ├── config/
        │   └── LangChain4jConfig.java       # AI 配置类
        ├── service/
        │   ├── ChatAssistant.java            # 基础对话 AI Service
        │   ├── CustomerAssistant.java        # 客服 AI Service
        │   └── RagAssistant.java             # RAG 知识库 AI Service
        ├── tools/
        │   └── RuoYiTools.java               # 若依系统工具(供 AI 调用)
        ├── controller/
        │   └── AiChatController.java         # 对话接口
        └── domain/
            └── ChatRequest.java              # 请求 DTO

五、功能一:基础对话

5.1 定义 AI Service

java 复制代码
package com.ruoyi.ai.service;

import dev.langchain4j.service.AiService;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;

/**
 * 基础对话 AI Service
 * 声明式接口,LangChain4j 自动实现
 */
@AiService
public interface ChatAssistant {

    /**
     * 简单对话(无记忆)
     */
    @SystemMessage("你是若依管理系统的智能助手,请用简洁专业的中文回答问题。")
    String chat(@UserMessage String message);
}

5.2 Controller 层

java 复制代码
package com.ruoyi.ai.controller;

import com.ruoyi.ai.service.ChatAssistant;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * AI 对话接口
 */
@RestController
@RequestMapping("/ai")
public class AiChatController extends BaseController {

    @Autowired
    private ChatAssistant chatAssistant;

    /**
     * 基础对话
     */
    @PostMapping("/chat")
    public AjaxResult chat(@RequestParam String message) {
        String reply = chatAssistant.chat(message);
        return AjaxResult.success(reply);
    }
}

5.3 测试

bash 复制代码
curl -X POST "http://localhost:8080/ai/chat" \
  -d "message=你好,请介绍一下若依框架"

响应:

json 复制代码
{
  "code": 200,
  "msg": "操作成功",
  "data": "若依(RuoYi)是一款基于 Spring Boot 的权限管理系统框架,提供了用户管理、角色管理、菜单管理等基础功能..."
}

六、功能二:多轮对话(带记忆)

6.1 配置对话记忆

java 复制代码
package com.ruoyi.ai.config;

import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * LangChain4j 配置类
 */
@Configuration
public class LangChain4jConfig {

    /**
     * 对话记忆提供者
     * 每个用户(memoryId)独立维护一份对话历史
     */
    @Bean
    public ChatMemoryProvider chatMemoryProvider() {
        return memoryId -> MessageWindowChatMemory.builder()
            .id(memoryId)
            .maxMessages(20)  // 最多保留最近 20 条消息
            .build();
    }
}

6.2 带记忆的 AI Service

java 复制代码
package com.ruoyi.ai.service;

import dev.langchain4j.service.AiService;
import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;

/**
 * 带记忆的对话 AI Service
 * 支持多轮对话,每个用户独立维护对话历史
 */
@AiService
public interface MemoryChatAssistant {

    @SystemMessage("""
        你是若依管理系统的智能客服助手。
        你可以帮助用户解答系统使用问题、功能介绍等。
        请用友好、专业的中文回答。
        """)
    String chat(
        @MemoryId String userId,      // 用户ID,用于区分不同用户的对话历史
        @UserMessage String message   // 用户消息
    );
}

6.3 Controller 集成

java 复制代码
/**
 * 多轮对话接口
 */
@PostMapping("/chat/memory")
public AjaxResult chatWithMemory(@RequestParam String message) {
    // 获取当前登录用户ID,作为记忆隔离的 key
    Long userId = SecurityUtils.getUserId();
    String reply = memoryChatAssistant.chat(String.valueOf(userId), message);
    return AjaxResult.success(reply);
}

效果演示:

复制代码
用户:我叫张三
AI:你好,张三!有什么我可以帮助你的吗?

用户:我叫什么名字?
AI:你叫张三!(AI 记住了上下文)

用户:若依系统如何添加菜单?
AI:在若依系统中,添加菜单的步骤如下...

七、功能三:流式输出(打字机效果)

7.1 定义流式 AI Service

java 复制代码
package com.ruoyi.ai.service;

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

/**
 * 流式输出 AI Service
 * 返回 Flux<String>,逐字输出
 */
@AiService
public interface StreamingChatAssistant {

    @SystemMessage("你是若依系统的智能助手,请用中文回答。")
    Flux<String> chat(
        @MemoryId String userId,
        @UserMessage String message
    );
}

7.2 SSE 流式接口

java 复制代码
package com.ruoyi.ai.controller;

import com.ruoyi.ai.service.StreamingChatAssistant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import reactor.core.publisher.Flux;

/**
 * 流式对话接口(SSE)
 */
@RestController
@RequestMapping("/ai")
public class AiStreamController {

    @Autowired
    private StreamingChatAssistant streamingChatAssistant;

    /**
     * 流式对话(Server-Sent Events)
     * 前端使用 EventSource 接收
     */
    @GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter streamChat(@RequestParam String message,
                                  @RequestParam(defaultValue = "default") String userId) {
        SseEmitter emitter = new SseEmitter(60_000L);  // 60秒超时

        streamingChatAssistant.chat(userId, message)
            .subscribe(
                // 每个 token 发送一次
                token -> {
                    try {
                        emitter.send(SseEmitter.event()
                            .data(token)
                            .name("message"));
                    } catch (Exception e) {
                        emitter.completeWithError(e);
                    }
                },
                // 错误处理
                emitter::completeWithError,
                // 完成
                () -> {
                    try {
                        emitter.send(SseEmitter.event()
                            .data("[DONE]")
                            .name("done"));
                        emitter.complete();
                    } catch (Exception e) {
                        emitter.completeWithError(e);
                    }
                }
            );

        return emitter;
    }
}

7.3 前端接收流式输出

javascript 复制代码
/**
 * 前端:使用 EventSource 接收流式输出
 * 实现打字机效果
 */
function streamChat(message) {
    const resultDiv = document.getElementById('result');
    resultDiv.innerHTML = '';

    const url = `/ai/chat/stream?message=${encodeURIComponent(message)}&userId=${userId}`;
    const eventSource = new EventSource(url);

    // 接收每个 token
    eventSource.addEventListener('message', (event) => {
        resultDiv.innerHTML += event.data;
    });

    // 接收完成信号
    eventSource.addEventListener('done', (event) => {
        eventSource.close();
        console.log('对话完成');
    });

    // 错误处理
    eventSource.onerror = (error) => {
        console.error('SSE 错误:', error);
        eventSource.close();
    };
}

八、功能四:RAG 知识库问答

8.1 什么是 RAG?

复制代码
┌─────────────────────────────────────────────────────────────────────┐
│                    RAG(检索增强生成)原理                           │
├─────────────────────────────────────────────────────────────────────┤
│                                                                     │
│  传统 LLM 问答:                                                    │
│  用户问题 → LLM → 回答(只有训练数据,不了解你的业务)              │
│                                                                     │
│  RAG 问答:                                                         │
│  用户问题 → 向量检索(从知识库找相关内容)→ 拼接到 Prompt → LLM → 回答│
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  知识库构建(离线):                                         │   │
│  │  文档 → 分块 → 向量化 → 存入向量数据库                       │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
│  ┌─────────────────────────────────────────────────────────────┐   │
│  │  问答流程(在线):                                           │   │
│  │  用户问题 → 向量化 → 相似度检索 → 取 Top-K 文档              │   │
│  │  → 拼接到 Prompt → LLM 生成回答                              │   │
│  └─────────────────────────────────────────────────────────────┘   │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

8.2 构建知识库

java 复制代码
package com.ruoyi.ai.config;

import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.DocumentSplitter;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.embedding.onnx.allminilml6v2.AllMiniLmL6V2EmbeddingModel;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.List;

/**
 * RAG 知识库配置
 */
@Configuration
public class RagConfig {

    /**
     * 向量化模型(本地,无需 API Key)
     * 使用 all-MiniLM-L6-v2,轻量高效
     */
    @Bean
    public EmbeddingModel embeddingModel() {
        return new AllMiniLmL6V2EmbeddingModel();
    }

    /**
     * 向量数据库(内存版,生产环境建议使用 Milvus/Qdrant/PgVector)
     */
    @Bean
    public EmbeddingStore<TextSegment> embeddingStore() {
        return new InMemoryEmbeddingStore<>();
    }

    /**
     * 初始化知识库(应用启动时加载文档)
     */
    @Bean
    public EmbeddingStoreIngestor embeddingStoreIngestor(
            EmbeddingModel embeddingModel,
            EmbeddingStore<TextSegment> embeddingStore) {

        // 文档分块器:每块 500 字符,重叠 50 字符
        DocumentSplitter splitter = DocumentSplitters.recursive(500, 50);

        return EmbeddingStoreIngestor.builder()
            .documentSplitter(splitter)
            .embeddingModel(embeddingModel)
            .embeddingStore(embeddingStore)
            .build();
    }
}

8.3 加载若依系统文档

java 复制代码
package com.ruoyi.ai.service;

import dev.langchain4j.data.document.Document;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * 应用启动时加载知识库文档
 */
@Component
public class KnowledgeBaseLoader implements ApplicationRunner {

    @Autowired
    private EmbeddingStoreIngestor ingestor;

    @Override
    public void run(ApplicationArguments args) {
        // 加载若依系统使用手册(可以从数据库、文件、URL 加载)
        List<Document> documents = List.of(
            Document.from("""
                若依系统用户管理:
                1. 进入系统管理 → 用户管理
                2. 点击新增按钮,填写用户信息
                3. 用户名、昵称为必填项
                4. 密码默认为 admin123
                5. 可以为用户分配角色
                """),
            Document.from("""
                若依系统菜单管理:
                1. 进入系统管理 → 菜单管理
                2. 菜单类型分为:目录、菜单、按钮
                3. 目录:一级菜单,不对应具体页面
                4. 菜单:对应具体的页面路由
                5. 按钮:页面内的操作权限控制
                6. 路由地址填写前端路由路径
                7. 组件路径填写 Vue 组件相对路径
                """),
            Document.from("""
                若依系统角色管理:
                1. 进入系统管理 → 角色管理
                2. 新增角色,填写角色名称和权限字符
                3. 为角色分配菜单权限
                4. 在用户管理中为用户分配角色
                5. 数据权限:全部、本部门、本部门及以下、仅本人
                """),
            Document.from("""
                若依系统常见问题:
                Q: 如何重置用户密码?
                A: 在用户管理列表,点击操作列的重置密码按钮,输入新密码即可。
                
                Q: 如何修改系统 Logo?
                A: 在系统配置中修改 Logo 路径,或直接替换 /profile/upload 目录下的图片。
                
                Q: 如何开启验证码?
                A: 在 application.yml 中设置 ruoyi.captchaEnabled=true
                """)
        );

        // 将文档向量化并存入向量数据库
        ingestor.ingest(documents);
        System.out.println("✅ 若依知识库加载完成,共 " + documents.size() + " 个文档");
    }
}

8.4 定义 RAG AI Service

java 复制代码
package com.ruoyi.ai.service;

import dev.langchain4j.service.AiService;
import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;

/**
 * RAG 知识库问答 AI Service
 * 自动从知识库检索相关内容,增强回答质量
 */
@AiService
public interface RagAssistant {

    @SystemMessage("""
        你是若依管理系统的专业客服助手。
        请根据提供的知识库内容回答用户问题。
        如果知识库中没有相关信息,请如实告知用户,不要编造答案。
        回答要简洁、准确、友好。
        """)
    String chat(
        @MemoryId String userId,
        @UserMessage String question
    );
}

8.5 注册 RAG 检索器

java 复制代码
package com.ruoyi.ai.config;

import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.store.embedding.EmbeddingStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RagConfig {

    /**
     * RAG 内容检索器
     * 自动注入到 @AiService 中
     */
    @Bean
    public ContentRetriever contentRetriever(
            EmbeddingStore<TextSegment> embeddingStore,
            EmbeddingModel embeddingModel) {

        return EmbeddingStoreContentRetriever.builder()
            .embeddingStore(embeddingStore)
            .embeddingModel(embeddingModel)
            .maxResults(3)          // 最多检索 3 个相关片段
            .minScore(0.6)          // 相似度阈值(0-1)
            .build();
    }
}

8.6 RAG 接口

java 复制代码
/**
 * RAG 知识库问答接口
 */
@PostMapping("/chat/rag")
public AjaxResult ragChat(@RequestParam String question) {
    Long userId = SecurityUtils.getUserId();
    String answer = ragAssistant.chat(String.valueOf(userId), question);
    return AjaxResult.success(answer);
}

效果演示:

复制代码
用户:如何重置密码?
AI:在若依系统中,重置用户密码的步骤如下:
    1. 进入系统管理 → 用户管理
    2. 在用户列表中找到需要重置密码的用户
    3. 点击操作列的"重置密码"按钮
    4. 输入新密码并确认即可
    (AI 基于知识库内容回答,而非凭空生成)

九、功能五:Function Calling(工具调用)

9.1 什么是 Function Calling?

复制代码
传统 AI:只能回答文字问题
Function Calling:AI 可以调用你的 Java 方法,获取实时数据

例如:
用户:现在系统有多少个用户?
AI:(调用 getUserCount() 方法)→ 系统目前共有 1234 个用户

9.2 定义工具类

java 复制代码
package com.ruoyi.ai.tools;

import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.system.service.ISysMenuService;
import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.agent.tool.P;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 若依系统工具集
 * AI 可以调用这些方法获取实时数据
 */
@Component
public class RuoYiTools {

    @Autowired
    private ISysUserService userService;

    @Autowired
    private ISysMenuService menuService;

    /**
     * 获取系统用户数量
     */
    @Tool("获取系统当前用户总数")
    public String getUserCount() {
        int count = userService.selectUserList(new SysUser()).size();
        return "系统当前共有 " + count + " 个用户";
    }

    /**
     * 根据用户名查询用户信息
     */
    @Tool("根据用户名查询用户信息")
    public String getUserByName(@P("用户名") String userName) {
        SysUser user = userService.selectUserByUserName(userName);
        if (user == null) {
            return "未找到用户名为 " + userName + " 的用户";
        }
        return String.format("用户信息:用户名=%s,昵称=%s,邮箱=%s,状态=%s",
            user.getUserName(),
            user.getNickName(),
            user.getEmail(),
            "0".equals(user.getStatus()) ? "正常" : "停用");
    }

    /**
     * 获取系统菜单数量
     */
    @Tool("获取系统菜单总数")
    public String getMenuCount() {
        int count = menuService.selectMenuList(new SysMenu(), 1L).size();
        return "系统当前共有 " + count + " 个菜单";
    }

    /**
     * 获取当前时间
     */
    @Tool("获取当前系统时间")
    public String getCurrentTime() {
        return "当前时间:" + LocalDateTime.now()
            .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
    }
}

9.3 带工具的 AI Service

java 复制代码
package com.ruoyi.ai.service;

import dev.langchain4j.service.AiService;
import dev.langchain4j.service.MemoryId;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;

/**
 * 带工具调用的 AI Service
 * 可以调用 RuoYiTools 中的方法
 */
@AiService
public interface SmartAssistant {

    @SystemMessage("""
        你是若依管理系统的智能助手,你可以:
        1. 查询系统实时数据(用户数、菜单数等)
        2. 查询用户信息
        3. 获取当前时间
        当用户询问系统数据时,请主动调用相关工具获取最新数据。
        """)
    String chat(
        @MemoryId String userId,
        @UserMessage String message
    );
}

效果演示:

复制代码
用户:系统现在有多少用户?
AI:(自动调用 getUserCount())
    系统当前共有 1234 个用户。

用户:帮我查一下 admin 用户的信息
AI:(自动调用 getUserByName("admin"))
    用户信息:用户名=admin,昵称=管理员,邮箱=admin@163.com,状态=正常

十、完整配置汇总

10.1 完整 LangChain4jConfig

java 复制代码
package com.ruoyi.ai.config;

import com.ruoyi.ai.tools.RuoYiTools;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.memory.chat.ChatMemoryProvider;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.embedding.onnx.allminilml6v2.AllMiniLmL6V2EmbeddingModel;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * LangChain4j 完整配置
 */
@Configuration
public class LangChain4jConfig {

    /**
     * 对话记忆提供者(每个用户独立)
     */
    @Bean
    public ChatMemoryProvider chatMemoryProvider() {
        return memoryId -> MessageWindowChatMemory.builder()
            .id(memoryId)
            .maxMessages(20)
            .build();
    }

    /**
     * 本地向量化模型(无需 API Key)
     */
    @Bean
    public EmbeddingModel embeddingModel() {
        return new AllMiniLmL6V2EmbeddingModel();
    }

    /**
     * 内存向量数据库(生产环境替换为 Milvus/Qdrant)
     */
    @Bean
    public EmbeddingStore<TextSegment> embeddingStore() {
        return new InMemoryEmbeddingStore<>();
    }

    /**
     * RAG 内容检索器
     */
    @Bean
    public ContentRetriever contentRetriever(
            EmbeddingStore<TextSegment> embeddingStore,
            EmbeddingModel embeddingModel) {
        return EmbeddingStoreContentRetriever.builder()
            .embeddingStore(embeddingStore)
            .embeddingModel(embeddingModel)
            .maxResults(3)
            .minScore(0.6)
            .build();
    }
}

10.2 完整 Controller

java 复制代码
package com.ruoyi.ai.controller;

import com.ruoyi.ai.service.*;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.framework.web.service.TokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

/**
 * AI 对话完整 Controller
 */
@RestController
@RequestMapping("/ai")
public class AiChatController extends BaseController {

    @Autowired
    private ChatAssistant chatAssistant;

    @Autowired
    private MemoryChatAssistant memoryChatAssistant;

    @Autowired
    private StreamingChatAssistant streamingChatAssistant;

    @Autowired
    private RagAssistant ragAssistant;

    @Autowired
    private SmartAssistant smartAssistant;

    /**
     * 简单对话(无记忆)
     */
    @PostMapping("/chat")
    public AjaxResult chat(@RequestParam String message) {
        return AjaxResult.success(chatAssistant.chat(message));
    }

    /**
     * 多轮对话(带记忆)
     */
    @PostMapping("/chat/memory")
    public AjaxResult chatWithMemory(@RequestParam String message) {
        String userId = String.valueOf(SecurityUtils.getUserId());
        return AjaxResult.success(memoryChatAssistant.chat(userId, message));
    }

    /**
     * 流式对话(SSE)
     */
    @GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter streamChat(@RequestParam String message) {
        String userId = String.valueOf(SecurityUtils.getUserId());
        SseEmitter emitter = new SseEmitter(60_000L);

        streamingChatAssistant.chat(userId, message)
            .subscribe(
                token -> {
                    try {
                        emitter.send(SseEmitter.event().data(token).name("message"));
                    } catch (Exception e) {
                        emitter.completeWithError(e);
                    }
                },
                emitter::completeWithError,
                () -> {
                    try {
                        emitter.send(SseEmitter.event().data("[DONE]").name("done"));
                        emitter.complete();
                    } catch (Exception e) {
                        emitter.completeWithError(e);
                    }
                }
            );

        return emitter;
    }

    /**
     * RAG 知识库问答
     */
    @PostMapping("/chat/rag")
    public AjaxResult ragChat(@RequestParam String question) {
        String userId = String.valueOf(SecurityUtils.getUserId());
        return AjaxResult.success(ragAssistant.chat(userId, question));
    }

    /**
     * 智能助手(带工具调用)
     */
    @PostMapping("/chat/smart")
    public AjaxResult smartChat(@RequestParam String message) {
        String userId = String.valueOf(SecurityUtils.getUserId());
        return AjaxResult.success(smartAssistant.chat(userId, message));
    }
}

十一、常见问题

Q1:JDK 版本不够怎么办?

xml 复制代码
<!-- 若依默认 JDK 8,LangChain4j 1.x 需要 JDK 17 -->
<!-- 方案一:升级 JDK 到 17(推荐) -->

<!-- 方案二:使用 LangChain4j 0.36.x(支持 JDK 8) -->
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-spring-boot-starter</artifactId>
    <version>0.36.2</version>
</dependency>
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-open-ai-spring-boot-starter</artifactId>
    <version>0.36.2</version>
</dependency>

Q2:国内无法访问 OpenAI 怎么办?

yaml 复制代码
# 方案一:使用通义千问(阿里云,国内可用)
langchain4j:
  community:
    dashscope:
      chat-model:
        api-key: your-dashscope-api-key
        model-name: qwen-plus

# 方案二:使用 Ollama 本地模型(完全离线)
langchain4j:
  ollama:
    chat-model:
      base-url: http://localhost:11434
      model-name: qwen2.5:7b
      temperature: 0.7

Q3:如何切换不同的 LLM?

java 复制代码
// LangChain4j 的优势:切换模型只需改配置,代码不变
// 只需修改 application.yml 中的模型配置即可
// AI Service 接口代码完全不需要改动

Q4:生产环境向量数据库推荐?

数据库 特点 推荐场景
InMemory 内存存储,重启丢失 开发测试
PgVector PostgreSQL 扩展,稳定 中小规模
Milvus 专业向量数据库,高性能 大规模生产
Qdrant 轻量,易部署 中等规模

十二、总结

功能 实现方式 难度
基础对话 @AiService + ChatModel
多轮对话 @MemoryId + ChatMemoryProvider ⭐⭐
流式输出 Flux<String> + SSE ⭐⭐
RAG 知识库 EmbeddingStore + ContentRetriever ⭐⭐⭐
工具调用 @Tool 注解 ⭐⭐

LangChain4j 的核心优势:

  • 声明式 API:像写 Service 一样开发 AI 功能
  • 模型无关:切换 LLM 只改配置,代码不变
  • Spring Boot 原生集成:自动装配,开箱即用
  • 功能完整:对话记忆、RAG、工具调用、流式输出一应俱全

原创不易,如果对你有帮助,点个赞再走~

关注我,持续分享 Java AI 应用开发实战!

相关推荐
学电子她就能回来吗2 小时前
liunx嵌入式基础:socket通信
linux·运维·服务器·人工智能·单片机·嵌入式硬件·学习
吴佳浩 Alben2 小时前
Claude Code 源码泄露事件深度剖析
人工智能·arcgis·语言模型·自然语言处理·npm·node.js
禁默2 小时前
自动化智能体生成+外接MCP,我用 ModelEngine Nexent 5分钟手搓了一个小红书爆款收割机
运维·人工智能·自动化
数智顾问2 小时前
(100页PPT)数字化转型德勤集团信息化顶层规划方案(附下载方式)
大数据·人工智能
苏渡苇2 小时前
ConcurrentHashMap.computeIfAbsent():高并发下安全初始化的终极方案
java·安全·jdk·高并发·hashmap·concurrent
汽车仪器仪表相关领域2 小时前
动态间隙精准诊断:NHJX-13 型底盘间隙仪机动车底盘安全检测全方案
大数据·人工智能·机器学习·单元测试·压力测试·可用性测试
wenzhangli72 小时前
推荐一款 面向企业级应用的开源AI Agent 操作系统Apex OS
人工智能
FinelyYang2 小时前
nginx的docker镜像封禁地区IP
java·nginx·docker
唯创知音2 小时前
WTK6900FC鼾声识别芯片:基于DNN-HMM算法的高性能鼾声识别检测处理方案
人工智能·算法·dnn·鼾声识别芯片·鼾声检测芯片