Advisors API
Spring AI Advisors API 提供了一種靈活且強大的方式,用於攔截、修改和增強您 Spring 應用中的 AI 驅動互動。透過利用 Advisors API,開發者可以建立更復雜、可複用且更易於維護的 AI 元件。
其主要優勢包括封裝重複出現的生成式 AI 模式,轉換髮送到大型語言模型 (LLMs) 和從大型語言模型接收的資料,以及在各種模型和用例之間提供可移植性。
您可以使用 ChatClient API 配置現有的建議器,示例如下
var chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(
new MessageChatMemoryAdvisor(chatMemory), // chat-memory advisor
new QuestionAnswerAdvisor(vectorStore) // RAG advisor
)
.build();
String response = this.chatClient.prompt()
// Set advisor parameters at runtime
.advisors(advisor -> advisor.param("chat_memory_conversation_id", "678")
.param("chat_memory_response_size", 100))
.user(userText)
.call()
.content();
建議在構建時使用構建器的 defaultAdvisors()
方法註冊建議器。
建議器也參與到可觀測性堆疊中,因此您可以檢視與其執行相關的指標和跟蹤資訊。
核心元件
該 API 包含用於非流式場景的 CallAroundAdvisor
和 CallAroundAdvisorChain
,以及用於流式場景的 StreamAroundAdvisor
和 StreamAroundAdvisorChain
。它還包括用於表示未密封的 Prompt 請求的 AdvisedRequest
,以及用於 Chat Completion 響應的 AdvisedResponse
。兩者都持有一個 advise-context
,用於在建議器鏈中共享狀態。

nextAroundCall()
和 nextAroundStream()
是關鍵的建議器方法,通常執行諸如檢查未密封的 Prompt 資料、自定義和增強 Prompt 資料、呼叫建議器鏈中的下一個實體、可選地阻止請求、檢查聊天完成響應以及丟擲異常以指示處理錯誤等操作。
此外,getOrder()
方法決定了建議器在鏈中的順序,而 getName()
方法提供了建議器的唯一名稱。
由 Spring AI 框架建立的建議器鏈(Advisor Chain)允許按照建議器的 getOrder()
值順序呼叫多個建議器。值較低的建議器首先執行。最後一個自動新增的建議器會將請求傳送給 LLM。
以下流程圖說明了建議器鏈與聊天模型之間的互動

-
Spring AI 框架會根據使用者的
Prompt
建立一個AdvisedRequest
,同時建立一個空的AdvisorContext
物件。 -
鏈中的每個建議器都會處理請求,並可能對其進行修改。或者,它也可以選擇透過不呼叫下一個實體來阻止請求。在後一種情況下,建議器負責填充響應。
-
由框架提供的最後一個建議器將請求傳送給聊天模型。
-
然後,聊天模型的響應會透過建議器鏈傳遞回來,並轉換為
AdvisedResponse
。後者包含了共享的AdvisorContext
例項。 -
每個建議器都可以處理或修改響應。
-
最終的
AdvisedResponse
透過提取ChatCompletion
返回給客戶端。
建議器順序
建議器在鏈中的執行順序由 getOrder()
方法決定。需要理解的關鍵點:
-
order 值較低的建議器首先執行。
-
建議器鏈作為棧執行
-
鏈中的第一個建議器是第一個處理請求的。
-
它也是最後一個處理響應的。
-
-
要控制執行順序
-
將 order 值設定為接近
Ordered.HIGHEST_PRECEDENCE
,以確保建議器在鏈中首先執行(先處理請求,後處理響應)。 -
將 order 值設定為接近
Ordered.LOWEST_PRECEDENCE
,以確保建議器在鏈中最後執行(後處理請求,先處理響應)。
-
-
值越高表示優先順序越低。
-
如果多個建議器具有相同的 order 值,其執行順序不保證。
order 值與執行順序看似矛盾,原因在於建議器鏈的棧狀特性
|
提醒一下,以下是 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 概覽
主要的建議器介面位於 org.springframework.ai.chat.client.advisor.api
包中。以下是您建立自己的建議器時會遇到的關鍵介面
public interface Advisor extends Ordered {
String getName();
}
同步和響應式建議器的兩個子介面是
public interface CallAroundAdvisor extends Advisor {
/**
* Around advice that wraps the ChatModel#call(Prompt) method.
* @param advisedRequest the advised request
* @param chain the advisor chain
* @return the response
*/
AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain);
}
和
public interface StreamAroundAdvisor extends Advisor {
/**
* Around advice that wraps the invocation of the advised request.
* @param advisedRequest the advised request
* @param chain the chain of advisors to execute
* @return the result of the advised request
*/
Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain);
}
要在您的 Advice 實現中繼續 Advice 鏈,請使用 CallAroundAdvisorChain
和 StreamAroundAdvisorChain
介面是
public interface CallAroundAdvisorChain {
AdvisedResponse nextAroundCall(AdvisedRequest advisedRequest);
}
和
public interface StreamAroundAdvisorChain {
Flux<AdvisedResponse> nextAroundStream(AdvisedRequest advisedRequest);
}
實現一個建議器
要建立一個建議器,請實現 CallAroundAdvisor
或 StreamAroundAdvisor
(或兩者)。需要實現的關鍵方法是用於非流式的 nextAroundCall()
或用於流式的 nextAroundStream()
。
示例
我們將提供一些動手示例,以說明如何實現用於觀察和增強用例的建議器。
日誌建議器
我們可以實現一個簡單的日誌建議器,它在呼叫鏈中的下一個建議器之前記錄 AdvisedRequest
,並在之後記錄 AdvisedResponse
。請注意,該建議器只觀察請求和響應,不會修改它們。此實現支援非流式和流式場景。
public class SimpleLoggerAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {
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 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)); (3)
}
}
1 | 提供建議器的唯一名稱。 |
2 | 您可以透過設定 order 值來控制執行順序。值較低的先執行。 |
3 | MessageAggregator 是一個工具類,它將 Flux 響應聚合到一個 AdvisedResponse 中。這對於記錄日誌或觀察整個響應而非流中單個項的其他處理非常有用。請注意,您不能在 MessageAggregator 中更改響應,因為它是一個只讀操作。 |
重複閱讀 (Re2) 建議器
《重複閱讀提高了大型語言模型的推理能力》這篇文章介紹了一種名為重複閱讀 (Re2) 的技術,可以提高大型語言模型的推理能力。Re2 技術需要像這樣增強輸入提示詞:
{Input_Query} Read the question again: {Input_Query}
實現一個將 Re2 技術應用於使用者輸入查詢的建議器可以像這樣完成:
public class ReReadingAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {
private AdvisedRequest before(AdvisedRequest advisedRequest) { (1)
Map<String, Object> advisedUserParams = new HashMap<>(advisedRequest.userParams());
advisedUserParams.put("re2_input_query", advisedRequest.userText());
return AdvisedRequest.from(advisedRequest)
.userText("""
{re2_input_query}
Read the question again: {re2_input_query}
""")
.userParams(advisedUserParams)
.build();
}
@Override
public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) { (2)
return chain.nextAroundCall(this.before(advisedRequest));
}
@Override
public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) { (3)
return chain.nextAroundStream(this.before(advisedRequest));
}
@Override
public int getOrder() { (4)
return 0;
}
@Override
public String getName() { (5)
return this.getClass().getSimpleName();
}
}
1 | before 方法應用重複閱讀技術來增強使用者的輸入查詢。 |
2 | aroundCall 方法攔截非流式請求並應用重複閱讀技術。 |
3 | aroundStream 方法攔截流式請求並應用重複閱讀技術。 |
4 | 您可以透過設定 order 值來控制執行順序。值較低的先執行。 |
5 | 提供建議器的唯一名稱。 |
Spring AI 內建建議器
Spring AI 框架提供了幾個內建的建議器,以增強您的 AI 互動。以下是可用建議器的概覽:
流式 vs 非流式

-
非流式建議器處理完整的請求和響應。
-
流式建議器使用響應式程式設計概念(例如,用於響應的 Flux)將請求和響應作為連續流進行處理。
@Override
public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {
return Mono.just(advisedRequest)
.publishOn(Schedulers.boundedElastic())
.map(request -> {
// This can be executed by blocking and non-blocking Threads.
// Advisor before next section
})
.flatMapMany(request -> chain.nextAroundStream(request))
.map(response -> {
// Advisor after next section
});
}
API 破壞性變更
Spring AI 建議器鏈從版本 1.0 M2 到 1.0 M3 發生了重大變化。以下是主要修改內容:
建議器介面
-
在 1.0 M2 中,存在獨立的
RequestAdvisor
和ResponseAdvisor
介面。-
RequestAdvisor
在ChatModel.call
和ChatModel.stream
方法之前被呼叫。 -
ResponseAdvisor
在這些方法之後被呼叫。
-
-
在 1.0 M3 中,這些介面已被以下介面取代:
-
CallAroundAdvisor
-
StreamAroundAdvisor
-
-
StreamResponseMode
,之前是ResponseAdvisor
的一部分,已被移除。
上下文 Map 處理
-
在 1.0 M2 中
-
上下文 map 是一個單獨的方法引數。
-
該 map 是可變的,並在鏈中傳遞。
-
-
在 1.0 M3 中
-
上下文 map 現在是
AdvisedRequest
和AdvisedResponse
記錄的一部分。 -
該 map 是不可變的。
-
要更新上下文,請使用
updateContext
方法,該方法將建立一個包含更新內容的新不可修改 map。
-
在 1.0 M3 中更新上下文的示例
@Override
public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {
this.advisedRequest = advisedRequest.updateContext(context -> {
context.put("aroundCallBefore" + getName(), "AROUND_CALL_BEFORE " + getName()); // Add multiple key-value pairs
context.put("lastBefore", getName()); // Add a single key-value pair
return context;
});
// Method implementation continues...
}