当前位置: 代码网 > it编程>编程语言>Java > SpringBoot集成Langchain4J的保姆级教程分享

SpringBoot集成Langchain4J的保姆级教程分享

2026年08月04日 Java 我要评论
你好!作为一名java开发,我也曾看着python那边的langchain生态眼馋。不过现在好了,langchain4j 让我们java开发者也能优雅地接入大模型了。下面我就把整套方案给你梳理出来,从

你好!作为一名java开发,我也曾看着python那边的langchain生态眼馋。不过现在好了,langchain4j 让我们java开发者也能优雅地接入大模型了。下面我就把整套方案给你梳理出来,从零到一,保姆级,咱们直接开干!

一、项目初始化

1.1 技术栈版本(建议)

  • jdk 17+(langchain4j 要求 jdk 17 起步)
  • spring boot 3.x
  • mysql 8.0+
  • redis stack 7.x(必须开启 redisearch 模块,用于向量检索)
  • maven 3.6+

1.2 核心依赖(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 
         https://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.4.5</version>
        <relativepath/>
    </parent>
    <groupid>com.example</groupid>
    <artifactid>langchain4j-springboot-demo</artifactid>
    <version>1.0.0</version>
    <properties>
        <java.version>21</java.version>
        <langchain4j.version>1.0.0-beta3</langchain4j.version>
    </properties>
    <!-- bom统一管理版本,防止依赖冲突 -->
    <dependencymanagement>
        <dependencies>
            <dependency>
                <groupid>dev.langchain4j</groupid>
                <artifactid>langchain4j-bom</artifactid>
                <version>${langchain4j.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencymanagement>
    <dependencies>
        <!-- spring boot web -->
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-web</artifactid>
        </dependency>
        <!-- langchain4j 核心 -->
        <dependency>
            <groupid>dev.langchain4j</groupid>
            <artifactid>langchain4j</artifactid>
        </dependency>
        <!-- langchain4j spring boot starter(声明式ai服务、rag、tools等) -->
        <dependency>
            <groupid>dev.langchain4j</groupid>
            <artifactid>langchain4j-spring-boot-starter</artifactid>
        </dependency>
        <!-- openai兼容接口的spring boot starter(支持通义千问等) -->
        <dependency>
            <groupid>dev.langchain4j</groupid>
            <artifactid>langchain4j-open-ai-spring-boot-starter</artifactid>
        </dependency>
        <!-- ========== redis 相关依赖(用于向量存储) ========== -->
        <!-- spring boot redis starter(提供 redis 客户端) -->
        <dependency>
            <groupid>org.springframework.boot</groupid>
            <artifactid>spring-boot-starter-data-redis</artifactid>
        </dependency>
        <!-- langchain4j redis 向量存储 spring boot starter -->
        <dependency>
            <groupid>dev.langchain4j</groupid>
            <artifactid>langchain4j-community-redis-spring-boot-starter</artifactid>
            <version>1.0.1-beta6</version>
        </dependency>
        <!-- mybatis-plus(orm框架,操作数据库) -->
        <dependency>
            <groupid>com.baomidou</groupid>
            <artifactid>mybatis-plus-spring-boot3-starter</artifactid>
            <version>3.5.6</version>
        </dependency>
        <!-- mysql驱动 -->
        <dependency>
            <groupid>com.mysql</groupid>
            <artifactid>mysql-connector-j</artifactid>
            <scope>runtime</scope>
        </dependency>
        <!-- lombok(简化代码) -->
        <dependency>
            <groupid>org.projectlombok</groupid>
            <artifactid>lombok</artifactid>
            <optional>true</optional>
        </dependency>
        <!-- jackson(json序列化,langchain4j内部已包含,此处显式引入确保版本一致) -->
        <dependency>
            <groupid>com.fasterxml.jackson.core</groupid>
            <artifactid>jackson-databind</artifactid>
        </dependency>
    </dependencies>
</project>

小贴士langchain4j-community-redis-spring-boot-starter 依赖了 jedis,如果和 spring-boot-starter-data-redis(默认用 lettuce)版本冲突,可以手动排除 jedis 或保持两者并存,实际测试中 lettuce 和 jedis 可以共存。

二、配置文件(application.yml)

server:
  port: 8080
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/langchain4j_db?useunicode=true&characterencoding=utf8&usessl=false&servertimezone=asia/shanghai
    username: root
    password: your_password
    driver-class-name: com.mysql.cj.jdbc.driver
  # ============ redis 配置(用于向量存储) ============
  data:
    redis:
      host: localhost
      port: 6379
      # password: your_redis_password  # 如果有密码则配置
      database: 0
      timeout: 5000ms
      lettuce:
        pool:
          max-active: 8
          max-idle: 8
          min-idle: 0
# langchain4j 配置
langchain4j:
  open-ai:
    chat-model:
      base-url: https://dashscope.aliyuncs.com/compatible-mode/v1  # 通义千问兼容openai接口
      api-key: sk-your-api-key-here  # 去阿里云百炼申请
      model-name: qwen-max
      log-requests: true
      log-responses: true
# mybatis-plus配置
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.stdoutimpl
  global-config:
    db-config:
      id-type: auto

注意:rag 的向量检索依赖 redis stack(带 redisearch 模块)。如果用 docker,推荐命令:docker run -d --name redis-vector -p 6379:6379 -p 8001:8001 redis/redis-stack:latest

三、数据库表设计

根据需求,我们需要两张表:会话表(conversation)消息表(message)

-- 创建数据库
create database if not exists langchain4j_db default character set utf8mb4;
use langchain4j_db;
-- 会话表:存储每个对话会话的元信息
create table conversation (
    id bigint primary key auto_increment comment '主键id',
    conversation_id varchar(64) not null unique comment '会话唯一标识(对外暴露)',
    user_id varchar(64) not null comment '用户id(用于多用户隔离)',
    title varchar(200) default '' comment '会话标题',
    status tinyint default 1 comment '状态:1-活跃 0-已关闭',
    created_at datetime default current_timestamp comment '创建时间',
    updated_at datetime default current_timestamp on update current_timestamp comment '更新时间',
    index idx_user_id (user_id),
    index idx_conversation_id (conversation_id)
) engine=innodb default charset=utf8mb4 comment='对话会话表';
-- 消息表:存储每条对话消息
create table message (
    id bigint primary key auto_increment comment '主键id',
    conversation_id varchar(64) not null comment '所属会话id',
    role varchar(20) not null comment '角色:user/assistant/system/tool',
    content text not null comment '消息内容',
    tool_name varchar(100) default '' comment '工具名称(仅tool角色时有值)',
    tool_execution_id varchar(100) default '' comment '工具执行id',
    created_at datetime default current_timestamp comment '创建时间',
    index idx_conversation_id (conversation_id),
    index idx_created_at (created_at)
) engine=innodb default charset=utf8mb4 comment='对话消息表';

四、实体类与mapper

4.1 会话实体(conversation.java)

package com.example.demo.entity;

import com.baomidou.mybatisplus.annotation.*;
import lombok.data;
import java.time.localdatetime;

@data
@tablename("conversation")
public class conversation {
    
    @tableid(type = idtype.auto)
    private long id;
    
    @tablefield("conversation_id")
    private string conversationid;
    
    @tablefield("user_id")
    private string userid;
    
    private string title;
    
    private integer status;  // 1-活跃 0-已关闭
    
    @tablefield("created_at")
    private localdatetime createdat;
    
    @tablefield("updated_at")
    private localdatetime updatedat;
}

4.2 消息实体(message.java)

package com.example.demo.entity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.data;
import java.time.localdatetime;
@data
@tablename("message")
public class message {
    @tableid(type = idtype.auto)
    private long id;
    @tablefield("conversation_id")
    private string conversationid;
    private string role;  // user / assistant / system / tool
    private string content;
    @tablefield("tool_name")
    private string toolname;
    @tablefield("tool_execution_id")
    private string toolexecutionid;
    @tablefield("created_at")
    private localdatetime createdat;
}

4.3 mapper接口

package com.example.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.basemapper;
import com.example.demo.entity.conversation;
import org.apache.ibatis.annotations.mapper;

@mapper
public interface conversationmapper extends basemapper<conversation> {
}
package com.example.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.basemapper;
import com.example.demo.entity.message;
import org.apache.ibatis.annotations.mapper;

@mapper
public interface messagemapper extends basemapper<message> {
}

五、服务层(核心业务)

5.1 会话服务(conversationservice.java)

package com.example.demo.service;

import com.baomidou.mybatisplus.core.conditions.query.lambdaquerywrapper;
import com.baomidou.mybatisplus.extension.service.impl.serviceimpl;
import com.example.demo.entity.conversation;
import com.example.demo.mapper.conversationmapper;
import lombok.extern.slf4j.slf4j;
import org.springframework.stereotype.service;

import java.util.list;
import java.util.uuid;

@slf4j
@service
public class conversationservice extends serviceimpl<conversationmapper, conversation> {

    /**
     * 创建新会话
     */
    public conversation createconversation(string userid, string title) {
        conversation conversation = new conversation();
        conversation.setconversationid(uuid.randomuuid().tostring().replace("-", ""));
        conversation.setuserid(userid);
        conversation.settitle(title != null ? title : "新对话");
        conversation.setstatus(1);
        save(conversation);
        log.info("创建会话成功:conversationid={}, userid={}", conversation.getconversationid(), userid);
        return conversation;
    }

    /**
     * 获取用户的会话列表
     */
    public list<conversation> listbyuserid(string userid) {
        lambdaquerywrapper<conversation> wrapper = new lambdaquerywrapper<>();
        wrapper.eq(conversation::getuserid, userid)
                .orderbydesc(conversation::getupdatedat);
        return list(wrapper);
    }

    /**
     * 关闭会话
     */
    public boolean closeconversation(string conversationid, string userid) {
        lambdaquerywrapper<conversation> wrapper = new lambdaquerywrapper<>();
        wrapper.eq(conversation::getconversationid, conversationid)
                .eq(conversation::getuserid, userid);
        conversation conversation = getone(wrapper);
        if (conversation == null) {
            return false;
        }
        conversation.setstatus(0);
        return updatebyid(conversation);
    }
}

5.2 消息服务(messageservice.java)

package com.example.demo.service;

import com.baomidou.mybatisplus.core.conditions.query.lambdaquerywrapper;
import com.baomidou.mybatisplus.extension.service.impl.serviceimpl;
import com.example.demo.entity.message;
import com.example.demo.mapper.messagemapper;
import lombok.extern.slf4j.slf4j;
import org.springframework.stereotype.service;

import java.util.list;

@slf4j
@service
public class messageservice extends serviceimpl<messagemapper, message> {

    /**
     * 保存一条消息
     */
    public void savemessage(string conversationid, string role, string content) {
        savemessage(conversationid, role, content, null, null);
    }

    /**
     * 保存一条消息(含工具信息)
     */
    public void savemessage(string conversationid, string role, string content, 
                            string toolname, string toolexecutionid) {
        message message = new message();
        message.setconversationid(conversationid);
        message.setrole(role);
        message.setcontent(content);
        message.settoolname(toolname != null ? toolname : "");
        message.settoolexecutionid(toolexecutionid != null ? toolexecutionid : "");
        save(message);
        log.debug("保存消息成功:conversationid={}, role={}, content长度={}", 
                  conversationid, role, content != null ? content.length() : 0);
    }

    /**
     * 查询会话的所有消息(按时间升序)
     */
    public list<message> listbyconversationid(string conversationid) {
        lambdaquerywrapper<message> wrapper = new lambdaquerywrapper<>();
        wrapper.eq(message::getconversationid, conversationid)
                .orderbyasc(message::getcreatedat);
        return list(wrapper);
    }
}

六、mysql持久化的chatmemorystore实现

这一步是关键!我们需要实现 chatmemorystore 接口,让langchain4j的对话记忆能持久化到mysql。

package com.example.demo.store;

import com.example.demo.entity.message;
import com.example.demo.service.messageservice;
import dev.langchain4j.data.message.chatmessage;
import dev.langchain4j.data.message.chatmessagedeserializer;
import dev.langchain4j.data.message.chatmessageserializer;
import dev.langchain4j.memory.chatmemory;
import dev.langchain4j.memory.chat.messagewindowchatmemory;
import dev.langchain4j.store.memory.chat.chatmemorystore;
import lombok.requiredargsconstructor;
import lombok.extern.slf4j.slf4j;
import org.springframework.stereotype.component;

import java.util.arraylist;
import java.util.list;

/**
 * 基于mysql的chatmemorystore实现
 * 将对话记忆持久化到mysql,每个会话的消息以json数组形式存储
 */
@slf4j
@component
@requiredargsconstructor
public class mysqlchatmemorystore implements chatmemorystore {

    private final messageservice messageservice;

    /**
     * 根据memoryid获取该会话的所有消息
     * memoryid 对应 conversationid
     */
    @override
    public list<chatmessage> getmessages(object memoryid) {
        string conversationid = memoryid.tostring();
        log.debug("从mysql加载会话消息:conversationid={}", conversationid);
        
        list<message> messages = messageservice.listbyconversationid(conversationid);
        list<chatmessage> chatmessages = new arraylist<>();
        
        for (message msg : messages) {
            // 将数据库中的消息反序列化为chatmessage对象
            string json = string.format(
                "{\"role\":\"%s\",\"text\":\"%s\"}", 
                msg.getrole(), 
                msg.getcontent().replace("\"", "\\\"")
            );
            // 使用langchain4j内置的序列化工具
            chatmessage chatmessage = chatmessagedeserializer.messagefromjson(json);
            chatmessages.add(chatmessage);
        }
        
        return chatmessages;
    }

    /**
     * 更新会话的所有消息(全量替换)
     * langchain4j的chatmemory在每次对话后都会调用此方法
     */
    @override
    public void updatemessages(object memoryid, list<chatmessage> messages) {
        string conversationid = memoryid.tostring();
        log.debug("更新mysql会话消息:conversationid={}, 消息数={}", conversationid, messages.size());
        
        // 简单起见:先删除该会话所有旧消息,再批量插入新消息
        // 生产环境可优化为增量更新
        messageservice.lambdaupdate()
                .eq(message::getconversationid, conversationid)
                .remove();
        
        for (chatmessage msg : messages) {
            string role = msg.type().name().tolowercase();
            string content = msg.text();
            messageservice.savemessage(conversationid, role, content);
        }
    }

    /**
     * 删除会话的所有消息
     */
    @override
    public void deletemessages(object memoryid) {
        string conversationid = memoryid.tostring();
        log.info("删除mysql会话消息:conversationid={}", conversationid);
        messageservice.lambdaupdate()
                .eq(message::getconversationid, conversationid)
                .remove();
    }
}

特别说明:上面的 getmessages 方法中,我用了简化的json反序列化方式。生产环境中建议使用 chatmessageserializerchatmessagedeserializer 配合jackson来做完整的序列化/反序列化。

七、配置类:组装ai服务

package com.example.demo.config;

import com.example.demo.store.mysqlchatmemorystore;
import dev.langchain4j.memory.chatmemory;
import dev.langchain4j.memory.chat.messagewindowchatmemory;
import dev.langchain4j.model.chat.chatlanguagemodel;
import dev.langchain4j.service.aiservices;
import dev.langchain4j.service.memoryid;
import dev.langchain4j.service.systemmessage;
import dev.langchain4j.service.usermessage;
import lombok.requiredargsconstructor;
import lombok.extern.slf4j.slf4j;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;

import java.util.function.function;

@slf4j
@configuration
@requiredargsconstructor
public class langchain4jconfig {

    private final chatlanguagemodel chatmodel;
    private final mysqlchatmemorystore memorystore;

    /**
     * 基础ai服务(无记忆)
     */
    @bean
    public simpleaiservice simpleaiservice() {
        return aiservices.builder(simpleaiservice.class)
                .chatmodel(chatmodel)
                .build();
    }

    /**
     * 带提示词模板的ai服务
     */
    @bean
    public promptaiservice promptaiservice() {
        return aiservices.builder(promptaiservice.class)
                .chatmodel(chatmodel)
                .build();
    }

    /**
     * 带会话记忆的ai服务(保留对话轮次)
     * 使用messagewindowchatmemory,保留最近n条消息
     */
    @bean
    public memoryaiservice memoryaiservice() {
        return aiservices.builder(memoryaiservice.class)
                .chatmodel(chatmodel)
                // 每个会话独立记忆,最多保留20条消息
                .chatmemoryprovider(memoryid -> 
                    messagewindowchatmemory.builder()
                        .id(memoryid)
                        .maxmessages(20)
                        .chatmemorystore(memorystore)  // 持久化到mysql
                        .build()
                )
                .build();
    }

    /**
     * rag + tool calling + 会话管理的综合ai服务
     */
    @bean
    public advancedaiservice advancedaiservice(
            dev.langchain4j.rag.content.retriever.contentretriever contentretriever,
            list<object> tools) {
        
        var builder = aiservices.builder(advancedaiservice.class)
                .chatmodel(chatmodel)
                // 会话记忆:使用mysql持久化
                .chatmemoryprovider(memoryid -> 
                    messagewindowchatmemory.builder()
                        .id(memoryid)
                        .maxmessages(30)
                        .chatmemorystore(memorystore)
                        .build()
                )
                // rag检索增强
                .contentretriever(contentretriever);
        
        // 注册工具
        if (tools != null && !tools.isempty()) {
            builder.tools(tools.toarray());
        }
        
        return builder.build();
    }
}

八、ai服务接口定义(声明式)

8.1 基础ai服务(普通对话)

package com.example.demo.service.ai;

import dev.langchain4j.service.aiservice;
import dev.langchain4j.service.usermessage;

@aiservice
public interface simpleaiservice {
    
    /**
     * 普通对话接口:用户说什么,ai回什么
     */
    string chat(@usermessage string usermessage);
}

8.2 带提示词模板的ai服务

package com.example.demo.service.ai;

import dev.langchain4j.service.aiservice;
import dev.langchain4j.service.systemmessage;
import dev.langchain4j.service.usermessage;
import dev.langchain4j.service.v;

/**
 * 带提示词模板的ai服务
 * 通过@systemmessage设定角色,@v绑定变量
 */
@aiservice
public interface promptaiservice {

    /**
     * 带系统提示词的对话
     * @param usermessage 用户输入
     * @return ai响应
     */
    @systemmessage("你是一位资深的java技术专家,擅长spring boot和微服务架构。请用专业且易懂的方式回答问题。")
    string chat(@usermessage string usermessage);

    /**
     * 带变量的提示词模板
     * @param name 用户名称
     * @param question 用户问题
     * @return ai响应
     */
    @systemmessage("你是一位{{role}}专家")
    @usermessage("你好{{name}},请回答:{{question}}")
    string chatwithtemplate(@v("role") string role, 
                            @v("name") string name, 
                            @v("question") string question);
}

8.3 保留对话轮次的ai服务(带记忆)

package com.example.demo.service.ai;

import dev.langchain4j.service.aiservice;
import dev.langchain4j.service.memoryid;
import dev.langchain4j.service.usermessage;

/**
 * 带会话记忆的ai服务
 * 通过@memoryid实现多用户/多会话隔离
 */
@aiservice
public interface memoryaiservice {

    /**
     * 带记忆的对话
     * @param conversationid 会话id(用于隔离不同会话的记忆)
     * @param usermessage 用户输入
     * @return ai响应
     */
    string chat(@memoryid string conversationid, @usermessage string usermessage);
}

8.4 综合ai服务:rag + tool calling + 会话管理

package com.example.demo.service.ai;

import dev.langchain4j.service.aiservice;
import dev.langchain4j.service.memoryid;
import dev.langchain4j.service.systemmessage;
import dev.langchain4j.service.usermessage;

@aiservice
public interface advancedaiservice {

    /**
     * 综合对话接口:支持rag检索 + 工具调用 + 会话记忆
     * 
     * @param conversationid 会话id(用于记忆隔离和持久化)
     * @param usermessage 用户输入
     * @return ai响应(包含检索增强和工具调用的结果)
     */
    @systemmessage("""
        你是一个智能助手,可以访问知识库和调用工具来帮助用户。
        如果用户的问题涉及专业知识,请优先从知识库中检索相关信息。
        如果需要实时数据或执行特定操作,请调用相应的工具。
        回答要准确、简洁、友好。
        """)
    string chat(@memoryid string conversationid, @usermessage string usermessage);
}

九、rag配置(检索增强生成)- 基于redis向量存储

rag需要向量数据库的支持。这里我们选择 redis stack(带 redisearch 模块),相比 pgvector,redis 能提供亚毫秒级的向量检索速度,非常适合实时对话场景。

9.1 redis连接配置(可选,用于自定义)

如果默认的 redisconnectionfactory 自动配置不满足需求,可以手动配置:

package com.example.demo.config;

import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.data.redis.connection.redisstandaloneconfiguration;
import org.springframework.data.redis.connection.lettuce.lettuceconnectionfactory;
import org.springframework.data.redis.core.redistemplate;
import org.springframework.data.redis.serializer.stringredisserializer;

@configuration
public class redisconfig {

    @bean
    public lettuceconnectionfactory redisconnectionfactory() {
        redisstandaloneconfiguration config = new redisstandaloneconfiguration();
        config.sethostname("localhost");
        config.setport(6379);
        // config.setpassword(redispassword.of("your_password"));
        config.setdatabase(0);
        return new lettuceconnectionfactory(config);
    }

    @bean
    public redistemplate<string, object> redistemplate(lettuceconnectionfactory connectionfactory) {
        redistemplate<string, object> template = new redistemplate<>();
        template.setconnectionfactory(connectionfactory);
        template.setkeyserializer(new stringredisserializer());
        template.setvalueserializer(new stringredisserializer());
        return template;
    }
}

9.2 rag核心配置类

package com.example.demo.config;

import dev.langchain4j.community.store.embedding.redis.redisembeddingstore;
import dev.langchain4j.data.document.document;
import dev.langchain4j.data.document.loader.filesystemdocumentloader;
import dev.langchain4j.data.document.parser.textdocumentparser;
import dev.langchain4j.data.document.splitter.documentsplitters;
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 dev.langchain4j.store.embedding.embeddingstoreingestor;
import jakarta.annotation.postconstruct;
import lombok.requiredargsconstructor;
import lombok.extern.slf4j.slf4j;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.data.redis.connection.redisconnectionfactory;
import org.springframework.data.redis.connection.lettuce.lettuceconnectionfactory;

import java.nio.file.path;
import java.nio.file.paths;
import java.util.list;

@slf4j
@configuration
@requiredargsconstructor
public class ragconfig {

    private final embeddingmodel embeddingmodel;
    private final redisconnectionfactory redisconnectionfactory;

    /**
     * 配置 redis 作为向量存储
     * 
     * redis stack 必须安装 redisearch 模块才能支持向量搜索
     * docker 部署命令:
     *   docker run -d --name redis-vector -p 6379:6379 -p 8001:8001 redis/redis-stack:latest
     */
    @bean
    public embeddingstore<textsegment> embeddingstore() {
        // 从 redisconnectionfactory 中获取连接信息
        lettuceconnectionfactory factory = (lettuceconnectionfactory) redisconnectionfactory;
        string host = factory.gethostname();
        int port = factory.getport();
        string password = factory.getpassword();
        int database = factory.getdatabase();

        log.info("初始化 redisembeddingstore:host={}, port={}, database={}", host, port, database);

        return redisembeddingstore.builder()
                .host(host)
                .port(port)
                .password(password != null ? password : "")
                .database(database)
                // 索引名称,用于在 redis 中标识向量索引
                .indexname("knowledge_vectors")
                // 向量维度,必须与 embeddingmodel 的输出维度一致
                // 通义千问 text-embedding-v4 的维度是 1536
                .dimension(1536)
                // 距离度量类型:cosine(余弦相似度)、euclidean(欧氏距离)、ip(内积)
                .distancemetric(redis.embedding.distancemetric.cosine)
                .build();
    }

    /**
     * 配置内容检索器
     */
    @bean
    public contentretriever contentretriever(embeddingstore<textsegment> embeddingstore) {
        return embeddingstorecontentretriever.builder()
                .embeddingstore(embeddingstore)
                .embeddingmodel(embeddingmodel)
                .maxresults(3)   // 最多检索 3 条相关片段
                .minscore(0.7)   // 最低相似度阈值
                .build();
    }

    /**
     * 启动时加载知识文档到 redis 向量库
     */
    @postconstruct
    public void loadknowledgedocuments(embeddingstore<textsegment> embeddingstore) {
        try {
            // 从 resources/knowledge 目录加载文档
            path path = paths.get("src/main/resources/knowledge");
            list<document> documents = filesystemdocumentloader.loaddocuments(
                    path,
                    new textdocumentparser()
            );

            if (documents.isempty()) {
                log.warn("未找到知识文档,跳过加载");
                return;
            }

            // 分割文档并存入 redis 向量库
            embeddingstoreingestor ingestor = embeddingstoreingestor.builder()
                    .documentsplitter(documentsplitters.recursive(500, 0))
                    .embeddingmodel(embeddingmodel)
                    .embeddingstore(embeddingstore)
                    .build();

            ingestor.ingest(documents);
            log.info("知识文档加载完成,共 {} 个文档已存入 redis", documents.size());

        } catch (exception e) {
            log.error("加载知识文档到 redis 失败", e);
        }
    }
}

十、工具类定义(tool calling)

package com.example.demo.tool;

import dev.langchain4j.agent.tool.tool;
import lombok.extern.slf4j.slf4j;
import org.springframework.stereotype.component;

import java.time.localdatetime;
import java.time.format.datetimeformatter;

/**
 * 天气查询工具
 * 演示tool calling的基本用法
 */
@slf4j
@component
public class weathertool {

    @tool("查询指定城市的当前天气信息")
    public string getweather(string city) {
        log.info("调用天气工具:city={}", city);
        
        // 模拟天气数据(实际可调用第三方api)
        string[] weathers = {"晴", "多云", "小雨", "阴天"};
        string weather = weathers[(int) (math.random() * weathers.length)];
        int temperature = 15 + (int) (math.random() * 20);
        
        return string.format("【%s】当前天气:%s,温度:%d℃", 
                city, weather, temperature);
    }
}

/**
 * 计算器工具
 */
@slf4j
@component
public class calculatortool {

    @tool("计算两个数字的和")
    public double sum(double a, double b) {
        log.info("调用计算工具:{} + {}", a, b);
        return a + b;
    }

    @tool("计算两个数字的差")
    public double subtract(double a, double b) {
        log.info("调用计算工具:{} - {}", a, b);
        return a - b;
    }

    @tool("计算两个数字的乘积")
    public double multiply(double a, double b) {
        log.info("调用计算工具:{} × {}", a, b);
        return a * b;
    }
}

/**
 * 时间工具
 */
@slf4j
@component
public class datetimetool {

    @tool("获取当前日期和时间")
    public string getcurrentdatetime() {
        log.info("调用时间工具");
        return localdatetime.now().format(
                datetimeformatter.ofpattern("yyyy-mm-dd hh:mm:ss")
        );
    }
}

十一、controller接口层

package com.example.demo.controller;

import com.example.demo.entity.conversation;
import com.example.demo.service.conversationservice;
import com.example.demo.service.ai.*;
import io.swagger.v3.oas.annotations.operation;
import io.swagger.v3.oas.annotations.parameter;
import io.swagger.v3.oas.annotations.tags.tag;
import lombok.requiredargsconstructor;
import lombok.extern.slf4j.slf4j;
import org.springframework.web.bind.annotation.*;

import java.util.hashmap;
import java.util.list;
import java.util.map;

@slf4j
@restcontroller
@requestmapping("/api/chat")
@requiredargsconstructor
@tag(name = "ai对话接口", description = "langchain4j集成spring boot演示")
public class chatcontroller {

    private final simpleaiservice simpleaiservice;
    private final promptaiservice promptaiservice;
    private final memoryaiservice memoryaiservice;
    private final advancedaiservice advancedaiservice;
    private final conversationservice conversationservice;

    // ==================== 1. 普通ai对话接口 ====================
    
    @getmapping("/simple")
    @operation(summary = "普通对话", description = "无记忆、无提示词,最简单的ai对话")
    public map<string, string> simplechat(
            @requestparam @parameter(description = "用户输入") string prompt) {
        log.info("普通对话请求:prompt={}", prompt);
        string result = simpleaiservice.chat(prompt);
        return map.of("response", result);
    }

    // ==================== 2. 带提示词的对话接口 ====================
    
    @getmapping("/prompt")
    @operation(summary = "带提示词对话", description = "使用@systemmessage设定ai角色")
    public map<string, string> promptchat(
            @requestparam @parameter(description = "用户输入") string prompt) {
        log.info("带提示词对话请求:prompt={}", prompt);
        string result = promptaiservice.chat(prompt);
        return map.of("response", result);
    }

    @getmapping("/prompt/template")
    @operation(summary = "带模板变量的提示词对话")
    public map<string, string> prompttemplatechat(
            @requestparam @parameter(description = "角色") string role,
            @requestparam @parameter(description = "姓名") string name,
            @requestparam @parameter(description = "问题") string question) {
        log.info("模板对话请求:role={}, name={}, question={}", role, name, question);
        string result = promptaiservice.chatwithtemplate(role, name, question);
        return map.of("response", result);
    }

    // ==================== 3. 保留对话轮次的接口(带记忆) ====================
    
    @postmapping("/memory")
    @operation(summary = "带记忆对话", description = "同一conversationid会记住对话历史")
    public map<string, string> memorychat(
            @requestparam @parameter(description = "会话id") string conversationid,
            @requestparam @parameter(description = "用户输入") string prompt) {
        log.info("带记忆对话请求:conversationid={}, prompt={}", conversationid, prompt);
        string result = memoryaiservice.chat(conversationid, prompt);
        return map.of("response", result);
    }

    // ==================== 4. 保留会话的接口(会话管理) ====================
    
    @postmapping("/conversation/create")
    @operation(summary = "创建新会话")
    public map<string, object> createconversation(
            @requestparam @parameter(description = "用户id") string userid,
            @requestparam(required = false) @parameter(description = "会话标题") string title) {
        log.info("创建会话请求:userid={}, title={}", userid, title);
        conversation conversation = conversationservice.createconversation(userid, title);
        map<string, object> result = new hashmap<>();
        result.put("conversationid", conversation.getconversationid());
        result.put("title", conversation.gettitle());
        result.put("createdat", conversation.getcreatedat());
        return result;
    }

    @getmapping("/conversation/list")
    @operation(summary = "获取用户会话列表")
    public list<conversation> listconversations(
            @requestparam @parameter(description = "用户id") string userid) {
        log.info("获取会话列表:userid={}", userid);
        return conversationservice.listbyuserid(userid);
    }

    @postmapping("/conversation/close")
    @operation(summary = "关闭会话")
    public map<string, boolean> closeconversation(
            @requestparam @parameter(description = "会话id") string conversationid,
            @requestparam @parameter(description = "用户id") string userid) {
        log.info("关闭会话请求:conversationid={}, userid={}", conversationid, userid);
        boolean result = conversationservice.closeconversation(conversationid, userid);
        return map.of("success", result);
    }

    @postmapping("/conversation/chat")
    @operation(summary = "在会话上下文中对话", description = "自动关联会话,保留完整对话历史")
    public map<string, string> conversationchat(
            @requestparam @parameter(description = "会话id") string conversationid,
            @requestparam @parameter(description = "用户输入") string prompt) {
        log.info("会话对话请求:conversationid={}, prompt={}", conversationid, prompt);
        // 使用memoryaiservice,conversationid作为@memoryid
        string result = memoryaiservice.chat(conversationid, prompt);
        return map.of("response", result);
    }

    // ==================== 5. 综合接口:rag + tool calling + 会话管理 ====================
    
    @postmapping("/advanced")
    @operation(summary = "综合ai对话", description = "rag检索增强 + 工具调用 + 会话记忆持久化")
    public map<string, string> advancedchat(
            @requestparam @parameter(description = "会话id") string conversationid,
            @requestparam @parameter(description = "用户输入") string prompt) {
        log.info("综合对话请求:conversationid={}, prompt={}", conversationid, prompt);
        long starttime = system.currenttimemillis();
        
        string result = advancedaiservice.chat(conversationid, prompt);
        
        long costtime = system.currenttimemillis() - starttime;
        log.info("综合对话完成,耗时:{}ms", costtime);
        
        map<string, string> response = new hashmap<>();
        response.put("response", result);
        response.put("costtime", costtime + "ms");
        return response;
    }
}

十二、spring boot启动类

package com.example.demo;

import org.springframework.boot.springapplication;
import org.springframework.boot.autoconfigure.springbootapplication;

@springbootapplication
public class langchain4jdemoapplication {
    public static void main(string[] args) {
        springapplication.run(langchain4jdemoapplication.class, args);
        system.out.println("╔══════════════════════════════════════════════════════════╗");
        system.out.println("║   🚀 langchain4j + spring boot 集成成功!              ║");
        system.out.println("║   📌 访问 http://localhost:8080/api/chat/simple 试试    ║");
        system.out.println("║   🧠 向量存储:redis stack (redisearch)                ║");
        system.out.println("╚══════════════════════════════════════════════════════════╝");
    }
}

十三、接口测试示例

接口方法说明示例
/api/chat/simple?prompt=你好get普通对话返回ai基础回答
/api/chat/prompt?prompt=什么是微服务get带提示词ai以"java技术专家"身份回答
/api/chat/memory?conversationid=xxx&prompt=我叫张post带记忆ai记住你是谁
/api/chat/conversation/create?userid=user001&title=技术咨询post创建会话返回conversationid
/api/chat/conversation/chat?conversationid=xxx&prompt=继续刚才的话题post会话对话基于历史上下文回答
/api/chat/advanced?conversationid=xxx&prompt=帮我查下今天的天气post综合接口rag+工具调用+记忆

十四、生产避坑指南(redis 特别版)

1. 会话记忆 vs 历史记录

langchain4j提供的chatmemory是服务于大模型的"短期记忆",用于拼接上下文。而"历史记录"是面向用户展示的完整对话流水,需要你手动维护message表中。本文的方案中,mysqlchatmemorystoreupdatemessages是全量替换,生产环境建议改成增量追加

2. redis 向量维度必须匹配

redisembeddingstore.builder().dimension(1536) 中的维度必须与你使用的 embeddingmodel 输出维度一致。通义千问 text-embedding-v4 是 1536 维,如果用其他模型(如 openai text-embedding-ada-002 是 1536 维,text-embedding-3-small 是 1536 维),请相应调整。

3. redis stack 部署检查

redis 原生不支持向量搜索,必须安装 redisearch 模块。检查方法:连接 redis 后执行 module list,看是否包含 search。推荐直接使用 redis/redis-stack 镜像,一步到位。

4. 工具描述要清晰

@tool注解的value描述一定要写清楚,ai能否正确调用工具全看这个描述。

5. 会话隔离

使用@memoryid注解标识会话id,不同会话的记忆互不干扰。多用户场景下,建议用userid + conversationid组合作为记忆id。

6. 向量检索的实时性

redis 向量检索基于内存,速度极快,适合对延迟敏感的实时对话场景。但如果知识库非常大(超过百万级向量),需要考虑内存容量规划。

7. 版本兼容性(重要)

langchain4j从0.36.0起要求jdk 17。langchain4j-community-redis-spring-boot-starter 是社区模块,建议使用与 bom 版本匹配的版本(本文使用 1.0.1-beta6)。如果遇到 jedis/lettuce 版本冲突,可以在 pom.xml 中显式排除。

好了,整套方案就这些了。从最简单的 hello world 到综合的 rag+tool calling+会话管理,向量存储也换成了更轻量快速的 redis,该有的都有了。代码可以直接复制到项目里跑,有问题随时交流!

以上就是springboot集成langchain4j的保姆级教程分享的详细内容,更多关于springboot集成langchain4j的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com