跳到主要内容
版本:1.0.7

Advisors

Advisor(切面)是 Spring AI 的横切关注点机制,采用围绕调用链模式——在每个模型请求前后插入自定义逻辑,实现日志记录、内容安全、对话记忆、RAG 检索等能力。

1. 注册 Advisor

ChatClient 提供两种注册方式:defaultAdvisors() 在构建时设置全局生效,advisors() 在单次调用时追加。

// 全局注册:所有请求生效
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(new SimpleLoggerAdvisor())
.build();

// 单次注册:仅本次调用生效
chatClient.prompt()
.user("今天的天气怎么样?")
.advisors(new SafeGuardAdvisor())
.call()
.content();

全局和单次注册的 Advisor 可以叠加使用,按优先级排序后依次执行。


2. 内置 Advisor

2.1 请求日志

SimpleLoggerAdvisor 在请求前后记录日志,输出请求内容和模型响应,调试时无需手动打印。

ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(new SimpleLoggerAdvisor())
.build();

// 调试时自动输出:
// [Request]: user="北京天气?"
// [Response]: "北京今天晴,22°C"

默认使用 SLF4J debug 级别,可通过 Builder 自定义日志格式:

SimpleLoggerAdvisor advisor = SimpleLoggerAdvisor.builder()
.requestToString(req -> "用户说了: " + req.prompt().getInstructions())
.responseToString(resp -> "模型回复: " + resp.chatResponse().getResult().getOutput().getText())
.build();

2.2 内容安全

SafeGuardAdvisor 在请求到达模型之前检测敏感词,命中则直接返回拒绝响应,不调用模型。

ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(new SafeGuardAdvisor())
.build();

// 包含敏感词时直接返回:
// "I'm unable to respond to that due to sensitive content..."

默认使用内置的敏感词列表,可通过构造函数传入自定义的 List<String>

List<String> customWords = List.of("密码", "密钥", "token");
SafeGuardAdvisor advisor = new SafeGuardAdvisor(customWords);

2.3 对话记忆——基于消息

MessageChatMemoryAdvisor 自动将每次对话的 Message 对象存入 ChatMemory,下次请求时检索历史并追加到消息列表中。

ChatMemory chatMemory = new InMemoryChatMemory();

ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory))
.build();

// 第一轮
chatClient.prompt()
.user("我叫张三")
.advisors(a -> a.param("chat_memory_conversation_id", "conv-001"))
.call()
.content();

// 第二轮——自动携带上轮记忆
chatClient.prompt()
.user("我叫什么名字?")
.advisors(a -> a.param("chat_memory_conversation_id", "conv-001"))
.call()
.content();
// 模型回答: "你叫张三"

2.4 对话记忆——基于提示词

PromptChatMemoryAdvisor 将对话历史渲染到 System Prompt 中,而非追加为独立 Message。适合不希望消息列表无限增长的场景。

ChatMemory chatMemory = new InMemoryChatMemory();

ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(new PromptChatMemoryAdvisor(chatMemory))
.build();

chatClient.prompt()
.user("我叫李四")
.advisors(a -> a.param("chat_memory_conversation_id", "conv-002"))
.call()
.content();

自定义记忆提示词模板:

PromptChatMemoryAdvisor advisor = PromptChatMemoryAdvisor.builder(chatMemory)
.systemTextTemplate("以下是对话历史,请参考:\n{memory}")
.build();

2.5 向量检索增强

QuestionAnswerAdvisor 在请求模型前,先用用户问题检索 VectorStore,将检索到的文档作为上下文注入提示词。

VectorStore vectorStore = new SimpleVectorStore(embeddingModel);

ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(new QuestionAnswerAdvisor(vectorStore))
.build();

String response = chatClient.prompt()
.user("Spring AI 如何实现函数调用?")
.call()
.content();
// 模型基于检索到的相关文档回答

2.6 模块化 RAG

RetrievalAugmentationAdvisor 提供完整的 RAG 流水线,支持查询转换、检索、后处理、增强等可配置阶段。适合需要精细控制 RAG 流程的场景。

RetrievalAugmentationAdvisor advisor = RetrievalAugmentationAdvisor.builder()
.documentRetriever(vectorStore)
.queryAugmenter(new ContextualQueryAugmenter())
.build();

ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(advisor)
.build();

3. 传递参数给 Advisor

通过 AdvisorSpec.param() 向 Advisor 传递参数,如对话 ID、检索阈值等。

chatClient.prompt()
.user("我叫王五")
.advisors(a -> a
.param("chat_memory_conversation_id", "conv-003")
.param("chat_memory_response_size", 10)
)
.call()
.content();

param() 的参数会被写入请求上下文,各 Advisor 按需读取。常用参数:

参数名适用的 Advisor说明
chat_memory_conversation_id记忆类 Advisor对话唯一标识,区分不同会话
chat_memory_response_size记忆类 Advisor每次检索的历史消息数量

4. 完整示例

完整示例:AdvisorExample.java
public class AdvisorExample {

public static void main(String[] args) {
OllamaApi ollamaApi = OllamaApi.builder()
.baseUrl("http://localhost:11434")
.build();
OllamaChatModel chatModel = OllamaChatModel.builder()
.ollamaApi(ollamaApi)
.defaultOptions(OllamaOptions.builder().model("qwen3:8b").build())
.build();

// 创建带记忆的 ChatClient
ChatMemory chatMemory = new InMemoryChatMemory();
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(
new SimpleLoggerAdvisor(),
new MessageChatMemoryAdvisor(chatMemory)
)
.defaultSystem("你是一个乐于助人的助手")
.build();

// 第一轮对话
String response1 = chatClient.prompt()
.user("我叫张三,是一名 Java 工程师")
.advisors(a -> a.param("chat_memory_conversation_id", "conv-001"))
.call()
.content();
System.out.println("第一轮: " + response1);

// 第二轮对话——自动携带记忆
String response2 = chatClient.prompt()
.user("我叫什么名字?做什么工作?")
.advisors(a -> a.param("chat_memory_conversation_id", "conv-001"))
.call()
.content();
System.out.println("第二轮: " + response2);
}
}