news 2026/4/3 4:22:30

SpringAi基于PgSQL数据库存储扩展ChatMemory

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringAi基于PgSQL数据库存储扩展ChatMemory

一、环境准备

SpringAI入门学习

<!-- SpringAI--> <dependency> <groupId>com.alibaba.cloud.ai</groupId> <artifactId>spring-ai-alibaba-starter</artifactId> <version>1.0.0-M6.1</version> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <version>42.3.1</version> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId> <version>1.0.0-M6</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>

上文中实现了基于内存的会话保持功能,现在要基于数据库扩展实现

JdbcTemplate+PgSQL数据库实现扩展ChatMemory实现。

二、扩展实现

package org.springframework.ai.chat.memory; import java.util.List; import org.springframework.ai.chat.messages.Message; public interface ChatMemory { default void add(String conversationId, Message message) { this.add(conversationId, List.of(message)); } void add(String conversationId, List<Message> messages); List<Message> get(String conversationId, int lastN); void clear(String conversationId); }
  • 表结构准备
CREATE TABLE chat_messages ( id BIGSERIAL PRIMARY KEY, conversation_id VARCHAR(255) NOT NULL, message_type VARCHAR(50) NOT NULL, content TEXT NOT NULL, created_at TIMESTAMP NOT NULL ); -- 创建索引 CREATE INDEX idx_conversation_id ON chat_messages (conversation_id);
  • ChatDao接口定义
package org.spring.springaiprojet.dao; import org.spring.springaiprojet.entity.ChatMessageEntity; import java.util.List; public interface ChatDao { /** * 保存表 * @param messages */ void insertMessages(List<ChatMessageEntity> messages); /** * 查询最近的N条消息 * @param conversationId * @param lastN * @return */ List<ChatMessageEntity> findLastNMessages(String conversationId, int lastN); /** * 删除会话 * @param conversationId */ void deleteByConversationId(String conversationId); }
  • ChatImpl接口实现
import org.spring.springaiprojet.dao.ChatDao; import org.spring.springaiprojet.entity.ChatMessageEntity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Repository; import java.time.LocalDateTime; import java.util.List; @Repository public class ChatDaoImpl implements ChatDao { @Autowired private JdbcTemplate jdbcTemplate; @Override public void insertMessages(List<ChatMessageEntity> messages) { jdbcTemplate.batchUpdate("insert into chat_messages (conversation_id, message_type, content, created_at) values (?, ?, ?, ?)", messages, messages.size(), (ps, message) -> { ps.setString(1, message.getConversationId()); ps.setString(2, message.getMessageType()); ps.setString(3, message.getContent()); ps.setObject(4, message.getCreatedAt()); }); } @Override public List<ChatMessageEntity> findLastNMessages(String conversationId, int lastN) { return jdbcTemplate.query( "select * from chat_messages where conversation_id = ? order by created_at desc limit ?", (rs, rowNum) -> { ChatMessageEntity message = new ChatMessageEntity(); message.setConversationId(rs.getString("conversation_id")); message.setMessageType(rs.getString("message_type")); message.setContent(rs.getString("content")); message.setCreatedAt(rs.getObject("created_at", LocalDateTime.class)); return message; }, conversationId, lastN ); } @Override public void deleteByConversationId(String conversationId) { jdbcTemplate.update("delete from chat_messages where conversation_id = ?", conversationId); } }
  • ChatMessageEntity
package org.spring.springaiprojet.entity; import java.time.LocalDateTime; public class ChatMessageEntity { private Long id; private String conversationId; private String messageType; private String content; private LocalDateTime createdAt; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getConversationId() { return conversationId; } public void setConversationId(String conversationId) { this.conversationId = conversationId; } public String getMessageType() { return messageType; } public void setMessageType(String messageType) { this.messageType = messageType; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public LocalDateTime getCreatedAt() { return createdAt; } public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; } }
  • PgSQLChatMemory
package org.spring.springaiprojet.config.chat; import org.spring.springaiprojet.dao.ChatDao; import org.spring.springaiprojet.entity.ChatMessageEntity; import org.spring.springaiprojet.entity.MessageEnum; import org.springframework.ai.chat.memory.ChatMemory; import org.springframework.ai.chat.messages.AssistantMessage; import org.springframework.ai.chat.messages.Message; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.time.LocalDateTime; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @Component public class PgSQLChatMemory implements ChatMemory { @Autowired private ChatDao chatDao; @Override public void add(String conversationId, List<Message> messages) { if (messages == null || messages.isEmpty()) { return; } List<ChatMessageEntity> entities = messages.stream() .map(msg -> { ChatMessageEntity entity = new ChatMessageEntity(); entity.setConversationId(conversationId); entity.setContent(msg.getText()); if (msg instanceof UserMessage) { entity.setMessageType(MessageEnum.USER.getValue()); } else if (msg instanceof AssistantMessage) { entity.setMessageType(MessageEnum.ASSISTANT.getValue()); } entity.setCreatedAt(LocalDateTime.now()); return entity; }) .collect(Collectors.toList()); chatDao.insertMessages(entities); } @Override public List<Message> get(String conversationId, int lastN) { List<ChatMessageEntity> entities = chatDao.findLastNMessages(conversationId, lastN); if (entities == null || entities.isEmpty()) { return Collections.emptyList(); } // 倒序 Collections.reverse(entities); return entities.stream() .map(entity -> { switch (entity.getMessageType()) { case "user": return new UserMessage(entity.getContent()); case "assistant": return new AssistantMessage(entity.getContent()); default: throw new IllegalArgumentException("未知的消息类型!"); } }) .collect(Collectors.toList()); } @Override public void clear(String conversationId) { if (conversationId == null){ return; } chatDao.deleteByConversationId(conversationId); } }
  • AiConfig实现
/** * 基于PgSQL实现会话记忆 */ @Autowired private PgSQLChatMemory pgSQLChatMemory; @Bean public ChatClient chatClient(ChatClient.Builder builder) { // defaultSystem,默认系统角色,带有对话身份 return builder // .defaultSystem("请以通俗开发者角度介绍") // 增加会话记忆,基于 .defaultAdvisors(new PromptChatMemoryAdvisor(pgSQLChatMemory)).build(); // .defaultAdvisors(new SimpleLoggerAdvisor()).build(); }
  • 控制器实现
/** * 普通对话模式 * @param question * @return */ @RequestMapping("/qwen/chat/api") public String chat(String question) { return chatClient.prompt().user(question).call().content(); }

三、测试调用

  • 第一次调用如下接口
GET http://localhost:8088/boot/ai/qwen/chat/api?question=SpringBoot框架各个模块作用

已经写入数据表中了

  • 第二次基于上文会话内容,写入表中
GET http://localhost:8088/boot/ai/qwen/chat/api?question=基于上述回答内容,SpringBoot哪个模块学习难度较大,评估一下

再次查看数据库表,发现已经基于会话保存回答问题了

  • 第三次提问,会话记忆
GET http://localhost:8088/boot/ai/qwen/chat/api?question=基于上述回答内容,SpringBoot哪个模块学习难度相对较小,比较容易上手,评估一下

再次查看数据库,以及基本保存进来,基于数据库内容实现上下文会话回答了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/1 17:44:36

新版本《八仙过海》

更新到新版本《八仙过海》 一恨胡适慧眼亮&#xff0c;反诈先锋不上当 二恨爱玲急转向&#xff0c;空舞皮带无处放 三恨倪匡 冒险王&#xff0c;荒野求生刀工强 四恨大千情场浪&#xff0c;会晤青霞老益壮 五恨钱穆 遁香港&#xff0c;修史办学书声朗 六恨政道不返乡&…

作者头像 李华
网站建设 2026/3/26 12:22:24

【SRE专家亲授】云原生Agent故障恢复的7个关键检查点

第一章&#xff1a;云原生Agent故障恢复概述在现代云原生架构中&#xff0c;Agent作为连接控制平面与数据平面的关键组件&#xff0c;承担着指标采集、配置同步、健康检查等核心职责。由于其部署环境的高度动态性&#xff0c;网络波动、节点失效或资源争用可能导致Agent进程异常…

作者头像 李华
网站建设 2026/3/20 20:28:21

卫星Agent信号丢失应急处理:5分钟快速定位与恢复操作手册

第一章&#xff1a;卫星Agent信号丢失应急处理概述在卫星通信系统中&#xff0c;Agent节点负责采集遥测数据、执行指令与状态上报。当发生信号丢失时&#xff0c;可能导致地面站无法及时掌握卫星运行状态&#xff0c;进而影响任务连续性与安全性。因此&#xff0c;建立一套高效…

作者头像 李华
网站建设 2026/3/29 4:36:35

还在用传统方法评估金融Agent?新一代图驱动风险评估体系来了

第一章&#xff1a;金融图 Agent 风险评估的范式变革传统的金融风险评估依赖静态模型和中心化数据处理&#xff0c;难以应对复杂关联网络中的动态风险传播。随着图神经网络&#xff08;GNN&#xff09;与多智能体系统&#xff08;Multi-Agent System, MAS&#xff09;的融合&am…

作者头像 李华
网站建设 2026/3/25 13:07:53

仅限前沿团队掌握的技术:元宇宙Agent实时协同交互算法首次公开

第一章&#xff1a;元宇宙 Agent 的交互逻辑在元宇宙环境中&#xff0c;Agent&#xff08;智能代理&#xff09;作为用户或系统的自主代表&#xff0c;承担着感知环境、决策行为与执行交互的核心职责。其交互逻辑建立在事件驱动与状态机模型之上&#xff0c;确保在复杂虚拟空间…

作者头像 李华