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

相关推荐
IT大白鼠2 天前
Redis 系列 · 第 01 篇——认知入门:Redis 是什么
redis·nosql
彧azz2 天前
Linux 环境下 Redis 学习总结:数据类型、持久化、锁、事务、主从与缓存问题
linux·redis·笔记·学习·面试
lbb 小魔仙2 天前
OpenClaw + cpolar 实战:远程 NAS、分享小游戏、RDP,再配置公网 AI 入口
数据库·人工智能·redis·oracle·prometheus
夕除2 天前
redis--018
redis
两锭子2 天前
Redis基础
redis
烟沙九洲2 天前
Redis 缓存穿透、缓存击穿、缓存雪崩
数据库·redis
字节探索2 天前
Redis 只会当缓存用?这 10 大实战场景,让你的系统快到飞起
redis·后端
于樱花森上飞舞2 天前
【Redis】哨兵详解
java·开发语言·数据库·redis
Rain的Java大神之路2 天前
如何快速上传10G文件
java·spring boot·redis·后端·mysql·spring cloud·面试
Wang's Blog2 天前
Java 接入Redis: 通用命令与键管理
java·服务器·redis