欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 会展 > Spring AI开发跃迁指南(第二章:急速上手3——Advisor核心原理、源码讲解及使用实例)

Spring AI开发跃迁指南(第二章:急速上手3——Advisor核心原理、源码讲解及使用实例)

2025/5/2 17:32:36 来源:https://blog.csdn.net/qq_35754073/article/details/147636049  浏览:    关键词:Spring AI开发跃迁指南(第二章:急速上手3——Advisor核心原理、源码讲解及使用实例)

在这里插入图片描述


1.Advisor简介

Spring AI 中的 Advisor 是一种核心机制,用于拦截和增强 AI 应用程序中的请求与响应流。其设计灵感来源于 Spring AOP(面向切面编程)中的切面(Aspect)概念,但专门针对 AI 交互场景进行了优化。

1.1.核心概念

  1. 定义与定位

Advisor 是 Spring AI 中负责动态干预聊天请求和响应流程的组件,通过链式结构(Chain of Responsibility 模式)串联多个处理单元。每个 Advisor 可以修改请求参数、增强数据、拦截敏感操作,甚至中断请求传递。

  1. 与AOP的关系

其功能类似于 Spring 的 AspectJ,但更专注于 AI 交互场景。例如,通过 AroundAdvisor 接口实现请求前后的增强逻辑。

1.2.Advisord的核心特点

  1. 模块化封装
  • 重复任务封装:将生成式 AI 的通用模式(如上下文记忆、敏感词过滤)抽象为可复用的组件。
  • 数据转换:优化发送至语言模型(LLM)的输入数据格式,并处理返回的响应(如结构化输出转换)。
  • 可移植性:同一 Advisor 可适配不同模型(如 OpenAI、HuggingFace)和用例,提升代码灵活性。
  1. 链式处理机制

多个 Advisor 按顺序执行,每个环节可修改请求或响应,并决定是否继续传递。某些 Advisor(如 SafeGuardAdvisor)可能直接中断链式流程。

  1. 内置与扩展性

Spring AI 提供多种内置 Advisor,同时也支持开发者自定义扩展,满足个性化需求。

2.Advisor源码及核心原理

2.1.关键类与关系

实现接口
实现接口
实现接口
依赖
依赖
参数
返回
参数
返回
<泛型元素>
生成
调用链
0..*
0..*
调用链
0..*
0..*
«interface»
Ordered
+getOrder() : int
«interface»
Advisor
+getName() : String
CallAroundAdvisor
+aroundCall(AdvisedRequest, CallAroundAdvisorChain) : AdvisedResponse
StreamAroundAdvisor
+aroundStream(AdvisedRequest, StreamAroundAdvisorChain) : Flux<AdvisedResponse>
AdvisedRequest
+... mutable Prompt data ...
+adviseContext: Map<String, Object>
AdvisedResponse
+callResponse: ChatResponse
+adviseContext: Map<String, Object>
CallAroundAdvisorChain
+nextAroundCall(AdvisedRequest) : AdvisedResponse
StreamAroundAdvisorChain
+nextAroundStream(AdvisedRequest) : Flux<AdvisedResponse>
«Reactive Stream»
Flux<AdvisedResponse>
+ ... 操作流的方法 ...
  1. 核心接口

    • Ordered
      定义 getOrder() 方法,用于控制 Advisor 的执行顺序(类似 Spring 的优先级机制)。

      小order先管请求、后管响应,大order反之;同值顺序随机。

    • Advisor
      基础接口,定义 getName() 方法标识 Advisor 名称。

  2. 具体 Advisor 实现

    • CallAroundAdvisor
      同步调用增强器,通过 aroundCall() 方法拦截请求并返回响应。
    • StreamAroundAdvisor
      流式调用增强器,通过 aroundStream() 方法处理流式响应(返回 Flux<AdvisedResponse>)。
  3. 请求与响应对象

    • AdvisedRequest
      包含可变的 Prompt 数据和上下文信息(adviseContext)。
    • AdvisedResponse
      封装 Chat 响应和上下文信息。
  4. 调用链(Chain)类

    • CallAroundAdvisorChain
      通过 nextAroundCall() 方法传递请求,支持链式调用多个 CallAroundAdvisor
    • StreamAroundAdvisorChain
      类似 CallAroundAdvisorChain,但用于流式场景的链式调用。

2.2.核心交互流程

在这里插入图片描述

  1. 请求初始化
    Spring AI框架将用户的输入(Prompt)转换为AdvisedRequest,并创建一个空的共享上下文对象AdvisorContext

  2. Advisor链处理请求

    • 每个Advisor依次处理AdvisedRequest,可以:
      • 修改请求内容(例如调整参数、添加元数据)。
      • 直接拦截请求,阻止后续调用,并自行生成响应(AdvisedResponse)。
    • 若未拦截,请求会传递到链中的下一个Advisor
  3. 调用Chat Model

    最后一个框架内置的Advisor负责将处理后的请求发送给Chat Model(如GPT模型)。

  4. 响应逆向传递

    • Chat Model生成的原始响应(ChatResponse)会转换为AdvisedResponse,并携带共享的AdvisorContext
    • 响应沿Advisor链反向传递,每个Advisor可对响应进行二次处理或修改(例如过滤敏感内容、格式化输出)。
  5. 响应返回

    最终的AdvisedResponse通过提取ChatCompletion(响应核心内容)返回给客户端。

流程图如下:

flowchart TDsubgraph 请求阶段A[Client Prompt] --> B[生成 AdvisedRequest\n+ AdvisorContext]B --> C[Advisor1]C -->|修改请求| D[Advisor2]D -->|拦截请求| E[生成 AdvisedResponse]D -->|放行| F[调用 Chat Model]endsubgraph 响应阶段F --> G[ChatResponse → AdvisedResponse]G --> H[Advisor2 处理响应]H --> I[Advisor1 处理响应]I --> J[返回最终响应]endE --> JJ --> K[Client]

2.3.设计特点

  1. 责任链模式
    通过 AdvisorChain 实现多个 Advisor 的链式调用,支持动态扩展增强逻辑。
  2. 上下文传递
    AdvisedRequestAdvisedResponse 中的 adviseContext 允许在链式调用中共享数据。
  3. 同步与流式分离
    通过 CallAroundAdvisorStreamAroundAdvisor 区分处理普通请求和流式请求。

2.4.Avisor相关源码解析

Advisor 接口位于org.springframework.ai.chat.client.advisor.api包中,是实现自定义Advisor的关键接口:

public interface Advisor extends Ordered {String getName();
}

同步和流式Advisor的两个子接口继承自Advisor:

  • 同步的CallAroundAdvisor
/*** Around advisor that wraps the ChatModel#call(Prompt) method.** @author Christian Tzolov* @author Dariusz Jedrzejczyk* @since 1.0.0*/public interface CallAroundAdvisor extends Advisor {/*** Around advice that wraps the ChatModel#call(Prompt) method.* @param advisedRequest the advised request* @param chain the advisor chain* @return the response*/AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain);}
  • 流式的StreamAroundAdvisor:
/*** Around advisor that runs around stream based requests.** @author Christian Tzolov* @author Dariusz Jedrzejczyk* @since 1.0.0*/
public interface StreamAroundAdvisor extends Advisor {/*** Around advice that wraps the invocation of the advised request.* @param advisedRequest the advised request* @param chain the chain of advisors to execute* @return the result of the advised request*/Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain);}

要使用Advice链,需在你的Advice实现中使用CallAroundAdvisorChainStreamAroundAdvisorChain

public interface CallAroundAdvisorChain {AdvisedResponse nextAroundCall(AdvisedRequest advisedRequest);}
public interface StreamAroundAdvisorChain {Flux<AdvisedResponse> nextAroundStream(AdvisedRequest advisedRequest);}

自定义Advisor就需要实现CallAroundAdvisorStreamAroundAdvisor 中的最少一个。其中的关键方法是nextAroundCall()nextAroundStream()

3.Spring AI内置的Advisor

  • MessageChatMemoryAdvisor

    作用:

    责维护对话的上下文记忆,将用户的问题与模型的回答保存到内存中,确保多轮对话的连贯性。例如,在连续对话中,历史记录会被自动附加到新请求中,帮助模型理解上下文。

    核心特点:

    • 上下文增强:通过 messages 参数传递历史对话记录。
    • 模型兼容性要求:仅适用于支持 messages 参数的模型(如 OpenAI 的 GPT 系列)。
    • 动态扩展:每次对话自动更新内存中的历史记录。
  • PromptChatMemoryAdvisor

    作用

    MessageChatMemoryAdvisor 类似,但将上下文历史记录封装到 systemPrompt 提示词中,而非直接依赖 messages 参数。这使得无论模型是否支持 messages 参数,都能实现上下文记忆。

    核心特点:

    • 灵活性:通过修改系统提示词(systemPrompt)嵌入历史对话,兼容性更广。
    • 无侵入式设计:不依赖模型底层接口,仅通过提示词工程实现上下文管理。
  • QuestionAnswerAdvisor

    作用:

    实现检索增强生成(RAG),通过查询向量数据库或知识库获取相关文本片段,并将其附加到用户问题后,提升回答的准确性和相关性。

    核心特点:

    • 知识库集成:支持自定义向量数据库(如:Milvus、Weaviate、Elasticsearch、FAISS)。
    • 默认拒绝机制:若知识库无匹配内容,则拒绝回答用户问题,避免生成错误信息。
    • 提示词优化:内置默认提示词模板,确保回答与检索内容强相关。
  • SafeGuardAdvisor

    作用:

    敏感词过滤与安全拦截,防止用户输入或模型输出包含违规内容。当检测到敏感词时,直接中断请求链,避免调用大模型处理。

    核心特点:

    • 实时拦截:基于预定义规则或动态词库进行校验。
    • 链式中断:直接终止后续处理,降低资源消耗与安全风险。
  • VectorStoreChatMemoryAdvisor

    作用:

    将对话历史长期存储到向量数据库中,并在每次提问时检索相关历史记录,增强上下文提示的准确性和长期记忆能力。

    核心特点:

    • 向量化存储:利用向量数据库高效检索相似历史对话片段。
    • 关键参数管理:依赖 chat_memory_conversation_id 标识用户会话,避免数据冗余。
    • 数据清理机制:需定期清理旧数据,防止数据库膨胀。
  • SimpleLoggerAdvisor

    作用:

    记录请求与响应的日志信息,便于调试和监控 AI 交互流程。例如,可打印用户输入、模型输出及处理耗时。

    核心特点:

    • 轻量级工具:无需复杂配置,快速集成日志功能。
    • 可扩展性:支持自定义日志格式与存储方式。

4.自定义Advisor实现

4.1.自定义日志Advisor

我们自定义一个日志Advisor,在调用链中的下一个顾问之前记录AdvisedRequest,之后记录AdvisedResponse

此advisor只观察请求和响应,不做任何膝盖且同时支持非流和流场景。

public class SimpleLoggerAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {private static final Logger logger = LoggerFactory.getLogger(SimpleLoggerAdvisor.class);// 为Advisor提供一个唯一的名称@Overridepublic String getName() {return this.getClass().getSimpleName();}// 设置order值来控制执行顺序,值较小的将优先执行@Overridepublic int getOrder() {return 0;}@Overridepublic AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {logger.debug("BEFORE: {}", advisedRequest);AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest);logger.debug("AFTER: {}", advisedResponse);return advisedResponse;}@Overridepublic Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {logger.debug("BEFORE: {}", advisedRequest);Flux<AdvisedResponse> advisedResponses = chain.nextAroundStream(advisedRequest);// MessageAggregator是一个实用程序类,它将Flux响应聚合到单个AdvisedResponse中,// MessageAggregator为只读,不可更改其中的响应return new MessageAggregator().aggregateAdvisedResponse(advisedResponses,advisedResponse -> logger.debug("AFTER: {}", advisedResponse));}
}

4.2.Re2 Advisor

“重读提升大型语言模型的推理能力”一文介绍了一种名为重读(Re2)的技术,它可以提升大型语言模型的推理能力。Re2 技术需要像这样扩充输入提示:

{输入查询}
再次阅读问题:{Input_Query}

实现Re2技术的Advisor:

public class ReReadingAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {// 应用Re2增强用户的输入查询private AdvisedRequest before(AdvisedRequest advisedRequest) {Map<String, Object> advisedUserParams = new HashMap<>(advisedRequest.userParams());advisedUserParams.put("re2_input_query", advisedRequest.userText());return AdvisedRequest.from(advisedRequest).userText("""{re2_input_query}Read the question again: {re2_input_query}""").userParams(advisedUserParams).build();}// 拦截非流式请求并应用Re2@Overridepublic AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {return chain.nextAroundCall(this.before(advisedRequest));}// 拦截流试请求并应用Re2技术@Overridepublic Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {return chain.nextAroundStream(this.before(advisedRequest));}@Overridepublic int getOrder() {return 0;}@Overridepublic String getName() {return this.getClass().getSimpleName();}
}

5.Advisor使用示例

我们以MessageChatMemoryAdvisor为例做一个advisor的使用实例,主要在ChatClient.builder中使用defaultAdvisors()方法配置Advisor,此方法可配置多个Advisor。

Builder defaultAdvisors(Advisor... advisor);Builder defaultAdvisors(Consumer<AdvisorSpec> advisorSpecConsumer);Builder defaultAdvisors(List<Advisor> advisors);

示例主要有controller、service及config配置,相关的pom依赖主要使用spring-ai-starter-model-minimax包等,具体的pom及minimax模型的配置请参考:Spring AI开发跃迁指南(第二章:极速上手——ChatClient20行代码构建人工智能应用)

5.1.示例代码

  • ChatConfig
package com.common;import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemory;import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class ChatConfig {@Beanpublic ChatMemory chatMemory() {return new InMemoryChatMemory(); // 简单内存实现}// 2. 创建 MessageChatMemoryAdvisor@Beanpublic MessageChatMemoryAdvisor chatMemoryAdvisor(ChatMemory chatMemory) {return new MessageChatMemoryAdvisor(chatMemory);}// 配置 ChatClient,绑定 Advisor@Beanpublic ChatClient chatClient(ChatModel chatModel, MessageChatMemoryAdvisor advisor) {return ChatClient.builder(chatModel).defaultAdvisors(advisor) // 添加 Advisor.build();}
}
  • service
@Service
public class TestSpringAIServiceImpl {@Autowiredprivate ChatClient chatClient;@Overridepublic String generate(String message) {return chatClient.prompt().advisors(advisorSpec -> advisorSpec.param("chat_memory_conversation_id", "111").param("chat_memory_response_size", 100)).user(message).call().content();}
}	
  • controller
@RestController
public class TestSpringAIController {@Autowiredprivate TestSpringAIServiceImpl service;  @GetMapping("/ai/generate")public String generate(@RequestParam(value = "message", defaultValue = "生成一个中国女演员及其电影作品") String message) {return service.generate(message);}
}
  • 运行结果
  1. 第一次运行结果:
    在这里插入图片描述

  2. 第二次运行结果:
    在这里插入图片描述

  3. 执行结果分析

  • 第一次对话:声明自己的名字为“张三”,MessageChatMemoryAdvisor 会将此对话记录(用户输入和模型回复)保存到内存中。
  • 第二次对话:用户提问“我的名字是什么?”,模型会根据 MessageChatMemoryAdvisor 提供的上下文历史(包含第一次对话)生成正确回答。

5.2.关键机制解释

  1. 内存管理
    InMemoryChatMemory 默认以 List<Message> 形式存储对话历史,每次调用 chatClient.call() 时,MessageChatMemoryAdvisor 会自动将历史记录附加到当前请求中(通过 messages 参数传递)。

  2. 上下文传递
    模型(如 OpenAI GPT)接收到完整的 messages 列表后,会自动解析历史对话,确保回答的连贯性。

  3. 会话隔离
    若需区分不同用户的对话,需通过 ChatMemoryconversationId 参数管理会话标识:

    // 为不同用户分配唯一会话 ID
    ChatMemory memory = new InMemoryChatMemory("user-123"); 
    

若需自定义历史记录的条数或存储策略:

@Bean
public ChatMemory chatMemory() {InMemoryChatMemory memory = new InMemoryChatMemory();memory.setMaxHistorySize(10); // 限制最多保留 10 条历史记录return memory;
}

注意:

  • 模型兼容性:确保底层模型(如 OpenAI、MiniMax)支持 messages 参数格式。
  • 内存泄漏风险:若使用 InMemoryChatMemory,需在长期运行的应用中定期清理过期会话。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词