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);
}
相关推荐
AI人工智能+电脑小能手3 小时前
【大白话说Java面试题 第87题】【Mysql篇】第17题:分布式事务的实现原理?
java·数据库·分布式·mysql·面试
键盘上的猫头鹰6 小时前
【MySQL 教程(八)】索引、事务、用户管理、导入导出与分页查询
数据库·python·mysql
Rick19938 小时前
索引的排序和分组
数据库·mysql
不爱编程的小陈9 小时前
事务的进化:从MySQL单机事务到TiDB分布式事务的探究
分布式·mysql·tidb
ServBay9 小时前
不要再盲选了,PostgreSQL、MySQL与SQLite真实性能对比
数据库·mysql·sqlite
無限進步D9 小时前
MySQL 创建和管理表
数据库·mysql
Rick199310 小时前
mysql联合索引经典实例
java·数据库·mysql
独隅10 小时前
MySQL 接入不同 AI 大模型进行数据管理的全面指南(MySQL + AI)
数据库·人工智能·mysql
我是一颗柠檬12 小时前
【MySQL全面教学】MySQL备份与恢复Day14(2026年)
数据库·后端·mysql
渣渣盟12 小时前
MySQL DDL操作全解析:从入门到精通,包含索引视图分区表等全操作解析
大数据·数据库·mysql