文章摘要Spring AI项目中Advisor常被用于日志、Memory、RAG、权限、内容审核和工具执行但开发者经常遇到“明明注册了却没有执行”“请求生效但响应处理顺序不对”“同步接口正常、流式接口失效”等问题。本文从ChatClient实例、defaultAdvisors与运行时advisors、getOrder顺序、CallAdvisor与StreamAdvisor、Context参数和日志观测六个方面给出完整排查方法。一、先确认你调用的是不是同一个ChatClient最常见的问题不是Advisor代码错误而是Advisor注册在A客户端业务调用的却是B客户端。例如BeanChatClientragChatClient(ChatClient.Builderbuilder,QuestionAnswerAdvisoradvisor){returnbuilder.defaultAdvisors(advisor).build();}业务类却重新使用BuilderServicepublicclassAiService{privatefinalChatClientchatClient;publicAiService(ChatClient.Builderbuilder){this.chatClientbuilder.build();}}这里创建的是一个新ChatClient没有注册RAG Advisor。正确做法ServicepublicclassAiService{privatefinalChatClientragChatClient;publicAiService(Qualifier(ragChatClient)ChatClientragChatClient){this.ragChatClientragChatClient;}}多ChatClient项目必须明确命名。二、defaultAdvisors和advisors有什么区别defaultAdvisors在构建ChatClient时注册对这个客户端的所有调用生效ChatClientchatClientbuilder.defaultAdvisors(loggingAdvisor,memoryAdvisor).build();适合通用日志安全检查租户上下文默认Memory默认RAG成本统计。advisors在单次请求中注册或传递参数StringanswerchatClient.prompt().advisors(advisorSpec-advisorSpec.advisors(customAdvisor).param(tenantId,tenantId)).user(message).call().content();适合某一次请求临时启用传递conversationId动态RAG过滤条件用户级策略临时审核规则。常见错误是只传参数却没有注册对应Advisor。三、Advisor执行顺序不是“越大越先执行”Spring AI按照getOrder()排序数值越小 → 优先级越高 → 请求阶段越早执行例如OverridepublicintgetOrder(){returnOrdered.HIGHEST_PRECEDENCE100;}请求阶段安全Advisor → 租户Advisor → Memory Advisor → RAG Advisor → 模型响应阶段会像栈一样反向返回。如果两个Advisor返回相同order执行顺序不保证稳定。推荐集中定义publicfinalclassAdvisorOrders{publicstaticfinalintSECURITY-1000;publicstaticfinalintTENANT-800;publicstaticfinalintMEMORY-500;publicstaticfinalintRAG-200;publicstaticfinalintLOGGING1000;privateAdvisorOrders(){}}四、同步接口和流式接口需要不同能力Spring AI Advisor核心接口包括CallAdvisorStreamAdvisor如果自定义Advisor只实现CallAdvisor它只会参与.call()不会自动参与.stream()。一个简化的同步AdvisorComponentpublicclassRequestLoggingAdvisorimplementsCallAdvisor{privatestaticfinalLoggerlogLoggerFactory.getLogger(RequestLoggingAdvisor.class);OverridepublicChatClientResponseadviseCall(ChatClientRequestrequest,CallAdvisorChainchain){log.info(AI request context{},request.context());ChatClientResponseresponsechain.nextCall(request);log.info(AI response received);returnresponse;}OverridepublicStringgetName(){returnrequestLoggingAdvisor;}OverridepublicintgetOrder(){returnAdvisorOrders.LOGGING;}}最容易遗漏的是chain.nextCall(request)如果没有调用后续链又没有自己构造响应请求就会被阻断。五、Advisor可能主动阻断请求安全Advisor可以直接返回受控响应因此当模型没有被调用时要检查是否有Advisor提前返回是否抛出异常是否命中缓存是否触发安全策略是否错误判断输入为空是否忘记调用下一条链。建议在每个Advisor中记录advisor_name request_enter request_exit response_enter response_exit blocked duration_ms六、运行时参数名称是否一致传参.advisors(spec-spec.param(tenantId,tenantId).param(conversationId,conversationId))Advisor读取StringtenantId(String)request.context().get(tenantId);名称不一致时不会自动报错只会得到null。建议使用常量。七、Context更新后是否传入下一条链自定义Advisor如果增加上下文需要创建更新后的请求再传给后续链。伪代码ChatClientRequestupdatedRequestrequest.mutate().context(AdvisorContextKeys.TENANT_ID,tenantId).build();returnchain.nextCall(updatedRequest);不要只修改局部Map然后仍然把旧request传给下一条链。八、Prompt修改是否真的写回请求错误做法StringenhancedPromptoriginalPrompt\n补充上下文;returnchain.nextCall(request);虽然生成了新字符串但没有更新request。核心原则是修改结果必须进入传给下一条链的ChatClientRequest。九、TemplateRenderer不会自动影响Advisor内部模板ChatClient可以配置.templateRenderer(customRenderer)但它只影响直接通过ChatClient链定义的user和system模板不会自动影响QuestionAnswerAdvisor或自定义RAG模板。如果主Prompt正常、RAG增强内容异常需要单独检查Advisor模板配置。十、Memory Advisor不生效的常见原因conversationId没有传递每次请求生成新的conversationIdChatMemoryRepository没有持久化Advisor顺序不合理历史消息被上下文裁剪。稳定会话ID示例.advisors(spec-spec.param(ChatMemory.CONVERSATION_ID,conversationId))十一、RAG Advisor不生效的常见原因检查VectorStore是否有数据 Embedding维度是否一致 检索过滤条件是否过严 相似度阈值是否过高 tenantId是否正确 检索结果是否进入Prompt Advisor是否注册到实际ChatClient建议把检索结果数量写入Context便于从Trace判断问题发生在哪一层。十二、开启可观测性生产项目建议接入ActuatorMicrometerOpenTelemetryTrace ID日志MDC。日志示例traceIdabc123 advisortenantAdvisor phaserequest order-800 durationMs2不要记录完整敏感Prompt可以记录Prompt哈希、字符数、Token估算、模型、Advisor名称与执行耗时。十三、最小排查清单□ 业务调用的是注册Advisor的ChatClient □ defaultAdvisors确实执行 □ 单次advisors参数名称正确 □ getOrder没有重复 □ 数值越小优先级越高 □ 同步Advisor用于call □ 流式Advisor用于stream □ 调用了nextCall或nextStream □ 修改后的request传入下一条链 □ Context更新被正确写回 □ TemplateRenderer作用范围正确 □ Memory使用稳定conversationId □ RAG检索结果非空 □ Trace中能看到Advisor总结Advisor“不生效”通常集中在四类问题注册错ChatClient 执行顺序理解错误 同步与流式接口不匹配 修改结果没有写回请求链先沿着ChatClient实例、Advisor注册、order、Context和链式调用逐层检查比反复修改Prompt更有效。