使用 Spring AI Advisors 強化您的 AI 應用程式

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

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

這些 advisors 可以強化您的 AI 應用程式,使其更模組化、可移植且更易於維護。

如果閱讀文章不方便,您可以收聽這個實驗性 podcast,它是從部落格內容AI 生成的。

什麼是 Spring AI Advisors?

從本質上講,Spring AI Advisors 是攔截並可能修改 AI 應用程式中聊天完成請求和回應流程的元件。此系統中的關鍵角色是 AroundAdvisor,它允許開發人員動態轉換或利用這些互動中的資訊。

使用 Advisors 的主要優點包括

  1. 封裝重複性任務:將常見的 GenAI 模式封裝到可重複使用的單元中。
  2. 轉換:擴充傳送到語言模型 (LLM) 的資料,並格式化傳回給用戶端的Responses。
  3. 可移植性:建立可在各種模型和使用案例中使用的可重複使用轉換元件。

Advisors 如何運作

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

spring-ai-advisors-flow

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

使用 Advisors

Spring AI 提供了多個預先建置的 Advisors 來處理常見情況和 Gen 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(適用於串流案例)組成。它還包括 AdvisedRequest(用於表示未密封的 Prompt 請求資料)和 AdvisedResponse(用於聊天完成資料)。AdvisedRequestAdvisedResponse 具有一個 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(...) 實用程式將 AdviseResponse 區塊組合到單個 AdvisedResponse 中,返回原始串流並接受已完成結果的 Consumer 回呼。 它保留了原始內容和上下文。

重新閱讀 (Re2) Advisor

讓我們根據 Re-Reading (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() 方法決定。 Advisor 之間的狀態共享是透過共享的 advise-context 物件啟用的,從而促進了複雜的多 advisor 案例。 該系統支援串流和非串流 advisor,允許處理完整的請求和回應,或使用反應式編程概念處理連續資料串流。

控制 Advisor 順序

鏈中 Advisors 的順序至關重要,並且由 getOrder() 方法決定。 訂單值較低的 Advisors 首先執行。 因為 advisor 鏈是堆疊,所以鏈中的第一個 advisor 是最後一個處理請求的,也是第一個處理回應的。 如果要確保 advisor 最後執行,請將其順序設定為接近 Ordered.LOWEST_PRECEDENCE 值,反之,要先執行,請將其順序設定為接近 Ordered.HIGHEST_PRECEDENCE 值。 如果有多個 advisor 具有相同的訂單值,則無法保證執行順序。

使用 AdvisorContext 進行狀態共享

AdvisedRequestAdvisedResponse 都共享一個 advise-context 物件。 您可以使用 advise-context 在鏈中的 advisor 之間共享狀態,並建立涉及多個 advisor 的更複雜的處理情境。

串流與非串流

Spring AI 支援串流和非串流 Advisor。 非串流 Advisor 處理完整的請求和回應,而串流 Advisor 使用反應式程式設計概念(例如,用於回應的 Flux)處理連續串流。

對於串流 Advisor,請務必注意,單個 AdvisedResponse 實例僅代表整個 Flux<AdvisedResponse> 回應的一個區塊(即一部分)。 相比之下,對於非串流 Advisor,AdvisedResponse 包含完整的回應。

最佳實務

  1. 保持 Advisor 專注於特定任務,以獲得更好的模組化。
  2. 在必要時使用 advise-context 在 Advisor 之間共享狀態。
  3. 實作串流和非串流版本的 Advisor,以獲得最大的彈性。
  4. 仔細考慮 Advisor 在鏈中的順序,以確保正確的資料流。

結論

Spring AI Advisor 提供了一種強大且靈活的方式來增強您的 AI 應用程式。 通過利用此 API,您可以建立更複雜、可重複使用和可維護的 AI 組件。 無論您是實作自定義邏輯、管理對話歷史記錄還是改進模型推理,Advisor 都提供了一個簡潔高效的解決方案。

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

祝您編碼愉快,並願您的 AI 應用程式更加智慧和反應靈敏!

取得 Spring 電子報

保持與 Spring 電子報的聯繫

訂閱

領先一步

VMware 提供培訓和認證,以加速您的進展。

了解更多

取得支援

Tanzu Spring 在一個簡單的訂閱中提供對 OpenJDK™、Spring 和 Apache Tomcat® 的支援和二進制檔案。

了解更多

即將舉行的活動

查看 Spring 社群中所有即將舉行的活動。

查看所有