SpringAi-Advisor续

1.局部作用

timerAdvisor只在call2起作用,在call1不起作用

2.传参

2.1全局传参

java 复制代码
package com.jiazhong.mingxing.ai.advisor.glm.advisor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClientRequest;
import org.springframework.ai.chat.client.ChatClientResponse;
import org.springframework.ai.chat.client.advisor.api.StreamAdvisor;
import org.springframework.ai.chat.client.advisor.api.StreamAdvisorChain;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
@Component
@Slf4j
public class ThirdAdvisor implements StreamAdvisor {
    @Override
    public Flux<ChatClientResponse> adviseStream(ChatClientRequest chatClientRequest, StreamAdvisorChain streamAdvisorChain) {
        UserMessage userMessage=chatClientRequest.prompt().getUserMessage();
        String question=userMessage.getText();
        log.info("用户的提问是:{}",question);
        Object a = chatClientRequest.context().get("A");
        log.info("获取到了用户传递过来的参数:{}",a);
        Flux<ChatClientResponse> chatClientResponseFlux = streamAdvisorChain.nextStream(chatClientRequest);
        return chatClientResponseFlux;
    }

    @Override
    public String getName() {
        return "third";
    }

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

运行结果

2.案例--基于Redis与Advisor实现多轮会话

在前边的案例中,我们讲解了多轮会话,分别基于内存和 JDBC ⽅式,⽽官⽅的 ChatMemory 并没 有提供基于 Redis 的实现⽅式。 下来我们使⽤⾃定义的 advisor 来实现⾃⼰的多轮会话。

2.1. 引入依赖

XML 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.fastjson2</groupId>
    <artifactId>fastjson2</artifactId>
    <version>2.0.59</version>
</dependency>

2.2配置yml文件

复制代码
server:
  port: 8004
spring:
  application:
    name: ai-siliconflow-glm
  ai:
    openai:
      base-url: https://api.siliconflow.cn
      api-key: sk-rlpneielwjrtbzwghvmtnkrfzsqoorkclubnimumojlptvqz
      chat:
        options:
          model: "Qwen/Qwen2.5-7B-Instruct"
          temperature: 0.7
  data:
   redis:
     host: 192.168.11.88
     port: 6379

2.3⾃定义可序列化的 Message 包装类

因为 Message 接⼝下的常⽤实现类均没有序列化操作,我们需要⾃定义⼀个类去进⾏序列化
和反序列化操作。

java 复制代码
package com.jiazhong.mingxing.ai.siliconflow.advisor.glm.redis.bean;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.MessageType;
import org.springframework.ai.chat.messages.UserMessage;


import java.io.Serializable;
import java.util.Map;
@Data
@NoArgsConstructor
//因为Message没有序列化,不能存到Redis中,要包装一下,所以要写SerializableMessage
public class SerializableMessage implements Serializable {
    private static final long serialVersionUID = 12354846L;
    private MessageType messageType;
    private String content;
    private Map<String,Object> properties;

    public SerializableMessage(Message message) {
        this.messageType=message.getMessageType();// 消息类型
        this.content=message.getText();// 消息内容
        this.properties=message.getMetadata();// 消息元数据
    }
    public Message getMessage(){
        if (this.messageType.equals(MessageType.USER)){
            return new UserMessage(this.content);
        }else if (this.messageType.equals(MessageType.ASSISTANT)){
            return new AssistantMessage(this.content,this.properties);
        }
        return null;
    }
}

2.4 ⾃定义 Memory 类

定义⼀个类 RedisMessageWindowChatMemory ,实现 ChatMemory 接⼝,重点书写添加和
获取的⽅法。

java 复制代码
package com.jiazhong.mingxing.ai.siliconflow.advisor.glm.redis.memory;

import com.alibaba.fastjson2.JSONArray;
import com.jiazhong.mingxing.ai.siliconflow.advisor.glm.redis.bean.SerializableMessage;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.Message;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

@Component
@Slf4j
public class RedisMessageWindowChatMemory implements ChatMemory {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    private static final String prefix_key="REDIS_MESSAGE_KEY";
    @Override
    public void add(String conversationId, List<Message> messages) {
        //1.创建存储消息的空集合 message -> serializableMessages
        List<SerializableMessage> serializableMessages=new ArrayList<>();
        //2.因为Redis中只能存储序列化的消息,所以要将消息转到序列化消息数组中
        for (Message message :messages){
            SerializableMessage serializableMessage=new SerializableMessage(message);
            serializableMessages.add(serializableMessage);
        }
        //3.redis中只能存储Json格式
        String jsonString = JSONArray.toJSONString(serializableMessages);
        stringRedisTemplate.opsForValue().set(prefix_key+conversationId,jsonString);
    }

    @Override
    public List<Message> get(String conversationId) {
        String jsonString = stringRedisTemplate.opsForValue().get(prefix_key + conversationId);
        List<SerializableMessage> serializableMessages=JSONArray.parseArray(jsonString,SerializableMessage.class);
        if (serializableMessages!=null){
            List<Message> messages=new ArrayList<>();
            for (SerializableMessage serializableMessage:serializableMessages){
                Message message = serializableMessage.getMessage();
                messages.add(message);
            }
            return messages;
        }
        return List.of();
    }

    @Override
    public void clear(String conversationId) {
        stringRedisTemplate.delete(prefix_key+conversationId);

    }
}

2.5⾃定义 advisor 类

javascript 复制代码
package com.jiazhong.mingxing.ai.siliconflow.advisor.glm.redis.advisor;

import com.jiazhong.mingxing.ai.siliconflow.advisor.glm.redis.memory.RedisMessageWindowChatMemory;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.ai.chat.client.ChatClientMessageAggregator;
import org.springframework.ai.chat.client.ChatClientRequest;
import org.springframework.ai.chat.client.ChatClientResponse;
import org.springframework.ai.chat.client.advisor.api.BaseAdvisor;
import org.springframework.ai.chat.client.advisor.api.StreamAdvisor;
import org.springframework.ai.chat.client.advisor.api.StreamAdvisorChain;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;


@Component
public class RedisAdvisor implements StreamAdvisor {
    @Resource
    private RedisMessageWindowChatMemory redisChatMemory;

    private String id="";
    @Override
    public Flux<ChatClientResponse> adviseStream(ChatClientRequest chatClientRequest, StreamAdvisorChain streamAdvisorChain) {
        Mono var10000 = Mono.just(chatClientRequest).publishOn(BaseAdvisor.DEFAULT_SCHEDULER)
                        .map((request) -> this.before(request));//调用请求前方法
        return var10000.flatMapMany((Function<ChatClientRequest, Publisher<?
                                        extends ChatClientResponse>>) streamAdvisorChain::nextStream)
                .transform((Function<Flux<ChatClientResponse>,
                        Flux<ChatClientResponse>>) flux ->//通过循环将流一个一个打出来
                        (new ChatClientMessageAggregator())
                                .aggregateChatClientResponse(flux, response ->
                                        observeAfter(response)));//调用响应后的方法
    }
    //请求大模型之前
    private ChatClientRequest before(ChatClientRequest request){
        //1.获取conversationId
        id=request.context().get("id").toString();
        //2.获取当前conversationId的会话记忆
        List<Message> messageList=this.redisChatMemory.get(id);
        //3.合并消息列表
        String question=request.prompt().getUserMessage().getText();//得到问题
        UserMessage message=new UserMessage(question);//将当次的问题写成UserMessage
        if (messageList.isEmpty()){
            messageList=List.of(message);
        }else {
            messageList.add(message);
        }
        //4.创建一个新的请求
        ChatClientRequest processedChatClientRequest=request.mutate().prompt(request.prompt().mutate().messages(messageList).build()).build();
        //5。将新的消息添加到对话记忆中
        this.redisChatMemory.add(id,messageList);//历史记录和问的记录,回答的记录在调用大模型响应之后写
        //6.提交本次请求
        return processedChatClientRequest;
    }
    //响应之后 大模型拿到之后
    private ChatClientResponse observeAfter(ChatClientResponse chatClientResponse){
        List<AssistantMessage> assistantMessages=new ArrayList<>();//new一个助手消息
        //获取到刚才的助手消息
        if (chatClientResponse.chatResponse()!=null){
            assistantMessages=chatClientResponse.chatResponse().getResults().stream().map((g)->g.getOutput()).toList();
        }
        List<Message> messages=this.redisChatMemory.get(id);
        messages.addAll(assistantMessages);//拿到过去的历史记录
        this.redisChatMemory.add(id,messages);//将助手消息和历史记录添加到Redis中
        return chatClientResponse;
    }


    @Override
    public String getName() {
        return "redisAdvisor";
    }

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

2.6配置类

java 复制代码
package com.jiazhong.mingxing.ai.siliconflow.advisor.glm.redis.config;

import com.jiazhong.mingxing.ai.siliconflow.advisor.glm.redis.advisor.RedisAdvisor;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ChatClientConfig {
    @Resource
    private OpenAiChatModel openAiChatModel;
    @Resource
    private RedisAdvisor redisAdvisor;
    @Bean("openAiChatClient")
    public ChatClient openAiChatClient(){
        return ChatClient.builder(openAiChatModel)
                .defaultAdvisors(redisAdvisor)
                .build();
    }
}

2.7controller类

java 复制代码
package com.jiazhong.mingxing.ai.siliconflow.advisor.glm.redis.controller;

import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

@RestController
@RequestMapping("/redis")
public class RedisController {
    @Resource
    private ChatClient openAiChatClient;
    @GetMapping(value = "/stream",produces = "text/html;charset=utf-8")
    public Flux<String> stream(@RequestParam("question") String question,@RequestParam("id") String id){
        return openAiChatClient.prompt()
                .advisors(e->e.param("id",id))
                .user(question)
                .stream().content();
    }
}

2.8启动类

java 复制代码
package com.jiazhong.mingxing.ai.siliconflow.advisor.glm.redis;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AiSiliconflowAdvisorGlmRedisApplication {
    public static void main(String[] args) {
        SpringApplication.run(AiSiliconflowAdvisorGlmRedisApplication.class);
    }
}

会在Redis中获得数据

相关推荐
*才华有限公司*5 小时前
# 彻底解决 RedisReadOnlyException:从「只读从节点」到「独立主节点」的实战指南
redis
成为你的宁宁6 小时前
【Zabbix 监控 Redis 实战教程(附图文教程):从 Zabbix-Server 部署、Agent2 安装配置到自带监控模板应用全流程】
数据库·redis·zabbix
墨雨晨曦888 小时前
如何保证redis和mysql数据一致性方案对比
数据库·redis·mysql
超级种码8 小时前
Redis:Redis 常见问题及解决思路
数据库·redis·缓存
结衣结衣.9 小时前
Redis中的string字符串介绍
数据库·redis·缓存
Python少年班9 小时前
MySQL MongoDB Redis官方本地百度网盘下载链接
redis·mysql·mongodb
Codeking__9 小时前
Redis分布式——分布式锁
数据库·redis·分布式
霸道流氓气质10 小时前
SpringBoot+modbus4j实现ModebusTCP通讯定时读取多个plc设备数并存储进redis中
java·spring boot·redis·modbustcp·plc
optimistic_chen10 小时前
【Redis系列】哨兵模式
linux·数据库·redis·分布式·哨兵