Spring AI 聊天記憶功能實戰(一):從接口設計到生產實踐

Spring AI 聊天記憶功能實戰(一):從接口設計到生產實踐

在構建AI對話應用時,聊天記憶管理及存儲是實現連貫上下文交互的關鍵組件。而大模型(LLM)本質上是無狀態的,這意味著它們不會保留歷史交互信息。當需要跨多輪交互保持上下文時,這一特性會帶來局限。為此,Spring AI 提供了聊天記憶功能,支持在 LLM 交互過程中存儲和檢索上下文數據。

入門理解

Spring AI 框架通過模塊化設計提供了靈活的聊天記憶解決方案,其核心在于ChatMemoryRepositoryChatMemory的協同工作機制。

ChatMemoryRepository 接口提供了各種存儲方式實現的統一抽象,增加了聊天記憶功能的靈活性及擴展性,目前 Spring AI(1.0.0 版本)框架提供了基于 內存(默認)、JDBC、Cassandra、Neo4j 四種存儲方式的實現。

public interface ChatMemoryRepository {// 獲取所有會話IDList<String> findConversationIds();// 獲取指定會話ID的聊天消息List<Message> findByConversationId(String conversationId);// 存儲整個會話ID的歷史消息(替換式更新)void saveAll(String conversationId, List<Message> messages);// 清理指定會話ID中的聊天消息void deleteByConversationId(String conversationId);
}

ChatMemory 抽象層支持實現多種記憶類型以滿足不同場景需求。消息的底層存儲由 ChatMemoryRepository 處理,其唯一職責是存儲和檢索消息。ChatMemory 實現類可自主決定消息保留策略 —— 例如保留最近 N 條消息、按時間周期保留或基于 Token 總量限制保留。

public interface ChatMemory {// 從聊天記憶的上下文檢索會話IDString CONVERSATION_ID = "chat_memory_conversation_id";// 將聊天消息保存到指定會話ID的存儲中void add(String conversationId, List<Message> messages);// 獲取指定會話ID中的聊天消息List<Message> get(String conversationId);// 清理指定會話ID中的聊天消息void clear(String conversationId);
}

快速使用

Spring AI 自動配置 ChatMemory Bean 供直接使用。默認采用內存存儲(InMemoryChatMemoryRepository)及 MessageWindowChatMemory 實現管理會話歷史。若已配置其他 Repository(如 Cassandra / JDBC / Neo4j ),則自動切換至對應實現。

記憶類型:MessageWindowChatMemory 維護固定容量的消息窗口(默認 20 條)。當消息超限時,自動移除較早的對話消息(始終保留系統消息)。

// 自動注入
@Autowired
ChatMemory chatMemory;// 手動創建
MessageWindowChatMemory memory = MessageWindowChatMemory.builder().maxMessages(10).build();

記憶存儲:InMemoryChatMemoryRepository 內存存儲為默認的聊天記憶存儲實現。同樣地,Spring AI 提供了統一的自動配置,可直接自動注入使用。

// 自動注入
@Autowired
ChatMemoryRepository chatMemoryRepository;// 手動創建
ChatMemoryRepository repository = new InMemoryChatMemoryRepository();

自動配置在默認情況下,Spring AI 注入了如下 Bean 對象:

@Bean
ChatMemoryRepository chatMemoryRepository() {return new InMemoryChatMemoryRepository();
}@Bean
ChatMemory chatMemory(ChatMemoryRepository chatMemoryRepository) {return MessageWindowChatMemory.builder().chatMemoryRepository(chatMemoryRepository).build();
}

原理實現

聊天記憶存儲的實現主要依靠 ChatMemoryRepository 接口 及 ChatMemory 接口,它們的默認實現類分別為 InMemoryChatMemoryRepositoryMessageWindowChatMemory

InMemoryChatMemoryRepository實現類:

public final class InMemoryChatMemoryRepository implements ChatMemoryRepository {Map<String, List<Message>> chatMemoryStore = new ConcurrentHashMap<>();// 方法實現...
}

InMemoryChatMemoryRepository 使用并發安全的 ConcurrentHashMap 實現聊天記錄的增刪改操作,存儲實現依賴 Map 的 get、put、remove 操作。需要注意的是,這里 saveAll(寫入記錄)方法實現是替換舊數據的更新邏輯,若對應到外部存儲的寫入方式應該先刪除后插入數據,而限制聊天對話歷史長度的邏輯在 MessageWindowChatMemory 類實現。

MessageWindowChatMemory 實現類:

public final class MessageWindowChatMemory implements ChatMemory {private final ChatMemoryRepository chatMemoryRepository;private final int maxMessages;// 方法實現...private List<Message> process(List<Message> memoryMessages, List<Message> newMessages) {// 根據最大消息數處理消息窗口}
}

處理消息窗口容量的主要邏輯為:

  • 識別新增的SystemMessage,當有新系統消息時,清除所有舊系統消息、所有的新SystemMessage不受容量限制;
  • 保留非系統消息歷史+新增消息,按添加順序移除最早的非系統消息,確保 最終消息數 == maxMessages、返回處理后的新消息;

消息窗口的聊天記憶管理與數據存儲交互的 UML 類圖 如下:

?interface?
ChatMemory
+add(String, List<Message>)
+get(String) : List<Message>
+clear(String)
MessageWindowChatMemory
-chatMemoryRepository: ChatMemoryRepository
-maxMessages: int
-process(List<Message> memoryMessages, List<Message> newMessages) : List<Message>
?interface?
ChatMemoryRepository
+findConversationIds() : List<Message>
+findByConversationId(String) : List<Message>
+saveAll(String, List<Message>)
+deleteByConversationId(String)

實戰案例

接下來我們使用MessageWindowChatMemory+JdbcChatMemoryRepository的方式實現一個AI聊天助手的功能。

JdbcChatMemoryRepository 是內置的 JDBC 實現,支持多種關系型數據庫,適用于需要持久化存儲聊天記憶的場景。JDBC 實現是基于 spring-jdbc 模塊,我們可通過 JdbcTemplate 來配置任一數據源。

1、導入模塊

首先,在 spring-boot 項目中添加以下依賴:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-openai</artifactId>
</dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId>
</dependency><dependency><groupId>org.postgresql</groupId><artifactId>postgresql</artifactId><scope>runtime</scope>
</dependency>

2、配置參數

新建 application.yml,添加配置 spring.aiJdbcTemplate 相關參數:

spring:application:name: AI Assistantai:openai:api-key: ${OPENAI_API_KEY:NONE}base-url: ${OPENAI_BASE_URL:NONE}chat:options:model: DeepSeek-V3completions-path: /v1/chat/completionschat:memory:repository:jdbc:initialize-schema: always # Always initializedatasource:url: jdbc:postgresql://localhost:5432/testusername: rootpassword: 123456driver-class-name: org.postgresql.Driver

3、創建 Bean 實例

Spring AI 為 JdbcChatMemoryRepository 提供了自動配置,可直接在應用中使用 (默認使用 hsqldb 嵌入式數據庫),也可手動創建 Bean 實例。

@Configuration
public class GeneralChatClientConfig {private final JdbcChatMemoryRepository jdbcChatMemoryRepository;public GeneralChatClientConfig(JdbcChatMemoryRepository jdbcChatMemoryRepository) {this.jdbcChatMemoryRepository = jdbcChatMemoryRepository;}@Bean(name = "messageWindowChatMemoryWithJdbc")public MessageWindowChatMemory messageWindowChatMemoryWithJdbc() {int maxMessages = 20;return MessageWindowChatMemory.builder().chatMemoryRepository(jdbcChatMemoryRepository) // default: new InMemoryChatMemoryRepository().maxMessages(maxMessages).build();}
}

目前 Spring AI 支持的數據庫與方言抽象層:

  • PostgreSQL
  • MySQL / MariaDB
  • SQL Server
  • HSQLDB

使用 JdbcChatMemoryRepositoryDialect.from(DataSource) 時可基于 JDBC URL 自動識別正確方言。通過實現 JdbcChatMemoryRepositoryDialect 接口可擴展其他數據庫方言支持。

4、聊天記憶客戶端

創建使用 ChatClient API 時,可通過注入 ChatMemory 實現來維護跨多輪交互的會話上下文。

Spring AI 提供多種內置 Advisor,用于按需配置 ChatClient 的記憶行為。

  • MessageChatMemoryAdvisor:通過指定 ChatMemory 實現管理會話記憶。每次交互時從記憶庫檢索歷史消息,并將其作為消息集合注入提示詞。
  • PromptChatMemoryAdvisor:基于指定 ChatMemory 實現管理會話記憶。每次交互時從記憶庫檢索歷史對話,并以純文本形式追加至系統(system)提示詞。
  • VectorStoreChatMemoryAdvisor:通過指定 VectorStore 實現管理會話記憶。每次交互時從向量存儲檢索歷史對話,并以純文本形式追加至系統(system)消息。

例如,若需結合 MessageWindowChatMemoryPromptChatMemoryAdvisor,可按如下方式配置:

    @Bean(name = "deepseekV3ClientWithJdbc")public ChatClient deepseekV3ClientWithJdbc(@Value("${spring.ai.openai.base-url}") String baseUrl,@Value("${spring.ai.openai.chat.options.model}") String modelName,@Value("${spring.ai.openai.api-key}") String apiKey,@Qualifier("messageWindowChatMemoryWithJdbc") MessageWindowChatMemory messageWindowChatMemoryWithJdbc) {OpenAiApi build = OpenAiApi.builder().apiKey(apiKey).baseUrl(baseUrl).build();OpenAiChatModel openAiChatModel =OpenAiChatModel.builder().openAiApi(build).defaultOptions(OpenAiChatOptions.builder().model(modelName).build()).build();return ChatClient.builder(openAiChatModel).defaultAdvisors(PromptChatMemoryAdvisor.builder(messageWindowChatMemoryWithJdbc).build()).defaultAdvisors(new SimpleLoggerAdvisor()).build();}

調用 ChatClient 時,ChatMemoryAdvisor 將自動管理記憶存儲。系統會根據指定的會話 ID 從記憶庫檢索歷史對話。

我們新建一個 Controller ,注入配置好的 ChatClient,定義一個api實現大模型的 chat 調用,代碼如下:

@RestController
@RequestMapping("/ai/v3/chat/")
@Slf4j
public class GeneralChatController {private final ChatClient jdbcChatClient;public GeneralChatController(@Qualifier("deepseekV3ClientWithJdbc") ChatClient jdbcChatClient) {this.jdbcChatClient = jdbcChatClient;}@PostMapping(value = "/t1", produces = {MediaType.TEXT_EVENT_STREAM_VALUE})@ResponseBodypublic Object chatWithJdbc(@RequestBody String body) {JSONObject entries = JSONUtil.parseObj(body);String text = entries.getStr("text");Boolean stream = entries.getBool("stream", false);String conversationId = entries.getStr("conversationId");log.info("開始對話聊天,會話ID:{}", conversationId);var request = jdbcChatClient.prompt().system("你是樂觀小王,回答問題簡練精要。").advisors(advisor -> advisor.param(CONVERSATION_ID, conversationId)).user(text);try {log.info("開始生成回答,是否流式輸出:{}", stream);return stream ? request.stream().content() : request.call().content();} catch (Exception e) {log.error("對話聊天發生異常,會話ID:{}", conversationId, e);throw e;}}
}

5、測試AI聊天功能

通過上述的簡單配置和代碼編寫,我們已經實現了一個通用的帶有記憶功能的AI聊天助手。

接下來讓我們測試一下這個聊天助手的使用效果,設置幾條 USER 的提問內容,具體內容如下:

# Openai Chat API
### 我的名字叫小明,你叫什么名字?
### 我是誰?
### 我們之間的第一句問話是什么?
POST http://localhost:10001/ai/v3/chat/t1
Content-Type: application/json{
"text": "我的名字叫小明,你叫什么名字?",
"stream": false,
"conversationId": "bnA9f525-l7ae-5c66-ae21-vh53547c96cf"
}

聊天消息記錄會被存儲到 PostgreSQL 數據庫中,http順序請求后的大模型返回結果 如圖所示:

小總結

通過上述測試結果可以清晰看到,基于 Spring AI 搭建的聊天助手憑借聊天記憶存儲功能,能夠準確關聯上下文信息,針對連續提問給出符合對話邏輯的回答。無論是識別用戶身份,還是追溯對話起始內容,系統都能有效利用歷史消息,實現連貫且智能的交互體驗,充分利用聊天記憶存儲機制可以更好的維護對話上下文及提高模型回答的準確性。

目前 Spring AI (1.0.0 版本) 官方還沒有提供 Redis 的聊天記憶外部存儲實現,那么,下一篇文章我們將聚焦于 自定義 Redis 聊天記憶的外部存儲實現,通過 自定義一個外部存儲方式,自己實現一個聊天記憶存儲功能,來進一步提升聊天助手的擴展性與數據持久性,讓AI聊天助手在高并發、大規模對話場景中穩定運行。敬請期待接下來的深度技術解析!

文章案例項目代碼:spring-ai-model-chat-memory-repository-redis

  • https://github.com/Cyanty/spring-ai-model-chat-memory-repository-redis

大數據

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

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

相關文章

Element Plus 對話框 el-dialog 和 抽屜 el-drawer 的使用注意項(使用 div 包裹)

總結&#xff1a;使用 div 包裹&#xff01;&#xff01;&#xff01; 詳細說明&#xff1a; 對話框 el-dialog 或 抽屜 el-drawer 樣式的設置說明&#xff1a; 要想有效設置 el-dialog 或 el-drawer 的樣式&#xff0c;需確保 el-dialog 或 el-drawer 的上層不是template&am…

【python】簡單演示 gateway、service、client的工作原理

gateway 看起來主要是做協議轉換的A gateway is a network node that acts as an entrance and exit point, connecting two networks that use different protocols. It allows data to flow between these networks, essentially acting as a translator between different c…

數據倉庫面試題合集⑥

實時指標體系設計 + Flink 優化實戰:面試高頻問題 + 項目答題模板 面試中不僅會問“你做過實時處理嗎?”,更會追問:“實時指標體系是怎么搭建的?”、“你們的 Flink 穩定性怎么保證?” 本篇聚焦實時指標體系設計與 Flink 優化場景,幫你答出架構設計力,也答出調優實戰感…

Vue + AbortController 請求取消彈窗 hook 封裝

背景 實際業務開發場景中&#xff0c;往往存在有些大數據請求的需求&#xff0c;一旦請求發起加載遮罩后用戶就無法操作了&#xff0c;直接尬住&#xff0c;所以提供一個支持取消查詢的功能還是很有必要的&#xff0c;為了在全業務接口都能使用封裝一個hook。 ?為什么要用 A…

數據結構相關

1 問題 如何辨析數據對象和數據結構&#xff1f;如何設計多種儲存結構以及他們特性有什么&#xff1f;內存條和硬盤的區別&#xff1f; 2 方法 明晰倆者的定義數據對象是性質相同的有限個數據元素的集合&#xff0c;他是數據的一個子集。數據結構是指所涉及的數據元素的集合以及…

MacOS內存管理-刪除冗余系統數據System Data

文章目錄 一、問題復現二、解決思路三、解決流程四、附錄 一、問題復現 以題主的的 Mac 為例&#xff0c;我們可以看到System Data所占數據高達77.08GB&#xff0c;遠遠超出系統所占內存 二、解決思路 占據大量空間的是分散在系統中各個位置Cache數據&#xff1b; 其中容量最…

純視覺SOTA!華科小米推出ReCogDrive:結合VLM和強化學習的端到端自動駕駛框架

摘要 端到端自動駕駛的研究目前越來越火熱&#xff0c;現有方法通過視覺語言模型&#xff08;VLM&#xff09;來解決其在長尾場景中性能降低的問題&#xff0c;但是仍然存在一些局限性。本文提出了ReCogDrive&#xff0c;它將VLM與基于擴散的軌跡規劃器相結合&#xff0c;并且采…

MySQL慢SQL優化全攻略:從診斷到調優

目錄 慢SQL日志分析與診斷 開啟慢查詢日志 慢查詢日志分析工具 慢SQL優化策略 1. 避免SELECT * 查詢 2. 創建高效索引 索引選擇原則 索引使用注意事項 3. 使用EXPLAIN分析執行計劃 4. 優化排序操作 5. 解決深分頁問題 6. 避免全表掃描 7. 優化JOIN操作 8. 合理使用…

OPENPPP2 VMUX 技術探秘(高級指南)

&#x1f680; VMUX技術分析&#xff1a;OPENPPP2中的虛擬多路復用技術 &#x1f31f; 一、技術目標 &#x1f517; 連接多路復用 通過單個或多個物理鏈路&#xff0c;承載多個邏輯TCP連接。 &#x1f680; 高性能傳輸 支持數據包亂序重組實現動態流量控制&#xff08;擁塞檢測…

Linux系統時間不對導致mysql初始化失敗:Data Dictionary initialization failed.(數據字典版本驗證失敗)

文章目錄 問題描述分析**問題原因分析****解決方案****1. 修正系統時間****2. 檢查數據目錄完整性****3. 重新初始化數據目錄****4. 調整 MySQL 配置** **驗證與后續步驟****注意事項** 其他說明 問題描述 mysql數據初始化失敗&#xff0c;發現系統時間是1970年&#xff0c;我…

有趣的python程序Part1:如何根據記憶曲線使用python編寫一個單詞記憶默寫程序

目錄 前言 1. 數據管理模塊 2. 記憶算法實現 3. 持久化存儲 4. 用戶界面實現 5.整合與測試 前言 此篇文章為“有趣的python程序”專欄的第一篇文章&#xff0c;本專欄致力于分享一些有趣的編程作品&#xff0c;如果能夠使您產生興趣&#xff0c;不妨來動手改編使之成為更好…

【案例】性能優化在持續集成與持續交付中的應用

【案例】性能優化在持續集成與持續交付中的應用 為了更好地理解性能優化在CI/CD流程中的實際應用&#xff0c;本節將結合一個典型案例&#xff0c;從代碼提交到部署上線的完整流程中&#xff0c;講解如何嵌入性能檢測與自動化優化機制&#xff0c;并使用結構化流程圖直觀展示關…

P7 QT項目----會學天氣預報(完結)

7.8 QMap 在 Qt 中&#xff0c;如果你想要將 JSON 數據解析到一個 QMap 中&#xff0c;你可以遍歷 JSON 對象的所有鍵值對&#xff0c;并將它們添加到 QMap 里。這個方法特別適合于當你的 JSON 對象是一個簡單的鍵值對集合時。以下是一個如何實現這一點的示例。 示例&#…

操作系統筆記(關于進程引入和狀態的切換)

1.前言 今天下午結束了英語的四六級考試&#xff0c;終于是結束了&#xff0c;最近的這個考試太密集&#xff0c;周四的專業基礎課考試&#xff0c;周五的這個線性代數的考試和這個周六的英語四六級考試&#xff0c;吧我都要烤焦了&#xff0c;最近也是疲于應對這個考試&#…

M1芯片macOS安裝Xinference部署大模型

如果你看的是官方手冊&#xff1a;安裝 — Xinference 千萬不要直接運行&#xff1a; pip install "xinference[all]" 會遇到幾個問題&#xff1a; 1&#xff09;Python版本如果太新可能安裝失敗 2&#xff09;全量安裝會失敗 3&#xff09;未科學上網可能會time…

【ONNX量化實戰】使用ONNX Runtime進行靜態量化

目錄 什么是量化量化實現的原理實戰準備數據執行量化 驗證量化結語 什么是量化 量化是一種常見的深度學習技術&#xff0c;其目的在于將原始的深度神經網絡權重從高位原始位數被動態縮放至低位目標尾數。例如從FP32&#xff08;32位浮點&#xff09;量化值INT8&#xff08;8位…

【量子計算】格羅弗算法

文章目錄 &#x1f50d; 一、算法原理與工作機制? 二、性能優勢&#xff1a;二次加速的體現&#x1f310; 三、應用場景?? 四、局限性與挑戰&#x1f52e; 五、未來展望&#x1f48e; 總結 格羅弗算法&#xff08;Grover’s algorithm&#xff09;是量子計算領域的核心算法之…

C++ 互斥量

在 C 中&#xff0c;互斥量&#xff08;std::mutex&#xff09;是一種用于多線程編程中保護共享資源的機制&#xff0c;防止多個線程同時訪問某個資源&#xff0c;從而避免數據競爭&#xff08;data race&#xff09;和不一致的問題。 &#x1f512; 一、基礎用法&#xff1a;s…

CSS Content符號編碼大全

資源寶整理分享&#xff1a;?https://www.httple.net? 前端開發中常用的特殊符號查詢工具&#xff0c;包含Unicode編碼和HTML實體編碼&#xff0c;方便開發者快速查找和使用各種符號。支持基本形狀、箭頭、數學符號、貨幣符號等多種分類。 前端最常用符號 圖標形狀十進制十…

RPC常見問題回答

項目流程和架構設計 1.服務端的功能&#xff1a; 1.提供rpc調用對應的函數 2.完成服務注冊 服務發現 上線/下線通知 3.提供主題的操作 (創建/刪除/訂閱/取消訂閱) 消息的發布 2.服務的模塊劃分 1.網絡通信模塊 net 底層套用的moude庫 2.應用層通信協議模塊 1.序列化 反序列化數…