使用 Spring AI Advisors 為您的 AI 應用注入超強動力

工程 | Christian Tzolov | 2024 年 10 月 02 日 | ...

在快速發展的人工智慧世界中,開發者不斷尋求增強其 AI 應用的方法。Spring AI,一個用於構建 AI 驅動應用的 Java 框架,引入了一個強大的特性:Spring AI Advisors

這些 Advisor 可以極大地增強您的 AI 應用,使其更加模組化、可移植且易於維護。

如果不方便閱讀本文,您可以收聽這個實驗性播客,它是根據部落格內容AI 生成的

什麼是 Spring AI Advisors?

其核心在於,Spring AI Advisors 是攔截並可能修改 AI 應用中聊天補全請求和響應流程的元件。該系統中的關鍵參與者是 AroundAdvisor,它允許開發者在這些互動中動態轉換或利用資訊。

使用 Advisors 的主要優勢包括

  1. 封裝重複任務:將常見的生成式 AI 模式打包成可複用的單元。
  2. 轉換:增強傳送到語言模型 (LLM) 的資料,並格式化傳送回客戶端的響應。
  3. 可移植性:建立可在各種模型和用例中工作的可複用轉換元件。

Advisors 的工作原理

Advisor 系統以鏈式方式運作,序列中的每個 Advisor 都有機會處理傳入請求和傳出響應。以下是一個簡化流程:

spring-ai-advisors-flow

  1. 從使用者的提示建立 AdvisedRequest,並帶有一個空的 advisor-context
  2. 鏈中的每個 Advisor 處理請求,可能會對其進行修改,然後將執行轉發給鏈中的下一個 Advisor。或者,它也可以選擇不呼叫下一個實體來阻止請求。
  3. 最後的 Advisor 將請求傳送給聊天模型。
  4. 聊天模型的響應作為 AdvisedResponse 透過 Advisor 鏈傳回,它是原始 ChatResponse 和鏈輸入路徑中的 advise context 的組合。
  5. 每個 Advisor 都可以處理或修改響應。
  6. 最終 AdvisedResponse 中增強的 ChatResponse 返回給客戶端。

使用 Advisors

Spring AI 提供了幾個預構建的 Advisors,用於處理常見場景和生成式 AI 模式

使用 ChatClient API,您可以註冊您的管道所需的 Advisors

var chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(
        new MessageChatMemoryAdvisor(chatMemory), // chat-memory advisor
        new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults()) // RAG advisor
    )
    .build();

String response = chatClient.prompt()
    // Set chat memory parameters at runtime
    .advisors(advisor -> advisor.param("chat_memory_conversation_id", "678")
            .param("chat_memory_response_size", 100))
    .user(userText)
    .call()
	.content();

實現您自己的 Advisor

Advisor API 包括用於非流式場景的 CallAroundAdvisorCallAroundAdvisorChain,以及用於流式場景的 StreamAroundAdvisorStreamAroundAdvisorChain。它還包括用於表示未封裝的 Prompt 請求資料的 AdvisedRequest 和用於聊天補全資料的 AdvisedResponseAdvisedRequestAdvisedResponse 都有一個 advise-context 欄位,用於在整個 Advisor 鏈中共享狀態。

簡單日誌記錄 Advisor

建立自定義 Advisor 很簡單。讓我們來實現一個簡單的日誌記錄 Advisor 來演示整個過程

public class SimpleLoggerAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {
    private static final Logger logger = LoggerFactory.getLogger(SimpleLoggerAdvisor.class);

    @Override
    public String getName() {
        return this.getClass().getSimpleName();
    }

    @Override
    public int getOrder() {
        return 0;
    }

    @Override
    public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {
        logger.debug("BEFORE: {}", advisedRequest);
        AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest);
        logger.debug("AFTER: {}", advisedResponse);
        return advisedResponse;
    }

    @Override
    public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {
        logger.debug("BEFORE: {}", advisedRequest);
        Flux<AdvisedResponse> advisedResponses = chain.nextAroundStream(advisedRequest);
        return new MessageAggregator().aggregateAdvisedResponse(advisedResponses,
                advisedResponse -> logger.debug("AFTER: {}", advisedResponse));
    }
}

這個 Advisor 會在請求處理前和響應接收後記錄日誌,為 AI 互動過程提供有價值的洞察。

aggregateAdvisedResponse(...) 工具將 AdvisedResponse 塊組合成一個單一的 AdvisedResponse,返回原始流並接受一個 Consumer 回撥來處理完成的結果。它保留了原始內容和上下文。

重讀 (Re2) Advisor

讓我們基於重讀 (Re2) 技術實現一個更高階的 Advisor,該技術受這篇論文啟發,可以提高大型語言模型的推理能力

public class ReReadingAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {
    private static final String DEFAULT_USER_TEXT_ADVISE = """
        {re2_input_query}
        Read the question again: {re2_input_query}
        """;

    @Override
    public String getName() {
        return this.getClass().getSimpleName();
    }

    @Override
    public int getOrder() {
        return 0;
    }

    private AdvisedRequest before(AdvisedRequest advisedRequest) {

        String inputQuery = advisedRequest.userText(); //original user query

        Map<String, Object> params = new HashMap<>(advisedRequest.userParams());        
        params.put("re2_input_query", inputQuery);

        return AdvisedRequest.from(advisedRequest)
                .withUserText(DEFAULT_USER_TEXT_ADVISE)
                .withUserParams(params)
                .build();
    }

    @Override
    public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {
        return chain.nextAroundCall(before(advisedRequest));
    }

    @Override
    public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {
        return chain.nextAroundStream(before(advisedRequest));
    }
}

這個 Advisor 修改輸入查詢以包含一個“重讀”步驟,可能提高 AI 模型對問題的理解和推理能力。

高階主題

Spring AI 的高階主題涵蓋了 Advisor 管理的重要方面,包括順序控制狀態共享流式處理能力。Advisor 的執行順序由 getOrder() 方法決定。透過共享的 advise-context 物件,可以實現 Advisor 之間的狀態共享,從而促成複雜的多 Advisor 場景。該系統支援流式和非流式 Advisors,允許處理完整的請求和響應,或使用響應式程式設計概念處理連續資料流。

控制 Advisor 順序

鏈中 Advisors 的順序至關重要,它由 getOrder() 方法決定。order 值較低的 Advisors 首先執行。由於 Advisor 鏈是一個棧,鏈中的第一個 Advisor 是最後一個處理請求和第一個處理響應的。如果您想確保某個 Advisor 最後執行,請將其 order 值設定接近 Ordered.LOWEST_PRECEDENCE;反之,如果想使其首先執行,請將其 order 值設定接近 Ordered.HIGHEST_PRECEDENCE。如果多個 Advisors 的 order 值相同,則執行順序無法保證。

使用 AdvisorContext 進行狀態共享

AdvisedRequestAdvisedResponse 都共享一個 advise-context 物件。您可以使用 advise-context 在鏈中的 Advisors 之間共享狀態,並構建涉及多個 Advisors 的更復雜的處理場景。

流式與非流式

Spring AI 同時支援流式和非流式 Advisors。非流式 Advisors 處理完整的請求和響應,而流式 Advisors 使用響應式程式設計概念(例如,用於響應的 Flux)處理連續的資料流。

對於流式 Advisors,需要注意的是,單個 AdvisedResponse 例項僅表示整個 Flux<AdvisedResponse> 響應的一部分(即一個塊)。相比之下,對於非流式 Advisors,AdvisedResponse 包含完整的響應。

最佳實踐

  1. 使 Advisors 專注於特定任務,以提高模組化程度。
  2. 必要時使用 advise-context 在 Advisors 之間共享狀態。
  3. 實現您的 Advisor 的流式和非流式版本,以獲得最大靈活性。
  4. 仔細考慮鏈中 Advisors 的順序,以確保正確的資料流。

結論

Spring AI Advisors 提供了一種強大且靈活的方式來增強您的 AI 應用。透過利用這個 API,您可以建立更復雜、可複用且易於維護的 AI 元件。無論您是實現自定義邏輯、管理對話歷史,還是改進模型推理,Advisors 都提供了一個清晰高效的解決方案。

我們鼓勵您在專案中嘗試 Spring AI Advisors,並與社群分享您的自定義實現。可能性是無限的,您的創新可以幫助塑造 AI 應用開發的未來!

程式設計愉快,願您的 AI 應用越來越智慧和響應迅速!

訂閱 Spring 資訊

保持與 Spring 資訊的連線

訂閱

搶先一步

VMware 提供培訓和認證,助您快速提升。

瞭解更多

獲取支援

Tanzu Spring 透過一個簡單的訂閱,為 OpenJDK™、Spring 和 Apache Tomcat® 提供支援和二進位制檔案。

瞭解更多

即將舉辦的活動

檢視 Spring 社群所有即將舉辦的活動。

檢視全部