<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Spring AI 玩轉多輪對話

      AI "失憶"怎么辦?本文帶你用 Spring AI 一招搞定多輪對話,讓你的 AI 應用擁有超強記憶!從 ChatClient、Advisors 到實戰編碼,三步打造一個能記住上下文的智能歷史專家。

      大家好,我是程序員NEO。

      你是否遇到過這樣的 AI?上一秒剛告訴它你的名字,下一秒就問你是誰。這種“金魚記憶”的 AI 簡直讓人抓狂!在智能客服、虛擬助手等場景,如果 AI 無法記住上下文,用戶體驗將大打折扣。

      別擔心,今天 NEO 就帶你用 Spring AI 框架,徹底解決這個難題,輕松為你的 AI 應用植入“記憶芯片”!

      為了方便演示,我們將一起創建一個“歷史知識專家”AI。它不僅能對答如流,還能記住我們之前的對話,實現真正流暢的智能交流。

      準備好了嗎?讓我們開始吧!

      更強大的 ChatClient

      要讓 AI 擁有“記憶力”,首先得掌握與它高效溝通的工具。Spring AI 提供了 ChatClient API,這是我們與大模型交互的瑞士軍刀。

      很多同學可能習慣了直接注入 ChatModel,但 ChatClient 提供了功能更豐富、更靈活的鏈式調用(Fluent API),是官方更推薦的方式。

      看看對比,高下立判:

      // 基礎用法(ChatModel)
      ChatResponse response = chatModel.call(new Prompt("你好"));
      
      // 高級用法(ChatClient)
      ChatClient chatClient = ChatClient.builder(chatModel)
          .defaultSystem("你是歷史顧問")
          .build();
          
      String response = chatClient.prompt().user("你好").call().content();
      

      ChatClient 的構建方式也很靈活,可以通過構造器注入或使用建造者模式:

      // 方式1:使用構造器注入
      @Service
      public class ChatService {
          private final ChatClient chatClient;
          
          public ChatService(ChatClient.Builder builder) {
              this.chatClient = builder
                  .defaultSystem("你是歷史顧問")
                  .build();
          }
      }
      
      // 方式2:使用建造者模式
      ChatClient chatClient = ChatClient.builder(chatModel)
          .defaultSystem("你是歷史顧問")
          .build();
      

      它還支持多種響應格式,無論是包含 Token 信息的完整響應、自動映射的 Java 對象,還是實現打字機效果的流式輸出,都能輕松搞定。

      // ChatClient支持多種響應格式
      // 1. 返回 ChatResponse 對象(包含元數據如 token 使用量)
      ChatResponse chatResponse = chatClient.prompt()
          .user("Tell me a joke")
          .call()
          .chatResponse();
      
      // 2. 返回實體對象(自動將 AI 輸出映射為 Java 對象)
      // 2.1 返回單個實體
      record ActorFilms(String actor, List<String> movies) {}
      ActorFilms actorFilms = chatClient.prompt()
          .user("Generate the filmography for a random actor.")
          .call()
          .entity(ActorFilms.class);
      
      // 2.2 返回泛型集合
      List<ActorFilms> multipleActors = chatClient.prompt()
          .user("Generate filmography for Tom Hanks and Bill Murray.")
          .call()
          .entity(new ParameterizedTypeReference<List<ActorFilms>>() {});
      
      // 3. 流式返回(適用于打字機效果)
      Flux<String> streamResponse = chatClient.prompt()
          .user("Tell me a story")
          .stream()
          .content();
      
      // 也可以流式返回ChatResponse
      Flux<ChatResponse> streamWithMetadata = chatClient.prompt()
          .user("Tell me a story")
          .stream()
          .chatResponse();
      

      更棒的是,你可以為 ChatClient 設置默認的“人設”(系統提示詞),甚至在對話中動態替換模板變量,讓 AI 的角色扮演更加生動。

      // 定義默認系統提示詞
      ChatClient chatClient = ChatClient.builder(chatModel)
              .defaultSystem("You are a friendly chat bot that answers question in the voice of a {voice}")
              .build();
      
      // 對話時動態更改系統提示詞的變量
      chatClient.prompt()
              .system(sp -> sp.param("voice", voice))
              .user(message)
              .call()
              .content());
      

      Advisors 攔截器

      如果說 ChatClient 是 AI 的軀體,那 Advisors(顧問)就是給它加持的各種“外掛”和“Buff”。

      你可以把 Advisors 理解為一系列可插拔的攔截器。在請求發給 AI 前或收到 AI 響應后,它們可以執行各種騷操作:

      • 前置增強:悄悄改寫你的提問,讓它更符合 AI 的胃口;或者進行安全檢查,過濾掉危險問題。
      • 后置增強:記錄調用日志,或者對 AI 的回答進行二次加工。

      用法非常簡單,直接在構建 ChatClient 時配置 defaultAdvisors 即可。比如,MessageChatMemoryAdvisor 就是我們實現對話記憶的關鍵“外掛”。

      var chatClient = ChatClient.builder(chatModel)
          .defaultAdvisors(
              new MessageChatMemoryAdvisor(chatMemory), // 對話記憶 advisor
              new QuestionAnswerAdvisor(vectorStore)    // RAG 檢索增強 advisor
          )
          .build();
      
      String response = this.chatClient.prompt()
          // 對話時動態設定攔截器參數,比如指定對話記憶的 id 和長度
          .advisors(advisor -> advisor.param("chat_memory_conversation_id", "678")
                  .param("chat_memory_response_size", 100))
          .user(userText)
          .call()
      	.content();
      

      Advisors 的工作原理就像一條精密的流水線(責任鏈模式):

      Advisors 工作原理圖

      流水線流程解讀:

      1. 用戶的請求進來,被包裝成一個 AdvisedRequest
      2. 請求在 Advisor 鏈上依次傳遞,每個 Advisor 都可以對它進行處理或修改。
      3. 最終,請求被發送給 ChatModel
      4. 模型的響應再沿著流水線反向傳回,每個 Advisor 也可以處理響應。
      5. 最后,客戶端收到經過層層“加持”的最終結果。

      注意Advisor 的執行順序由其 getOrder() 方法決定,值越小,優先級越高,跟代碼書寫順序無關哦!

      Advisor 類圖關系

      Chat Memory Advisor

      要實現對話記憶,ChatMemoryAdvisor 是我們的不二之選。它有幾種實現方式,最常用的是 MessageChatMemoryAdvisor

      • MessageChatMemoryAdvisor:將歷史對話作為完整的消息列表(包含用戶和 AI 的角色)添加到提示中。這是最符合現代大模型交互方式的選擇。
      • PromptChatMemoryAdvisor:將歷史對話拼接成一段文本,塞進系統提示詞里。
      • VectorStoreChatMemoryAdvisor:使用向量數據庫來存儲和檢索歷史對話,適用于更復雜的場景。

      ChatMemoryAdvisor 的幾種實現

      MessageChatMemoryAdvisor 保留了對話的原始結構,能讓 AI 更好地理解上下文,因此 強烈推薦使用

      Chat Memory

      ChatMemoryAdvisor 只是“搬運工”,真正存儲對話歷史的是 Chat Memory。Spring AI 提供了多種“記憶倉庫”:

      • InMemoryChatMemory:內存存儲,簡單快捷,適合測試(我們今天就用它)。
      • JdbcChatMemory, CassandraChatMemory, Neo4jChatMemory:持久化存儲,可將對話歷史保存在數據庫中,適合生產環境。

      打造一個“歷史學家”AI

      理論講完了,上代碼!

      初始化 ChatClient

      我們通過構造器注入 ChatModel,然后構建 ChatClient。在構建時,設定好“歷史學家”的人設(SYSTEM_PROMPT),并裝上我們的記憶“外掛”——MessageChatMemoryAdvisor

      /**
       * @author 程序員NEO
       * @version 1.0
       * @description 歷史知識專家應用
       * @since 2025-07-07
       **/
      @Component
      @Slf4j
      public class HistoryExpertApp {
      
          private final ChatClient chatClient;
      
          private static final String SYSTEM_PROMPT = "你是一位風趣幽默的歷史知識專家,學識淵博。" +
                  "你需要根據用戶的提問,生動、清晰地回答相關的歷史知識。" +
                  "如果用戶的問題不清晰,你需要引導用戶提供更多信息。";
      
          public HistoryExpertApp(ChatModel chatModel) {
              // 初始化基于內存的對話記憶
              ChatMemory chatMemory = new InMemoryChatMemory();
              chatClient = ChatClient.builder(chatModel)
                      .defaultSystem(SYSTEM_PROMPT)
                      .defaultAdvisors(
                              new MessageChatMemoryAdvisor(chatMemory)
                      )
                      .build();
          }
          // ... doChat 方法
      }
      

      這里我們使用了 InMemoryChatMemory,它將對話歷史存在內存里。對于生產環境,記得換成 Redis 或數據庫等持久化方案。

      編寫對話方法

      核心的 doChat 方法接收用戶消息(message)和會話 ID(chatId)。chatId 是區分不同對話的關鍵,確保每個用戶的聊天記錄相互獨立。

      /**
       * 執行聊天操作,處理用戶消息并返回 AI 的響應。
       *
       * @param message 用戶發送的消息
       * @param chatId  對話 ID,用于標識當前會話
       * @return AI 的響應內容
       */
      public String doChat(String message, String chatId) {
          ChatResponse chatResponse = chatClient
                  .prompt()
                  .user(message)
                  .advisors(spec -> spec
                          .param(MessageChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, chatId) // 設置對話 ID
                          .param(MessageChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10)) // 設置記憶容量
                  .call()
                  .chatResponse();
      
          String content = chatResponse.getResult().getOutput().getContent();
          log.info("AI Response: {}", content);
          return content;
      }
      

      .advisors() 方法中,我們傳入了兩個關鍵參數:

      • CHAT_MEMORY_CONVERSATION_ID_KEY: 會話 ID,確保每個用戶的對話歷史是隔離的。
      • CHAT_MEMORY_RETRIEVE_SIZE_KEY: 對話記憶檢索大小。設置為 10 表示 AI 在回答時,會參考最近的 10 條消息(5 輪對話)。

      見證奇跡的時刻!

      我們用一個單元測試來驗證 AI 是否真的擁有了記憶。

      @SpringBootTest
      public class HistoryExpertAppTest {
      
          @Resource
          private HistoryExpertApp historyExpertApp;
      
          @Test
          void testChat() {
              String chatId = UUID.randomUUID().toString();
              
              // 第一輪對話
              System.out.println("--- 第一輪對話 ---");
              String message1 = "我叫NEO,我最喜歡的數字是7。";
              System.out.println("我: " + message1);
              String answer1 = historyExpertApp.doChat(message1, chatId);
              Assertions.assertNotNull(answer1);
              System.out.println("AI: " + answer1);
      
              // 第二輪對話
              System.out.println("\n--- 第二輪對話 ---");
              String message2 = "我叫什么名字?我最喜歡的數字是幾?";
              System.out.println("我: " + message2);
              String answer2 = historyExpertApp.doChat(message2, chatId);
              Assertions.assertNotNull(answer2);
              System.out.println("AI: " + answer2);
          }
      }
      

      場景一:擁有完整記憶

      CHAT_MEMORY_RETRIEVE_SIZE_KEY 設置為 10 時,AI 能輕松記住我們在第一輪對話中提供的信息。

      測試結果

      --- 第一輪對話 ---
      我: 我叫NEO,我最喜歡的數字是7。
      AI: 哈哈,Neo!很高興認識你!7確實是一個神奇的數字——不僅是上帝創造世界的天數,也是彩虹的顏色數、一周的天數,甚至還是詹姆斯·邦德的代號!看來你和神秘事物很投緣啊!
      
      既然你喜歡7,那我考考你:你知道人類歷史上有哪些著名的"七"嗎?比如七大奇跡、七星瓢蟲,或者...《七龍珠》???
      
      說說看,你是更喜歡歷史中的神秘"七",還是生活里有趣的"七"呢?我可以從任何方向展開聊聊!
      --- 第二輪對話 ---
      我: 我叫什么名字?我最喜歡的數字是幾?
      AI: 哎呀!這像是在考考我這個"博學多才"的歷史專家了是不是???
      
      讓我想想...上一秒鐘我還記得清清楚楚來著...哦對了!你叫NEO,跟我分享過你最喜歡數字7的奧秘。就像《黑客帝國》里的主角一樣充滿傳奇色彩的名字,配上神秘的7,簡直完美搭配!
      
      不過說真的,能告訴我你是從哪部《黑客帝國》開始認識NEO這個角色的呢?是第一部經典之作,還是后來的續集?還有,為什么偏偏是7這個數字吸引了你?我覺得這里面一定有段有趣的故事要聽!??
      

      AI 擁有記憶的測試結果

      成功了!AI 準確地記住了我們的信息。

      場景二:秒變“金魚記憶”

      現在,我們將記憶容量改為 1,這意味著 AI 只能看到當前用戶的提問,完全不記得之前的對話。

      .param(MessageChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY, 1)
      

      再次運行測試,看看會發生什么。

      測試結果(失憶版)

      --- 第一輪對話 ---
      我: 我叫NEO,我最喜歡的數字是7。
      AI: 哎呀,歡迎Neo!讓我猜猜...你喜歡數字7是不是因為"Matrix"里的第七號巧合?不過既然你提到了數字7,讓我告訴你一個有趣的歷史故事吧!
      
      你知道嗎?在古希臘人眼中,7可是個神奇的數字組合 - 就像我這個歷史專家平時吃著橄欖,看著帕特農神廟時想的那樣。他們發現了七個智慧賢人,觀察到天空中有七顆可見的行星(當然那是沒有望遠鏡的時代),甚至連他們的神話里都有七重天的說法。
      
      嘿,作為一個喜歡解密歷史的人,我發現你這個名字和數字7的組合讓我想起了一些有意思的東西。比如,柏拉圖在他的著作里經常提到"理想國",而那個國家正好有七個等級制度。或者亞歷山大圖書館,它可是古代世界七大奇跡之一!
      
      說起來,你是更喜歡探索數字7在數學上的獨特性質,還是它在不同文化中的神秘象征?讓我們一起揭開這個數字背后的更多秘密吧!
      --- 第二輪對話 ---
      我: 我叫什么名字?我最喜歡的數字是幾?
      AI: 哎呀,這可真是個讓人摸不著頭腦的問題!讓我這個歷史專家翻遍了古希臘的羊皮卷、巴比倫的泥板和中國的甲骨文都沒找到答案。難道你是想讓我展示特洛伊木馬式的驚喜?還是像斯芬克斯謎語那樣考考我?
      
      不過作為一個精通歷史的學者,我可以告訴你一些有趣的背景:在古代埃及,人們會給新生兒起非常特別的名字,有時候是根據他們出生的日子來取的。而說到數字,畢達哥拉斯可是堅信萬物皆數呢!
      

      AI 失憶的測試結果

      看到了嗎?僅僅是一個參數的差別,AI 就從“智能”變成了“智障”。這個對比鮮明地展示了對話記憶的重要性。

      希望這篇文章對你有幫助!如果你覺得內容不錯,點個贊??分享給更多朋友吧!有任何問題,歡迎在評論區留言交流。

      關注公眾號【程序員NEO】,第一時間獲取更多 AIGC 實戰干貨!

      posted @ 2025-07-07 18:12  BNTang  閱讀(523)  評論(2)    收藏  舉報
      主站蜘蛛池模板: 国精偷拍一区二区三区| 国产午夜亚洲精品福利| 天堂在/线中文在线资源 官网| 亚洲欧美日韩精品久久亚洲区| 亚洲精品毛片一区二区| 十八禁在线观看视频播放免费| 麻豆av一区二区三区| 国产色无码专区在线观看| 亚洲性无码av在线| 成人精品视频一区二区三区| 黑色丝袜脚交视频麻豆| 洪泽县| 伊人中文在线最新版天堂| 蜜臀AⅤ永久无码精品| 亚洲一区二区偷拍精品| 激情四射激情五月综合网| 东方四虎av在线观看| 乱女伦露脸对白在线播放| 成人亚洲a片v一区二区三区动漫| 高清自拍亚洲精品二区| 忘忧草社区在线www| av在线播放无码线| 色成年激情久久综合国产| 99国产精品永久免费视频| 国产精品久久久久久久久久妞妞| 亚洲免费成人av一区| 丝袜高潮流白浆潮喷在线播放| 欧美成人精品手机在线| 国产拍拍拍无码视频免费| 欧美成年性h版影视中文字幕 | 色吊丝免费av一区二区| 亚洲精品成人区在线观看| 国产精品久久精品国产| 精品尤物国产尤物在线看| XXXXXHD亚洲日本HD| 国产成人精品日本亚洲网站| 99精品国产综合久久久久五月天| 欧美国产成人精品二区芒果视频| 久热这里只有精品12| 国产一区二区三区高清视频 | 国产69久久精品成人看|