SpringAI+DeepSeek大模型應用開發——6基于MongDB持久化對話

持久化對話

默認情況下,聊天記憶存儲在內存中ChatMemory chatMemory = new InMemoryChatMemory()

如果需要持久化存儲,可以實現一個自定義的聊天記憶存儲類,以便將聊天消息存儲在你選擇的任何持久化存儲介質中。

MongoDB

文檔型數據庫,數據以JSON - like的文檔形式存儲,具有高度的靈活性和可擴展性。它不需要預先定義嚴格的表結構,適合存儲半結構化或非結構化的數據。

當聊天記憶中包含多樣化的信息,如文本消息、圖片、語音等多媒體數據,或者消息格式可能會頻繁變化時,MongoDB 能很好地適應這種靈活性。例如,一些社交應用中用戶可能會發送各種格式的消息,使用 MongoDB 可以方便地存儲和管理這些不同類型的數據。

整合SpringBoot

引入MongoDB依賴:

<!-- Spring Boot Starter Data MongoDB -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

添加遠程連接配置:

#MongoDB連接配置
spring:data:mongodb:uri: mongodb://localhost:27017/chat_memory_dbusername: rootpassword: xxx
實體類

映射MongoDB中的文檔(相當與MySQL的表)

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.bson.types.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;@Data
@AllArgsConstructor
@NoArgsConstructor
@Document("chatMessages")
public class ChatMessages {//唯一標識,映射到 MongoDB 文檔的 _id 字段@Idprivate ObjectId id;private String conversationId;  //會話IDprivate String messagesJson;  //消息JSON}
消息序列化器

對聊天消息message進行 序列化 和 反序列化 操作

消息序列化(messagesToJson 方法):

將一組 Message 對象(如 UserMessage、AssistantMessage)轉換為 JSON 字符串。

用于將內存中的聊天記錄保存到存儲介質(如數據庫、文件)或通過網絡傳輸。

消息反序列化(messagesFromJson 方法):

將 JSON 字符串還原為 Message 對象列表。
用于從持久化存儲或網絡接收的數據中恢復聊天消息對象。

支持多態反序列化(MessageDeserializer 類)

根據 JSON 中的 messageType 字段判斷消息類型(如 “USER” 或 “ASSISTANT”),并創建對應的子類實例。

解決了 Jackson 默認無法識別接口或抽象類具體實現的問題。

格式美化(可選)

啟用了 SerializationFeature.INDENT_OUTPUT,使輸出的 JSON 更具可讀性(適合調試和日志輸出)。

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;import java.io.IOException;
import java.util.List;
import java.util.Map;
//聊天消息序列化器
public class MessageSerializer {private static final ObjectMapper objectMapper = new ObjectMapper();static {objectMapper.enable(SerializationFeature.INDENT_OUTPUT);SimpleModule module = new SimpleModule();module.addDeserializer(Message.class, new MessageDeserializer());objectMapper.registerModule(module);}public static String messagesToJson(List<Message> messages) throws JsonProcessingException {return objectMapper.writeValueAsString(messages);}public static List<Message> messagesFromJson(String json) throws JsonProcessingException {return objectMapper.readValue(json, new TypeReference<List<Message>>() {});}private static class MessageDeserializer extends JsonDeserializer<Message> {@Overridepublic Message deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {Map<String, Object> node = p.readValueAs(Map.class);String type = (String) node.get("messageType");switch (type) {case "USER":return new UserMessage((String) node.get("text"));case "ASSISTANT":return new AssistantMessage((String) node.get("text"));default:throw new IOException("未知消息類型: " + type);}}}
}
持久化類
@Component
@RequiredArgsConstructor
public class MongoChatMemory implements ChatMemory {@Resourceprivate MongoTemplate mongoTemplate;@Overridepublic void add(String conversationId, List<Message> messages) {Query query = new Query(Criteria.where("conversationId").is(conversationId));ChatMessages chatMessages = mongoTemplate.findOne(query, ChatMessages.class);List<Message> updatedMessages;if (chatMessages != null) {try {updatedMessages = new java.util.ArrayList<>(chatMessages.getMessagesJson() != null? MessageSerializer.messagesFromJson(chatMessages.getMessagesJson()) : Collections.emptyList());} catch (JsonProcessingException e) {throw new RuntimeException("序列化消息失敗", e);}updatedMessages.addAll(messages);} else {updatedMessages = new java.util.ArrayList<>(messages);}try {String json = MessageSerializer.messagesToJson(updatedMessages);if (chatMessages != null) {Update update = new Update().set("messagesJson", json);mongoTemplate.updateFirst(query, update, ChatMessages.class);} else {ChatMessages newChatMessages = new ChatMessages();newChatMessages.setConversationId(conversationId);newChatMessages.setMessagesJson(json);mongoTemplate.insert(newChatMessages);}} catch (JsonProcessingException e) {throw new RuntimeException("序列化消息失敗", e);}}@Overridepublic List<Message> get(String conversationId, int lastN) {Query query = new Query(Criteria.where("conversationId").is(conversationId));ChatMessages chatMessages = mongoTemplate.findOne(query, ChatMessages.class);if (chatMessages == null || chatMessages.getMessagesJson() == null) {return Collections.emptyList();}try {List<Message> allMessages = MessageSerializer.messagesFromJson(chatMessages.getMessagesJson());int size = allMessages.size();int fromIndex = Math.max(0, size - lastN);return allMessages.subList(fromIndex, size);} catch (JsonProcessingException e) {throw new RuntimeException("反序列化消息失敗", e);}}@Overridepublic void clear(String conversationId) {Query query = new Query(Criteria.where("conversationId").is(conversationId));mongoTemplate.remove(query, ChatMessages.class);}
}
測試

初始化ChatClient時,注入MongoChatMemory

//健康報告對話
@Component
@Slf4j
public class HealthReportApp {private final ChatClient chatClient1;private static final String SYSTEM_PROMPT = "你的名字是“小鹿”,你是一家名為“北京協和醫院”的智能客服。你是一個訓練有素的醫療顧問和醫療伴診助手。你態度友好、禮貌且言辭簡潔。\n" +"1、請僅在用戶發起第一次會話時,和用戶打個招呼,并介紹你是誰。\n" ;//初始化ChatClientpublic HealthReportApp(ChatModel dashscopeChatModel,MongoChatMemory mongoChatMemory) throws IOException {chatClient1 = ChatClient.builder(dashscopeChatModel).defaultSystem(SYSTEM_PROMPT)  //系統預設.defaultAdvisors(new MessageChatMemoryAdvisor(mongoChatMemory), //對話記憶//自定義日志  Advisor,可按需開啟new MyLoggerAdvisor(),// 自定義違禁詞 Advisor,可按需開啟new ProhibitedWordAdvisor()//自定義推理增強,可按需開啟//new ReReadingAdvisor()).build();}public record HealthReport(String title, List<String> suggestions) { }//生成健康報告對話public HealthReport doChatWithReport(String message, String chatId) {HealthReport healthReport = chatClient1.prompt().system(SYSTEM_PROMPT + "分析用戶提供的信息,每次對話后都要生成健康報告,標題為{用戶名}的健康報告,內容為建議列表").user(message).advisors(spec -> spec.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId).param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10)).call().entity(HealthReport.class);log.info("healthReport: {}", healthReport);return  healthReport;}}       

mongodb記錄如下

[ {"messageType" : "USER","metadata" : {"messageType" : "USER"},"media" : [ ],"text" : "你好,我是程序員kk"
}, {"messageType" : "ASSISTANT","metadata" : {"messageType" : "ASSISTANT"},"toolCalls" : [ ],"media" : [ ],"text" : "{\n  \"suggestions\": [\n    \"您好,程序員kk,我是北京協和醫院的智能客服小鹿,很高興為您服務。\",\n    \"長期從事編程工作可能導致久坐,建議您定時起身活動,保持良好姿勢。\",\n    \"注意用眼衛生,每工作40-50分鐘休息一下眼睛。\",\n    \"合理安排作息時間,保證充足睡眠以維持身體和心理健康。\"\n  ],\n  \"title\": \"程序員kk的健康報告\"\n}"
}, {"messageType" : "USER","metadata" : {"messageType" : "USER"},"media" : [ ],"text" : "你是誰"
}, {"messageType" : "ASSISTANT","metadata" : {"finishReason" : "STOP","id" : "0110f881-f29f-9cef-b142-7a7cf9e3e76c","role" : "ASSISTANT","messageType" : "ASSISTANT","reasoningContent" : ""},"toolCalls" : [ ],"media" : [ ],"text" : "{\n  \"suggestions\": [\n    \"您好,我是北京協和醫院的智能客服小鹿,很高興為您服務。\",\n    \"作為您的醫療顧問和伴診助手,我將為您提供專業建議。\",\n    \"請告訴我您的需求或問題,我會盡力幫助您。\"\n  ],\n  \"title\": \"程序員kk的健康報告\"\n}"
} ]

接口開發

為了在Controller層實現AI對話歷史記錄的功能,將添加兩個接口:根據chatId查詢特定對話歷史記錄和查詢所有對話的摘要列表。

//獲取所有conversationId
public List<String> findAllConversationIds(String userId) {if (userId == null || userId.isEmpty()) {return Collections.emptyList(); // 或拋出異常}// 構建正則表達式:以 userId + "_" 開頭Pattern pattern = Pattern.compile("^" + Pattern.quote(userId) + "_");// 使用 regex 替代 matchesQuery query = new Query(Criteria.where("conversationId").regex(pattern));return mongoTemplate.findDistinct(query,"conversationId",ChatMessages.class,String.class);
}
/*** 根據 chatId 獲取歷史聊天記錄* 支持參數 lastN 控制獲取最近 N 條消息(默認獲取全部)*/@GetMapping("/history/{chatId}")public BaseResponse<List<Message>> getChatHistory(@PathVariable String chatId,@RequestParam(defaultValue = "-1") int lastN) {int effectiveLastN = lastN <= 0 ? Integer.MAX_VALUE : lastN;List<Message> history = chatMemory.get(chatId, effectiveLastN);return ResultUtils.success(history);}/*** 獲取所有 chatId 列表(用于展示對話歷史頁面)*/@GetMapping("/conversations")public BaseResponse<List<String>> getAllConversations() {Long currentUserId= BaseContext.getCurrentId();String userId = currentUserId.toString();List<String> conversationIds =  chatMemory.findAllConversationIds(userId);return ResultUtils.success(conversationIds);}/*** 新建對話:生成新的 chatId,并在 MongoDB 中插入一條空記錄*/@PostMapping("/conversations/add")public BaseResponse<String> createNewConversation() {Long currentUserId= BaseContext.getCurrentId();String conversationId = currentUserId + "_" + UUID.randomUUID().toString();chatMemory.add(conversationId, Collections.emptyList()); // 插入空消息記錄return ResultUtils.success(conversationId);}/*** 刪除指定 chatId 的對話記錄*/@DeleteMapping("/conversations/{chatId}")public BaseResponse<Boolean> deleteConversation(@PathVariable String chatId) {chatMemory.clear(chatId);return ResultUtils.success(true);}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/87596.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/87596.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/87596.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Mac電腦-音視頻剪輯編輯-Final Cut Pro X(fcpx)

Final Cut Pro Mac是一款專業的視頻剪輯工具&#xff0c;專為蘋果用戶設計。 它具備強大的視頻剪輯、音軌、圖形特效和調色功能&#xff0c;支持整片輸出&#xff0c;提升創作效率。 經過Apple芯片優化&#xff0c;利用Metal引擎動力&#xff0c;可處理更復雜的項目&#xff…

不同程度多徑效應影響下的無線通信網絡電磁信號仿真數據生成程序

生成.mat數據&#xff1a; %創建時間&#xff1a;2025年6月19日 %zhouzhichao %遍歷生成不同程度多徑效應影響的無線通信網絡拓撲推理數據用于測試close all clearsnr 40; n 30;dataset_n 100;for bias 0.1:0.1:0.9nodes_P ones(n,1);Sampling_M 3000;%獲取一幀信號及對…

Eureka 和 Feign(二)

Eureka 和 Feign 是 Spring Cloud 微服務架構中協同工作的兩個核心組件&#xff0c;它們的關系可以通過以下比喻和詳解來說明&#xff1a; 關系核心&#xff1a;服務發現 → 動態調用 組件角色核心功能Eureka服務注冊中心服務實例的"電話簿"Feign聲明式HTTP客戶端根…

Springboot仿抖音app開發之RabbitMQ 異步解耦(進階)

Springboot仿抖音app開發之評論業務模塊后端復盤及相關業務知識總結 Springboot仿抖音app開發之粉絲業務模塊后端復盤及相關業務知識總結 Springboot仿抖音app開發之用短視頻務模塊后端復盤及相關業務知識總結 Springboot仿抖音app開發之用戶業務模塊后端復盤及相關業務知識…

1.部署KVM虛擬化平臺

一.KVM原理簡介 廣義的KVM實際上包含兩部分&#xff0c;一部分是基于Linux內核支持的KVM內核模塊&#xff0c;另一部分就是經過簡化和修改的Qemuo KVM內核模塊是模擬處理器和內存以支持虛擬機的運行&#xff0c;Qemu主要處理丨℃以及為用戶提供一個用戶空間工具來進行虛擬機的…

優化與管理數據庫連接池

優化與管理數據庫連接池 在現代高并發系統中,數據庫連接池是保障數據庫訪問性能的核心組件之一。合理配置、優化和管理連接池,可以有效緩解連接創建成本高、連接頻繁斷開重連等問題,從而提升系統整體的響應速度與穩定性。 數據庫連接池的作用與價值 數據庫連接池的核心思…

實現回顯服務器(基于UDP)

目錄 一.回顯服務器的基本概念 二.回顯服務器的簡單示意圖 三.實現回顯服務器&#xff08;基于UDP&#xff09;必須要知道的API 1.DatagramSocket 2.DatagramPacket 3.InetSocketAddress 4.二者區別 1. 功能職責 2. 核心作用 3. 使用場景流程 四.實現服務器端的主…

LabVIEW電液伺服閥自動測試

針對航空航天及工業液壓領域電液伺服閥測試需求&#xff0c;采用 LabVIEW 圖形化編程平臺&#xff0c;集成 NI、GE Druck 等品牌硬件&#xff0c;構建集靜態特性&#xff08;流量/ 壓力 / 泄漏&#xff09;與動態特性&#xff08;頻率響應&#xff09;測試于一體的自動化系統&a…

性能優化 - 高級進階: Spring Boot服務性能優化

文章目錄 Pre引言&#xff1a;為何提前暴露指標與分析的重要性指標暴露與監控接入Prometheus 集成 性能剖析工具&#xff1a;火焰圖與 async-profilerasync-profiler 下載與使用結合 Flame 圖優化示例 HTTP 及 Web 層優化CDN 與靜態資源加速Cache-Control/Expires 在 Nginx 中配…

力扣網C語言編程題:除自身以外數組的乘積

一. 簡介 本文記錄力扣網上涉及數組方面的編程題&#xff0c;主要以 C語言實現。 二. 力扣上C語言編程題&#xff1a;涉及數組 題目&#xff1a;除自身以外數組的乘積 給你一個整數數組 nums&#xff0c;返回 數組 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i…

SpringBoot擴展——發送郵件!

發送郵件 在日常工作和生活中經常會用到電子郵件。例如&#xff0c;當注冊一個新賬戶時&#xff0c;系統會自動給注冊郵箱發送一封激活郵件&#xff0c;通過郵件找回密碼&#xff0c;自動批量發送活動信息等。郵箱的使用基本包括這幾步&#xff1a;先打開瀏覽器并登錄郵箱&…

【html】iOS26 液態玻璃實現效果

<!DOCTYPE html> <html lang"zh"><head><meta charset"UTF-8" /><meta name"viewport" content"widthdevice-width, initial-scale1.0" /><title>液體玻璃效果演示</title><style>bo…

探索算法秘境:量子隨機游走算法及其在圖論問題中的創新應用

目錄 ?編輯 一、量子隨機游走算法的起源與原理 二、量子隨機游走算法在圖論問題中的創新應用 三、量子隨機游走算法的優勢與挑戰 四、結語 在算法研究的浩瀚星空中&#xff0c;總有一些領域如同遙遠星系&#xff0c;閃爍著神秘而誘人的光芒。今天&#xff0c;我們將一同深…

C# 一維數組和矩形數組全解析

在編程的世界里&#xff0c;數組是一種非常重要的數據結構。今天&#xff0c;我們就來詳細了解一下一維數組和矩形數組。 數組基礎認知 數組實例是從 System.Array 繼承類型的對象。由于它從 BCL 基類派生而來&#xff0c;所以繼承了許多有用的成員&#xff1a; Rank 屬性&a…

WebStorm編輯器側邊欄

目錄 編輯器側邊欄行號配置行號隱藏行號 代碼折疊側邊欄圖標書簽添加匿名書簽添加助記符書簽 運行和調試管理斷點配置斷點圖標 版本控制配置Git Blame注釋 編輯器側邊欄 編輯器左側的垂直區域。當編寫代碼時&#xff0c;提供重要信息和操作圖標。外觀和行為可以根據你的喜好進…

騰訊云TCCA認證考試報名 - TDSQL數據庫交付運維工程師(PostgreSQL版)

數據庫交付運維工程師-騰訊云TDSQL(PostgreSQL版)認證 適合人群&#xff1a; 適合從事TDSQL(PostgreSQL版)交付、運維、售前咨詢以及TDSQL(PostgreSQL版)相關項目的管理人員。 認證考試 單選*40道多選*20道 成績查詢 70分及以上通過認證&#xff0c;官網個人中心->認證考…

attn_mask 為 (1, 1) 時什么意思? 7,7又是什么意思?

在深度學習中&#xff0c;特別是在 Transformer 模型和注意力機制&#xff08;Attention Mechanism&#xff09;中&#xff0c;attn_mask&#xff08;注意力掩碼&#xff09;是一個用于控制注意力計算的張量。它決定了在計算注意力分數時&#xff0c;哪些位置應該被關注&#x…

Qt聯合Halcon開發二:Halcon窗口綁定Qt控件顯示Hobject圖像【詳細圖解流程】

1. 項目準備 在本項目中&#xff0c;我們將使用Qt框架與Halcon庫結合&#xff0c;展示圖像并進行圖像處理。首先&#xff0c;確保你已經配置好Qt和Halcon的開發環境。 環境配置可查看上篇文章 2. 創建Qt界面 在Qt中&#xff0c;創建一個窗口并拖入按鈕和Graphics View控件。G…

Redis 持久化機制詳解:RDB、AOF 原理與面試最佳實踐(AOF篇)

在上一章我們深入學習了 Redis 中重要的數據持久化機制 ——RDB&#xff08;Redis Database&#xff09;&#xff0c;了解了其通過周期性快照將數據以二進制文件形式保存到磁盤的原理&#xff0c;包括觸發條件、文件結構以及優缺點等核心內容。 Redis 持久化機制詳解&#xff…

【GateWay】和權限驗證

【GateWay】網關詳解和權限驗證 一、Gateway 核心概念與架構二、路由斷言&#xff08;Route Predicates&#xff09;詳解三、過濾器&#xff08;Filters&#xff09;機制四、權限認證的核心理論模型五、Spring Cloud Gateway Security OAuth2 集成方案六、OAuth2.0 集成 一、…