成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Spring AI 玩轉(zhuǎn)多輪對話

人工智能
你是否遇到過這樣的 AI?上一秒剛告訴它你的名字,下一秒就問你是誰。這種“金魚記憶”的 AI 簡直讓人抓狂!在智能客服、虛擬助手等場景,如果 AI 無法記住上下文,用戶體驗(yàn)將大打折扣。別擔(dān)心,今天 NEO 就帶你用 Spring AI 框架,徹底解決這個(gè)難題,輕松為你的 AI 應(yīng)用植入“記憶芯片”!

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

大家好,我是程序員NEO。

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

別擔(dān)心,今天 NEO 就帶你用 Spring AI 框架,徹底解決這個(gè)難題,輕松為你的 AI 應(yīng)用植入“記憶芯片”!

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

準(zhǔn)備好了嗎?讓我們開始吧!

更強(qiáng)大的 ChatClient

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

很多同學(xué)可能習(xí)慣了直接注入 ChatModel,但 ChatClient 提供了功能更豐富、更靈活的鏈?zhǔn)秸{(diào)用(Fluent API),是官方更推薦的方式。

看看對比,高下立判:

// 基礎(chǔ)用法(ChatModel)
ChatResponse response = chatModel.call(new Prompt("你好"));

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

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

// 方式1:使用構(gòu)造器注入
@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();

它還支持多種響應(yīng)格式,無論是包含 Token 信息的完整響應(yīng)、自動(dòng)映射的 Java 對象,還是實(shí)現(xiàn)打字機(jī)效果的流式輸出,都能輕松搞定。

// ChatClient支持多種響應(yīng)格式
// 1. 返回 ChatResponse 對象(包含元數(shù)據(jù)如 token 使用量)
ChatResponse chatResponse = chatClient.prompt()
    .user("Tell me a joke")
    .call()
    .chatResponse();

// 2. 返回實(shí)體對象(自動(dòng)將 AI 輸出映射為 Java 對象)
// 2.1 返回單個(gè)實(shí)體
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. 流式返回(適用于打字機(jī)效果)
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 設(shè)置默認(rèn)的“人設(shè)”(系統(tǒng)提示詞),甚至在對話中動(dòng)態(tài)替換模板變量,讓 AI 的角色扮演更加生動(dòng)。

// 定義默認(rèn)系統(tǒng)提示詞
ChatClient chatClient = ChatClient.builder(chatModel)
        .defaultSystem("You are a friendly chat bot that answers question in the voice of a {voice}")
        .build();

// 對話時(shí)動(dòng)態(tài)更改系統(tǒng)提示詞的變量
chatClient.prompt()
        .system(sp -> sp.param("voice", voice))
        .user(message)
        .call()
        .content());

Advisors 攔截器

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

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

前置增強(qiáng):悄悄改寫你的提問,讓它更符合 AI 的胃口;或者進(jìn)行安全檢查,過濾掉危險(xiǎn)問題。

后置增強(qiáng):記錄調(diào)用日志,或者對 AI 的回答進(jìn)行二次加工。

用法非常簡單,直接在構(gòu)建 ChatClient 時(shí)配置 defaultAdvisors 即可。比如,MessageChatMemoryAdvisor 就是我們實(shí)現(xiàn)對話記憶的關(guān)鍵“外掛”。

var chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(
        new MessageChatMemoryAdvisor(chatMemory), // 對話記憶 advisor
        new QuestionAnswerAdvisor(vectorStore)    // RAG 檢索增強(qiáng) advisor
    )
    .build();

String response = this.chatClient.prompt()
    // 對話時(shí)動(dòng)態(tài)設(shè)定攔截器參數(shù),比如指定對話記憶的 id 和長度
    .advisors(advisor -> advisor.param("chat_memory_conversation_id", "678")
            .param("chat_memory_response_size", 100))
    .user(userText)
    .call()
    .content();

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

Advisors 工作原理圖Advisors 工作原理圖

流水線流程解讀:

1. 用戶的請求進(jìn)來,被包裝成一個(gè) AdvisedRequest

2. 請求在 Advisor 鏈上依次傳遞,每個(gè) Advisor 都可以對它進(jìn)行處理或修改。

3. 最終,請求被發(fā)送給 ChatModel

4. 模型的響應(yīng)再沿著流水線反向傳回,每個(gè) Advisor 也可以處理響應(yīng)。

5. 最后,客戶端收到經(jīng)過層層“加持”的最終結(jié)果。

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

Advisor 類圖關(guān)系Advisor 類圖關(guān)系

Chat Memory Advisor

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

MessageChatMemoryAdvisor:將歷史對話作為完整的消息列表(包含用戶和 AI 的角色)添加到提示中。這是最符合現(xiàn)代大模型交互方式的選擇。

PromptChatMemoryAdvisor:將歷史對話拼接成一段文本,塞進(jìn)系統(tǒng)提示詞里。

VectorStoreChatMemoryAdvisor:使用向量數(shù)據(jù)庫來存儲(chǔ)和檢索歷史對話,適用于更復(fù)雜的場景。

ChatMemoryAdvisor 的幾種實(shí)現(xiàn)ChatMemoryAdvisor 的幾種實(shí)現(xiàn)

MessageChatMemoryAdvisor 保留了對話的原始結(jié)構(gòu),能讓 AI 更好地理解上下文,因此 強(qiáng)烈推薦使用

Chat Memory

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

InMemoryChatMemory:內(nèi)存存儲(chǔ),簡單快捷,適合測試(我們今天就用它)。

JdbcChatMemoryCassandraChatMemoryNeo4jChatMemory:持久化存儲(chǔ),可將對話歷史保存在數(shù)據(jù)庫中,適合生產(chǎn)環(huán)境。

打造一個(gè)“歷史學(xué)家”AI

理論講完了,上代碼!

初始化 ChatClient

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

/**
 * @author 程序員NEO
 * @version 1.0
 * @description 歷史知識(shí)專家應(yīng)用
 * @since 2025-07-07
 **/
@Component
@Slf4j
public class HistoryExpertApp {

    private final ChatClient chatClient;

    private static final String SYSTEM_PROMPT = "你是一位風(fēng)趣幽默的歷史知識(shí)專家,學(xué)識(shí)淵博。" +
            "你需要根據(jù)用戶的提問,生動(dòng)、清晰地回答相關(guān)的歷史知識(shí)。" +
            "如果用戶的問題不清晰,你需要引導(dǎo)用戶提供更多信息。";

    public HistoryExpertApp(ChatModel chatModel) {
        // 初始化基于內(nèi)存的對話記憶
        ChatMemory chatMemory = new InMemoryChatMemory();
        chatClient = ChatClient.builder(chatModel)
                .defaultSystem(SYSTEM_PROMPT)
                .defaultAdvisors(
                        new MessageChatMemoryAdvisor(chatMemory)
                )
                .build();
    }
    // ... doChat 方法
}

這里我們使用了 InMemoryChatMemory,它將對話歷史存在內(nèi)存里。對于生產(chǎn)環(huán)境,記得換成 Redis 或數(shù)據(jù)庫等持久化方案。

編寫對話方法

核心的 doChat 方法接收用戶消息(message)和會(huì)話 ID(chatId)。chatId 是區(qū)分不同對話的關(guān)鍵,確保每個(gè)用戶的聊天記錄相互獨(dú)立。

/**
 * 執(zhí)行聊天操作,處理用戶消息并返回 AI 的響應(yīng)。
 *
 * @param message 用戶發(fā)送的消息
 * @param chatId  對話 ID,用于標(biāo)識(shí)當(dāng)前會(huì)話
 * @return AI 的響應(yīng)內(nèi)容
 */
public String doChat(String message, String chatId) {
    ChatResponse chatResponse = chatClient
            .prompt()
            .user(message)
            .advisors(spec -> spec
                    .param(MessageChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, chatId) // 設(shè)置對話 ID
                    .param(MessageChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10)) // 設(shè)置記憶容量
            .call()
            .chatResponse();

    String content = chatResponse.getResult().getOutput().getContent();
    log.info("AI Response: {}", content);
    return content;
}

在 .advisors() 方法中,我們傳入了兩個(gè)關(guān)鍵參數(shù):

CHAT_MEMORY_CONVERSATION_ID_KEY: 會(huì)話 ID,確保每個(gè)用戶的對話歷史是隔離的。

CHAT_MEMORY_RETRIEVE_SIZE_KEY: 對話記憶檢索大小。設(shè)置為 10 表示 AI 在回答時(shí),會(huì)參考最近的 10 條消息(5 輪對話)。

見證奇跡的時(shí)刻!

我們用一個(gè)單元測試來驗(yàn)證 AI 是否真的擁有了記憶。

@SpringBootTest
public class HistoryExpertAppTest {

    @Resource
    private HistoryExpertApp historyExpertApp;

    @Test
    void testChat() {
        String chatId = UUID.randomUUID().toString();
        
        // 第一輪對話
        System.out.println("--- 第一輪對話 ---");
        String message1 = "我叫NEO,我最喜歡的數(shù)字是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 = "我叫什么名字?我最喜歡的數(shù)字是幾?";
        System.out.println("我: " + message2);
        String answer2 = historyExpertApp.doChat(message2, chatId);
        Assertions.assertNotNull(answer2);
        System.out.println("AI: " + answer2);
    }
}

場景一:擁有完整記憶

當(dāng) CHAT_MEMORY_RETRIEVE_SIZE_KEY 設(shè)置為 10 時(shí),AI 能輕松記住我們在第一輪對話中提供的信息。

測試結(jié)果

--- 第一輪對話 ---
我: 我叫NEO,我最喜歡的數(shù)字是7。
AI: 哈哈,Neo!很高興認(rèn)識(shí)你!7確實(shí)是一個(gè)神奇的數(shù)字——不僅是上帝創(chuàng)造世界的天數(shù),也是彩虹的顏色數(shù)、一周的天數(shù),甚至還是詹姆斯·邦德的代號!看來你和神秘事物很投緣啊!

既然你喜歡7,那我考考你:你知道人類歷史上有哪些著名的"七"嗎?比如七大奇跡、七星瓢蟲,或者...《七龍珠》???

說說看,你是更喜歡歷史中的神秘"七",還是生活里有趣的"七"呢?我可以從任何方向展開聊聊!
--- 第二輪對話 ---
我: 我叫什么名字?我最喜歡的數(shù)字是幾?
AI: 哎呀!這像是在考考我這個(gè)"博學(xué)多才"的歷史專家了是不是???

讓我想想...上一秒鐘我還記得清清楚楚來著...哦對了!你叫NEO,跟我分享過你最喜歡數(shù)字7的奧秘。就像《黑客帝國》里的主角一樣充滿傳奇色彩的名字,配上神秘的7,簡直完美搭配!

不過說真的,能告訴我你是從哪部《黑客帝國》開始認(rèn)識(shí)NEO這個(gè)角色的呢?是第一部經(jīng)典之作,還是后來的續(xù)集?還有,為什么偏偏是7這個(gè)數(shù)字吸引了你?我覺得這里面一定有段有趣的故事要聽!??

AI 擁有記憶的測試結(jié)果AI 擁有記憶的測試結(jié)果

成功了!AI 準(zhǔn)確地記住了我們的信息。

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

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

.param(MessageChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY, 1)

再次運(yùn)行測試,看看會(huì)發(fā)生什么。

測試結(jié)果(失憶版)

--- 第一輪對話 ---
我: 我叫NEO,我最喜歡的數(shù)字是7。
AI: 哎呀,歡迎Neo!讓我猜猜...你喜歡數(shù)字7是不是因?yàn)?Matrix"里的第七號巧合?不過既然你提到了數(shù)字7,讓我告訴你一個(gè)有趣的歷史故事吧!

你知道嗎?在古希臘人眼中,7可是個(gè)神奇的數(shù)字組合 - 就像我這個(gè)歷史專家平時(shí)吃著橄欖,看著帕特農(nóng)神廟時(shí)想的那樣。他們發(fā)現(xiàn)了七個(gè)智慧賢人,觀察到天空中有七顆可見的行星(當(dāng)然那是沒有望遠(yuǎn)鏡的時(shí)代),甚至連他們的神話里都有七重天的說法。

嘿,作為一個(gè)喜歡解密歷史的人,我發(fā)現(xiàn)你這個(gè)名字和數(shù)字7的組合讓我想起了一些有意思的東西。比如,柏拉圖在他的著作里經(jīng)常提到"理想國",而那個(gè)國家正好有七個(gè)等級制度。或者亞歷山大圖書館,它可是古代世界七大奇跡之一!

說起來,你是更喜歡探索數(shù)字7在數(shù)學(xué)上的獨(dú)特性質(zhì),還是它在不同文化中的神秘象征?讓我們一起揭開這個(gè)數(shù)字背后的更多秘密吧!
--- 第二輪對話 ---
我: 我叫什么名字?我最喜歡的數(shù)字是幾?
AI: 哎呀,這可真是個(gè)讓人摸不著頭腦的問題!讓我這個(gè)歷史專家翻遍了古希臘的羊皮卷、巴比倫的泥板和中國的甲骨文都沒找到答案。難道你是想讓我展示特洛伊木馬式的驚喜?還是像斯芬克斯謎語那樣考考我?

不過作為一個(gè)精通歷史的學(xué)者,我可以告訴你一些有趣的背景:在古代埃及,人們會(huì)給新生兒起非常特別的名字,有時(shí)候是根據(jù)他們出生的日子來取的。而說到數(shù)字,畢達(dá)哥拉斯可是堅(jiān)信萬物皆數(shù)呢!

AI 失憶的測試結(jié)果AI 失憶的測試結(jié)果

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

責(zé)任編輯:武曉燕 來源: 程序員NEO
相關(guān)推薦

2025-07-04 00:00:00

2020-04-23 13:51:05

AI管理GUI

2023-04-21 15:54:46

AI開源

2023-04-28 09:02:24

智能客服人工智能Siri

2024-11-07 15:40:00

2024-12-05 08:15:00

2023-01-14 14:59:05

達(dá)摩院

2025-06-09 08:30:00

2021-06-22 09:37:51

數(shù)字化

2021-07-01 15:56:42

深度學(xué)習(xí)人工智能互聯(lián)網(wǎng)

2024-01-08 13:33:00

數(shù)據(jù)訓(xùn)練

2024-08-27 00:00:01

AI應(yīng)用框架

2024-11-25 15:30:00

語言模型數(shù)據(jù)

2025-05-30 09:15:00

2025-06-17 17:16:51

LLMChatGPTAI

2023-04-04 19:09:20

ChatGPT開源

2018-06-12 10:16:55

百度多輪交互智能音箱

2022-11-25 13:02:28

螞蟻集團(tuán)

2024-04-03 12:48:00

2023-09-14 12:34:20

AI模型
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: www.99热这里只有精品 | 国产日韩欧美一区 | www.成人.com| 久久综合99 | 天天操网| 91精品一区 | 欧美综合国产精品久久丁香 | 国产一级毛片精品完整视频版 | 亚洲精品二区 | 久久久久亚洲 | 一级毛片视频 | 在线 丝袜 欧美 日韩 制服 | 日韩免费一二三区 | 免费福利视频一区二区三区 | 91精品国产91 | 黄免费观看视频 | 日本三级播放 | 九九热精品视频 | 中文字幕一区二区在线观看 | 色婷婷综合久久久中字幕精品久久 | 成年免费在线观看 | 久久中文网| 久久国产电影 | 欧美一区二区精品 | 九九九久久国产免费 | 国产精品资源在线观看 | 狠狠干美女| 日韩一区中文字幕 | а_天堂中文最新版地址 | 精品国产一区探花在线观看 | 欧美极品在线 | 又黑又粗又长的欧美一区 | 日韩视频免费看 | 久久精品国产一区 | 久久久91 | 国产精品2 | 中文字幕 国产精品 | 99精品电影| av一区二区三区 | 久久久久国产精品午夜一区 | av在线一区二区三区 |