spring-ai-alibaba第七章阿里dashscope集成RedisChatMemory实现对话记忆

1、RedisChatMemory的实现

从git上 https://github.com/alibaba/spring-ai-alibaba 下载代码;

对 RedisChatMemory进行简单改造,改造后的代码如下

java 复制代码
/*
 * Copyright 2024-2025 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.cloud.ai.memory.redis;

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

import com.alibaba.cloud.ai.memory.redis.serializer.MessageDeserializer;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.Message;

public class RedisChatMemory implements ChatMemory, AutoCloseable {

	private static final Logger logger = LoggerFactory.getLogger(RedisChatMemory.class);

	private static final String DEFAULT_KEY_PREFIX = "spring_ai_alibaba_chat_memory:";

	private static final String DEFAULT_HOST = "127.0.0.1";

	private static final int DEFAULT_PORT = 6379;

	private static final String DEFAULT_PASSWORD = null;

	private final JedisPool jedisPool;

	private final Jedis jedis;

	private final ObjectMapper objectMapper;

	public RedisChatMemory() {

		this(DEFAULT_HOST, DEFAULT_PORT, DEFAULT_PASSWORD);
	}

	public RedisChatMemory(String host, int port, String password) {

		JedisPoolConfig poolConfig = new JedisPoolConfig();

		this.jedisPool = new JedisPool(poolConfig, host, port, 2000, password);
		this.jedis = jedisPool.getResource();
		this.objectMapper = new ObjectMapper();
		SimpleModule module = new SimpleModule();
		module.addDeserializer(Message.class, new MessageDeserializer());
		this.objectMapper.registerModule(module);

		logger.info("Connected to Redis at {}:{}", host, port);
	}

	@Override
	public void add(String conversationId, List<Message> messages) {

		String key = DEFAULT_KEY_PREFIX + conversationId;

		for (Message message : messages) {
			try {
				String messageJson = objectMapper.writeValueAsString(message);
				jedis.rpush(key, messageJson);
			}
			catch (JsonProcessingException e) {
				throw new RuntimeException("Error serializing message", e);
			}
		}

		logger.info("Added messages to conversationId: {}", conversationId);
	}

	@Override
	public List<Message> get(String conversationId, int lastN) {

		String key = DEFAULT_KEY_PREFIX + conversationId;

		List<String> messageStrings = jedis.lrange(key, -lastN, -1);
		List<Message> messages = new ArrayList<>();

		for (String messageString : messageStrings) {
			try {
				Message message = objectMapper.readValue(messageString, Message.class);
				messages.add(message);
			}
			catch (JsonProcessingException e) {
				throw new RuntimeException("Error deserializing message", e);
			}
		}

		logger.info("Retrieved {} messages for conversationId: {}", messages.size(), conversationId);

		return messages;
	}

	@Override
	public void clear(String conversationId) {

		String key = DEFAULT_KEY_PREFIX + conversationId;

		jedis.del(key);
		logger.info("Cleared messages for conversationId: {}", conversationId);
	}

	@Override
	public void close() {

		if (jedis != null) {

			jedis.close();

			logger.info("Redis connection closed.");
		}
		if (jedisPool != null) {

			jedisPool.close();

			logger.info("Jedis pool closed.");
		}
	}

	public void clearOverLimit(String conversationId, int maxLimit, int deleteSize) {
		try {
			String key = DEFAULT_KEY_PREFIX + conversationId;

			List<String> all = jedis.lrange(key, 0, -1);

			if (all.size() >= maxLimit) {
				all = all.stream().skip(Math.max(0, deleteSize)).toList();
			}
			this.clear(conversationId);
			for (String message : all) {
				jedis.rpush(key, message);
			}
		}
		catch (Exception e) {
			logger.error("Error clearing messages from Redis chat memory", e);
			throw new RuntimeException(e);
		}
	}

	public void updateMessageById(String conversationId, String messages) {
		String key = DEFAULT_KEY_PREFIX + conversationId;
		try {
			this.jedis.del(key);
			this.jedis.rpush(key, new String[] { messages });
		}
		catch (Exception var6) {
			logger.error("Error updating messages from Redis chat memory", var6);
			throw new RuntimeException(var6);
		}
	}

}

然后用maven编译安装,

bash 复制代码
 mvn install -DskipTests   

记下安装后的版本,此时我用的版本是

复制代码
1.0.0-M6.2-SNAPSHOT

2、spring-ai 大模型应用程序通过RedisChatMemory 实现对话记忆

pom文件

html 复制代码
<dependencies>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>com.alibaba.cloud.ai</groupId>
			<artifactId>spring-ai-alibaba-starter</artifactId>
			<version>${spring-ai-alibaba.version}</version>
		</dependency>

		<dependency>
			<groupId>com.alibaba.cloud.ai</groupId>
			<artifactId>spring-ai-alibaba-redis-memory</artifactId>
			<version>1.0.0-M6.2-SNAPSHOT</version>
		</dependency>

		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-pool2</artifactId>
			<version>2.12.1</version>
		</dependency>

		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
			<version>5.2.0</version>
		</dependency>


	</dependencies>

yml配置文件

html 复制代码
server:
  port: 8080

spring:
  ai:
    dashscope:
      api-key: sk-xoxoxoxoxoxox
      chat:
        model: qwq-plus

controller

java 复制代码
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.alibaba.example.chatmemory.controller;


import com.alibaba.cloud.ai.memory.redis.RedisChatMemory;
import org.springframework.http.MediaType;
import reactor.core.publisher.Flux;
import jakarta.servlet.http.HttpServletResponse;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.model.ChatModel;
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 static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY;
import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY;

/**
 * @author yuluo
 * @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>
 */

@RestController
@RequestMapping("/chat-memory")
public class ChatMemoryController {

    private final ChatClient chatClient;

    public ChatMemoryController(ChatModel chatModel) {
        this.chatClient = ChatClient.builder(chatModel).build();
    }


    @GetMapping(value = "/redis", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> redis(
            @RequestParam("prompt") String prompt,
            @RequestParam("chatId") String chatId,
            HttpServletResponse response
    ) {
        response.setCharacterEncoding("UTF-8");
        return chatClient.prompt(prompt).advisors(
                new MessageChatMemoryAdvisor(new RedisChatMemory(
                        "127.0.0.1",
                        6379,
                        "zsj381599113QQ"
                ))
        ).advisors(
                a -> a
                        .param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
                        .param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100)
        ).stream().content();
    }

}

启动类

java 复制代码
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.alibaba.example.chatmemory;

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

/**
 * @author yuluo
 * @author <a href="mailto:yuluo08290126@gmail.com">yuluo</a>
 */

@SpringBootApplication
public class ChatMemoryApplication {

	public static void main(String[] args) {

		SpringApplication.run(ChatMemoryApplication.class, args);
	}

}

测试如下

相关推荐
稚辉君.MCA_P8_Java7 分钟前
豆包 Java的23种设计模式
java·linux·jvm·设计模式·kubernetes
tanyongxi668 分钟前
C++ 特殊类设计与单例模式解析
java·开发语言·数据结构·c++·算法·单例模式
遗憾皆是温柔10 分钟前
24. 什么是不可变对象,好处是什么
java·开发语言·面试·学习方法
midsummer_woo16 分钟前
基于springboot的IT技术交流和分享平台的设计与实现(源码+论文)
java·spring boot·后端
Peter(阿斯拉)27 分钟前
[Java性能优化]_[时间优化]_[字符串拼接的多种方法性能分析]
java·性能优化·stringbuilder·string·字符串拼接·stringbuffer·时间优化
这里有鱼汤38 分钟前
miniQMT+Qlib才是AI量化的正确打开方式
后端
无奈何杨40 分钟前
风控系统事件分析中心,关联关系、排行、时间分布
前端·后端
Moment1 小时前
nginx 如何配置防止慢速攻击 🤔🤔🤔
前端·后端·nginx
rannn_1111 小时前
【MySQL学习|黑马笔记|Day7】触发器和锁(全局锁、表级锁、行级锁、)
笔记·后端·学习·mysql
qq_三哥啊1 小时前
【IDEA】设置Debug调试时调试器不进入特定类(Spring框架、Mybatis框架)
spring·intellij-idea·mybatis