SpringAI提供了Advisors API來實現請求和響應的攔截,修改,增強Spring應用程序和AI模型的互動。
可以使用ChatClient API來配置現有的advisor,例如:
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();
Advisor中的核心組件:
包括非流式場景中的CallAroundAdvisor和CallAroundAdvisorChain構成,其中CallAroundAdvisorChain是由一系列CallAroundAdvisor構成的鏈。非流式場景中的StreamAroundAdvisor和StreamAroundAdvisorChain構成。
還有AdvisorRequest用于表示沒有密封的Prompt請求,AdvisorResponse表示聊天完成的響應,還有上下文AdviseContext,這個上下文用于保存
AdvisorRequest和AdvisorResponse在鏈中的共享狀態。
callAroundNext()和streamAroundNext()是關鍵的方法,在這個方法中對請求和響應進行響應的操作,并且可以決定是否調用下一個advisor進行后續的操作。
getOrder()提供鏈中advisor的訪問順序,返回值越小的在鏈中的位置越靠前,還有getName()方法返回advisor的名稱。最后一個advisor由SpringAI框架自動添加,并把請求發送到AI模型。
在鏈條中越靠前的advisor先執行對AdvisorRequest的操作,越后執行對AdvisorResponse的操作。注意:如果有多個advisor的getOrder()的返回值相同,可能無法保證順序執行,因此要避免多個相同的order值。
SpringAI框架中的Advisor接口關系:
接下來自定義實現一個advisor:
上述實現了一個問題重讀的advisor,就是讓AI模型把問題讀兩遍。
SpringAI框架也提供了一些內置的advisor,比如:
1.MessageChatMemoryAdvisor:添加歷史對話為消息列表,以.message(…)的形式插入到prompt,維護完整的對話結構,適用于完整對話上下文,高級模型(部分模型不支持該advisor)
2.PromptChatMemoryAdvisor:添加歷史對話為系統提示,拼接成一個字符串插入到系統提示中。適用于簡單記憶,對所有模型兼容。
3.VectorStoreChatMemoryAdvisor:從vectorStore中檢索相關信息添加到系統提示中,通常用于提供背景信息。
4.QuestionAnswerAdvisor:用戶提問時,自動從vectorStore中檢索最相關的文檔,添加到用戶提示中,常用于RAG的精準回答。
SafeGardAdvisor:用于防止模型生成有害或不適信息
要更新 context,需要使用 .updateContext(...) 方法,它會創建一個新的不可變 map,并包含更新后的內容,例如:
@Override
public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {
??? this.advisedRequest = advisedRequest.updateContext(context -> {
??????? context.put("aroundCallBefore" + getName(), "AROUND_CALL_BEFORE " + getName());
??????? context.put("lastBefore", getName());
??????? return context;
??? });
??? // 后續邏輯...
}