AI超级智能开发系列从入门到上天第四篇:自定义Advisor
一自定义Advisor学过 Servlet 和 Spring AOP 的同学应该对拦截 / 切面处理请求响应的功能并不陌生比如记录请求响应日志、鉴权等。Spring AI 的Advisor可理解为拦截器用于对 AI 调用请求进行增强例如调用 AI 前鉴权、校验 Prompt 等调用 AI 后记录日志、处理返回结果等官方已提供部分内置 Advisor但可能无法满足实际业务需求此时可使用 Spring AI 提供的自定义 Advisor功能按指定步骤操作即可实现。1如何自定义1实现接口CallAroundAdvisor StreamAroundAdvisor2重写aroundCall方法 StreamAroundAdvisor的方法。3设定执行顺序可以通过getOrder方法指定advisor在链中执行顺序值越小优先级越高越先执行。4提供个唯一名称。1. 非流式处理实现AroundAdvisor接口Override public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) { // 1. 处理请求前置处理 AdvisedRequest modifiedRequest processRequest(advisedRequest); // 2. 调用链中的下一个 Advisor AdvisedResponse response chain.nextAroundCall(modifiedRequest); // 3. 处理响应后置处理 return processResponse(response); }2. 流式处理实现StreamAroundAdvisor接口Override public FluxAdvisedResponse aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) { // 1. 处理请求 AdvisedRequest modifiedRequest processRequest(advisedRequest); // 2. 调用链中的下一个 Advisor 并处理流式响应 return chain.nextAroundStream(modifiedRequest) .map(response - processResponse(response)); }2自定义日志Advisorpackage com.yupi.yuaiagent.advisor; import lombok.extern.slf4j.Slf4j; import org.springframework.ai.chat.client.ChatClientMessageAggregator; import org.springframework.ai.chat.client.ChatClientRequest; import org.springframework.ai.chat.client.ChatClientResponse; import org.springframework.ai.chat.client.advisor.api.CallAdvisor; import org.springframework.ai.chat.client.advisor.api.CallAdvisorChain; import org.springframework.ai.chat.client.advisor.api.StreamAdvisor; import org.springframework.ai.chat.client.advisor.api.StreamAdvisorChain; import reactor.core.publisher.Flux; /** * 自定义日志 Advisor * 打印 info 级别日志、只输出单次用户提示词和 AI 回复的文本 */ Slf4j public class MyLoggerAdvisor implements CallAdvisor, StreamAdvisor { Override public String getName() { return this.getClass().getSimpleName(); } Override public int getOrder() { return 0; } private ChatClientRequest before(ChatClientRequest request) { log.info(AI Request: {}, request.prompt()); return request; } private void observeAfter(ChatClientResponse chatClientResponse) { log.info(AI Response: {}, chatClientResponse.chatResponse().getResult().getOutput().getText()); } Override public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain chain) { chatClientRequest before(chatClientRequest); ChatClientResponse chatClientResponse chain.nextCall(chatClientRequest); observeAfter(chatClientResponse); return chatClientResponse; } Override public FluxChatClientResponse adviseStream(ChatClientRequest chatClientRequest, StreamAdvisorChain chain) { chatClientRequest before(chatClientRequest); FluxChatClientResponse chatClientResponseFlux chain.nextStream(chatClientRequest); return (new ChatClientMessageAggregator()).aggregateChatClientResponse(chatClientResponseFlux, this::observeAfter); } }3如何自定义Re-Reading AdvisorRe-Reading又称 Re2是一种通过让模型重新阅读问题来提升大语言模型推理能力的技术有文献验证其效果。⚠️注意该技术会使调用成本加倍因此不建议在面向 C 端的 AI 应用中使用。2. 实现原理核心是拦截并改写用户 Prompt将用户输入格式化为{Input_Query} Read the question again: {Input_Query}通过让模型重复阅读问题强化对问题的理解从而提升推理表现。3. 实现方式需要自定义 Advisor在请求阶段拦截并修改userText将原始输入替换为上述格式。Re2 并非完全没用它适合B 端企业级高价值场景比如金融领域的复杂风控推理工业场景的故障诊断长文本、多条件推理科研领域的数据分析需要模型精准理解复杂问题Re2 提升的是「复杂推理能力」但 C 端用户的提问大多是简单问题如「今天天气怎么样」「怎么改密码」日常闲聊如「讲个笑话」「推荐一首歌」基础信息查询如「Java 变量怎么定义」。这些场景根本不需要「强化推理」Re2 带来的「推理提升」感知不到但「成本增加、响应变慢」是用户和开发者都能直接感受到的 —— 相当于你花双倍的钱却只换来和原来几乎一样的回答完全不划算。4拦截器最佳实践保持单一职责每个 Advisor 应专注于一项特定任务避免在一个拦截器中混杂多种逻辑。注意执行顺序通过合理设置getOrder()方法的返回值确保多个 Advisor 按预期顺序执行。同时支持流式和非流式尽可能同时实现AroundAdvisor非流式和StreamAroundAdvisor流式接口以提升通用性和灵活性。高效处理请求避免在 Advisor 中执行耗时操作如慢查询、阻塞 IO防止拖慢整体响应速度。测试边界情况确保 Advisor 能优雅处理异常、空请求、空响应等边界场景避免引发系统级错误。流式场景复杂处理对于需要更复杂逻辑的流式场景可使用 Reactor 提供的操作符如map、doOnNext、filter等来处理流式响应。可以使用adviseContext在Advisor链中共享状态