langchain4j+mysql+历史记录

langchain4j+mysql+历史记录

一、前言

在构建 AI 聊天应用的过程中,很多开发者往往会把"记忆"和"聊天记录"混为一谈。但实际上,这两者在系统中的角色完全不同。

在 LangChain4j 的体系中,"记忆(Memory)"更多是服务于大模型本身,用于拼接上下文、提升对话连贯性,通常依赖于 Redis 等高性能存储实现短期缓存。而"聊天记录"则是面向用户的功能,需要支持历史查询、分页展示,甚至多会话管理,这类数据更适合持久化存储在 MySQL 中。

因此,在一个完整的 AI 聊天系统中,我们往往需要同时设计两套存储体系:

一套用于服务模型的"短期记忆",另一套用于服务用户的"长期历史记录"。

本文将接上期来实现长期历史记录。

二、环境与依赖

环境上期有说到,与上次相似但需要mysql。

依赖

复制代码
<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>
</dependency>

application.yml

复制代码
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/langchain4j_db?useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: 123456

ChatHistory

复制代码
@Data
public class ChatHistory {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String sessionId;
    private String userId;
    private String role;
    private String content;
    private LocalDateTime createTime;
}

ChatHistoryService

复制代码
@Service
public class ChatHistoryService {

    @Resource
    private ChatHistoryMapper mapper;

    public void save(String sessionId, String userId, String role, String content) {
        ChatHistory chat = new ChatHistory();
        chat.setSessionId(sessionId);
        chat.setUserId(userId);
        chat.setRole(role);
        chat.setContent(content);

        mapper.insert(chat);
    }
}

ChatHistoryMapper

复制代码
public interface ChatHistoryMapper extends BaseMapper<ChatHistory> {
}

AiController

复制代码
@Resource
private AiCodeHelperService aiCodeHelperService;

@Resource
private ChatHistoryService chatHistoryService;



@GetMapping(value = "/chat", produces = "text/event-stream")
public Flux<ServerSentEvent<String>> chat(int memoryId, String message) {

    if (message == null || message.isBlank()) {
        return Flux.empty();
    }

    String sessionId = String.valueOf(memoryId);
    String userId = "default";

    // 存用户消息
    chatHistoryService.save(sessionId, userId, "user", message);

    StringBuilder fullAnswer = new StringBuilder();

    return aiCodeHelperService.chatStream(memoryId, message)

            .doOnNext(chunk -> fullAnswer.append(chunk))

            .bufferUntil(chunk ->
                    chunk.endsWith("。") ||
                            chunk.endsWith("!") ||
                            chunk.endsWith("?") ||
                            chunk.endsWith("\n")
            )
            .map(list -> String.join("", list))
            .map(sentence -> ServerSentEvent.<String>builder().data(sentence).build())

            // 存AI回复
            .doOnComplete(() -> {
                chatHistoryService.save(
                        sessionId,
                        userId,
                        "assistant",
                        fullAnswer.toString()
                );
            });
}

Langchain4jApplication

复制代码
@MapperScan("cn.langchain4j.ai.mapper")

创建数据库 langchain4j_db

复制代码
CREATE TABLE chat_history (

    id BIGINT PRIMARY KEY AUTO_INCREMENT,

    session_id VARCHAR(64),

    user_id VARCHAR(64),

    role VARCHAR(20),

    content TEXT,

    create_time DATETIME DEFAULT CURRENT_TIMESTAMP

);

到这里就完成了对聊天信息的存储,接下来做查询。

MybatisPlusConfig

复制代码
@Configuration
public class MybatisPlusConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return interceptor;
    }
}

ChatHistoryService

复制代码
public Page<ChatHistory> page(String sessionId, int page, int size) {

    Page<ChatHistory> pageParam = new Page<>(page, size);

    LambdaQueryWrapper<ChatHistory> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(ChatHistory::getSessionId, sessionId)
            .orderByDesc(ChatHistory::getCreateTime);

    return mapper.selectPage(pageParam, wrapper);
}

AiController

复制代码
@GetMapping("/history")
public Page<ChatHistory> history(
        String sessionId,
        int page,
        int size) {

    return chatHistoryService.page(sessionId, page, size);
}
相关推荐
斯内普吖10 分钟前
(开源)宠物饲养系统实战指南 基于 Java + SSM + Vue + MySQL
java·vue.js·mysql·开源·宠物
ao-weilai21 分钟前
MySQL数据库:表约束
数据库·mysql·adb
云和数据.ChenGuang1 小时前
JAVAEE AI工程师路线图
java·人工智能·java-ee·fastapi·springai·langchain4j
FYKJ_20103 小时前
springboot心理健康管理系统86254-计算机课程设计、毕业设计
vue.js·spring boot·后端·python·mysql·django·课程设计
做运维的阿瑞3 小时前
MYSQL从语义到执行计划:UNION、EXISTS、IN 的深度辨析
数据库·sql·mysql
小马同学-14 小时前
MySQL主从复制和读写分离
数据库·mysql
海绵宝宝转agent14 小时前
MySql高频面试八股开源笔记总结
mysql·面试·开源
余槐i16 小时前
数据没回滚也不报错:@Transactional 自调用失效的 3 种复现与修复
spring boot·mysql·多线程·spring 事务
ZealSinger18 小时前
LangChain4j用AiServices拼Tool
java·rag·ai智能体·langchain4j
写后端的胖头鱼20 小时前
【高频面试题】FullText 全文索引(MySQL)
数据库·mysql·索引·全文索引