Spring AI框架借鉴了Spring AOP(面向切面编程)的思想,通过Advisor机制为AI交互提供了强大的拦截能力。这种设计模式允许开发者在模型调用前后插入自定义逻辑,而无需修改核心业务代码。
Advisor机制本质上是一种拦截器模式,它基于"好莱坞原则"(Don't call us, we'll call you)。当ChatClient执行请求时,框架会自动调用注册的Advisor链,开发者只需关注业务逻辑的实现。
这种设计带来几个显著优势:
Spring AI定义了以下核心接口来支持Advisor机制:
java复制public interface Advisor {
int getOrder(); // 决定执行顺序
ChatClientRequest before(ChatClientRequest request, AdvisorChain chain);
ChatClientResponse after(ChatClientResponse response, AdvisorChain chain);
}
public interface CallAdvisor extends Advisor {} // 非流式场景
public interface StreamAdvisor extends Advisor {} // 流式场景
框架还提供了BaseAdvisor抽象类,实现了模板方法模式,让开发者可以只覆盖需要的方法:
java复制public abstract class BaseAdvisor implements Advisor {
@Override
public int getOrder() { return 0; }
@Override
public ChatClientRequest before(ChatClientRequest request, AdvisorChain chain) {
return chain.next(request);
}
// after方法类似...
}
SimpleLoggerAdvisor是Spring AI提供的内置Advisor,主要解决AI交互过程中的可观测性问题。在开发调试阶段特别有用。
有两种主要配置方式:
java复制ChatClient.builder(chatModel)
.defaultAdvisors(new SimpleLoggerAdvisor())
.build();
java复制chatClient.prompt()
.user("Hello")
.advisors(new SimpleLoggerAdvisor())
.call()
.content();
提示:生产环境建议结合日志级别控制,避免产生过多日志。可以通过以下配置限制日志输出:
properties复制logging.level.org.springframework.ai.chat.client.advisor=DEBUG
启用后输出的日志包含关键信息:
典型日志示例:
code复制DEBUG o.s.a.c.c.advisor.SimpleLoggerAdvisor - Request:
{
"messages": [
{
"role": "user",
"content": "Hello"
}
]
}
DEBUG o.s.a.c.c.advisor.SimpleLoggerAdvisor - Response:
{
"message": {
"content": "Hi there! How can I help you today?"
}
}
SafeGuardAdvisor实现了敏感词过滤功能,是内容安全的重要防线。
java复制@Test
public void testSensitiveWordFilter() {
ChatClient client = ChatClient.builder(chatModel)
.defaultAdvisors(new SafeGuardAdvisor(List.of("敏感词1", "敏感词2")))
.build();
String response = client.prompt()
.user("包含敏感词的问题")
.call()
.content();
}
当检测到敏感词时,Advisor会抛出IllegalArgumentException阻止请求继续执行。
java复制public class DynamicSafeGuardAdvisor extends BaseAdvisor {
private final SensitiveWordRepository repo;
@Override
public ChatClientRequest before(ChatClientRequest request, AdvisorChain chain) {
List<String> bannedWords = repo.findAllActiveWords();
String userInput = request.prompt().getUserMessage().getText();
bannedWords.forEach(word -> {
if(userInput.contains(word)) {
throw new SecurityException("包含违禁词汇: " + word);
}
});
return chain.next(request);
}
}
java复制Pattern pattern = Pattern.compile("傻[逼|B|b]");
if(pattern.matcher(input).find()) {
// 处理敏感词
}
重读(Re2)策略是一种提升AI理解能力的有效方法,通过让模型重复审视问题来获得更好的回答。
java复制public class ReReadingAdvisor extends BaseAdvisor {
private static final String RE2_TEMPLATE = """
{original_question}
Please read the question again carefully:
{original_question}
Consider all aspects before answering.
""";
@Override
public ChatClientRequest before(ChatClientRequest request, AdvisorChain chain) {
String originalText = request.prompt().getUserMessage().getText();
String processedPrompt = PromptTemplate.builder()
.template(RE2_TEMPLATE)
.build()
.render(Map.of("original_question", originalText));
return request.builder()
.prompt(Prompt.builder().content(processedPrompt).build())
.build();
}
}
原始提问:
java复制chatClient.prompt().user("量子计算的主要优势是什么?").call().content();
使用Re2 Advisor后实际发送的prompt:
code复制量子计算的主要优势是什么?
Please read the question again carefully:
量子计算的主要优势是什么?
Consider all aspects before answering.
实测表明,使用重读策略后:
下面是一个记录响应时间的实用Advisor:
java复制public class TimingAdvisor extends BaseAdvisor {
private static final Logger logger = LoggerFactory.getLogger(TimingAdvisor.class);
@Override
public ChatClientRequest before(ChatClientRequest request, AdvisorChain chain) {
request.attributes().put("startTime", System.currentTimeMillis());
return chain.next(request);
}
@Override
public ChatClientResponse after(ChatClientResponse response, AdvisorChain chain) {
long duration = System.currentTimeMillis() -
(long)response.attributes().get("startTime");
logger.info("Request took {} ms", duration);
return chain.next(response);
}
}
Advisor可以形成处理链,按order属性决定的顺序执行:
java复制List<Advisor> advisors = Arrays.asList(
new TimingAdvisor(), // order=0
new SimpleLoggerAdvisor(), // order=1
new SafeGuardAdvisor(words) // order=2
);
ChatClient client = ChatClient.builder(model)
.defaultAdvisors(advisors)
.build();
执行顺序为:Timing → Logger → SafeGuard
对于流式响应,需要使用StreamAdvisor接口:
java复制public class StreamingLoggerAdvisor implements StreamAdvisor {
@Override
public Publisher<ChatClientResponse> stream(
ChatClientRequest request,
StreamAdvisorChain chain) {
System.out.println("Stream request: " + request);
return Flux.from(chain.next(request))
.doOnNext(response -> {
System.out.println("Received chunk: " + response);
});
}
}
实现跨请求的上下文记忆:
java复制public class ContextAdvisor extends BaseAdvisor {
private final ThreadLocal<Map<String, Object>> context =
ThreadLocal.withInitial(HashMap::new);
@Override
public ChatClientRequest before(ChatClientRequest request, AdvisorChain chain) {
request.attributes().putAll(context.get());
return chain.next(request);
}
@Override
public ChatClientResponse after(ChatClientResponse response, AdvisorChain chain) {
context.get().putAll(response.attributes());
return chain.next(response);
}
}
java复制public class AsyncLoggingAdvisor extends BaseAdvisor {
private final Executor executor = Executors.newFixedThreadPool(2);
@Override
public ChatClientResponse after(ChatClientResponse response, AdvisorChain chain) {
executor.execute(() -> {
// 异步记录日志
logToExternalSystem(response);
});
return chain.next(response);
}
}
建议实现统一的错误处理Advisor:
java复制public class ErrorHandlingAdvisor extends BaseAdvisor {
@Override
public ChatClientResponse after(ChatClientResponse response, AdvisorChain chain) {
if(response.error() != null) {
// 重试逻辑
if(isRetryable(response.error())) {
return retryRequest(response.request());
}
// 降级处理
return fallbackResponse(response);
}
return chain.next(response);
}
}
java复制@Component
@Order(1)
public class MyAdvisor extends BaseAdvisor {
// 实现...
}
java复制@Bean
@Profile("prod")
public Advisor productionAdvisor() {
return new SafeGuardAdvisor(/*...*/);
}
properties复制app.advisor.safeguard.words=敏感词1,敏感词2
getOrder()返回值,数值越小优先级越高chain.next()当发现延迟增加时:
TimingAdvisor定位耗时环节记住Advisor是Spring AI的上层抽象,不会影响底层模型行为。如果发现模型响应不符合预期:
限流Advisor:
java复制public class RateLimitingAdvisor extends BaseAdvisor {
private final RateLimiter limiter = RateLimiter.create(10.0); // 10 QPS
@Override
public ChatClientRequest before(ChatClientRequest request, AdvisorChain chain) {
if(!limiter.tryAcquire()) {
throw new RateLimitExceededException();
}
return chain.next(request);
}
}
审计Advisor:
java复制public class AuditAdvisor extends BaseAdvisor {
private final AuditService auditService;
@Override
public ChatClientResponse after(ChatClientResponse response, AdvisorChain chain) {
auditService.logInteraction(
response.request().prompt(),
response.message()
);
return chain.next(response);
}
}
将多个简单Advisor组合实现复杂功能:
java复制public class ChainOfThoughtAdvisor extends BaseAdvisor {
private static final String COT_TEMPLATE = """
{question}
Let's think step by step:
1. Analyze the key elements of the question
2. Identify required knowledge domains
3. Build logical reasoning path
4. Finalize the answer
""";
// 实现类似ReReadingAdvisor...
}
在实际项目中,Advisor机制的价值会随着系统复杂度提升而愈发明显。我建议从简单的日志记录开始,逐步引入更多业务特定的处理逻辑,最终形成一套完整的AI交互治理层。