news 2026/9/1 7:10:41

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/9/1 7:13:49

如何用农业Agent将化肥成本降低40%?3个真实案例深度拆解

第一章&#xff1a;农业种植 Agent 的施肥量在现代农业智能化进程中&#xff0c;基于AI的农业种植 Agent 被广泛应用于作物管理决策中&#xff0c;其中施肥量的精准控制是提升产量与减少资源浪费的关键环节。通过采集土壤养分、气象数据和作物生长阶段等信息&#xff0c;Agent …

作者头像 李华
网站建设 2026/9/2 5:13:03

生物制药Agent实验优化实战(罕见高成功率方案曝光)

第一章&#xff1a;生物制药Agent实验设计的核心挑战在生物制药领域&#xff0c;基于智能Agent的实验设计正逐步成为优化药物研发流程的关键技术。然而&#xff0c;其实际应用面临多重核心挑战&#xff0c;涉及数据异构性、模型可解释性以及实验闭环控制等复杂问题。数据来源与…

作者头像 李华
网站建设 2026/9/1 2:15:53

【专家亲授】物流Transport Agent高可用架构设计:9个不可忽视的设计原则

第一章&#xff1a;物流运输 Agent 的时效保证在现代物流系统中&#xff0c;运输时效是衡量服务质量的核心指标之一。引入智能 Agent 技术可显著提升调度效率与履约准确性。这些 Agent 能够实时感知订单状态、交通状况和资源负载&#xff0c;并基于预设策略自主决策最优路径与交…

作者头像 李华
网站建设 2026/9/1 7:12:58

边缘AI推理速度提升300%?揭秘模型压缩与硬件协同优化黑科技

第一章&#xff1a;边缘 AI Agent 的推理速度在边缘计算场景中&#xff0c;AI Agent 的推理速度直接影响用户体验与系统响应能力。由于边缘设备资源受限&#xff0c;如何在低延迟、低功耗的前提下实现高效推理成为关键挑战。影响推理速度的核心因素 模型复杂度&#xff1a;参数…

作者头像 李华
网站建设 2026/9/1 15:42:39

AI Agent如何重塑学习路径?6个真实案例看懂推荐系统的威力

第一章&#xff1a;AI Agent驱动下的个性化学习新范式在人工智能技术迅猛发展的背景下&#xff0c;教育领域正经历一场由AI Agent引领的深刻变革。传统的“一刀切”教学模式逐渐被以学习者为中心的个性化学习路径所取代。AI Agent作为智能学习的核心引擎&#xff0c;能够实时分…

作者头像 李华