本文根据 Spring AI 官方文档 https://docs.spring.io/spring-ai/reference/api/advisors.html 整理,用大白话把原版内容讲清楚,代码可以直接复制使用。
一、这玩意儿是干啥的?
你在用 Spring AI 调用大模型的时候,是不是经常要干这些破事:
- 把聊天记录翻出来塞进 Prompt
- 从向量数据库搜点相关文档一起发给模型
- 记录一下用户问了啥、模型回了啥
- 检查一下模型有没有输出敏感词
如果每次调用都要手动写一遍这些逻辑,代码很快就没法看了。
Advisors 就是来解决这个问题的。它本质上是个拦截器,在请求到达大模型之前、响应返回之后,插一段自定义逻辑进去。
官网原话是:封装重复的生成式 AI 模式,让代码更干净、更可复用。
二、怎么用?先看个例子
// 先把需要的组件准备好
ChatMemory chatMemory = ...; // 记忆存储器
VectorStore vectorStore = ...; // 向量数据库
// 创建 ChatClient 的时候顺便把 Advisor 注册上
var chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(chatMemory).build(), // 记忆功能
QuestionAnswerAdvisor.builder(vectorStore).build() // RAG 功能
)
.build();
// 后面调用的时候,这些功能就自动生效了
String conversationId = "678";
String response = this.chatClient.prompt()
.advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, conversationId))
.user("你有什么功能?")
.call()
.content();
关键点:Advisor 推荐在构建 ChatClient 的时候通过 defaultAdvisors() 一次性注册,而不是每次调用的时候临时加。
三、核心执行流程
3.1 整体时序图
下面这张图是整个 Advisor 链的执行过程,建议仔细看:
大模型Advisor 3(order最大)Advisor 2Advisor 1(order最小)你的代码大模型Advisor 3(order最大)Advisor 2Advisor 1(order最小)你的代码#mermaid-svg-ZesB0u2WKPSLG0ib{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ZesB0u2WKPSLG0ib .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ZesB0u2WKPSLG0ib .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ZesB0u2WKPSLG0ib .error-icon{fill:#552222;}#mermaid-svg-ZesB0u2WKPSLG0ib .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ZesB0u2WKPSLG0ib .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ZesB0u2WKPSLG0ib .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ZesB0u2WKPSLG0ib .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ZesB0u2WKPSLG0ib .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ZesB0u2WKPSLG0ib .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ZesB0u2WKPSLG0ib .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ZesB0u2WKPSLG0ib .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ZesB0u2WKPSLG0ib .marker.cross{stroke:#333333;}#mermaid-svg-ZesB0u2WKPSLG0ib svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ZesB0u2WKPSLG0ib p{margin:0;}#mermaid-svg-ZesB0u2WKPSLG0ib .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ZesB0u2WKPSLG0ib text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-ZesB0u2WKPSLG0ib .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ZesB0u2WKPSLG0ib .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-ZesB0u2WKPSLG0ib .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-ZesB0u2WKPSLG0ib .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-ZesB0u2WKPSLG0ib #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-ZesB0u2WKPSLG0ib .sequenceNumber{fill:white;}#mermaid-svg-ZesB0u2WKPSLG0ib #sequencenumber{fill:#333;}#mermaid-svg-ZesB0u2WKPSLG0ib #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-ZesB0u2WKPSLG0ib .messageText{fill:#333;stroke:none;}#mermaid-svg-ZesB0u2WKPSLG0ib .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ZesB0u2WKPSLG0ib .labelText,#mermaid-svg-ZesB0u2WKPSLG0ib .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-ZesB0u2WKPSLG0ib .loopText,#mermaid-svg-ZesB0u2WKPSLG0ib .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-ZesB0u2WKPSLG0ib .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ZesB0u2WKPSLG0ib .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-ZesB0u2WKPSLG0ib .noteText,#mermaid-svg-ZesB0u2WKPSLG0ib .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-ZesB0u2WKPSLG0ib .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ZesB0u2WKPSLG0ib .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ZesB0u2WKPSLG0ib .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ZesB0u2WKPSLG0ib .actorPopupMenu{position:absolute;}#mermaid-svg-ZesB0u2WKPSLG0ib .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-ZesB0u2WKPSLG0ib .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ZesB0u2WKPSLG0ib .actor-man circle,#mermaid-svg-ZesB0u2WKPSLG0ib line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-ZesB0u2WKPSLG0ib :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}处理请求(如:记日志)处理请求(如:注入记忆)处理请求(如:RAG检索)处理响应处理响应处理响应1. 发送 ChatClientRequest2. chain.nextCall(request)3. chain.nextCall(request)4. 调用大模型5. 返回响应6. 返回响应7. 返回响应8. 返回 ChatClientResponse
关键理解:
- 请求往下传(1→2→3→4),响应往回走(5→6→7→8)
- 最后一个 Advisor 负责真正调用大模型
- 越靠近用户的 Advisor,getOrder() 数值越小
3.2 Order 值的含义(容易搞反)
#mermaid-svg-cVMP6mA6VYQQOY78{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cVMP6mA6VYQQOY78 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cVMP6mA6VYQQOY78 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cVMP6mA6VYQQOY78 .error-icon{fill:#552222;}#mermaid-svg-cVMP6mA6VYQQOY78 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cVMP6mA6VYQQOY78 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cVMP6mA6VYQQOY78 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cVMP6mA6VYQQOY78 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cVMP6mA6VYQQOY78 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cVMP6mA6VYQQOY78 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cVMP6mA6VYQQOY78 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cVMP6mA6VYQQOY78 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cVMP6mA6VYQQOY78 .marker.cross{stroke:#333333;}#mermaid-svg-cVMP6mA6VYQQOY78 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cVMP6mA6VYQQOY78 p{margin:0;}#mermaid-svg-cVMP6mA6VYQQOY78 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-cVMP6mA6VYQQOY78 .cluster-label text{fill:#333;}#mermaid-svg-cVMP6mA6VYQQOY78 .cluster-label span{color:#333;}#mermaid-svg-cVMP6mA6VYQQOY78 .cluster-label span p{background-color:transparent;}#mermaid-svg-cVMP6mA6VYQQOY78 .label text,#mermaid-svg-cVMP6mA6VYQQOY78 span{fill:#333;color:#333;}#mermaid-svg-cVMP6mA6VYQQOY78 .node rect,#mermaid-svg-cVMP6mA6VYQQOY78 .node circle,#mermaid-svg-cVMP6mA6VYQQOY78 .node ellipse,#mermaid-svg-cVMP6mA6VYQQOY78 .node polygon,#mermaid-svg-cVMP6mA6VYQQOY78 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-cVMP6mA6VYQQOY78 .rough-node .label text,#mermaid-svg-cVMP6mA6VYQQOY78 .node .label text,#mermaid-svg-cVMP6mA6VYQQOY78 .image-shape .label,#mermaid-svg-cVMP6mA6VYQQOY78 .icon-shape .label{text-anchor:middle;}#mermaid-svg-cVMP6mA6VYQQOY78 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-cVMP6mA6VYQQOY78 .rough-node .label,#mermaid-svg-cVMP6mA6VYQQOY78 .node .label,#mermaid-svg-cVMP6mA6VYQQOY78 .image-shape .label,#mermaid-svg-cVMP6mA6VYQQOY78 .icon-shape .label{text-align:center;}#mermaid-svg-cVMP6mA6VYQQOY78 .node.clickable{cursor:pointer;}#mermaid-svg-cVMP6mA6VYQQOY78 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-cVMP6mA6VYQQOY78 .arrowheadPath{fill:#333333;}#mermaid-svg-cVMP6mA6VYQQOY78 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-cVMP6mA6VYQQOY78 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-cVMP6mA6VYQQOY78 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cVMP6mA6VYQQOY78 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-cVMP6mA6VYQQOY78 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cVMP6mA6VYQQOY78 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-cVMP6mA6VYQQOY78 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-cVMP6mA6VYQQOY78 .cluster text{fill:#333;}#mermaid-svg-cVMP6mA6VYQQOY78 .cluster span{color:#333;}#mermaid-svg-cVMP6mA6VYQQOY78 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-cVMP6mA6VYQQOY78 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-cVMP6mA6VYQQOY78 rect.text{fill:none;stroke-width:0;}#mermaid-svg-cVMP6mA6VYQQOY78 .icon-shape,#mermaid-svg-cVMP6mA6VYQQOY78 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-cVMP6mA6VYQQOY78 .icon-shape p,#mermaid-svg-cVMP6mA6VYQQOY78 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-cVMP6mA6VYQQOY78 .icon-shape .label rect,#mermaid-svg-cVMP6mA6VYQQOY78 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-cVMP6mA6VYQQOY78 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-cVMP6mA6VYQQOY78 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-cVMP6mA6VYQQOY78 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
响应阶段
大模型
order=2最先处理响应
order=1
order=0最后处理响应
返回给用户
请求阶段
用户请求
order=0最先执行
order=1
order=2
大模型
记住:
- 请求阶段:order=0 最先执行
- 响应阶段:order=0 最后执行
- 这就是栈结构的特点
3.3 完整流程图(包含阻断情况)
#mermaid-svg-LLuAMlPBHeWrq0qQ{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-LLuAMlPBHeWrq0qQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-LLuAMlPBHeWrq0qQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-LLuAMlPBHeWrq0qQ .error-icon{fill:#552222;}#mermaid-svg-LLuAMlPBHeWrq0qQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LLuAMlPBHeWrq0qQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-LLuAMlPBHeWrq0qQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LLuAMlPBHeWrq0qQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LLuAMlPBHeWrq0qQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-LLuAMlPBHeWrq0qQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LLuAMlPBHeWrq0qQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LLuAMlPBHeWrq0qQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LLuAMlPBHeWrq0qQ .marker.cross{stroke:#333333;}#mermaid-svg-LLuAMlPBHeWrq0qQ svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LLuAMlPBHeWrq0qQ p{margin:0;}#mermaid-svg-LLuAMlPBHeWrq0qQ .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-LLuAMlPBHeWrq0qQ .cluster-label text{fill:#333;}#mermaid-svg-LLuAMlPBHeWrq0qQ .cluster-label span{color:#333;}#mermaid-svg-LLuAMlPBHeWrq0qQ .cluster-label span p{background-color:transparent;}#mermaid-svg-LLuAMlPBHeWrq0qQ .label text,#mermaid-svg-LLuAMlPBHeWrq0qQ span{fill:#333;color:#333;}#mermaid-svg-LLuAMlPBHeWrq0qQ .node rect,#mermaid-svg-LLuAMlPBHeWrq0qQ .node circle,#mermaid-svg-LLuAMlPBHeWrq0qQ .node ellipse,#mermaid-svg-LLuAMlPBHeWrq0qQ .node polygon,#mermaid-svg-LLuAMlPBHeWrq0qQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-LLuAMlPBHeWrq0qQ .rough-node .label text,#mermaid-svg-LLuAMlPBHeWrq0qQ .node .label text,#mermaid-svg-LLuAMlPBHeWrq0qQ .image-shape .label,#mermaid-svg-LLuAMlPBHeWrq0qQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-LLuAMlPBHeWrq0qQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-LLuAMlPBHeWrq0qQ .rough-node .label,#mermaid-svg-LLuAMlPBHeWrq0qQ .node .label,#mermaid-svg-LLuAMlPBHeWrq0qQ .image-shape .label,#mermaid-svg-LLuAMlPBHeWrq0qQ .icon-shape .label{text-align:center;}#mermaid-svg-LLuAMlPBHeWrq0qQ .node.clickable{cursor:pointer;}#mermaid-svg-LLuAMlPBHeWrq0qQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-LLuAMlPBHeWrq0qQ .arrowheadPath{fill:#333333;}#mermaid-svg-LLuAMlPBHeWrq0qQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-LLuAMlPBHeWrq0qQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-LLuAMlPBHeWrq0qQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LLuAMlPBHeWrq0qQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-LLuAMlPBHeWrq0qQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LLuAMlPBHeWrq0qQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-LLuAMlPBHeWrq0qQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-LLuAMlPBHeWrq0qQ .cluster text{fill:#333;}#mermaid-svg-LLuAMlPBHeWrq0qQ .cluster span{color:#333;}#mermaid-svg-LLuAMlPBHeWrq0qQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-LLuAMlPBHeWrq0qQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-LLuAMlPBHeWrq0qQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-LLuAMlPBHeWrq0qQ .icon-shape,#mermaid-svg-LLuAMlPBHeWrq0qQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-LLuAMlPBHeWrq0qQ .icon-shape p,#mermaid-svg-LLuAMlPBHeWrq0qQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-LLuAMlPBHeWrq0qQ .icon-shape .label rect,#mermaid-svg-LLuAMlPBHeWrq0qQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-LLuAMlPBHeWrq0qQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-LLuAMlPBHeWrq0qQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-LLuAMlPBHeWrq0qQ :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
是
是
是
否
否
否
用户调用 ChatClient
Step 1: 框架创建ChatClientRequest + 空上下文
Advisor链开始
Advisor A: order=0比如: 记录请求日志
调用 chain.nextCall?
Advisor B: order=1比如: 注入对话记忆
调用 chain.nextCall?
Advisor C: order=2比如: RAG检索增强
调用 chain.nextCall?
最后一个内置Advisor调用大模型
大模型返回响应
Advisor C 处理响应
Advisor B 处理响应
Advisor A 处理响应
返回 ChatClientResponse 给用户
Advisor A 直接返回响应阻断后续执行
Advisor B 直接返回响应阻断后续执行
Advisor C 直接返回响应阻断后续执行
说明:
- 绿色箭头是正常流程
- 红色虚线表示某个 Advisor 可以直接返回(比如安全过滤直接拦截了)
四、核心组件
4.1 两大场景的接口设计
| 非流式(一问一答) | CallAdvisor + CallAdvisorChain | 处理完整的请求和响应 |
| 流式(一个字一个字蹦) | StreamAdvisor + StreamAdvisorChain | 用 Reactor 的 Flux 处理数据流 |
另外还有两个数据载体:
- ChatClientRequest:封装用户的 Prompt,附带一个共享上下文
- ChatClientResponse:封装模型的返回结果,同样带着上下文
4.2 两个关键方法
// 非流式 Advisor 要实现的方法
ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain);
// 流式 Advisor 要实现的方法
Flux<ChatClientResponse> adviseStream(ChatClientRequest request, StreamAdvisorChain chain);
每个 Advisor 还需要实现:
- getOrder():决定执行顺序,数字越小越靠前
- getName():给 Advisor 起个唯一的名字
4.3 Order 值的官方常量
public interface Ordered {
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE; // 最高优先级(最先执行)
int LOWEST_PRECEDENCE = Integer.MAX_VALUE; // 最低优先级(最后执行)
}
五、自己动手写一个 Advisor
示例1:日志记录 Advisor(只看不改)
这个 Advisor 什么都不改,就是看一眼请求和响应,打条日志。
public class SimpleLoggerAdvisor implements CallAdvisor, StreamAdvisor {
private static final Logger log = LoggerFactory.getLogger(SimpleLoggerAdvisor.class);
@Override
public String getName() {
return "SimpleLoggerAdvisor";
}
@Override
public int getOrder() {
return 0; // 希望它最先执行
}
// 非流式版本
@Override
public ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) {
log.info("请求内容:{}", request);
ChatClientResponse response = chain.nextCall(request);
log.info("响应内容:{}", response);
return response;
}
// 流式版本(需要聚合流中的数据)
@Override
public Flux<ChatClientResponse> adviseStream(ChatClientRequest request, StreamAdvisorChain chain) {
log.info("请求内容:{}", request);
Flux<ChatClientResponse> responses = chain.nextStream(request);
// 用工具类把流式数据聚合成一条,方便打日志
return new ChatClientMessageAggregator()
.aggregateChatClientResponse(responses, this::logResponse);
}
private void logResponse(ChatClientResponse response) {
log.info("响应内容:{}", response);
}
}
注意:用 MessageAggregator 聚合流式数据时,只能读不能改。
示例2:Re-Reading 增强 Advisor(动手改请求)
有一篇论文(Re-Reading Improves Reasoning in LLMs)发现:让模型把问题再读一遍,能提高推理准确率。用 Advisor 实现这个很简单。
public class ReReadingAdvisor implements CallAdvisor {
private static final String TEMPLATE = """
用户问题:{question}
请再把问题读一遍:{question}
""";
private int order = 0;
@Override
public ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) {
// 拿到原始问题
String originalText = request.prompt().getUserMessage().getText();
// 套上模板,让问题重复一遍
String augmentedText = TEMPLATE.replace("{question}", originalText);
// 创建一个新请求(注意:Request 是不可变的,要调用 mutate())
ChatClientRequest newRequest = request.mutate()
.prompt(request.prompt().augmentUserMessage(augmentedText))
.build();
// 传给下一个 Advisor 或最终的大模型
return chain.nextCall(newRequest);
}
@Override
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
@Override
public String getName() {
return "ReReadingAdvisor";
}
}
六、流式 vs 非流式的区别
#mermaid-svg-BGERQZevxFy8XEpj{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BGERQZevxFy8XEpj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BGERQZevxFy8XEpj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BGERQZevxFy8XEpj .error-icon{fill:#552222;}#mermaid-svg-BGERQZevxFy8XEpj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BGERQZevxFy8XEpj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BGERQZevxFy8XEpj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BGERQZevxFy8XEpj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BGERQZevxFy8XEpj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BGERQZevxFy8XEpj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BGERQZevxFy8XEpj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BGERQZevxFy8XEpj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BGERQZevxFy8XEpj .marker.cross{stroke:#333333;}#mermaid-svg-BGERQZevxFy8XEpj svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BGERQZevxFy8XEpj p{margin:0;}#mermaid-svg-BGERQZevxFy8XEpj .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-BGERQZevxFy8XEpj .cluster-label text{fill:#333;}#mermaid-svg-BGERQZevxFy8XEpj .cluster-label span{color:#333;}#mermaid-svg-BGERQZevxFy8XEpj .cluster-label span p{background-color:transparent;}#mermaid-svg-BGERQZevxFy8XEpj .label text,#mermaid-svg-BGERQZevxFy8XEpj span{fill:#333;color:#333;}#mermaid-svg-BGERQZevxFy8XEpj .node rect,#mermaid-svg-BGERQZevxFy8XEpj .node circle,#mermaid-svg-BGERQZevxFy8XEpj .node ellipse,#mermaid-svg-BGERQZevxFy8XEpj .node polygon,#mermaid-svg-BGERQZevxFy8XEpj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BGERQZevxFy8XEpj .rough-node .label text,#mermaid-svg-BGERQZevxFy8XEpj .node .label text,#mermaid-svg-BGERQZevxFy8XEpj .image-shape .label,#mermaid-svg-BGERQZevxFy8XEpj .icon-shape .label{text-anchor:middle;}#mermaid-svg-BGERQZevxFy8XEpj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BGERQZevxFy8XEpj .rough-node .label,#mermaid-svg-BGERQZevxFy8XEpj .node .label,#mermaid-svg-BGERQZevxFy8XEpj .image-shape .label,#mermaid-svg-BGERQZevxFy8XEpj .icon-shape .label{text-align:center;}#mermaid-svg-BGERQZevxFy8XEpj .node.clickable{cursor:pointer;}#mermaid-svg-BGERQZevxFy8XEpj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BGERQZevxFy8XEpj .arrowheadPath{fill:#333333;}#mermaid-svg-BGERQZevxFy8XEpj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BGERQZevxFy8XEpj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BGERQZevxFy8XEpj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BGERQZevxFy8XEpj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BGERQZevxFy8XEpj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BGERQZevxFy8XEpj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BGERQZevxFy8XEpj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BGERQZevxFy8XEpj .cluster text{fill:#333;}#mermaid-svg-BGERQZevxFy8XEpj .cluster span{color:#333;}#mermaid-svg-BGERQZevxFy8XEpj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BGERQZevxFy8XEpj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BGERQZevxFy8XEpj rect.text{fill:none;stroke-width:0;}#mermaid-svg-BGERQZevxFy8XEpj .icon-shape,#mermaid-svg-BGERQZevxFy8XEpj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BGERQZevxFy8XEpj .icon-shape p,#mermaid-svg-BGERQZevxFy8XEpj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BGERQZevxFy8XEpj .icon-shape .label rect,#mermaid-svg-BGERQZevxFy8XEpj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BGERQZevxFy8XEpj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BGERQZevxFy8XEpj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BGERQZevxFy8XEpj :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
流式 StreamAdvisor
收到完整请求
处理请求
调用 chain.nextStream
拿到 Flux 数据流
逐个处理每个数据块
最终聚合或逐块输出
非流式 CallAdvisor
收到完整请求
处理请求
调用 chain.nextCall
等待完整响应
处理完整响应
返回 ChatClientResponse
流式 Advisor 的代码骨架(官网给的):
@Override
public Flux<ChatClientResponse> adviseStream(ChatClientRequest request, StreamAdvisorChain chain) {
return Mono.just(request)
.publishOn(Schedulers.boundedElastic()) // 换一个能处理阻塞操作的线程池
.map(req -> {
// 请求前置处理(可能有阻塞操作)
return req;
})
.flatMapMany(chain::nextStream)
.map(response -> {
// 响应后置处理
return response;
});
}
注意:千万别在响应式流里直接做阻塞操作(比如 Thread.sleep、数据库查询),会出问题。
七、Spring AI 内置的 Advisor(拿来就用)
#mermaid-svg-W2enIZ63TFEG3aiG{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-W2enIZ63TFEG3aiG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-W2enIZ63TFEG3aiG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-W2enIZ63TFEG3aiG .error-icon{fill:#552222;}#mermaid-svg-W2enIZ63TFEG3aiG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-W2enIZ63TFEG3aiG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-W2enIZ63TFEG3aiG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-W2enIZ63TFEG3aiG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-W2enIZ63TFEG3aiG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-W2enIZ63TFEG3aiG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-W2enIZ63TFEG3aiG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-W2enIZ63TFEG3aiG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-W2enIZ63TFEG3aiG .marker.cross{stroke:#333333;}#mermaid-svg-W2enIZ63TFEG3aiG svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-W2enIZ63TFEG3aiG p{margin:0;}#mermaid-svg-W2enIZ63TFEG3aiG .edge{stroke-width:3;}#mermaid-svg-W2enIZ63TFEG3aiG .section–1 rect,#mermaid-svg-W2enIZ63TFEG3aiG .section–1 path,#mermaid-svg-W2enIZ63TFEG3aiG .section–1 circle,#mermaid-svg-W2enIZ63TFEG3aiG .section–1 polygon,#mermaid-svg-W2enIZ63TFEG3aiG .section–1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .section–1 text{fill:#ffffff;}#mermaid-svg-W2enIZ63TFEG3aiG .node-icon–1{font-size:40px;color:#ffffff;}#mermaid-svg-W2enIZ63TFEG3aiG .section-edge–1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .edge-depth–1{stroke-width:17;}#mermaid-svg-W2enIZ63TFEG3aiG .section–1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled,#mermaid-svg-W2enIZ63TFEG3aiG .disabled circle,#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:lightgray;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:#efefef;}#mermaid-svg-W2enIZ63TFEG3aiG .section-0 rect,#mermaid-svg-W2enIZ63TFEG3aiG .section-0 path,#mermaid-svg-W2enIZ63TFEG3aiG .section-0 circle,#mermaid-svg-W2enIZ63TFEG3aiG .section-0 polygon,#mermaid-svg-W2enIZ63TFEG3aiG .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-W2enIZ63TFEG3aiG .section-0 text{fill:black;}#mermaid-svg-W2enIZ63TFEG3aiG .node-icon-0{font-size:40px;color:black;}#mermaid-svg-W2enIZ63TFEG3aiG .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-W2enIZ63TFEG3aiG .edge-depth-0{stroke-width:14;}#mermaid-svg-W2enIZ63TFEG3aiG .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled,#mermaid-svg-W2enIZ63TFEG3aiG .disabled circle,#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:lightgray;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:#efefef;}#mermaid-svg-W2enIZ63TFEG3aiG .section-1 rect,#mermaid-svg-W2enIZ63TFEG3aiG .section-1 path,#mermaid-svg-W2enIZ63TFEG3aiG .section-1 circle,#mermaid-svg-W2enIZ63TFEG3aiG .section-1 polygon,#mermaid-svg-W2enIZ63TFEG3aiG .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .section-1 text{fill:black;}#mermaid-svg-W2enIZ63TFEG3aiG .node-icon-1{font-size:40px;color:black;}#mermaid-svg-W2enIZ63TFEG3aiG .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .edge-depth-1{stroke-width:11;}#mermaid-svg-W2enIZ63TFEG3aiG .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled,#mermaid-svg-W2enIZ63TFEG3aiG .disabled circle,#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:lightgray;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:#efefef;}#mermaid-svg-W2enIZ63TFEG3aiG .section-2 rect,#mermaid-svg-W2enIZ63TFEG3aiG .section-2 path,#mermaid-svg-W2enIZ63TFEG3aiG .section-2 circle,#mermaid-svg-W2enIZ63TFEG3aiG .section-2 polygon,#mermaid-svg-W2enIZ63TFEG3aiG .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .section-2 text{fill:#ffffff;}#mermaid-svg-W2enIZ63TFEG3aiG .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-W2enIZ63TFEG3aiG .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .edge-depth-2{stroke-width:8;}#mermaid-svg-W2enIZ63TFEG3aiG .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled,#mermaid-svg-W2enIZ63TFEG3aiG .disabled circle,#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:lightgray;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:#efefef;}#mermaid-svg-W2enIZ63TFEG3aiG .section-3 rect,#mermaid-svg-W2enIZ63TFEG3aiG .section-3 path,#mermaid-svg-W2enIZ63TFEG3aiG .section-3 circle,#mermaid-svg-W2enIZ63TFEG3aiG .section-3 polygon,#mermaid-svg-W2enIZ63TFEG3aiG .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .section-3 text{fill:black;}#mermaid-svg-W2enIZ63TFEG3aiG .node-icon-3{font-size:40px;color:black;}#mermaid-svg-W2enIZ63TFEG3aiG .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .edge-depth-3{stroke-width:5;}#mermaid-svg-W2enIZ63TFEG3aiG .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled,#mermaid-svg-W2enIZ63TFEG3aiG .disabled circle,#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:lightgray;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:#efefef;}#mermaid-svg-W2enIZ63TFEG3aiG .section-4 rect,#mermaid-svg-W2enIZ63TFEG3aiG .section-4 path,#mermaid-svg-W2enIZ63TFEG3aiG .section-4 circle,#mermaid-svg-W2enIZ63TFEG3aiG .section-4 polygon,#mermaid-svg-W2enIZ63TFEG3aiG .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .section-4 text{fill:black;}#mermaid-svg-W2enIZ63TFEG3aiG .node-icon-4{font-size:40px;color:black;}#mermaid-svg-W2enIZ63TFEG3aiG .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .edge-depth-4{stroke-width:2;}#mermaid-svg-W2enIZ63TFEG3aiG .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled,#mermaid-svg-W2enIZ63TFEG3aiG .disabled circle,#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:lightgray;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:#efefef;}#mermaid-svg-W2enIZ63TFEG3aiG .section-5 rect,#mermaid-svg-W2enIZ63TFEG3aiG .section-5 path,#mermaid-svg-W2enIZ63TFEG3aiG .section-5 circle,#mermaid-svg-W2enIZ63TFEG3aiG .section-5 polygon,#mermaid-svg-W2enIZ63TFEG3aiG .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .section-5 text{fill:black;}#mermaid-svg-W2enIZ63TFEG3aiG .node-icon-5{font-size:40px;color:black;}#mermaid-svg-W2enIZ63TFEG3aiG .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .edge-depth-5{stroke-width:-1;}#mermaid-svg-W2enIZ63TFEG3aiG .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled,#mermaid-svg-W2enIZ63TFEG3aiG .disabled circle,#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:lightgray;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:#efefef;}#mermaid-svg-W2enIZ63TFEG3aiG .section-6 rect,#mermaid-svg-W2enIZ63TFEG3aiG .section-6 path,#mermaid-svg-W2enIZ63TFEG3aiG .section-6 circle,#mermaid-svg-W2enIZ63TFEG3aiG .section-6 polygon,#mermaid-svg-W2enIZ63TFEG3aiG .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .section-6 text{fill:black;}#mermaid-svg-W2enIZ63TFEG3aiG .node-icon-6{font-size:40px;color:black;}#mermaid-svg-W2enIZ63TFEG3aiG .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .edge-depth-6{stroke-width:-4;}#mermaid-svg-W2enIZ63TFEG3aiG .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled,#mermaid-svg-W2enIZ63TFEG3aiG .disabled circle,#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:lightgray;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:#efefef;}#mermaid-svg-W2enIZ63TFEG3aiG .section-7 rect,#mermaid-svg-W2enIZ63TFEG3aiG .section-7 path,#mermaid-svg-W2enIZ63TFEG3aiG .section-7 circle,#mermaid-svg-W2enIZ63TFEG3aiG .section-7 polygon,#mermaid-svg-W2enIZ63TFEG3aiG .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .section-7 text{fill:black;}#mermaid-svg-W2enIZ63TFEG3aiG .node-icon-7{font-size:40px;color:black;}#mermaid-svg-W2enIZ63TFEG3aiG .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .edge-depth-7{stroke-width:-7;}#mermaid-svg-W2enIZ63TFEG3aiG .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled,#mermaid-svg-W2enIZ63TFEG3aiG .disabled circle,#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:lightgray;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:#efefef;}#mermaid-svg-W2enIZ63TFEG3aiG .section-8 rect,#mermaid-svg-W2enIZ63TFEG3aiG .section-8 path,#mermaid-svg-W2enIZ63TFEG3aiG .section-8 circle,#mermaid-svg-W2enIZ63TFEG3aiG .section-8 polygon,#mermaid-svg-W2enIZ63TFEG3aiG .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .section-8 text{fill:black;}#mermaid-svg-W2enIZ63TFEG3aiG .node-icon-8{font-size:40px;color:black;}#mermaid-svg-W2enIZ63TFEG3aiG .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .edge-depth-8{stroke-width:-10;}#mermaid-svg-W2enIZ63TFEG3aiG .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled,#mermaid-svg-W2enIZ63TFEG3aiG .disabled circle,#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:lightgray;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:#efefef;}#mermaid-svg-W2enIZ63TFEG3aiG .section-9 rect,#mermaid-svg-W2enIZ63TFEG3aiG .section-9 path,#mermaid-svg-W2enIZ63TFEG3aiG .section-9 circle,#mermaid-svg-W2enIZ63TFEG3aiG .section-9 polygon,#mermaid-svg-W2enIZ63TFEG3aiG .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .section-9 text{fill:black;}#mermaid-svg-W2enIZ63TFEG3aiG .node-icon-9{font-size:40px;color:black;}#mermaid-svg-W2enIZ63TFEG3aiG .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .edge-depth-9{stroke-width:-13;}#mermaid-svg-W2enIZ63TFEG3aiG .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled,#mermaid-svg-W2enIZ63TFEG3aiG .disabled circle,#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:lightgray;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:#efefef;}#mermaid-svg-W2enIZ63TFEG3aiG .section-10 rect,#mermaid-svg-W2enIZ63TFEG3aiG .section-10 path,#mermaid-svg-W2enIZ63TFEG3aiG .section-10 circle,#mermaid-svg-W2enIZ63TFEG3aiG .section-10 polygon,#mermaid-svg-W2enIZ63TFEG3aiG .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .section-10 text{fill:black;}#mermaid-svg-W2enIZ63TFEG3aiG .node-icon-10{font-size:40px;color:black;}#mermaid-svg-W2enIZ63TFEG3aiG .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .edge-depth-10{stroke-width:-16;}#mermaid-svg-W2enIZ63TFEG3aiG .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled,#mermaid-svg-W2enIZ63TFEG3aiG .disabled circle,#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:lightgray;}#mermaid-svg-W2enIZ63TFEG3aiG .disabled text{fill:#efefef;}#mermaid-svg-W2enIZ63TFEG3aiG .section-root rect,#mermaid-svg-W2enIZ63TFEG3aiG .section-root path,#mermaid-svg-W2enIZ63TFEG3aiG .section-root circle,#mermaid-svg-W2enIZ63TFEG3aiG .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-W2enIZ63TFEG3aiG .section-root text{fill:#ffffff;}#mermaid-svg-W2enIZ63TFEG3aiG .section-root span{color:#ffffff;}#mermaid-svg-W2enIZ63TFEG3aiG .section-2 span{color:#ffffff;}#mermaid-svg-W2enIZ63TFEG3aiG .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-W2enIZ63TFEG3aiG .edge{fill:none;}#mermaid-svg-W2enIZ63TFEG3aiG .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-W2enIZ63TFEG3aiG :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}
Spring AI内置Advisor
对话记忆类
MessageChatMemoryAdvisor
PromptChatMemoryAdvisor
VectorStoreChatMemoryAdvisor
RAG问答类
QuestionAnswerAdvisor
RetrievalAugmentationAdvisor
推理增强类
ReReadingAdvisor
内容安全类
SafeGuardAdvisor
各 Advisor 的简要说明:
| 对话记忆 | MessageChatMemoryAdvisor | 把历史消息以列表形式塞进 Prompt |
| 对话记忆 | PromptChatMemoryAdvisor | 把历史消息拼到系统提示词里 |
| 对话记忆 | VectorStoreChatMemoryAdvisor | 从向量数据库搜索记忆(适合长期记忆) |
| RAG | QuestionAnswerAdvisor | 最简单 RAG:从向量库搜文档拼到 Prompt |
| RAG | RetrievalAugmentationAdvisor | 更灵活的模块化 RAG |
| 推理增强 | ReReadingAdvisor | 让模型再读一遍问题 |
| 内容安全 | SafeGuardAdvisor | 过滤不合适的内容 |
八、最佳实践
单一职责:一个 Advisor 只干一件事。记忆是记忆,日志是日志,别揉在一起。
用上下文传数据:如果多个 Advisor 之间需要共享数据,用 ChatClientRequest.adviseContext()。注意从 1.0 M3 开始上下文是不可变的。
同时实现两个接口:如果你的 Advisor 在流式和非流式场景下都需要,建议同时实现 CallAdvisor 和 StreamAdvisor。
合理配置 Order:通常的顺序建议:
- 日志(最高优先级)
- 安全过滤
- RAG 检索
- 记忆注入
- 调用模型(最后一个)
不可变上下文的更新方式:
// 错误:直接改会报错
request.context().put("key", "value");
// 正确:创建新的不可变副本
ChatClientRequest newRequest = request.updateContext(ctx ->
MapUtils.put(ctx, "key", "value")
);
九、版本变化(如果你是从老版本升级的)
| 1.0 M2 | 有 RequestAdvisor 和 ResponseAdvisor 两个接口 |
| 1.0 M3 | 合并成 CallAroundAdvisor 和 StreamAroundAdvisor,删除了 StreamResponseMode |
| 1.0.0 | 改名为 CallAdvisor 和 StreamAdvisor;上下文从独立参数移到了 Request/Response 里,并且变成不可变 |
如果你是从老版本升级上来的,这些变化要注意。
总结
Advisors API 本质上就是 AI 调用场景下的拦截器模式。它把那些跟核心业务无关但又不得不做的破事(记忆、检索、过滤、日志)抽出来,做成可插拔的组件。
用官网的话说就是:encapsulating recurring Generative AI patterns(封装重复的生成式 AI 模式)。
如果你发现自己每个 AI 调用前后都要写类似的代码,就该考虑用 Advisor 了。
参考文档:https://docs.spring.io/spring-ai/reference/api/advisors.html



