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 包含用於非流式場景的 CallAdvisor 和 CallAdvisorChain,以及用於流式場景的 StreamAdvisor 和 StreamAdvisorChain。它還包括用於表示未封裝的 Prompt 請求的 ChatClientRequest,以及用於聊天完成響應的 ChatClientResponse。兩者都包含一個 advise-context 以在 Advisor 鏈中共享狀態。
adviseCall() 和 adviseStream() 是關鍵的 Advisor 方法,通常執行諸如檢查未封裝的 Prompt 資料、自定義和增強 Prompt 資料、呼叫 Advisor 鏈中的下一個實體、可選地阻止請求、檢查聊天完成響應以及丟擲異常以指示處理錯誤等操作。
此外,getOrder() 方法確定 Advisor 在鏈中的順序,而 getName() 提供唯一的 Advisor 名稱。
由 Spring AI 框架建立的 Advisor 鏈允許按其 getOrder() 值排序的多個 Advisor 順序呼叫。值越低,執行越早。自動新增的最後一個 Advisor 將請求傳送到 LLM。
以下流程圖說明了 Advisor 鏈和聊天模型之間的互動
-
Spring AI 框架從使用者的
Prompt以及一個空的 Advisorcontext物件建立ChatClientRequest。 -
鏈中的每個 Advisor 處理請求,並可能修改它。或者,它可以選擇透過不呼叫下一個實體來阻止請求。在後一種情況下,Advisor 負責填充響應。
-
由框架提供的最後一個 Advisor 將請求傳送到
Chat Model。 -
然後,聊天模型的響應透過 Advisor 鏈傳遞迴並轉換為
ChatClientResponse。稍後包括共享的 Advisorcontext例項。 -
每個 Advisor 都可以處理或修改響應。
-
透過提取
ChatCompletion,最終的ChatClientResponse返回給客戶端。
Advisor 順序
Advisor 在鏈中的執行順序由 getOrder() 方法確定。需要理解的關鍵點是
-
訂單值較低的 Advisor 首先執行。
-
Advisor 鏈作為一個堆疊執行
-
鏈中的第一個 Advisor 是第一個處理請求的。
-
它也是最後一個處理響應的。
-
-
要控制執行順序
-
將順序設定為接近
Ordered.HIGHEST_PRECEDENCE,以確保 Advisor 在鏈中首先執行(首先處理請求,最後處理響應)。 -
將順序設定為接近
Ordered.LOWEST_PRECEDENCE,以確保 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();
}
|
對於需要在輸入和輸出端都排在鏈中首位的用例
|
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 實現中使用 CallAdvisorChain 和 StreamAdvisorChain
介面是
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,請實現 CallAdvisor 或 StreamAdvisor(或兩者)。要實現的關鍵方法是非流式 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(檢索增強生成)模式。
-
RetrievalAugmentationAdvisorAdvisor 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 處理完整的請求和響應。
-
流式 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
});
}
API 破壞性更改
Advisor 介面
-
在 1.0 M2 中,有單獨的
RequestAdvisor和ResponseAdvisor介面。-
RequestAdvisor在ChatModel.call和ChatModel.stream方法之前被呼叫。 -
ResponseAdvisor在這些方法之後被呼叫。
-
-
在 1.0 M3 中,這些介面已被以下介面替換
-
CallAroundAdvisor -
StreamAroundAdvisor
-
-
以前作為
ResponseAdvisor一部分的StreamResponseMode已被刪除。 -
在 1.0.0 中,這些介面已被替換
-
CallAroundAdvisor→CallAdvisor,StreamAroundAdvisor→StreamAdvisor,CallAroundAdvisorChain→CallAdvisorChain和StreamAroundAdvisorChain→StreamAdvisorChain。 -
AdvisedRequest→ChatClientRequest和AdivsedResponse→ChatClientResponse。
-