SpringAI实现AI应用-自定义顾问(Advisor)

SpringAI实战链接

1.SpringAl实现AI应用-快速搭建-CSDN博客

2.SpringAI实现AI应用-搭建知识库-CSDN博客

3.SpringAI实现AI应用-内置顾问-CSDN博客

4.SpringAI实现AI应用-使用redis持久化聊天记忆-CSDN博客

5.SpringAI实现AI应用-自定义顾问(Advisor)-CSDN博客

概述

经过前几篇帖子的鞭打,现在已经对SpringAI有了一定的了解,继续查看官方文档,顾问(Advisor)里面还有自定义顾问,这篇帖子就说一下如何进行自定义。

SpringAI官方文档

中文版:顾问 API :: Spring AI 参考 - Spring 框架

官方:Advisors API :: Spring AI Reference

Advisors原理

在写代码之前,得先了解Advisor的一些基本信息,虽然官方文档里有说明,但还是感觉太官方了

Spring AI 中的 Advisors(顾问) 是一个关键概念,用于在模型交互过程中动态调整或增强提示词(Prompt)、控制生成过程,或注入业务逻辑。它的核心思想是对 AI 模型的输入/输出进行拦截和增强,类似于 AOP(面向切面编程)中的拦截器。

核心作用

动态修改提示词:在发送给模型前,自动添加上下文、示例或格式化内容。

结果后处理:对模型生成的文本进行过滤、校验或结构化解析。

上下文管理:跨多次对话维护状态(如历史记录、用户偏好)。

业务规则注入:根据业务需求限制或引导模型的输出。

工作原理

Advisor 在请求处理链中的位置:

用户输入 → [Advisor 预处理] → 模型处理 → [Advisor 后处理] → 返回用户

预处理阶段:修改或增强输入的 AdvisedRequest。

后处理阶段:处理模型的 AdvisedResponse。

源码分析

通过官方文档和源码可知(在此就引用官方的图片了),SpringAI的几个内置顾问实现的都是Advisor。

代码实现

废话就不多说了,直接开始干!!!

根据前几篇帖子的,现在已经有一个完整的框架了,能够直接添加代码进行测试了

LoggingAdvisor(日志顾问)

import org.springframework.ai.chat.client.advisor.api.*;
import org.springframework.ai.chat.model.MessageAggregator;
import reactor.core.publisher.Flux;/*** @Author majinzhong* @Date 2025/5/8 11:27* @Version 1.0*/
public class LoggingAdvisor implements CallAroundAdvisor,StreamAroundAdvisor {@Overridepublic AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {System.out.println("调用AI Call之前:"+advisedRequest);AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest);System.out.println("调用AI Call之后:"+advisedResponse);return advisedResponse;}@Overridepublic Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {System.out.println("调用AI Stream之前:"+advisedRequest);Flux<AdvisedResponse> advisedResponseFlux = chain.nextAroundStream(advisedRequest);Flux<AdvisedResponse> advisedResponseFlux1 = new MessageAggregator().aggregateAdvisedResponse(advisedResponseFlux, advisedResponse -> System.out.println("调用AI Stream之后:"+advisedResponseFlux));return advisedResponseFlux1;}@Overridepublic String getName() {return this.getClass().getSimpleName();}@Overridepublic int getOrder() {return 0;}
}

AiConfig

    @BeanChatClient chatClient(ChatClient.Builder builder,VectorStore vectorStore) {return builder// 它定义了聊天机器人在回答问题时应当遵循的风格和角色定位。.defaultSystem("你是一个智能机器人,你的名字叫 Spring AI智能机器人")//这里可以添加多个顾问 order(优先级)越小,越先执行// 注意:顾问添加到链中的顺序至关重要,因为它决定了其执行的顺序。每个顾问都会以某种方式修改提示或上下文,一个顾问所做的更改会传递给链中的下一个顾问。// 在此配置中,将首先执行MessageChatMemoryAdvisor,将对话历史记录添加到提示中。然后,问答顾问将根据用户的问题和添加的对话历史进行搜索,从而可能提供更相关的结果。.defaultAdvisors(//内存存储对话记忆new MessageChatMemoryAdvisor(inMemoryChatMemory()),
//                        new PromptChatMemoryAdvisor(inMemoryChatMemory()),// QuestionAnswerAdvisor 此顾问使用矢量存储提供问答功能,实现RAG(检索增强生成)模式
//                        QuestionAnswerAdvisor.builder(vectorStore).order(1).build(),// SafeGuardAdvisor是一个安全防护顾问,它确保生成的内容符合道德和法律标准。SafeGuardAdvisor.builder().sensitiveWords(List.of("色情", "暴力")) // 敏感词列表.order(2) // 设置优先级.failureResponse("抱歉,我无法回答这个问题。").build(), // 敏感词过滤失败时的响应// SimpleLoggerAdvisor是一个记录ChatClient的请求和响应数据的顾问。这对于调试和监控您的AI交互非常有用,建议将其添加到链的末尾。new LoggingAdvisor()).defaultOptions(ChatOptions.builder().topP(0.7) // 取值越大,生成的随机性越高;取值越低,生成的随机性越低。默认值为0.8.build()).build();}

将原来代码中的SimpleLoggerAdvisor换成自定义的日志顾问,因为SimpleLoggerAdvisor没有按照自己的要求进行打印

测试

经过测试,已经在控制台进行打印了

ReReadingAdvisor(重新阅读RE2顾问)

import org.springframework.ai.chat.client.advisor.api.*;
import reactor.core.publisher.Flux;import java.util.HashMap;
import java.util.Map;/*** @Author majinzhong* @Date 2025/5/8 15:10* @Version 1.0*/
public class ReReadingAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {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("你的作用是什么,谁创造的你").userParams(advisedUserParams).build();}@Overridepublic AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {return chain.nextAroundCall(this.before(advisedRequest));}@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();}
}

继续修改AIConfig,添加自定义的顾问

测试

经测试可知,自定义的RE2顾问已经成功

RetryAdvisor(异常重试顾问)

import org.springframework.ai.chat.client.advisor.api.*;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import reactor.core.publisher.Flux;/*** @Author majinzhong* @Date 2025/5/8 15:21* @Version 1.0*/
public class RetryAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {private final RetryTemplate retryTemplate;public RetryAdvisor() {this.retryTemplate = new RetryTemplate();// 配置重试策略:最多2次,间隔1秒SimpleRetryPolicy policy = new SimpleRetryPolicy();policy.setMaxAttempts(2);FixedBackOffPolicy backOffPolicy = new FixedBackOffPolicy();backOffPolicy.setBackOffPeriod(1000); // 1秒retryTemplate.setRetryPolicy(policy);retryTemplate.setBackOffPolicy(backOffPolicy);}@Overridepublic AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest);return retryTemplate.execute(context -> {return advisedResponse;});}@Overridepublic Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {return chain.nextAroundStream(advisedRequest);}@Overridepublic String getName() {return this.getClass().getSimpleName();}@Overridepublic int getOrder() {return 0;}
}

还要修改AiConfig

这个没有进行测试,因为是返回值异常的时候才会进行重试

至此自定义顾问已经完成,可以按照自己的业务需求进行修改

补充

之前说过顾问可以提前统一进行设置,也可以单独在方法上进行设置

其实,每个AI大模型也可以单独在方法上进行设置

统一配置AI模型

AI大模型在application.yml文件里设置

在AiConfig里进行配置大模型的名称、顾问等信息

方法上配置AI大模型

    /*** 根据消息直接输出回答* @param map* @return*/@PostMapping("/ai/call")public String call(@RequestBody Map<String,String> map) {String message = map.get("message");
//        return chatClient.prompt().user(message).call().content().trim();OpenAiChatOptions options = OpenAiChatOptions.builder().model("THUDM/GLM-4-9B-0414").temperature(0.5).build();String content = chatClient.prompt().system("你是一个AI小助手,能帮助我们一起学习java").advisors(new LoggingAdvisor(),new ReReadingAdvisor()).user(message).options(options).call().content();return content;}

OpenAiChatOptions进行配置AI大模型

chatClient对OpenAiChatOptions配置的大模型进行配置

测试

统一配置

方法配置

通过测试的结果和日志可以知道,AI大模型既可以进行统一配置,也可以在方法上设置

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

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

相关文章

【HarmonyOS 5】App Linking 应用间跳转详解

目录 什么是 App Linking 使用场景 工作原理 如何开发 1.开通 App Linking 2.确定域名 3.服务端部署 applinking.json 文件 4.AGC绑定域名 5.项目配置 6.组装聚合链接 7.解析聚合链接中的参数 其他 如何获取应用ID 什么是 App Linking App Linking 是一款创建跨…

什么是变量提升?(形象的比喻)

当然&#xff01;可以用几个生活中的比喻来形象地解释变量提升&#xff1a; ​​1. 书架的占位符​​ 想象你有一个书架&#xff0c;但还没放书。 • 变量提升&#xff08;var&#xff09;&#xff1a; 你先在书架上贴了一个标签&#xff08;比如写“我的书”&#xff09;&…

C++面向对象编程入门:从类与对象说起(一)

C语言是面向过程&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;通过函数调用逐步解决问题&#xff0c;而C面向的是对象&#xff0c;关注的是对象&#xff0c;将一件事拆解成多个对象&#xff0c;靠对象之间互交完成。 目录 类的定义 类的两种定义 …

uniapp tabBar 中设置“custom“: true 在H5和app中无效解决办法

uniapp小程序自定义底部tabbar&#xff0c;但是在转成H5和app时发现"custom": true 无效&#xff0c;原生tabbar会显示出来 解决办法如下 在tabbar的list中设置 “visible”:false 代码如下&#xff1a;"tabBar": {"custom": true,//"cust…

SpringBoot学生操行评分系统源码设计开发

概述 基于SpringBoot框架开发的学生操行评分系统完整项目&#xff0c;该系统采用主流技术栈开发&#xff0c;包含完善的评分管理功能模块&#xff0c;是学校管理、教育培训机构理想的数字化解决方案&#xff0c;非常适合作为设计参考或二次开发基础项目。 主要内容 5.1 管理…

从代码学习深度学习 - 单发多框检测(SSD)PyTorch版

文章目录 前言工具函数数据处理工具 (`utils_for_data.py`)训练工具 (`utils_for_train.py`)检测相关工具 (`utils_for_detection.py`)可视化工具 (`utils_for_huitu.py`)模型类别预测层边界框预测层连接多尺度预测高和宽减半块基础网络块完整的模型训练模型读取数据集和初始化…

基于STM32的温湿度光照强度仿真设计(Proteus仿真+程序设计+设计报告+讲解视频)

这里写目录标题 **1.****主要功能****2.仿真设计****3.程序设计****4.设计报告****5.下载链接** 基于STM32的温湿度光照强度仿真设计(Proteus仿真程序设计设计报告讲解视频&#xff09; 仿真图Proteus 8.9 程序编译器&#xff1a;keil 5 编程语言&#xff1a;C语言 设计编号…

SSH 服务部署指南

本指南涵盖 OpenSSH 服务端的安装、配置密码/公钥/多因素认证&#xff0c;以及连接测试方法。 适用系统&#xff1a;Ubuntu/Debian、CentOS/RHEL 等主流 Linux 发行版。 1. 安装 SSH 服务端 Ubuntu/Debian # 更新软件包索引 sudo apt update# 安装 OpenSSH 服务端 sudo apt i…

《Python星球日记》 第46天:决策树与随机森林

名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏:《Python星球日记》,限时特价订阅中ing 目录 一、前言二、决策树算法原理1. 决策树简介2. 决策树的分裂准则(1) 信息熵与信息增益(2) 基尼不纯…

Vue2:引入公共JS,通过this调用

tools.js // 图片加上前缀 baseurl 是请求域名 img 是图片路径export function getimgurl(img) {return ${this.$baseurl}${img}}main.js import baseUrl from "/api/baseUrl.js" Vue.prototype.$baseurl baseUrlimport {getimgurl} from /api/tool.js; Vue.protot…

【Hot 100】 146. LRU 缓存

目录 引言LRU 缓存官方解题LRU实现&#x1f4cc; 实现步骤分解步骤 1&#xff1a;定义双向链表节点步骤 2&#xff1a;创建伪头尾节点&#xff08;关键设计&#xff09;步骤 3&#xff1a;实现链表基础操作操作 1&#xff1a;添加节点到头部操作 2&#xff1a;移除任意节点 步骤…

【Linux】swap交换分区管理

目录 一、Swap 交换分区的功能 二、swap 交换分区的典型大小的设置 2.1 查看交换分区的大小 2.1.1 free 2.1.2 cat /proc/swaps 或 swapon -s 2.1.3 top 三、使用交换分区的整体流程 3.1 案例一 3.2 案例二 一、Swap 交换分区的功能 计算机运行一个程序首先会将外存&am…

【计算机网络】用户从输入网址到网页显示,期间发生了什么?

1.URL解析 浏览器分解URL&#xff1a;https://www.example.com/page 协议&#xff1a;https域名&#xff1a;www.example.com路径&#xff1a;/page 2.DNS查询&#xff1a; 浏览器向DNS服务器发送查询请求&#xff0c;将域名解析为对应的IP地址。 3.CDN检查(如果有)&#…

架空输电线巡检机器人轨迹优化设计

架空输电线巡检机器人轨迹优化 摘要 本论文针对架空输电线巡检机器人的轨迹优化问题展开研究,综合考虑输电线复杂环境、机器人运动特性及巡检任务需求,结合路径规划算法、智能优化算法与机器人动力学约束,构建了多目标轨迹优化模型。通过改进遗传算法与模拟退火算法,有效…

根据窗口大小自动调整页面缩放比例,并保持居中显示

vue 项目 直接上代码 图片u1.png 是个背景图片 图片u2.png 是个遮罩 <template><div id"app"><div class"viewBox"><divclass"screen":style"{ transform: translate(-50%,-50%…

初学Python爬虫

文章目录 前言一、 爬虫的初识1.1 什么是爬虫1.2 爬虫的核心1.3 爬虫的用途1.4 爬虫分类1.5 爬虫带来的风险1.6. 反爬手段1.7 爬虫网络请求1.8 爬虫基本流程 二、urllib库初识2.1 http和https协议2.2 编码解码的使用2.3 urllib的基本使用2.4 一个类型六个方法2.5 下载网页数据2…

oracle 数据库sql 语句处理过程

14.1SQL语句处理过程 在进行SQL语句处理优化前&#xff0c;需要先熟悉和了解SQL语句的处理过程。 每种类型的语句在执行时都需要如下阶段&#xff1a; 第1步: 创建游标。 第2步: 分析语句。 第5步: 绑定变量。 第7步: t运行语句。 第9步: 关闭游标。 如果使用了并行功能&#x…

pm2 list查询服务时如何通过name或者namespace进行区分

在 PM2 中&#xff0c;如果 pm2 list 显示的所有服务名称&#xff08;name&#xff09;相同&#xff0c;就无法直观地区分不同的进程。这时可以通过 --namespace&#xff08;命名空间&#xff09; 或 自定义 name 来区分服务。以下是解决方案&#xff1a; 方法 1&#xff1a;启…

[python] 函数基础

二 函数参数 2.1 必备参数(位置参数) 含义: 传递和定义参数的顺序及个数必须一致 格式: def func(a,b) def func_1(id,passwd):print("id ",id)print("passwd ",passwd) func_1(10086,123456) 2.2 默认参数 函数: 为函数的参数提供一个默认值,如果调…

超大规模SoC后仿真流程与优化

在超大规模SoC设计中,是否需要进行全芯片后仿真(Full-Chip Post-layout Simulation)取决于多个因素,包括设计复杂度、项目风险、资源限制以及验证目标。以下是针对这一问题的系统性分析: 1. 全芯片后仿真的必要性 需要全芯片后仿真的场景 系统级交互验证: 跨模块信号交互…