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拆代码的曹操4 小时前
MySQL GROUP BY 性能优化:执行计划分析 + 索引重构实战
mysql·性能优化·重构
安_8 小时前
MySQL SQL 调优完整指南
数据库·sql·mysql
艾莉丝努力练剑12 小时前
【MYSQL】MYSQL学习的一大重点:事务(上)- 原子性与持久性
android·数据库·学习·mysql·面试
全栈派森1 天前
告别大表膨胀!MySQL 冷热分离落地全过程(含 CDC 与单行锁抢占实战)
大数据·mysql
篮框坏了1 天前
大事务 DELETE 锁表排查:删 3344 万行,一个清理任务变成周期炸弹
mysql
LinuxGeek10241 天前
Kylin-Server-V11、openEuler-22.03和openEuler-24.03适配原生rpm的MySQL 8.4.11版本正式发布
大数据·mysql·kylin
Lucifer三思而后行1 天前
TDSQL MySQL 版 DBA 实用 100 条命令
mysql·tdsql
佛祖让我来巡山1 天前
Langchain4J+SpringBoot 保姆级教程来了
langchain4j·langchain4j+springboot
染指11101 天前
72.高级RAG-sql检索器
python·sql·mysql·llama·llamaindex·高级rag
wWYy.1 天前
Mysql:覆盖索引
android·数据库·mysql