Spring AI 1.x 系列【12】Advisors API:AI 交互拦截增强

张开发
2026/4/30 19:55:06 15 分钟阅读

分享文章

Spring AI 1.x 系列【12】Advisors API:AI 交互拦截增强
文章目录1. 概述2. 核心组件2.1 Advisor2.2 CallAdvisor2.3 StreamAdvisor2.4 AdvisorChain2.5 ChatClientRequest2.6 ChatClientResponse3. 执行流程4. 案例演示4.1 请求、响应日志打印4.1.1 日志增强器4.1.2 配置4.1.3 测试4.2 自定义增强器4.2.1 Re‑ReadingRe24.2.2 Re2Advisor4.2.3 测试1. 概述Advisors API是Spring AI提供的核心扩展机制本质是「AI交互的拦截器 / 增强器」它能在ChatClient与大模型LLM的交互全链路中调用前 / 调用后插入自定义逻辑实现对请求 / 响应的拦截、修改、增强最终让开发者无需重复编写通用AI逻辑快速构建复杂、可复用、易维护的AI组件。2. 核心组件Advisors相关接口位于包org.springframework.ai.chat.client.advisor.apiAdvisors API包含用于非流式场景的CallAdvisor与CallAdvisorChain以及用于流式场景的StreamAdvisor与StreamAdvisorChain。同时还包含用于表示待封装提示词请求的ChatClientRequest以及用于承载对话补全响应的ChatClientResponse。二者均持有一个增强上下文advise-context用于在增强器链中共享状态。2.1 AdvisorAdvisor是所有增强器的顶层父接口定义了增强器的最基础能力publicinterfaceAdvisorextendsOrdered{StringgetName();}能力介绍继承Ordered接口表示所有增强器都具备 “排序能力”可通过排序规则如优先级决定多个增强器的执行顺序。核心方法getName()获取增强器的唯一名称用于标识增强器、日志打印、异常排查。Advisor有两个子接口CallAdvisorStreamAdvisor2.2 CallAdvisor用于同步、非流式的对话增强适用于不需要实时流式返回、对响应完整性要求高的场景。接口定义// 同步非流式增强器publicinterfaceCallAdvisorextendsAdvisor{ChatClientResponseadviseCall(ChatClientRequestchatClientRequest,CallAdvisorChaincallAdvisorChain);}核心方法adviseCall(...)增强同步对话的核心方法接收两个参数、返回增强后的响应ChatClientRequest输入参数代表原始对话请求。CallAdvisorChain输入参数增强器链用于调用下一个增强器若有多个增强器通过该链条按优先级执行。ChatClientResponse返回值增强后的对话响应。所以子接口和子类2.3 StreamAdvisor用于响应式、流式的对话增强适用于需要实时反馈、响应内容较长的场景。其方法能力和CallAdvisor一致。接口定义// 响应式流式增强器publicinterfaceStreamAdvisorextendsAdvisor{FluxChatClientResponseadviseStream(ChatClientRequestchatClientRequest,StreamAdvisorChainstreamAdvisorChain);}所以子接口和子类2.4 AdvisorChainAdvisorChain是增强器执行链的核心载体设计理念完全类比Spring AOP切面链、Servlet过滤器链负责按优先级串联执行所有增强器并最终调用大模型接口。在ChatClient同步调用的核心方法中会先构建增强器链再将请求交由链条处理OverridepublicCallResponseSpeccall(){BaseAdvisorChainadvisorChainbuildAdvisorChain();returnnewDefaultCallResponseSpec(DefaultChatClientUtils.toChatClientRequest(this),advisorChain,this.observationRegistry,this.chatClientObservationConvention);}增强器链的顶层父接口提供观测注册能力publicinterfaceAdvisorChain{defaultObservationRegistrygetObservationRegistry(){returnObservationRegistry.NOOP;}}publicinterfaceCallAdvisorChainextendsAdvisorChain{ChatClientResponsenextCall(ChatClientRequestchatClientRequest);ListCallAdvisorgetCallAdvisors();CallAdvisorChaincopy(CallAdvisorafter);}Spring AI框架提供了多个内置增强器实现管理对话记忆存储中的对话历史MessageChatMemoryAdvisor检索记忆并以消息集合形式添加到提示词保留对话历史结构部分AI模型不支持。PromptChatMemoryAdvisor检索记忆并整合到提示词的系统文本中。VectorStoreChatMemoryAdvisor从向量库检索记忆并添加到提示词的系统文本中适用于从海量数据中高效检索相关信息。问答增强器QuestionAnswerAdvisor基于向量库实现问答能力采用朴素RAG检索增强生成模式。RetrievalAugmentationAdvisor基于org.springframework.ai.rag包的构建块遵循模块化RAG架构实现通用RAG流程。推理增强器ReReadingAdvisor实现RE2重读策略提升大语言模型在输入阶段的理解能力。内容安全增强器SafeGuardAdvisor简单的增强器用于防止模型生成有害或不当内容。2.5 ChatClientRequest用Java record定义的不可变数据载体类专门用于封装聊天客户端的请求数据核心包含prompt提示词和context增强上下文两部分信息。核心代码publicrecordChatClientRequest(Promptprompt,MapString,Objectcontext){publicChatClientRequest(Promptprompt,MapString,Objectcontext){Assert.notNull(prompt,prompt cannot be null);Assert.notNull(context,context cannot be null);Assert.noNullElements(context.keySet(),context keys cannot be null);this.promptprompt;this.contextcontext;}publicChatClientRequestcopy(){returnnewChatClientRequest(this.prompt.copy(),newHashMap(this.context));}publicBuildermutate(){return(newBuilder()).prompt(this.prompt.copy()).context(newHashMap(this.context));}publicstaticBuilderbuilder(){returnnewBuilder();}publicPromptprompt(){returnthis.prompt;}publicMapString,Objectcontext(){returnthis.context;}//.......}提示增强上下文用于在增强器链中共享状态。2.6 ChatClientResponse用Java record定义的不可变数据载体类专门用于封装聊天请求的返回结果核心包含ChatResponse核心响应内容和context上下文信息两部分信息。核心代码publicrecordChatClientResponse(ChatResponsechatResponse,MapString,Objectcontext){publicChatClientResponse(NullableChatResponsechatResponse,MapString,Objectcontext){Assert.notNull(context,context cannot be null);Assert.noNullElements(context.keySet(),context keys cannot be null);this.chatResponsechatResponse;this.contextcontext;}publicChatClientResponsecopy(){returnnewChatClientResponse(this.chatResponse,newHashMap(this.context));}publicBuildermutate(){return(newBuilder()).chatResponse(this.chatResponse).context(newHashMap(this.context));}publicstaticBuilderbuilder(){returnnewBuilder();}NullablepublicChatResponsechatResponse(){returnthis.chatResponse;}publicMapString,Objectcontext(){returnthis.context;}3. 执行流程以下流程图展示了增强器链与聊天模型的交互逻辑流程相关说明Spring AI框架根据用户的Prompt创建ChatClientRequest并初始化空的增强上下文对象。链中的每个增强器处理请求可能修改请求也可选择不调用下一个组件以阻塞请求此时需由该增强器填充响应。框架提供的最终增强器将请求发送至聊天模型。聊天模型的响应沿增强器链回传并转换为包含共享增强上下文的ChatClientResponse。每个增强器可处理或修改响应。提取ChatCompletion后最终的ChatClientResponse返回给客户端。增强器在链中的执行顺序由getOrder()方法决定核心规则值越小执行优先级越高先执行值越大优先级越低。若多个增强器order值相同执行顺序不保证。增强器链遵循栈式逻辑链中第一个增强器最先处理请求。最后处理响应。控制执行顺序的方式设置order接近Ordered.HIGHEST_PRECEDENCE确保增强器最先执行请求处理先执行响应处理最后执行设置order接近Ordered.LOWEST_PRECEDENCE确保增强器最后执行请求处理最后执行响应处理最先执行Spring Ordered接口语义参考publicinterfaceOrdered{/** 最高优先级常量 */intHIGHEST_PRECEDENCEInteger.MIN_VALUE;/** 最低优先级常量 */intLOWEST_PRECEDENCEInteger.MAX_VALUE;/** * 获取对象的顺序值 * p值越大优先级越低值越小优先级越高类似 Servlet 的 load-on-startup * p相同值会导致对象排序位置随机 * return 顺序值 */intgetOrder();}4. 案例演示4.1 请求、响应日志打印4.1.1 日志增强器Spring AI默认已经提供了一个日志打印实现实现了CallAdvisor,StreamAdvisor接口说明同时支持同步和流式场景publicclassSimpleLoggerAdvisorimplementsCallAdvisor,StreamAdvisor{publicstaticfinalFunctionChatClientRequest,StringDEFAULT_REQUEST_TO_STRINGChatClientRequest::toString;publicstaticfinalFunctionChatResponse,StringDEFAULT_RESPONSE_TO_STRINGModelOptionsUtils::toJsonStringPrettyPrinter;privatestaticfinalLoggerloggerLoggerFactory.getLogger(SimpleLoggerAdvisor.class);privatefinalFunctionChatClientRequest,StringrequestToString;privatefinalFunctionChatResponse,StringresponseToString;privatefinalintorder;publicSimpleLoggerAdvisor(){this(DEFAULT_REQUEST_TO_STRING,DEFAULT_RESPONSE_TO_STRING,0);}publicSimpleLoggerAdvisor(intorder){this(DEFAULT_REQUEST_TO_STRING,DEFAULT_RESPONSE_TO_STRING,order);}publicSimpleLoggerAdvisor(NullableFunctionChatClientRequest,StringrequestToString,NullableFunctionChatResponse,StringresponseToString,intorder){this.requestToStringrequestToString!null?requestToString:DEFAULT_REQUEST_TO_STRING;this.responseToStringresponseToString!null?responseToString:DEFAULT_RESPONSE_TO_STRING;this.orderorder;}OverridepublicChatClientResponseadviseCall(ChatClientRequestchatClientRequest,CallAdvisorChaincallAdvisorChain){logRequest(chatClientRequest);ChatClientResponsechatClientResponsecallAdvisorChain.nextCall(chatClientRequest);logResponse(chatClientResponse);returnchatClientResponse;}OverridepublicFluxChatClientResponseadviseStream(ChatClientRequestchatClientRequest,StreamAdvisorChainstreamAdvisorChain){logRequest(chatClientRequest);FluxChatClientResponsechatClientResponsesstreamAdvisorChain.nextStream(chatClientRequest);returnnewChatClientMessageAggregator().aggregateChatClientResponse(chatClientResponses,this::logResponse);}protectedvoidlogRequest(ChatClientRequestrequest){logger.debug(request: {},this.requestToString.apply(request));}protectedvoidlogResponse(ChatClientResponsechatClientResponse){logger.debug(response: {},this.responseToString.apply(chatClientResponse.chatResponse()));}4.1.2 配置SimpleLoggerAdvisor使用的是logger.debug所以还需要配置以下日志级别# 日志配置logging:level:org.springframework.ai.chat.client.advisor:DEBUGChatClient中配置增强器Bean(zhiPuAiChatClient)publicChatClientzhiPuAiChatClient(ZhiPuAiChatModelzhiPuAiChatModel){returnChatClient.builder(zhiPuAiChatModel).defaultAdvisors(SimpleLoggerAdvisor.builder().build()).build();}4.1.3 测试运行测试类并查看控制台TestpublicvoidzhiPuAiChatClientTest(){StringcontentzhiPuAiChatClient.prompt(今天周几).call().content();System.out.println(content);}4.2 自定义增强器创建增强器需实现CallAdvisor或StreamAdvisor或两者核心步骤实现nextCall()非流式或nextStream()流式方法。为增强器提供唯一名称。通过order值控制执行顺序值越小越先执行。Spring AI还提供了MessageAggregator工具类可将Flux响应聚合为单个ChatClientResponse适用于观测完整响应的场景仅只读操作无法修改响应。4.2.1 Re‑ReadingRe2Re2是论文 《Re‑Reading Improves Reasoning in Large Language Models》 里提出的一个超级简单但非常有效的技巧用来提升大模型的推理、思考、解题能力。核心思想让模型把问题 “再读一遍”推理正确率会明显变高。原始输入11等于几Re2之后的输入11等于几Readthe question again:11等于几4.2.2 Re2Advisor实现Re2Advisor处理逻辑/** * Re2Re-Reading顾问器自动扩充用户Prompt提升LLM推理能力 */publicclassRe2AdvisorimplementsCallAdvisor,StreamAdvisor,Ordered{// Re2提示词模板核心原问题 再读一遍原问题privatestaticfinalStringDEFAULT_RE2_TEMPLATE {user_query} Read the question again: {user_query} ;privatefinalPromptTemplatere2Template;// 执行顺序数值越小越先执行保证Re2最先修改PromptprivateintorderOrdered.HIGHEST_PRECEDENCE10;// 无参构造器使用默认模板publicRe2Advisor(){this.re2TemplatenewPromptTemplate(DEFAULT_RE2_TEMPLATE);}// 自定义模板构造器支持个性化Re2提示publicRe2Advisor(StringcustomTemplate){this.re2TemplatenewPromptTemplate(customTemplate);}OverridepublicStringgetName(){returnRe2Advisor;// 唯一名称便于监控/日志识别}OverridepublicintgetOrder(){returnthis.order;}// 自定义执行顺序publicRe2AdvisorwithOrder(intorder){this.orderorder;returnthis;}// 非流式调用同步 OverridepublicChatClientResponseadviseCall(ChatClientRequestchatClientRequest,CallAdvisorChainchain){// 1. 应用Re2规则生成增强后的PromptChatClientRequestenhancedRequestenhancePromptWithRe2(chatClientRequest);// 2. 调用链的下一个组件最终调用LLMreturnchain.nextCall(enhancedRequest);}// 流式调用异步 OverridepublicFluxChatClientResponseadviseStream(ChatClientRequestrequest,StreamAdvisorChainchain){// 1. 应用Re2规则生成增强后的PromptChatClientRequestenhancedRequestenhancePromptWithRe2(request);// 2. 调用链的下一个组件返回流式响应returnchain.nextStream(enhancedRequest);}// 核心Re2 Prompt增强逻辑 privateChatClientRequestenhancePromptWithRe2(ChatClientRequestoriginalRequest){// 1. 获取用户原始提问内容StringoriginalUserTextoriginalRequest.prompt().getUserMessage().getText();// 2. 跳过空内容防御性编程if(originalUserTextnull||originalUserText.isBlank()){returnoriginalRequest;}// 3. 应用Re2模板生成增强后的提问内容StringenhancedUserTextre2Template.create(Map.of(user_query,originalUserText)).getContents();// 4. 构建新的请求returnoriginalRequest.mutate().prompt(originalRequest.prompt().augmentUserMessage(enhancedUserText)).build();}}4.2.3 测试运行测试类并查看控制台TestpublicvoidzhiPuAiChatClientTest(){StringreasoningQuestion有3个苹果小明吃了1个妈妈又买了2个现在一共有几个苹果;// 调用ChatClientRe2Advisor 会自动增强 PromptStringresponsezhiPuAiChatClient.prompt().user(reasoningQuestion).call().content();System.out.println(response);}

更多文章