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

2.3.设计特点

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

2.4.Avisor相关源码解析

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

java 复制代码
public interface Advisor extends Ordered {
	String getName();
}

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

  • 同步的CallAroundAdvisor
java 复制代码
/**
 * 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:
java 复制代码
/**
 * 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

java 复制代码
public interface CallAroundAdvisorChain {

	AdvisedResponse nextAroundCall(AdvisedRequest advisedRequest);

}
java 复制代码
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只观察请求和响应,不做任何膝盖且同时支持非流和流场景。

java 复制代码
public class SimpleLoggerAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {

	private static final Logger logger = LoggerFactory.getLogger(SimpleLoggerAdvisor.class);

    // 为Advisor提供一个唯一的名称
	@Override
	public String getName() {
		return this.getClass().getSimpleName();
	}

    // 设置order值来控制执行顺序,值较小的将优先执行
	@Override
	public int getOrder() {
		return 0;
	}

	@Override
	public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {

		logger.debug("BEFORE: {}", advisedRequest);

		AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest);

		logger.debug("AFTER: {}", advisedResponse);

		return advisedResponse;
	}

	@Override
	public 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:

java 复制代码
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
	@Override
	public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {
		return chain.nextAroundCall(this.before(advisedRequest));
	}

    // 拦截流试请求并应用Re2技术
	@Override
	public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {
		return chain.nextAroundStream(this.before(advisedRequest));
	}

	@Override
	public int getOrder() {
		return 0;
	}

    @Override
    public String getName() {
		return this.getClass().getSimpleName();
	}
}

5.Advisor使用示例

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

java 复制代码
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
java 复制代码
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 {
    @Bean
    public ChatMemory chatMemory() {
        return new InMemoryChatMemory(); // 简单内存实现
    }

    // 2. 创建 MessageChatMemoryAdvisor
    @Bean
    public MessageChatMemoryAdvisor chatMemoryAdvisor(ChatMemory chatMemory) {
        return new MessageChatMemoryAdvisor(chatMemory);
    }

    // 配置 ChatClient,绑定 Advisor
    @Bean
    public ChatClient chatClient(ChatModel chatModel, MessageChatMemoryAdvisor advisor) {
        return ChatClient.builder(chatModel)
                .defaultAdvisors(advisor) // 添加 Advisor
                .build();
    }
}
  • service
java 复制代码
@Service
public class TestSpringAIServiceImpl {
    @Autowired
    private ChatClient chatClient;

    @Override
    public 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
java 复制代码
@RestController
public class TestSpringAIController {
    @Autowired
    private 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 参数管理会话标识:

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

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

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

注意:

  • 模型兼容性 :确保底层模型(如 OpenAI、MiniMax)支持 messages 参数格式。
  • 内存泄漏风险 :若使用 InMemoryChatMemory,需在长期运行的应用中定期清理过期会话。
相关推荐
LaughingZhu2 分钟前
Product Hunt 每日热榜 | 2026-04-30
人工智能·经验分享·深度学习·神经网络·产品运营
sunneo8 分钟前
专栏D-团队与组织-03-产品文化
人工智能·产品运营·aigc·产品经理·ai编程
Muyuan19988 分钟前
28.Paper RAG Agent 开发记录:修复 LLM Rerank 的解析、Fallback 与可验证性
linux·人工智能·windows·python·django·fastapi
jason.zeng@150220720 分钟前
Androidr入门环境搭建
java·kotlin
小呆呆66621 分钟前
Codex 穷鬼大救星
前端·人工智能·后端
摇滚侠23 分钟前
整洁的桌面和任务栏 Java 开发工程师提效方法
java·开发语言
薛定猫AI27 分钟前
【深度解析】Kimi K2.6 的长上下文 Agentic Coding 能力与 OpenAI 兼容 API 接入实践
人工智能·自动化·知识图谱
星爷AG I30 分钟前
20-6 记忆整合(AGI基础理论)
人工智能·agi
AI创界者32 分钟前
人工智能 GPT-Image DMXAPI Python AI绘画
人工智能
播播资源38 分钟前
GPT-5.5 模型功能深度解析:从模型介绍、核心特点到应用场景全景分析 如何快速接入使用
人工智能·gpt