SpringAI+DeepSeek大模型应用开发——4 对话机器人

发布于:2025-04-18 ⋅ 阅读:(20) ⋅ 点赞:(0)

目录​​​​​​​

​​​​​​​​​​​​​​项目初始化

pom文件

配置模型

ChatClient

同步调用

流式调用

日志功能

对接前端

解决跨域

会话记忆功能

ChatMemory

添加会话记忆功能

会话历史

管理会话id

保存会话id

查询会话历史

完善会话记忆

定义可序列化的Message

方案一:定期持久化

方案二:自定义ChatMemory

方案三:Cassandra

​​​​​​​项目初始化

创建一个新的SpringBoot工程,勾选Web、MySQL驱动、Ollama:

pom文件

主要引入的依赖如下

<properties>
    <java.version>17</java.version>
    <spring-ai.version>1.0.0-M6</spring-ai.version>
</properties>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.22</version>
</dependency>
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
    <version>3.5.10.1</version>
</dependency>
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-pdf-document-reader</artifactId>
</dependency>
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>${spring-ai.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

SpringAI完全适配了SpringBoot的自动装配功能,而且给不同的大模型提供了不同的starter,比如:

<!--Anthropic-->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-anthropic-spring-boot-starter</artifactId>
</dependency>

<!--Azure OpenAI-->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-azure-openai-spring-boot-starter</artifactId>
</dependency>

<!--DeepSeek-->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>

<!--Hugging Face-->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-huggingface-spring-boot-starter</artifactId>
</dependency>


<!--Ollama-->
<dependency>
   <groupId>org.springframework.ai</groupId>
   <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
</dependency>


<!--OpenAI-->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>

配置模型

在配置文件中配置模型的参数信息,以Ollama为例:

spring:
  application:
    name: heima-ai
  ai:
    ollama:
      base-url: http://localhost:11434
      chat:
        model: deepseek-r1:7b
    openai:
      base-url: https://dashscope.aliyuncs.com/compatible-mode
      api-key: 填百炼大模型平台自己的api key
      chat:
        options:
          model: qwen-plus
      embedding:
        options:
          model: text-embedding-v3
          dimensions: 1024

ChatClient

  • ChatClient中封装了与AI大模型对话的各种API,同时支持同步式或响应式交互;

  • 在使用之前,需要声明一个ChatClient;在ai.config包下新建一个CommonConfiguration类:

  • 系统预设:在SpringAI中,设置System信息非常方便,不需要在每次发送时封装到Message,而是创建ChatClient时指定即可;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CommonConfiguration {
    // 注意参数中的model就是使用的模型,这里用了Ollama,也可以选择OpenAIChatModel
    @Bean
    public ChatClient chatClient(OllamaChatModel model) {  
        return ChatClient.builder(model) // 创建ChatClient工厂,利用它可以自由选择模型、添加各种自定义配置
               .defaultOptions(ChatOptions.builder().model("qwen-omni-turbo").build())
               .defaultSystem("你是一个热心、可爱的智能助手,你的名字叫小团团,请以小团团的身份和语气回答问题。") //系统预设 
                .build(); // 构建ChatClient实例
    }
}
同步调用

定义一个Controller,在其中接收用户发送的提示词,然后把提示词发送给大模型,交给大模型处理,拿到结果后返回;

package com.shisan.ai.controller;

import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RequiredArgsConstructor
@RestController
@RequestMapping("/ai")
public class ChatController {

    private final ChatClient chatClient;
    // 请求方式和路径不要改动,将来要与前端联调
    @RequestMapping("/chat")
    public String chat(@RequestParam String prompt) {
        return chatClient
                .prompt(prompt)   // 传入user提示词
                .call()    // 同步调用请求,会等待AI全部输出完才返回结果
                .content(); //返回响应内容
    }
}

启动项目,在浏览器中访问:http://localhost:8080/ai/chat?prompt=你好

流式调用
  • 同步调用需要等待很长时间页面才能看到结果,用户体验不好。为了解决这个问题,可以改进调用方式为流式调用;

  • 使用了WebFlux技术实现流式调用;修改ChatController中的chat方法:

// 注意看返回值,是Flux<String>,也就是流式结果,另外需要设定响应类型和编码,不然前端会乱码
@RequestMapping(value = "/chat", produces = "text/html;charset=UTF-8")
public Flux<String> chat(@RequestParam(String prompt) {
    return chatClient
            .prompt(prompt)
            .stream() // 流式调用
            .content();
}
日志功能

默认情况下,AI交互时是不记录日志的,我们无法得知SpringAI 组织的提示词到底长什么样,这样不方便我们调试。

SpringAI基于AOP机制实现与大模型对话过程的增强、拦截、修改等功能,所有的增强通知都需要实现Advisor接口;Spring提供了一些Advisor的默认实现,来实现一些基本的增强功能:

  • SimpleLoggerAdvisor:日志记录的Advisor;

  • MessageChatMemoryAdvisor:会话记忆的Advisor;

  • QuestionAnswerAdvisor:实现RAG的Advisor;

当然,也可以自定义Advisor,具体可以参考:Advisors API 

添加日志功能

@Configuration
public class CommonConfiguration {
    // 注意参数中的model就是使用的模型,这里用了Ollama,也可以选择OpenAIChatModel
    @Bean
    public ChatClient chatClient(OllamaChatModel model) {  
        return ChatClient.builder(model) // 创建ChatClient工厂,利用它可以自由选择模型、添加各种自定义配置
               .defaultOptions(ChatOptions.builder().model("qwen-omni-turbo").build())
               .defaultSystem("你是一个热心、可爱的智能助手,你的名字叫小团团,请以小团团的身份和语气回答问题。") //系统预设
               .defaultAdvisors(
                        new SimpleLoggerAdvisor(),
                        new MessageChatMemoryAdvisor(chatMemory)
                )
                .build(); // 构建ChatClient实例
    }
}

日志级别

#配置 application.yaml即可,重启项目,再次聊天就能在IDEA的运行控制台中看到AI对话的日志信息了
logging:
  level:
    org.springframework.ai: debug
    com.itheima.ai: debug

对接前端

npm运行

进入spring-ai-protal文件夹(该文件夹要放在非中文目录下),然后执行cmd命令:

# 安装依赖
npm install
# 运行程序
npm run dev

启动后,访问http://localhost:5173即可看到页面:

nginx运行

若不关心源码,进入spring-ai-nginx文件夹(该文件夹要放在非中文目录下),然后执行cmd命令

# 启动Nginx
start nginx.exe
# 停止
nginx.exe -s stop
  • 启动后,访问http://localhost:5173即可看到页面。

解决跨域

前后端在不同端口,存在跨域问题,因此需要在服务端解决cors问题;在ai.config包中添加一个MvcConfiguration类:

@Configuration
public class MvcConfiguration implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("*")
                .exposedHeaders("Content-Disposition");
    }
}

测试

会话记忆功能

前面讲过,让AI有会话记忆的方式就是把每一次历史对话内容拼接到Prompt中,一起发送过去,这种方式比较挺麻烦;

如果使用了SpringAI,并不需要自己拼接,SpringAI自带了会话记忆功能,可以把历史会话保存下来,下一次请求AI时会自动拼接,非常方便。

ChatMemory

会话记忆功能同样是基于AOP实现,Spring提供了一个MessageChatMemoryAdvisor的通知,可以像之前添加日志通知一样添加到ChatClient即可;不过,要注意的是,MessageChatMemoryAdvisor需要指定一个ChatMemory实例,也就是会话历史保存的方式;

ChatMemory接口声明如下:

public interface ChatMemory {

    // TODO: consider a non-blocking interface for streaming usages
    default void add(String conversationId, Message message) {
       this.add(conversationId, List.of(message));
    }

    // 添加会话信息到指定conversationId的会话历史中
    void add(String conversationId, List<Message> messages);

    // 根据conversationId查询历史会话
    List<Message> get(String conversationId, int lastN);

    // 清除指定conversationId的会话历史
    void clear(String conversationId);
}

可以看到,所有的会话记忆都是与conversationId有关联的,也就是会话Id,将来不同会话Id的记忆自然是分开管理的;

目前,在SpringAI中有两个ChatMemory的实现:

  • InMemoryChatMemory:会话历史保存在内存中;

  • CassandraChatMemory:会话保存在Cassandra数据库中(需要引入额外依赖,并且绑定了向量数据库,不够灵活);

目前选择用InMemoryChatMemory来实现。

添加会话记忆功能

CommonConfiguration配置类中添加

@Bean
public ChatMemory chatMemory() {
    return new InMemoryChatMemory();
}
 @Bean
    public ChatClient chatClient(AlibabaOpenAiChatModel model, ChatMemory chatMemory) {
        return ChatClient
                .builder(model)
                .defaultOptions(ChatOptions.builder().model("qwen-omni-turbo").build())
                .defaultSystem("你是一个热心、可爱的智能助手,你的名字叫小团团,请以小团团的身份和语气回答问题。")
                .defaultAdvisors(
                        new SimpleLoggerAdvisor(),    //记录日志
                        new MessageChatMemoryAdvisor(chatMemory)  //添加会话记忆功能
                )
                .build();
    }

现在聊天会话已经有记忆功能了,不过现在的会话记忆还是不完善的,接下来的章节还会继续补充

会话历史

会话记忆:是指让大模型记住每一轮对话的内容,不至于前一句刚问完,下一句就忘了;

会话历史:是指要记录总共有多少不同的对话;

以DeepSeek为例,页面上的会话历史:

ChatMemory中,会记录一个会话中的所有消息,记录方式是以conversationId为key,以List<Message>为value,根据这些历史消息,大模型就能继续回答问题,这就是所谓的会话记忆;

而会话历史,其实就是每一个会话的conversationId,用它去查询List<Message>,注意,在接下来业务中,以chatId来代conversationId

管理会话id

由于会话记忆是以conversationId来管理的,也就是会话id(以后简称为chatId)将来要查询会话历史,其实就是查询历史中有哪些chatId;因此,为了实现查询会话历史记录,必须记录所有的chatId。

定义一个ai.repository包,然后新建一个ChatHistoryRepository管理会话历史接口:

public interface ChatHistoryRepository {
    /**
     * 保存会话记录
     * @param type 业务类型,如:chat、service、pdf
     * @param chatId 会话ID
     */
    void save(String type, String chatId);

    /**
     * 获取会话ID列表
     * @param type 业务类型,如:chat、service、pdf
     * @return 会话ID列表
     */
    List<String> getChatIds(String type);
}

在这个包下继续创建一个实现类InMemoryChatHistoryRepository

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Component
@RequiredArgsConstructor
public class InMemoryChatHistoryRepository implements ChatHistoryRepository {
    private Map<String, List<String>> chatHistory = new HashMap<>();  //存储不同类型的聊天 ID
	
    //业务类型,如:chat、service、pdf	
    //按类型存储聊天 ID,避免重复
    @Override
    public void save(String type, String chatId) {
        List<String> chatIds = chatHistory.computeIfAbsent(type, k -> new ArrayList<>());
        if (chatIds.contains(chatId)) {
            return;
        }
        chatIds.add(chatId);
    }
	//根据类型返回对应的聊天 ID 列表
    @Override
    public List<String> getChatIds(String type) {
        return chatHistory.getOrDefault(type, List.of());
    }
}

接下来,修改ChatController中的chat方法,做到以下3点:

  • 添加一个请求参数:chatId,每次前端请求AI时都需要传递chatId;

  • 每次处理请求时,将chatId存储到ChatRepository;

  • 每次发请求到AI大模型时,都传递自定义的chatId;

保存会话id

接下来,修改ChatController中的chat方法,做到以下3点:

  • 添加一个请求参数:chatId,每次前端请求AI时都需要传递chatId

  • 每次处理请求时,将chatId存储到ChatRepository;

  • 每次发请求到AI大模型时,都传递自定义的chatId;

private final ChatClient chatClient;
private final ChatHistoryRepository chatHistoryRepository;
//流式调用
@RequestMapping(value = "/chat", produces = "text/html;charset=utf-8")
public Flux<String> chat(
    @RequestParam("prompt") String prompt,
    @RequestParam("chatId") String chatId,
    @RequestParam(value = "files", required = false) List<MultipartFile> files) {
    // 1.保存会话id(如果存在会直接返回)
    chatHistoryRepository.save("chat", chatId);
    // 2.请求模型
    if (files == null || files.isEmpty()) {
        // 没有附件,纯文本聊天
        return textChat(prompt, chatId);
    } else {
        // 有附件,多模态聊天
        return multiModalChat(prompt, chatId, files);
    }
}

//纯文本聊天
private Flux<String> textChat(String prompt, String chatId) {
    return chatClient.prompt()  //请求模型
        .user(prompt)      //预设
        //通过AdvisorContext,也就是以key-value形式存入上下文
        .advisors(a -> a.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)) 
        .stream()
        .content();
}

查询会话历史

定义一个新的Controller,专门实现回话历史的查询。包含两个接口:

  • 根据业务类型查询会话历史列表(将来有3个不同业务,需要分别记录历史。可以自己扩展成按userId记录,根据UserId查询)

  • 根据chatId查询指定会话的历史消息;

private final ChatHistoryRepository chatHistoryRepository;
private final ChatMemory chatMemory;

//根据业务类型查询会话历史列表
@GetMapping("/{type}")
public List<String> getChatIds(@PathVariable("type") String type) {
    return chatHistoryRepository.getChatIds(type);
}
//根据chatId查询指定会话的历史消息
@GetMapping("/{type}/{chatId}")
public List<MessageVO> getChatHistory(@PathVariable("type") String type, @PathVariable("chatId") String chatId) {
    List<Message> messages = chatMemory.get(chatId, Integer.MAX_VALUE);
    if(messages == null) {
        return List.of();
    }
    //由于Message并不符合页面的需要,所以需要自己定义一个VO
    return messages.stream().map(MessageVO::new).toList();
}
@NoArgsConstructor
@Data
public class MessageVO {
    private String role;
    private String content;

    public MessageVO(Message message) {
        switch (message.getMessageType()) {
            case USER: 
                role = "user";  
                break;
            case ASSISTANT:
                role = "assistant";
                break;
            default:
                role = "";
                break;
        }
        this.content = message.getText();
    }
}

重启服务,现在AI聊天机器人就具备会话记忆和会话历史功能了!

完善会话记忆

目前,会话记忆是基于内存,重启服务就没了;如果要持久化保存,这里提供了3种办法:

  1. 依然是基于InMemoryChatMemory,但是在项目停机时,或者使用定时任务实现自动持久化;

  2. 自定义基于Redis的ChatMemory

  3. 基于SpringAI官方提供的CassandraChatMemory,同时会自动启用CassandraVectorStore。

定义可序列化的Message

前面的两种方案,都面临一个问题,SpringAI中的Message类未实现Serializable接口,也没提供public的构造方法,因此无法基于任何形式做序列化。所以必须定义一个可序列化的Message类,方便后续持久化。定义一ai.entity.po包,新建一个Msg类:

@NoArgsConstructor
@AllArgsConstructor
@Data
public class Msg {
    MessageType messageType;
    String text;
    Map<String, Object> metadata;
    List<AssistantMessage.ToolCall> toolCalls;
    //将SpringAI的Message转为我们的Msg
    public Msg(Message message) {
        this.messageType = message.getMessageType();
        this.text = message.getText();
        this.metadata = message.getMetadata();
        if(message instanceof AssistantMessage am) {
            this.toolCalls = am.getToolCalls();
        }
    }
    //实现将我们的Msg转为SpringAI的Message
    public Message toMessage() {
        return switch (messageType) {
            case SYSTEM -> new SystemMessage(text);
            case USER -> new UserMessage(text, List.of(), metadata);
            case ASSISTANT -> new AssistantMessage(text, metadata, toolCalls, List.of());
            default -> throw new IllegalArgumentException("Unsupported message type: " + messageType);
        };
    }
}

方案一:定期持久化

接下来,将SpringAI提供的InMemoryChatMemory中的数据持久化到本地磁盘,并且在项目启动时加载;

本方案中,采用Spring的生命周期方法,在项目启动时加载持久化文件,在项目停机时持久化数据

也可以考虑使用定时任务完成持久化,项目启动加载的方案;

修改ai.repository.InMemoryChatHistoryRepository类,添加持久化功能:

//项目启动时
@PostConstruct
private void init() {
    // 1.初始化会话历史记录
    this.chatHistory = new HashMap<>();
    // 2.加载本地会话历史和会话记忆
    FileSystemResource historyResource = new FileSystemResource("chat-history.json");
    FileSystemResource memoryResource = new FileSystemResource("chat-memory.json");
    if (!historyResource.exists()) {
        return;
    }
    try {
        // 会话历史
        Map<String, List<String>> chatIds = this.objectMapper.readValue(historyResource.getInputStream(), new TypeReference<>() {
        });
        if (chatIds != null) {
            this.chatHistory = chatIds;
        }
        // 会话记忆
        Map<String, List<Msg>> memory = this.objectMapper.readValue(memoryResource.getInputStream(), new TypeReference<>() {
        });
        if (memory != null) {
            memory.forEach(this::convertMsgToMessage);  //转成message
        }
    } catch (IOException ex) {
        throw new RuntimeException(ex);
    }
}

private void convertMsgToMessage(String chatId, List<Msg> messages) {
    this.chatMemory.add(chatId, messages.stream().map(Msg::toMessage).toList());
}

@PreDestroy
private void persistent() {
    String history = toJsonString(this.chatHistory);
    String memory = getMemoryJsonString();
    FileSystemResource historyResource = new FileSystemResource("chat-history.json");
    FileSystemResource memoryResource = new FileSystemResource("chat-memory.json");
    try (
        PrintWriter historyWriter = new PrintWriter(historyResource.getOutputStream(), true, StandardCharsets.UTF_8);
        PrintWriter memoryWriter = new PrintWriter(memoryResource.getOutputStream(), true, StandardCharsets.UTF_8)
    ) {
        historyWriter.write(history);
        memoryWriter.write(memory);
    } catch (IOException ex) {
        log.error("IOException occurred while saving vector store file.", ex);
        throw new RuntimeException(ex);
    } catch (SecurityException ex) {
        log.error("SecurityException occurred while saving vector store file.", ex);
        throw new RuntimeException(ex);
    } catch (NullPointerException ex) {
        log.error("NullPointerException occurred while saving vector store file.", ex);
        throw new RuntimeException(ex);
    }
}

private String getMemoryJsonString() {
    Class<InMemoryChatMemory> clazz = InMemoryChatMemory.class;
    try {
        Field field = clazz.getDeclaredField("conversationHistory");
        field.setAccessible(true);
        Map<String, List<Message>> memory = (Map<String, List<Message>>) field.get(chatMemory);
        Map<String, List<Msg>> memoryToSave = new HashMap<>();
        memory.forEach((chatId, messages) -> memoryToSave.put(chatId, messages.stream().map(Msg::new).toList()));
        return toJsonString(memoryToSave);
    } catch (NoSuchFieldException | IllegalAccessException e) {
        throw new RuntimeException(e);
    }
}

private String toJsonString(Object object) {
    ObjectWriter objectWriter = this.objectMapper.writerWithDefaultPrettyPrinter();
    try {
        return objectWriter.writeValueAsString(object);
    } catch (JsonProcessingException e) {
        throw new RuntimeException("Error serializing documentMap to JSON.", e);
    }
}

方案二:自定义ChatMemory

基于Redis来实现自定义ChatMemory;

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

ai.repository包中新建一个RedisChatMemory类:由于使用的是Redis的Set结构,无序的,因此要确保chatId是单调递增的。

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itheima.ai.entity.po.Msg;
import lombok.RequiredArgsConstructor;
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.List;

@RequiredArgsConstructor
@Component
public class RedisChatMemory implements ChatMemory {

    private final StringRedisTemplate redisTemplate;
    private final ObjectMapper objectMapper;
    private final static String PREFIX = "chat:";

    @Override
    public void add(String conversationId, List<Message> messages) {
        if (messages == null || messages.isEmpty()) {
            return;
        }
        List<String> list = messages.stream().map(Msg::new).map(msg -> {
            try {
                return objectMapper.writeValueAsString(msg);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }).toList();
        redisTemplate.opsForList().leftPushAll(PREFIX + conversationId, list);
    }

    @Override
    public List<Message> get(String conversationId, int lastN) {
        List<String> list = redisTemplate.opsForList().range(PREFIX + conversationId, 0, lastN);
        if (list == null || list.isEmpty()) {
            return List.of();
        }
        return list.stream().map(s -> {
            try {
                return objectMapper.readValue(s, Msg.class);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }).map(Msg::toMessage).toList();
    }

    @Override
    public void clear(String conversationId) {
        redisTemplate.delete(PREFIX + conversationId);
    }
}

方案三:Cassandra

  • SpringAI官方提供了CassandraChatMemory,但是是跟CassandraVectorStore绑定的,不太灵活;

  • 首先,需要安装一个Cassandra访问,使用Docker安装:

docker run -d --name cas -p 9042:9042  cassandra

在项目中添加cassandra依赖:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-cassandra-store-spring-boot-starter</artifactId>
</dependency>

配置Cassandra地址:

spring:
  cassandra:
    contact-points: 192.168.150.101:9042
    local-datacenter: datacenter1
  • 基于Cassandra的ChatMemory已经实现了,其它不变。

  • 注意:多种ChatMemory实现方案不能共存,只能选择其一。


网站公告

今日签到

点亮在社区的每一天
去签到