总结:

上下文要能连贯起来,需要有持久化的媒介,然后要有消息对话窗口聊天记忆(消息窗口的上限),顾问Adviser加强

一、解决持久化媒介(采用RedisChatMemoryRepository)

1.新建子模块Module

SAA-08Persistent

2..大模型的聊天内容要写入redis,要新增两个依赖

spring-ai-alibaba-starter-memory-redis是阿里巴巴和Reids整合的依赖

为什么不用RedisTemplate,而是用Jedis?

jedis依赖是因为阿里巴巴AI整合的框架就是jedis连接池

        <!--spring-ai-alibaba memory-redis-->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-memory-redis</artifactId>
        </dependency>
        <!--jedis-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

完整的pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.atguigu.study</groupId>
        <artifactId>SpringAIAlibaba-atguiguV1</artifactId>
        <version>1.0-SNAPSHOT</version>
    </parent>

    <artifactId>SAA-08Persistent</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--spring-ai-alibaba dashscope-->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
        </dependency>
        <!--spring-ai-alibaba memory-redis-->
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-memory-redis</artifactId>
        </dependency>
        <!--jedis-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.38</version>
        </dependency>
        <!--hutool-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.22</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <compilerArgs>
                        <arg>-parameters</arg>
                    </compilerArgs>
                    <source>21</source>
                    <target>21</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

</project>
 
 

3.改yml文件

server.port=8008

# ?????????
server.servlet.encoding.charset=utf-8
server.servlet.encoding.enabled=true
server.servlet.encoding.force=true

spring.application.name=SAA-08Persistent

# ====SpringAIAlibaba Config=============
spring.ai.dashscope.api-key=${aliQwen-api}


# ==========redis config ===============
spring.data.redis.host=localhost
spring.data.redis.port=6379
spring.data.redis.database=0
spring.data.redis.connect-timeout=3
spring.data.redis.timeout=2
spring.data.redis.password=123456



4.前置知识:步骤一

1.实现SpringAI框架规定的ChatMemoryRepository接口

Spring AI官网:Introduction :: Spring AI Referencehttps://docs.spring.io/spring-ai/reference/index.html

SpringAIAlibaba实现了SpringAI的ChatMemory

2.接口ChatMemoryRepository

这个接口两个实现类:

InMemoryChatMemoryRepository和RedisChatMemoryRepository

InMemoryChatMemoryRepository是保存在内存中的,是SpringAI的核心框架

RedisChatMemoryRepository是阿里巴巴的,用于保存到Redis中

3.RedisChatMemoryRepository源码

4.编码新建RedisMemoryConfig配置类

package com.atguigu.study.config;

import com.alibaba.cloud.ai.memory.redis.RedisChatMemoryRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author NoFear
 * @version 1.0
 * @description: TODO
 * @date 2025/10/5 13:54
 */
@Configuration
public class RedisMemoryConfig {


    @Value("${spring.data.redis.host}")
    private String host;
    @Value("${spring.data.redis.port}")
    private int port;
    @Value("${spring.data.redis.password}")
    private String password;

    @Bean
    public RedisChatMemoryRepository redisChatMemoryRepository() {
        return RedisChatMemoryRepository.builder()
                .host(host)
                .port(port)
                .password(password)
                .build();
    }

}

二、消息对话窗口,聊天记录上限

三、顾问(Advisors)MessageChatMemoryAdvisor

顾问对ChatMemory做了功能增强

1.可以将记录保存到Redis

2.消息窗口最多可以保存一百条

3.要以顾问增强器的方式打入到接口调用里面,告诉接口增加了新的功能

聊天客户端中的内存

使用 ChatClient API 时,您可以提供一种实现来维护多个交互的对话上下文。ChatMemory

Spring AI 提供了一些内置的 Advisor,您可以使用它们根据需要配置 的内存行为。ChatClient

目前,在执行工具调用时与大语言模型交换的中间消息不会存储在内存中。这是当前实现的限制,将在将来的版本中解决。如果需要存储这些消息,请参阅用户控制工具执行的说明。
  • MessageChatMemoryAdvisor.此顾问使用提供的实现来管理对话内存。在每次交互中,它都会从内存中检索对话历史记录,并将其作为消息集合包含在提示中。ChatMemory

  • PromptChatMemoryAdvisor.此顾问使用提供的实现来管理对话内存。在每次交互时,它都会从内存中检索对话历史记录,并将其作为纯文本附加到系统提示中。ChatMemory

  • VectorStoreChatMemoryAdvisor.此顾问使用提供的实现来管理对话内存。在每次交互时,它都会从矢量存储中检索对话历史记录,并将其作为纯文本追加到系统消息中。VectorStore

例如,如果要与 一起使用,可以按如下方式配置:MessageWindowChatMemoryMessageChatMemoryAdvisor

<span style="color:#191e1e"><span style="background-color:#ffffff"><code class="language-java">ChatMemory chatMemory = MessageWindowChatMemory.builder().build();

ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
    .build();</code></span></span>

当执行对 的调用时,内存将由 自动管理。将根据指定的对话 ID 从内存中检索对话历史记录:ChatClientMessageChatMemoryAdvisor

<span style="color:#191e1e"><span style="background-color:#ffffff"><code class="language-java">String conversationId = <span style="color:#032f62">"007"</span>;

chatClient.prompt()
    .user(<span style="color:#032f62">"Do I have license to code?"</span>)
    .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId))
    .call()
    .content();</code></span></span>

advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId))

可以把对话的内容存入到Redis,且每个消息窗口里面保存**条(可以为20条)

从而使得大模型功能更加强大

之前只是通过提示词与大模型进行对话

而现在不但可以和大模型对话,还可以通过Adviser的配置,将消息保存到Redis同时设置上限是20条,从而使得大模型更加智能

在原有的ChatClient中增加了顾问增强器,增强了功能,可以最多保存10条聊天记录,将对话上下文的记录保存到Redis

 @Bean(name = "deepseekChatClient")
    public ChatClient deepseekChatClient(@Qualifier("deepseek")ChatModel deepseek, RedisChatMemoryRepository redisChatMemoryRepository){

        MessageWindowChatMemory windowChatMemory = MessageWindowChatMemory.builder()
                .chatMemoryRepository(redisChatMemoryRepository)
                .maxMessages(10)
                .build();

        return ChatClient.builder(deepseek)
                .defaultAdvisors(MessageChatMemoryAdvisor.builder(windowChatMemory).build())
                .defaultOptions(ChatOptions.builder().model(DEEPSEEK_MODEL).build())
                .build();

    }


@Bean(name = "qwenChatClient")
    public ChatClient qwenChatClient(@Qualifier("qwen")ChatModel qwen, RedisChatMemoryRepository redisChatMemoryRepository){
        
        MessageWindowChatMemory windowChatMemory = MessageWindowChatMemory.builder()
                .chatMemoryRepository(redisChatMemoryRepository)
                .maxMessages(10)
                .build();
        
        return ChatClient.builder(qwen)
                .defaultOptions(ChatOptions.builder().model(QWEN_MODEL).build())
                .defaultAdvisors(MessageChatMemoryAdvisor.builder(windowChatMemory).build())
                .build();

    }

四、Controller层的编写和调用

也就是这样写代码,这个用户的聊天记录会自动保存到Redis中,无需手动编程,非常的方便,并且下次调用,如果Reids中的10条满了,还可以自动替换最早的信息并保存最新的消息,Redis中保存的Key是CONVERSATION_ID(RedisChatMemoryRepository中的常量,之前注册一个ChatClient时注入了这个属性) + 用户id,value是list类型的保存大模型回复的消息

package com.atguigu.study.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.RestController;

import java.util.function.Consumer;

import static org.springframework.ai.chat.memory.ChatMemory.CONVERSATION_ID;

/**
 * @author NoFear
 * @version 1.0
 * @description: TODO
 * @date 2025/10/5 15:07
 */
@RestController
public class ChatMemoryRedisController {

    @Resource(name = "qwenChatClient")
    private ChatClient qwenChatClient;


/*    @GetMapping("/chatmemory/chat")
    public String chat(String msg, String userId){
        return qwenChatClient.prompt(msg)
                .advisors(new Consumer<ChatClient.AdvisorSpec>() {
                    @Override
                    public void accept(ChatClient.AdvisorSpec advisorSpec) {
                        advisorSpec.param(CONVERSATION_ID, userId);
                    }
                })
                .call()
                .content();
    }*/

    @GetMapping("/chatmemory/chat")
    public String chat(String msg, String userId){
        return qwenChatClient.prompt(msg)
                .advisors(advisorSpec -> advisorSpec.param(CONVERSATION_ID, userId))
                .call()
                .content();
    }



}
.advisors(advisorSpec -> advisorSpec.param(CONVERSATION_ID, userId))

CONVERSATION_ID(RedisChatMemoryRepository.java中的常量

private static final String DEFAULT_KEY_PREFIX = "spring_ai_alibaba_chat_memory:";

)和userId共同组成了Redis中的key,value是用户提问的消息内容,是一个list集合

测试:http://localhost:8008/chatmemory/chat?msg=2加5等于多少&userId=7

Logo

更多推荐