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大模型既可以进行统一配置,也可以在方法上设置