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(日志顾问)

java 复制代码
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 {

    @Override
    public 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;
    }

    @Override
    public 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;
    }

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

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

AiConfig

java 复制代码
    @Bean
    ChatClient 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顾问)

java 复制代码
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();
    }

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

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

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

测试

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

RetryAdvisor(异常重试顾问)

java 复制代码
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);
    }

    @Override
    public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {
        AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest);
        return retryTemplate.execute(context -> {
            return advisedResponse;
        });
    }

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

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

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

还要修改AiConfig

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

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

补充

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

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

统一配置AI模型

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

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

方法上配置AI大模型

java 复制代码
    /**
     * 根据消息直接输出回答
     * @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大模型既可以进行统一配置,也可以在方法上设置

相关推荐
caihuayuan528 分钟前
升级element-ui步骤
java·大数据·spring boot·后端·课程设计
界面开发小八哥31 分钟前
界面控件DevExpress WinForms v25.1新功能预览 - 功能区组件全新升级
人工智能·.net·界面控件·winform·devexpress
zhz52141 小时前
开源数字人框架 AWESOME-DIGITAL-HUMAN 技术解析与应用指南
人工智能·ai·机器人·开源·ai编程·ai数字人·智能体
1296004521 小时前
pytorch基础的学习
人工智能·pytorch·学习
佩奇的技术笔记1 小时前
Java学习手册:单体架构到微服务演进
java·微服务·架构
沉默媛2 小时前
RuntimeError: expected scalar type ComplexDouble but found Float
人工智能·pytorch·深度学习
契合qht53_shine2 小时前
NLP基础
人工智能·自然语言处理
闭月之泪舞2 小时前
YOLO目标检测算法
人工智能·yolo·目标检测
zm2 小时前
服务器多客户端连接核心要点(1)
java·开发语言
FuckPatience2 小时前
关于C#项目中 服务层使用接口的问题
java·开发语言·c#