Spring AI 系列2: Advisors增强器简介

news2025/6/2 19:27:57

一、Advisors简介

1.1 Advisors定义

Advisors 是在 AI 应用程序中处理请求和响应的拦截器。我们可以使用它们为提示流程设置额外的功能。例如,可以建立聊天历史、排除敏感词或为每个请求添加额外的上下文。        

Spring AI的Advisor,本质上是一个拦截器,专门负责在AI模型的请求和响应流中“搞事情”——动态修改输入、增强输出,甚至中途拦截敏感请求。它就像给AI应用装了个“智能管家”,既能帮你处理重复性工作(比如记录聊天历史),又能让模型回答更精准(比如结合知识库检索)。

1.2 Advisors核心功能

  • 拦截请求:在用户问题发送给AI模型前,Advisor可以修改问题或添加上下文(比如加一句“深呼吸,慢慢想”)。

  • 增强响应:在模型返回答案后,Advisor可以格式化结果(比如将字符串转为JSON)或添加安全校验。

  • 链式处理:多个Advisor像流水线一样串联,每个环节都能对数据动手脚。

适用场景:聊天记忆管理、敏感词过滤、知识库检索(RAG)、日志记录等。

1.3  附加或增强带有上下文数据的提示,让模型回答更精准

在调用带有用户文本的 AI 模型时,一个常见的模式是附加或增强带有上下文数据的提示,这种上下文数据可以是不同类型。常见类型包括:

您自己的数据

这是 AI 模型尚未训练过的数据。即使模型见过类似的数据,附加的上下文数据在生成响应时也会优先考虑。

对话历史

聊天模型的 API 是无状态的。如果您告诉 AI 模型您的名字,它不会在后续交互中记住它。必须随每个请求发送对话历史,以确保在生成响应时考虑先前的交互。

1.4  Advisor 接口

Advisors API提供了一种灵活而强大的方式来拦截、修改和增强 Spring 应用程序中的 AI 驱动交互。该功能的核心组件是 CallAroundAdvisor 接口。我们通过实现该接口来创建 Advisor 链,从而影响我们的请求或响应。我们会将提示(prompt)发送到一个聊天模型,该模型关联了一个 Advisor 链。在发送提示之前,链上的每个 Advisor 都会执行其 before 操作。同样,在收到聊天模型的回复之前,每个 Advisor 都会调用自己的 after 操作。

ChatClient 流畅 API 提供了 AdvisorSpec 接口用于配置 advisors。这个接口提供了添加参数、一次设置多个参数以及向链中添加一个或多个 advisors 的方法。

interface AdvisorSpec {
    AdvisorSpec param(String k, Object v);
    AdvisorSpec params(Map<String, Object> p);
    AdvisorSpec advisors(Advisor... advisors);
    AdvisorSpec advisors(List<Advisor> advisors);
}

advisors 添加到链中的顺序至关重要,因为它决定了它们的执行顺序。每个 advisor 都以某种方式修改提示或上下文,一个 advisor 所做的更改会传递给链中的下一个。

ChatClient.builder(chatModel)
    .build()
    .prompt()
    .advisors(
        MessageChatMemoryAdvisor.builder(chatMemory).build(),
        QuestionAnswerAdvisor.builder(vectorStore).build()
    )
    .user(userText)
    .call()
    .content();

在此配置中,MessageChatMemoryAdvisor 将首先执行,将对话历史添加到提示中。然后,QuestionAnswerAdvisor 将基于用户的问题和添加的对话历史执行其搜索,可能会提供更相关的结果。


二、增强器 API

Spring AI 增强器 API 提供了一种灵活而强大的方式来拦截、修改和增强 Spring 应用程序中的 AI 驱动交互。 通过利用增强器 API,开发者可以创建更复杂、可重用和可维护的 AI 组件。主要优势包括封装重复出现的生成式 AI 模式、转换发送到大型语言模型(LLM) 的数据,以及提供跨各种模型和用例的可移植性。

可以使用 ChatClient API
 配置现有增强器,如下例所示:

var chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(
        MessageChatMemoryAdvisor.builder(chatMemory).build(), // chat-memory 增强器
        QuestionAnswerAdvisor.builder((vectorStore).build() // RAG 增强器
    )
    .build();

var conversationId = "678";

String response = this.chatClient.prompt()
    // 在运行时设置增强器参数	
    .advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, conversationId))
    .user(userText)
    .call()
	.content();

建议在构建时使用 builder 的 defaultAdvisors() 方法注册增强器。

2.1 核心组件

API 包含用于非流式场景的 CallAroundAdvisor 和 CallAroundAdvisorChain,以及用于流式场景的 StreamAroundAdvisor 和 StreamAroundAdvisorChain。 它还包括 AdvisedRequest 用于表示未密封的 Prompt 请求,AdvisedResponse 用于 Chat Completion 响应。两者都包含一个 advise-context 用于在增强器链中共享状态。

2.2 Advisors API 类

nextAroundCall() 和 nextAroundStream() 是关键增强器方法,通常执行诸如检查未密封的 Prompt 数据、自定义和增强 Prompt 数据、调用增强器链中的下一个实体、可选地阻止请求、检查聊天完成响应以及抛出异常以指示处理错误等操作。

此外,getOrder() 方法确定增强器在链中的顺序,而 getName() 提供唯一的增强器名称。

由 Spring AI 框架创建的增强器链允许按 getOrder() 值排序的多个增强器顺序调用。 较低的值首先执行。 最后一个增强器(自动添加)将请求发送到 LLM。

以下流程图说明了增强器链与聊天模型之间的交互:

Advisors API 流程

2.3 API 概述

主要增强器接口位于包 org.springframework.ai.chat.client.advisor.api 中。以下是创建自己的增强器时会遇到的关键接口

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

同步和响应式增强器的两个子接口是:

public interface CallAroundAdvisor extends Advisor {
	/**
	 * 环绕通知,包装 ChatModel#call(Prompt) 方法。
	 * @param advisedRequest 建议的请求
	 * @param chain 增强器链
	 * @return 响应
	 */
	AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain);
}
public interface StreamAroundAdvisor extends Advisor {
	/**
	 * 环绕通知,包装建议请求的调用。
	 * @param advisedRequest 建议的请求
	 * @param chain 要执行的增强器链
	 * @return 建议请求的结果
	 */
	Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain);
}

要继续建议链,在您的建议实现中使用 CallAroundAdvisorChain 和 StreamAroundAdvisorChain

接口是:

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

2.4 实现增强器

要创建增强器,实现 CallAroundAdvisor 或 StreamAroundAdvisor(或两者)。要实现的关键方法是用于非流式的 nextAroundCall() 或用于流式增强器的 nextAroundStream()

下面将提供一些实践示例来说明如何实现用于观察和增强用例的增强器。

(1)日志增强器

我们可以实现一个简单的日志增强器,在调用链中的下一个增强器之前记录 AdvisedRequest,之后记录 AdvisedResponse。 注意,增强器只观察请求和响应,不修改它们。 此实现同时支持非流式和流式场景。

public class SimpleLoggerAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {

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

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

	@Override
	public int getOrder() { // (2)
		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);
		
        return new MessageAggregator().aggregateAdvisedResponse(advisedResponses, 
                    advisedResponse -> logger.debug("AFTER: {}", advisedResponse)); // (3)
	}
}
  1. 为增强器提供唯一名称。

  2. 您可以通过设置顺序值来控制执行顺序。较低的值首先执行。

  3. MessageAggregator 是一个实用类,将 Flux 响应聚合为单个 AdvisedResponse。这对于记录或观察整个响应而不是流中的单个项目的其他处理很有用。注意,您不能在 MessageAggregator 中修改响应,因为它是只读操作。

(2)重读 (Re2) 增强器

“Re-Reading Improves Reasoning in Large Language Models” 文章介绍了一种称为重读 (Re2) 的技术,可以提高大型语言模型的推理能力。 Re2 技术要求像这样增强输入提示:

{Input_Query}
Read the question again: {Input_Query}

实现一个将 Re2 技术应用于用户输入查询的增强器可以这样做:

public class ReReadingAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {

	private AdvisedRequest before(AdvisedRequest advisedRequest) { // (1)

		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();
	}

	@Override
	public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) { // (2)
		return chain.nextAroundCall(this.before(advisedRequest));
	}

	@Override
	public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) { // (3)
		return chain.nextAroundStream(this.before(advisedRequest));
	}

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

    @Override
    public String getName() { // (5)
		return this.getClass().getSimpleName();
	}
}
  1. before 方法通过应用重读技术增强用户的输入查询。

  2. aroundCall 方法拦截非流式请求并应用重读技术。

  3. aroundStream 方法拦截流式请求并应用重读技术。

  4. 您可以通过设置顺序值来控制执行顺序。较低的值首先执行。

  5. 为增强器提供唯一名称。


三、Spring AI 内置增强器

Spring AI 框架提供了几个内置增强器来增强您的 AI 交互。以下是可用增强器的概述:

3.1 聊天内存增强器

这些增强器在聊天内存存储中管理对话历史:

MessageChatMemoryAdvisor

检索内存并将其作为消息集合添加到提示中。这种方法保持了对话历史的结构。注意,并非所有 AI 模型都支持这种方法。

PromptChatMemoryAdvisor

检索内存并将其合并到提示的系统文本中。

VectorStoreChatMemoryAdvisor

从 VectorStore 检索内存并将其添加到提示的系统文本中。此增强器对于从大型数据集中高效搜索和检索相关信息很有用。

3.2 QuestionAnswerAdvisor 问题回答增强器

此增强器使用向量存储来提供问答功能,实现 RAG(检索增强生成)模式。

3.3  SafeGuardAdvisor 内容安全增强器 

一个简单的增强器,旨在防止模型生成有害或不适当的内容。

  1. MessageChatMemoryAdvisor

  • 功能:自动记录聊天历史,让AI记住“你刚才说了啥”。

  • 坑点:不是所有模型都支持上下文记忆,用前先查文档。

     2.QuestionAnswerAdvisor

  • 功能:执行RAG(检索增强生成),从知识库中捞答案,让AI不再“一本正经地胡说八道”。

  • 原理:用户提问时,先检索相似文本,拼接到提示词中。

    3.SafeGuardAdvisor

  • 功能:敏感词拦截器,遇到“颜色内容”直接打断施法,保护应用合规性

   4.VectorStoreChatMemoryAdvisor

  • 功能:将聊天记录存入向量数据库,实现长期记忆,但需小心chat_memory_conversation_id管理不当引发“数据海啸”。


四、流式与非流式

增强器流式与非流式流程

  • 非流式增强器处理完整的请求和响应。

  • 流式增强器将请求和响应作为连续流处理,使用响应式编程概念(例如,用于响应的 Flux)。

实现带有阻塞和非阻塞代码的流式增强器的示例:

@Override
public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {
    
    return  Mono.just(advisedRequest)
            .publishOn(Schedulers.boundedElastic())
            .map(request -> {
                // 这可以由阻塞和非阻塞线程执行。
                // 增强器在下一个部分之前
            })
            .flatMapMany(request -> chain.nextAroundStream(request))
            .map(response -> {
                // 增强器在下一个部分之后
            });
}

注意事项:

专注责任

保持增强器专注于特定任务以获得更好的模块化。

共享状态

必要时使用 adviseContext 在增强器之间共享状态。

支持两种模式

实现增强器的流式和非流式版本以获得最大灵活性。

考虑顺序

仔细考虑增强器在链中的顺序以确保正确的数据流。


参考链接:

Spring AI Advisor 指南 - spring 中文网

如何用Spring AI的Advisor,让AI应用像“瑞士军刀”一样灵活?

Spring AI SafeGuardAdvisor-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2393917.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

通过Func实现飞书应用通知消息加急处理

前言 在现代企业运作中&#xff0c;及时响应告警信息对保障系统的稳定性和业务的连续性至关重要。随着业务的数字化转型&#xff0c;越来越多的企业依赖于复杂的技术架构&#xff0c;这使得故障和异常事件的及时处理变得愈发重要。传统的告警通知方式往往存在响应不及时、信息…

【目标检测】【AAAI-2022】Anchor DETR

Anchor DETR&#xff1a; Query Design for Transformer-Based Object Detection 锚点DETR&#xff1a;基于Transformer的目标检测查询设计 论文链接 代码链接 摘要 在本文中&#xff0c;我们提出了一种基于Transformer的目标检测新型查询设计。此前的Transformer检测器中&am…

智慧工厂整体解决方案

该方案围绕智能工厂建设,阐述其基于工业 4.0 和数字化转型需求,通过物联网、大数据、人工智能等技术实现生产自动化、数据化管理及联网协同的特点。建设步骤包括评估现状、设定目标、制定方案、测试调整、实施计划及持续改进,需整合 MES、ERP 等软件系统与传感器、机器人等硬…

秋招Day12 - 计算机网络 - TCP

详细说一下TCP的三次握手机制 TCP的三次握手机制是为了在两个主机之间建立可靠的连接&#xff0c;这个机制确保两端的通信是同步的&#xff0c;并且在开始传输数据前&#xff0c;双方都做好了要通信的准备。 说说SYN的概念&#xff1f; SYN 是 TCP 协议中用来建立连接的一个标…

vueflow

自定义节点&#xff0c;自定义线&#xff0c;具体细节还未完善&#xff0c;实现效果&#xff1a; 1.安装vueflow 2.目录如下 3. index.vue <script setup> import { ref } from vue import { VueFlow, useVueFlow } from vue-flow/core import { Background } from vue-…

LearnOpenGL-笔记-其十一

Normal Mapping 又到了介绍法线贴图的地方&#xff0c;我感觉我已经写了很多遍了... 法线贴图用最简单的话来介绍的话&#xff0c;就是通过修改贴图对应物体表面的法线来修改光照效果&#xff0c;从而在不修改物体实际几何形状的前提下实现不同于物体几何形状的视觉效果。 因…

openppp2 -- 1.0.0.25225 优化多线接入运营商路由调配

本文涉及到的内容&#xff0c;涉及到上个发行版本相关内容&#xff0c;人们在阅读本文之前&#xff0c;建议应当详细阅读上个版本之中的VBGP技术相关的介绍。 openppp2 -- 1.0.0.25196 版本新增的VBGP技术-CSDN博客 我们知道在现代大型的 Internet 网络服务商&#xff0c;很多…

详细到用手撕transformer下半部分

之前我们讨论了如何实现 Transformer 的核心多头注意力机制&#xff0c;那么这期我们来完整地实现整个 Transformer 的编码器和解码器。 Transformer 架构最初由 Vaswani 等人在 2017 年的论文《Attention Is All You Need》中提出&#xff0c;专为序列到序列&#xff08;seq2s…

【Sqoop基础】Sqoop生态集成:与HDFS、Hive、HBase等组件的协同关系深度解析

目录 1 Sqoop概述与大数据生态定位 2 Sqoop与HDFS的深度集成 2.1 技术实现原理 2.2 详细工作流程 2.3 性能优化实践 3 Sqoop与Hive的高效协同 3.1 集成架构设计 3.2 数据类型映射处理 3.3 案例演示 4 Sqoop与HBase的实时集成 4.1 数据模型转换挑战 4.2 详细集成流程…

MySQL + CloudCanal + Iceberg + StarRocks 构建全栈数据服务

简述 在业务数据快速膨胀的今天&#xff0c;企业对 低成本存储 与 实时查询分析能力 的需求愈发迫切。 本文将带你实战构建一条 MySQL 到 Iceberg 的数据链路&#xff0c;借助 CloudCanal 快速完成数据迁移与同步&#xff0c;并使用 StarRocks 完成数据查询等操作&#xff0c…

截屏精灵:轻松截屏,高效编辑

在移动互联网时代&#xff0c;截图已经成为我们日常使用手机时的一项基本操作。无论是记录重要信息、分享有趣内容&#xff0c;还是进行学习和工作&#xff0c;一款好用的截图工具都能极大地提升我们的效率。截屏精灵就是这样一款功能强大、操作简单的截图工具&#xff0c;它不…

【JavaWeb】基本概念、web服务器、Tomcat、HTTP协议

目录 1. 基本概念1.1 基本概念1.2 web应用程序1.3 静态web1.4 动态web 2. web服务器3. tomcat详解3.1 安装3.2 启动3.3 配置3.3.1 配置启动的端口号3.3.2 配置主机的名称3.3.3 其他常用配置项日志配置数据源配置安全配置 3.4 发布一个网站 4. Http协议4.1 什么是http4.2 http的…

云计算Linux Rocky day02(安装Linux系统、设备表示方式、Linux基本操作)

云计算Linux Rocky day02&#xff08;安装Linux系统、设备表示方式、Linux基本操作&#xff09; 目录 云计算Linux Rocky day02&#xff08;安装Linux系统、设备表示方式、Linux基本操作&#xff09;1、虚拟机VMware安装Rocky2、Linux命令行3、Linux Rocky修改字体大小和背景颜…

在 ODROID-H3+ 上安装 Win11 系统

在 ODROID-H3 上安装 Windows 11 系统。 以下是完整的步骤&#xff0c;包括 BIOS 设置、U 盘制作、安装和驱动处理&#xff0c;全程不保留之前的系统数据。 ✅ 准备工作 1. 准备一个 ≥8GB 的 USB 启动盘 用另一台电脑制作 Windows 11 安装盘。 &#x1f449; 推荐工具&…

使用el-input数字校验,输入汉字之后校验取消不掉

先说说复现方式 本来input是只能输入数字的&#xff0c;然后你不小心输入了汉字&#xff0c;触发校验了&#xff0c;然后这时候&#xff0c;你发现校验取消不掉了 就这样了 咋办啊&#xff0c;你一看校验没错啊&#xff0c;各种number啥的也写了,发现没问题啊 <el-inputv…

Docker容器启动失败的常见原因分析

我们在开发部署的时候&#xff0c;用 Docker 打包环境&#xff0c;理论上是“我装好了你就能跑”。但理想很丰满&#xff0c;现实往往一 docker run 下去就翻车了。 今天来盘点一下我实际工作中经常遇到的 Docker 容器启动失败的常见原因&#xff0c;顺便给点 debug 的小技巧&a…

立志成为一名优秀测试开发工程师(第七天)——unittest框架的学习

目录 unittest框架的学习 一、测试类的编写 创建相关测试类cal.py、CountTest.py 二、常见断言方法 使用unittest单元测试框架编写测试用例CountTest.py 注意&#xff1a;执行的时候光标一定要放在括号后面&#xff0c;鼠标右键运行 三、对测试环境的初始化和清除模块…

论坛系统(4)

用户详情 获取用户信息 实现逻辑 ⽤⼾提交请求&#xff0c;服务器根据是否传⼊Id参数决定返回哪个⽤⼾的详情 1. 不传⽤⼾Id&#xff0c;返回当前登录⽤⼾的详情(从session获取) 2. 传⼊⽤⼾Id&#xff0c;返回指定Id的⽤⼾详情(根据用户id去查) 俩种方式获得用户信息 参…

力扣面试150题--二叉树的层平均值

Day 54 题目描述 思路 初次做法&#xff08;笨&#xff09;&#xff1a;使用两个队列&#xff0c;一个队列存放树的节点&#xff0c;一个队列存放对应节点的高度&#xff0c;使用x存放上一个节点&#xff0c;highb存放上一个节点的高度&#xff0c;sum存放当前层的节点值之和…

【Doris入门】Doris初识:分布式分析型数据库的核心价值与架构解析

目录 1 Doris简介与核心价值 2 Doris架构深度解析 2.1 Frontend&#xff08;FE&#xff09;架构 2.2 Backend&#xff08;BE&#xff09;架构 3 Doris核心概念详解 3.1 数据分布模型 3.2 Tablet与Replica 3.3 数据模型 4 Doris关键技术解析 4.1 存储引擎 4.2 查询执…