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中获得数据

相关推荐
ALex_zry20 小时前
Redis Cluster 分布式缓存架构设计与实践
redis·分布式·缓存
乔江seven1 天前
【Flask 进阶】3 从同步到异步:基于 Redis 任务队列解决 API 高并发与长耗时任务阻塞
redis·python·flask
这周也會开心1 天前
Redis与MySQL回写中的数据类型存储设计
数据库·redis·mysql
shuair1 天前
redis缓存预热、缓存击穿、缓存穿透、缓存雪崩
redis·spring·缓存
shuair1 天前
guava布隆过滤器及cuckoo过滤器
redis·guava
上架ipa1 天前
redis图形化客户端功能对比
redis·缓存
落霞的思绪1 天前
Spring AI Alibaba 集成 Redis 向量数据库实现 RAG 与记忆功能
java·spring·rag·springai
indexsunny1 天前
互联网大厂Java面试实战:微服务与Spring生态技术解析
java·spring boot·redis·kafka·mybatis·hibernate·microservices
为什么不问问神奇的海螺呢丶1 天前
n9e categraf redis监控配置
前端·redis·bootstrap
笨蛋不要掉眼泪1 天前
RAG知识库核心API架构全解析:从文档加载到向量检索的完整流程
java·spring boot·redis·ai·架构