Advisors API

Spring AI Advisors API 提供了一種靈活而強大的方式來攔截、修改和增強 Spring 應用中的 AI 驅動互動。透過利用 Advisors API,開發者可以建立更復雜、可重用和可維護的 AI 元件。

其主要優勢包括封裝重複的生成式 AI 模式、轉換髮送到大型語言模型 (LLM) 和從其接收的資料,以及在各種模型和用例之間提供可移植性。

您可以使用ChatClient API配置現有 Advisor,示例如下

ChatMemory chatMemory = ... // Initialize your chat memory store
VectorStore vectorStore = ... // Initialize your vector store

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

var conversationId = "678";

String response = this.chatClient.prompt()
    // Set advisor parameters at runtime
    .advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, conversationId))
    .user(userText)
    .call()
	.content();

建議在構建時使用構建器的 defaultAdvisors() 方法註冊 Advisor。

Advisors 也參與可觀察性堆疊,因此您可以檢視與其執行相關的指標和追蹤。

核心元件

該 API 包含用於非流式場景的 CallAdvisorCallAdvisorChain,以及用於流式場景的 StreamAdvisorStreamAdvisorChain。它還包括用於表示未封裝的 Prompt 請求的 ChatClientRequest,以及用於聊天完成響應的 ChatClientResponse。兩者都包含一個 advise-context 以在 Advisor 鏈中共享狀態。

Advisors API Classes

adviseCall()adviseStream() 是關鍵的 Advisor 方法,通常執行諸如檢查未封裝的 Prompt 資料、自定義和增強 Prompt 資料、呼叫 Advisor 鏈中的下一個實體、可選地阻止請求、檢查聊天完成響應以及丟擲異常以指示處理錯誤等操作。

此外,getOrder() 方法確定 Advisor 在鏈中的順序,而 getName() 提供唯一的 Advisor 名稱。

由 Spring AI 框架建立的 Advisor 鏈允許按其 getOrder() 值排序的多個 Advisor 順序呼叫。值越低,執行越早。自動新增的最後一個 Advisor 將請求傳送到 LLM。

以下流程圖說明了 Advisor 鏈和聊天模型之間的互動

Advisors API Flow
  1. Spring AI 框架從使用者的 Prompt 以及一個空的 Advisor context 物件建立 ChatClientRequest

  2. 鏈中的每個 Advisor 處理請求,並可能修改它。或者,它可以選擇透過不呼叫下一個實體來阻止請求。在後一種情況下,Advisor 負責填充響應。

  3. 由框架提供的最後一個 Advisor 將請求傳送到 Chat Model

  4. 然後,聊天模型的響應透過 Advisor 鏈傳遞迴並轉換為 ChatClientResponse。稍後包括共享的 Advisor context 例項。

  5. 每個 Advisor 都可以處理或修改響應。

  6. 透過提取 ChatCompletion,最終的 ChatClientResponse 返回給客戶端。

Advisor 順序

Advisor 在鏈中的執行順序由 getOrder() 方法確定。需要理解的關鍵點是

  • 訂單值較低的 Advisor 首先執行。

  • Advisor 鏈作為一個堆疊執行

    • 鏈中的第一個 Advisor 是第一個處理請求的。

    • 它也是最後一個處理響應的。

  • 要控制執行順序

    • 將順序設定為接近 Ordered.HIGHEST_PRECEDENCE,以確保 Advisor 在鏈中首先執行(首先處理請求,最後處理響應)。

    • 將順序設定為接近 Ordered.LOWEST_PRECEDENCE,以確保 Advisor 在鏈中最後執行(最後處理請求,首先處理響應)。

  • 較高的值表示較低的優先順序。

  • 如果多個 Advisor 具有相同的順序值,則其執行順序不確定。

順序與執行序列之間的看似矛盾是由於 Advisor 鏈的堆疊式性質

  • 優先順序最高(順序值最低)的 Advisor 被新增到堆疊頂部。

  • 隨著堆疊展開,它將是第一個處理請求的。

  • 隨著堆疊回捲,它將是最後一個處理響應的。

提醒一下,以下是 Spring Ordered 介面的語義

public interface Ordered {

    /**
     * Constant for the highest precedence value.
     * @see java.lang.Integer#MIN_VALUE
     */
    int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;

    /**
     * Constant for the lowest precedence value.
     * @see java.lang.Integer#MAX_VALUE
     */
    int LOWEST_PRECEDENCE = Integer.MAX_VALUE;

    /**
     * Get the order value of this object.
     * <p>Higher values are interpreted as lower priority. As a consequence,
     * the object with the lowest value has the highest priority (somewhat
     * analogous to Servlet {@code load-on-startup} values).
     * <p>Same order values will result in arbitrary sort positions for the
     * affected objects.
     * @return the order value
     * @see #HIGHEST_PRECEDENCE
     * @see #LOWEST_PRECEDENCE
     */
    int getOrder();
}

對於需要在輸入和輸出端都排在鏈中首位的用例

  1. 為每一端使用單獨的 Advisor。

  2. 使用不同的順序值配置它們。

  3. 使用 Advisor 上下文在它們之間共享狀態。

API 概述

主要的 Advisor 介面位於包 org.springframework.ai.chat.client.advisor.api 中。以下是您在建立自己的 Advisor 時會遇到的關鍵介面

public interface Advisor extends Ordered {

	String getName();

}

同步和響應式 Advisor 的兩個子介面是

public interface CallAdvisor extends Advisor {

	ChatClientResponse adviseCall(
		ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain);

}

public interface StreamAdvisor extends Advisor {

	Flux<ChatClientResponse> adviseStream(
		ChatClientRequest chatClientRequest, StreamAdvisorChain streamAdvisorChain);

}

要繼續 Advice 鏈,請在 Advice 實現中使用 CallAdvisorChainStreamAdvisorChain

介面是

public interface CallAdvisorChain extends AdvisorChain {

	/**
	 * Invokes the next {@link CallAdvisor} in the {@link CallAdvisorChain} with the given
	 * request.
	 */
	ChatClientResponse nextCall(ChatClientRequest chatClientRequest);

	/**
	 * Returns the list of all the {@link CallAdvisor} instances included in this chain at
	 * the time of its creation.
	 */
	List<CallAdvisor> getCallAdvisors();

}

public interface StreamAdvisorChain extends AdvisorChain {

	/**
	 * Invokes the next {@link StreamAdvisor} in the {@link StreamAdvisorChain} with the
	 * given request.
	 */
	Flux<ChatClientResponse> nextStream(ChatClientRequest chatClientRequest);

	/**
	 * Returns the list of all the {@link StreamAdvisor} instances included in this chain
	 * at the time of its creation.
	 */
	List<StreamAdvisor> getStreamAdvisors();

}

實現 Advisor

要建立 Advisor,請實現 CallAdvisorStreamAdvisor(或兩者)。要實現的關鍵方法是非流式 Advisor 的 nextCall() 或流式 Advisor 的 nextStream()

示例

我們將提供一些實踐示例,以說明如何實現用於觀察和增強用例的 Advisor。

日誌 Advisor

我們可以實現一個簡單的日誌 Advisor,它在呼叫鏈中下一個 Advisor 之前記錄 ChatClientRequest,並在之後記錄 ChatClientResponse。請注意,該 Advisor 只觀察請求和響應,而不修改它們。此實現支援非流式和流式場景。

public class SimpleLoggerAdvisor implements CallAdvisor, StreamAdvisor {

	private static final Logger logger = LoggerFactory.getLogger(SimpleLoggerAdvisor.class);

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

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


	@Override
	public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain) {
		logRequest(chatClientRequest);

		ChatClientResponse chatClientResponse = callAdvisorChain.nextCall(chatClientRequest);

		logResponse(chatClientResponse);

		return chatClientResponse;
	}

	@Override
	public Flux<ChatClientResponse> adviseStream(ChatClientRequest chatClientRequest,
			StreamAdvisorChain streamAdvisorChain) {
		logRequest(chatClientRequest);

		Flux<ChatClientResponse> chatClientResponses = streamAdvisorChain.nextStream(chatClientRequest);

		return new ChatClientMessageAggregator().aggregateChatClientResponse(chatClientResponses, this::logResponse); (3)
	}

	private void logRequest(ChatClientRequest request) {
		logger.debug("request: {}", request);
	}

	private void logResponse(ChatClientResponse chatClientResponse) {
		logger.debug("response: {}", chatClientResponse);
	}

}
1 為 Advisor 提供一個唯一的名稱。
2 您可以透過設定順序值來控制執行順序。值越低,執行越早。
3 MessageAggregator 是一個實用程式類,它將 Flux 響應聚合到一個 ChatClientResponse 中。這對於記錄或觀察整個響應而不是流中單個專案的其他處理可能很有用。請注意,您不能在 MessageAggregator 中更改響應,因為它是一個只讀操作。

重讀 (Re2) Advisor

重讀提高大型語言模型的推理能力》文章介紹了一種名為重讀 (Re2) 的技術,可以提高大型語言模型的推理能力。Re2 技術需要像這樣增強輸入 Prompt

{Input_Query}
Read the question again: {Input_Query}

實現一個將 Re2 技術應用於使用者輸入查詢的 Advisor 可以這樣做

public class ReReadingAdvisor implements BaseAdvisor {

	private static final String DEFAULT_RE2_ADVISE_TEMPLATE = """
			{re2_input_query}
			Read the question again: {re2_input_query}
			""";

	private final String re2AdviseTemplate;

	private int order = 0;

	public ReReadingAdvisor() {
		this(DEFAULT_RE2_ADVISE_TEMPLATE);
	}

	public ReReadingAdvisor(String re2AdviseTemplate) {
		this.re2AdviseTemplate = re2AdviseTemplate;
	}

	@Override
	public ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain) { (1)
		String augmentedUserText = PromptTemplate.builder()
			.template(this.re2AdviseTemplate)
			.variables(Map.of("re2_input_query", chatClientRequest.prompt().getUserMessage().getText()))
			.build()
			.render();

		return chatClientRequest.mutate()
			.prompt(chatClientRequest.prompt().augmentUserMessage(augmentedUserText))
			.build();
	}

	@Override
	public ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) {
		return chatClientResponse;
	}

	@Override
	public int getOrder() { (2)
		return this.order;
	}

	public ReReadingAdvisor withOrder(int order) {
		this.order = order;
		return this;
	}

}
1 before 方法使用重讀技術增強使用者輸入查詢。
2 您可以透過設定順序值來控制執行順序。值越低,執行越早。

Spring AI 內建 Advisor

Spring AI 框架提供了幾個內建 Advisor 來增強您的 AI 互動。以下是可用 Advisor 的概述

聊天記憶 Advisor

這些 Advisor 在聊天記憶儲存中管理對話歷史記錄

  • MessageChatMemoryAdvisor

    檢索記憶並將其作為訊息集合新增到 Prompt 中。此方法維護對話歷史的結構。請注意,並非所有 AI 模型都支援此方法。

  • PromptChatMemoryAdvisor

    檢索記憶並將其合併到 Prompt 的系統文字中。

  • VectorStoreChatMemoryAdvisor

    從 VectorStore 檢索記憶並將其新增到 Prompt 的系統文字中。此 Advisor 對於高效搜尋和檢索大型資料集中的相關資訊非常有用。

問答 Advisor
  • QuestionAnswerAdvisor

    此 Advisor 使用向量儲存提供問答功能,實現 Naive RAG(檢索增強生成)模式。

  • RetrievalAugmentationAdvisor

    Advisor that implements common Retrieval Augmented Generation (RAG) flows using the building blocks defined in the `org.springframework.ai.rag` package and following the Modular RAG Architecture.
推理 Advisor
  • ReReadingAdvisor

    實現了 LLM 推理的重讀策略,稱為 RE2,以增強輸入階段的理解。基於文章:[重讀提高 LLM 中的推理能力](arxiv.org/pdf/2309.06275)。

內容安全 Advisor
  • SafeGuardAdvisor

    一個簡單的 Advisor,旨在防止模型生成有害或不適當的內容。

流式與非流式

Advisors Streaming vs Non-Streaming Flow
  • 非流式 Advisor 處理完整的請求和響應。

  • 流式 Advisor 使用響應式程式設計概念(例如,用於響應的 Flux)將請求和響應作為連續流處理。

@Override
public Flux<ChatClientResponse> adviseStream(ChatClientRequest chatClientRequest, StreamAdvisorChain chain) {

    return  Mono.just(chatClientRequest)
            .publishOn(Schedulers.boundedElastic())
            .map(request -> {
                // This can be executed by blocking and non-blocking Threads.
                // Advisor before next section
            })
            .flatMapMany(request -> chain.nextStream(request))
            .map(response -> {
                // Advisor after next section
            });
}

最佳實踐

  1. 讓 Advisor 專注於特定任務以獲得更好的模組化。

  2. 必要時使用 adviseContext 在 Advisor 之間共享狀態。

  3. 實現 Advisor 的流式和非流式版本,以獲得最大的靈活性。

  4. 仔細考慮 Advisor 在鏈中的順序,以確保正確的資料流。

API 破壞性更改

Advisor 介面

  • 在 1.0 M2 中,有單獨的 RequestAdvisorResponseAdvisor 介面。

    • RequestAdvisorChatModel.callChatModel.stream 方法之前被呼叫。

    • ResponseAdvisor 在這些方法之後被呼叫。

  • 在 1.0 M3 中,這些介面已被以下介面替換

    • CallAroundAdvisor

    • StreamAroundAdvisor

  • 以前作為 ResponseAdvisor 一部分的 StreamResponseMode 已被刪除。

  • 在 1.0.0 中,這些介面已被替換

    • CallAroundAdvisorCallAdvisorStreamAroundAdvisorStreamAdvisorCallAroundAdvisorChainCallAdvisorChainStreamAroundAdvisorChainStreamAdvisorChain

    • AdvisedRequestChatClientRequestAdivsedResponseChatClientResponse

上下文對映處理

  • 在 1.0 M2 中

    • 上下文對映是一個單獨的方法引數。

    • 該對映是可變的,並沿鏈傳遞。

  • 在 1.0 M3 中

    • 上下文對映現在是 AdvisedRequestAdvisedResponse 記錄的一部分。

    • 該對映是不可變的。

    • 要更新上下文,請使用 updateContext 方法,它建立一個新的不可修改的對映,其中包含更新後的內容。

© . This site is unofficial and not affiliated with VMware.