Java程序员学从0学AI(六)

发布于:2025-07-27 ⋅ 阅读:(18) ⋅ 点赞:(0)

一、前言

在上一篇文章中我们学习了如何使用Spring AI的 Structured Output Converter 让大模型格式化输出。今天我们将继续学习SpringAI的Chat Memory(聊天记忆)

二、Chat Memory

大模型和Http很相似都是无状态的,也就是说大模型其实是没办法知道上下文的。例如我告诉大模型我叫“哈迪”,下次再问他他依旧不知道我是谁。这个和Http很相似,无状态。这将极大的限制我们使用大模型;好在Spring AI提供了对话记忆功能(说白了,就是将之前对对话给到大模型,让大模型有更充足的上下文)

三、代码演示

1、不使用Chat Memory

我们先尝试一下不使用Chat Memory,代码如下

/**
 * @Author hardy(叶阳华)
 * @Description
 * @Date 2025/5/16 14:08
 * @Modified By: Copyright(c) cai-inc.com
 */
@RestController
@RequestMapping("/chat")
public class ChatController {

    private final ChatClient client;

    public ChatController() {
        client = ChatClient.builder(
            DeepSeekChatModel.builder().deepSeekApi(DeepSeekApi.builder().apiKey("换成自己的APIKey").build()).build()).build();
    }

    @GetMapping("/chatWithoutMemory")
    public String chatWithoutMemory(String msg) {
        return client.prompt(msg).call().content();
    }

}

第一轮对话:我们告诉大模型我叫hardy

第二轮对话:询问大模型我叫什么名字

可以看到,即使我们告诉了大模型我叫什么名字,但是在下一轮对话中大模型依旧无法知道我是谁。正如上文所说,大模型是无状态的。

2、使用Chat Memory

我们先试用最简单的基于内存的Chat Memory

/**
 * @Author hardy(叶阳华)
 * @Description
 * @Date 2025/5/16 14:08
 * @Modified By: Copyright(c) cai-inc.com
 */
@RestController
@RequestMapping("/chat")
public class ChatController {

    private final ChatClient client;

    public ChatController(ChatMemory chatMemory) {
        client = ChatClient.builder(DeepSeekChatModel.builder()
                .deepSeekApi(DeepSeekApi.builder().apiKey("替换成自己的APIKEY").build()).build())
            .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
            .build();
    }

    @GetMapping("/chatMemory")
    public String chatWithoutMemory(String msg) {
        return client.prompt(msg).call().content();
    }

}

第一轮对话:我们告诉大模型我叫hardy

第二轮对话:询问大模型我叫什么名字

神奇的事情发生了,大模型知道我叫哈迪。

四、知其然也知其所以然

上面的案例我们演示了一下使用最简单的Chat Memory实现了聊天记忆功能。那他是如何实现的呢?我们可以在请求前后做一层拦截,看看发生了什么,这里使用Advisors (之前的文章介绍过,笔记二中记录)。对上述代码稍加修改,我在请求前做了一次打印,接下来我们把刚才的实验重头再来一次

/**
 * @Author hardy(叶阳华)
 * @Description
 * @Date 2025/5/16 15:48
 * @Modified By: Copyright(c) cai-inc.com
 */
@Slf4j
public class SimpleLogAdvisor implements CallAdvisor {

    @Override
    public String getName() {
        return "SimpleLogAdvisor";
    }

    @Override
    public int getOrder() {
        return 0;
    }

    @Override
    public ChatClientResponse adviseCall(final ChatClientRequest chatClientRequest,
        final CallAdvisorChain callAdvisorChain) {
        log.info("chatClientRequest:{}", JSON.toJSONString(chatClientRequest));
        return callAdvisorChain.nextCall(chatClientRequest);
    }
}

第一轮对话:我们告诉大模型我叫哈迪,打印出来的请求参数如下:

{
	"context": {},
	"prompt": {
		"contents": "你好我叫哈迪",
		"instructions": [{
			"media": [],
			"messageType": "USER",
			"metadata": {
				"messageType": "USER"
			},
			"text": "你好我叫哈迪"
		}],
		"options": {
			"model": "deepseek-chat",
			"temperature": 0.7
		},
		"systemMessage": {
			"messageType": "SYSTEM",
			"metadata": {
				"messageType": "SYSTEM"
			},
			"text": ""
		},
		"userMessage": {
			"media": [],
			"messageType": "USER",
			"metadata": {
				"messageType": "USER"
			},
			"text": "你好我叫哈迪"
		},
		"userMessages": [{
			"media": [],
			"messageType": "USER",
			"metadata": {
				"messageType": "USER"
			},
			"text": "你好我叫哈迪"
		}]
	}
}

第二轮对话:我们询问大模型我叫什么名字,打印出来的请求参数如下:

{
	"context": {},
	"prompt": {
		"contents": "你好我叫哈迪你好哈迪!很高兴认识你~ 😊 我是DeepSeek Chat,可以叫我小深或者DeepSeek。有什么我可以帮你的吗?无论是聊天、解答问题,还是需要一些建议,我都会尽力帮你哦!✨我叫什么名字",
		"instructions": [{
			"media": [],
			"messageType": "USER",
			"metadata": {
				"messageType": "USER"
			},
			"text": "你好我叫哈迪"
		}, {
			"media": [],
			"messageType": "ASSISTANT",
			"metadata": {
				"finishReason": "STOP",
				"index": 0,
				"id": "363fafe6-81b0-434c-b922-4c616f174fb7",
				"role": "ASSISTANT",
				"messageType": "ASSISTANT"
			},
			"text": "你好哈迪!很高兴认识你~ 😊 我是DeepSeek Chat,可以叫我小深或者DeepSeek。有什么我可以帮你的吗?无论是聊天、解答问题,还是需要一些建议,我都会尽力帮你哦!✨",
			"toolCalls": []
		}, {
			"media": [],
			"messageType": "USER",
			"metadata": {
				"messageType": "USER"
			},
			"text": "我叫什么名字"
		}],
		"options": {
			"model": "deepseek-chat",
			"temperature": 0.7
		},
		"systemMessage": {
			"messageType": "SYSTEM",
			"metadata": {
				"messageType": "SYSTEM"
			},
			"text": ""
		},
		"userMessage": {
			"media": [],
			"messageType": "USER",
			"metadata": {
				"messageType": "USER"
			},
			"text": "我叫什么名字"
		},
		"userMessages": [{
			"media": [],
			"messageType": "USER",
			"metadata": {
				"messageType": "USER"
			},
			"text": "你好我叫哈迪"
		}, {
			"media": [],
			"messageType": "USER",
			"metadata": {
				"messageType": "USER"
			},
			"text": "我叫什么名字"
		}]
	}
}

可以看到SpringAI 是将本轮对话的上下文一股脑的发送到了大模型,所以“聊天记忆”功能靠的是让大模型知道所有的聊天上下文。

五、ChatMemory代码分析

1、接口介绍

ChatMemory是一个接口,接口定义的方法也非常简单分别是

  • 添加聊天记录
  • 获取聊天记录
  • 清空聊天记录

2、默认实现

SpringAI为我们提供了默认的ChatMemory实现MessageWindowChatMemory,从名字中不难看出这是一个有窗口的聊天记录(所谓的窗口,就是可以记录多少条数据),同时由于没有定义任何外部存储介质,所以聊天记录是存在内存中。Talk is Cheap ,Show Me the Code。我们看一下具体的实现代码(部分)

public final class MessageWindowChatMemory implements ChatMemory {
    //默认最大记录条数
    private static final int DEFAULT_MAX_MESSAGES = 20;
    //存储介质
    private final ChatMemoryRepository chatMemoryRepository;
    //最大记录条数
    private final int maxMessages;

    //省略构造函数
    @Override
    public void add(String conversationId, List<Message> messages) {
        List<Message> memoryMessages = this.chatMemoryRepository.findByConversationId(conversationId);
        List<Message> processedMessages = process(memoryMessages, messages);
        this.chatMemoryRepository.saveAll(conversationId, processedMessages);
    }

    @Override
    public List<Message> get(String conversationId) {
        return this.chatMemoryRepository.findByConversationId(conversationId);
    }

    @Override
    public void clear(String conversationId) {
        this.chatMemoryRepository.deleteByConversationId(conversationId);
    }
    ...忽略...

}

可以看MessageWindowChatMemory 大多数的逻辑是基于ChatMemoryRepository实现的,我们继续查看ChatMemoryRepository,ChatMemoryRepository也是一个接口,定义了一系列的方法

public interface ChatMemoryRepository {

	List<String> findConversationIds();

	List<Message> findByConversationId(String conversationId);

	/**
	 * Replaces all the existing messages for the given conversation ID with the provided
	 * messages.
	 */
	void saveAll(String conversationId, List<Message> messages);

	void deleteByConversationId(String conversationId);

}
在<font style="color:#080808;background-color:#ffffff;">MessageWindowChatMemory中使用的ChatMemoryRepository是InMemoryChatMemoryRepository,从名字中就可以看出这是一个基于内存的聊天记录存储介质。核心是第一个ConcurrentHashMap</font>

3、实现自定义ChatMemory

众所周知内存是宝贵的,我们不可能把大量的数据存放到内存中,那么是否可以存储到外部的介质中呢,比如Mysql、ES等等?当然可以,既然ChatMemory是一个接口,我们只要自定义实现即可。咱么这里还是使用MessageWindowChatMemory只不过我们替换其中的Repository,将数据持久化到Mysql中

1、首先我们创建一张表

CREATE TABLE `chat_memory` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
  `conversation_id` varchar(100)  DEFAULT NULL COMMENT '会话ID',
  `message` text COMMENT '消息',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='聊天记录表';

2、引入Mysql+Mybatis Plus依赖

<dependency>
  <groupId>com.mysql</groupId>
  <artifactId>mysql-connector-j</artifactId>
</dependency>

<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus</artifactId>
  <version>3.5.12</version>
</dependency>

3、编写实体类

@Data
@TableName("chat_memory")
public class ChatMemoryEntity {

    @TableId(type = IdType.AUTO)
    private Long id;

    private String conversationId;

    private String message;
}

4、编写Mapper接口

/**
 * @Author hardy(叶阳华)
 * @Description
 * @Date 2025/7/26 14:04
 * @Modified By: Copyright(c) cai-inc.com
 */
public interface ChatMemoryMapper extends BaseMapper<ChatMemoryEntity> {

}

5、编写自定义

/**
 * @Author hardy(叶阳华)
 * @Description
 * @Date 2025/7/26 13:38
 * @Modified By: Copyright(c) cai-inc.com
 */
@Component
public class MySqlChatMemoryRepository implements ChatMemoryRepository {

    @Resource
    private ChatMemoryMapper chatMemoryMapper;

    @Override
    public List<String> findConversationIds() {
        final List<ChatMemoryEntity> chatMemoryEntities = chatMemoryMapper.selectList(
            Wrappers.lambdaQuery(ChatMemoryEntity.class));
        return chatMemoryEntities.stream().map(ChatMemoryEntity::getConversationId).distinct().collect(Collectors.toList());
    }

    @Override
    public List<Message> findByConversationId(final String conversationId) {
        final List<ChatMemoryEntity> chatMemoryList = chatMemoryMapper.selectList(
            Wrappers.lambdaQuery(ChatMemoryEntity.class)
                .eq(ChatMemoryEntity::getConversationId, conversationId));
        return chatMemoryList.stream().map(memory-> JSON.parseObject(memory.getMessage(),Message.class)).collect(Collectors.toList());
    }

    /**
     * Replaces all the existing messages for the given conversation ID with the provided messages.
     */
    @Override
    public void saveAll(final String conversationId, final List<Message> messages) {
       List<ChatMemoryEntity> memoryEntities = new ArrayList<>();
        messages.forEach(message->{
            ChatMemoryEntity entity = new ChatMemoryEntity();
            entity.setMessage(JSON.toJSONString(message));
            entity.setConversationId(conversationId);
            memoryEntities.add(entity);
        });
        chatMemoryMapper.insert(memoryEntities);
    }

    @Override
    public void deleteByConversationId(final String conversationId) {
        chatMemoryMapper.delete(Wrappers.lambdaQuery(ChatMemoryEntity.class)
            .eq(ChatMemoryEntity::getConversationId,conversationId));
    }
}

6、编写接口

/**
 * @Author hardy(叶阳华)
 * @Description
 * @Date 2025/5/16 14:08
 * @Modified By: Copyright(c) cai-inc.com
 */
@RestController
@RequestMapping("/chat")
public class ChatController {

    private final ChatClient client;

    public ChatController(MySqlChatMemoryRepository mySqlChatMemoryRepository) {
        MessageWindowChatMemory memory = MessageWindowChatMemory.builder()
            .chatMemoryRepository(mySqlChatMemoryRepository).maxMessages(10).build();
        client = ChatClient.builder(DeepSeekChatModel.builder()
                .deepSeekApi(DeepSeekApi.builder().apiKey("APIKEY").build()).build())
            .defaultAdvisors(MessageChatMemoryAdvisor.builder(memory).build(), new SimpleLogAdvisor()).build();
    }

    @GetMapping("/chatMemory")
    public String chatWithoutMemory(String msg) {
        return client.prompt(msg).call().content();
    }

}

7、调用接口

8、查看数据库

可以看到数据库里存入了我们的数据,但是这么做是有问题的!!!!再次调用会发现反序列化失败

4、正确的打开方式

刚才自定义方式是有问题,那么正确的打开方式是什么呢?使用SpringAI为我们提供的JdbcChatMemoryRepository接口。

1、引入如下依赖
<dependency>
  <groupId>org.springframework.ai</groupId>
  <artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId>
</dependency>
2、修改配置
server:
  port: 8080
spring:
  application:
    name: spring-ai-demo
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://${MYSQL_HOST:127.0.0.1}:${MYSQL_PORT:3306}/learn?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
    password: zaige806
    username: root
  ai:
    chat:
      memory:
        repository:
          jdbc:
            initialize-schema: always
            platform: mysql
            schema: classpath:schema/schema-@@platform@@.sql

3、修改接口
/**
 * @Author hardy(叶阳华)
 * @Description
 * @Date 2025/5/16 14:08
 * @Modified By: Copyright(c) cai-inc.com
 */
@RestController
@RequestMapping("/chat")
public class ChatController {

    private final ChatClient client;


    public ChatController(JdbcTemplate jdbcTemplate) {
        // 创建聊天记忆存储库,使用JDBC实现并配置MySQL方言
        ChatMemoryRepository chatMemoryRepository = JdbcChatMemoryRepository.builder()
        .jdbcTemplate(jdbcTemplate)
        .dialect(new MysqlChatMemoryRepositoryDialect())
        .build();

        // 创建消息窗口聊天记忆,限制最多保存10条消息
        ChatMemory memory = MessageWindowChatMemory.builder()
        .chatMemoryRepository(chatMemoryRepository)
        .maxMessages(10)
        .build();
        // 构建聊天客户端,使用DeepSeek大模型并配置API密钥
        // 同时添加消息聊天记忆顾问以启用对话历史功能
        client = ChatClient.builder(DeepSeekChatModel.builder()
                                    .deepSeekApi(DeepSeekApi.builder().apiKey("sk-2f18dc5852134ed19d614f9ba09febe7").build()).build())
        .defaultAdvisors(MessageChatMemoryAdvisor.builder(memory).build(),new SimpleLogAdvisor()).build();
    }

    @GetMapping("/chatMemory")
    public String chatWithoutMemory(String msg,String conversationId) {
        //生成自定义的会话ID
        return client.prompt(msg).advisors(advisor->advisor.param(ChatMemory.CONVERSATION_ID,conversationId)).call().content();
    }
}

4、添加schema:位置可以随意,但是要和配置文件里的保持一致

CREATE TABLE IF NOT EXISTS SPRING_AI_CHAT_MEMORY
(
  `conversation_id` VARCHAR(36)                                  NOT NULL,
  `content`         TEXT                                         NOT NULL,
  `type`            ENUM ('USER', 'ASSISTANT', 'SYSTEM', 'TOOL') NOT NULL,
  `timestamp`       TIMESTAMP                                    NOT NULL,

  INDEX `SPRING_AI_CHAT_MEMORY_CONVERSATION_ID_TIMESTAMP_IDX` (`conversation_id`, `timestamp`)
);

5、启动项目

可以看到Spring为我们创建了一张表

6、调用接口

小插曲:官方Bug!!!

什么居然报错了?部分错误信息如下:

 [spring-ai-demo] [nio-8080-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar [SELECT content, type FROM SPRING_AI_CHAT_MEMORY WHERE conversation_id = ? ORDER BY `timestamp` DESC LIMIT ?]] with root cause

java.sql.SQLException: No value specified for parameter 2
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:130) ~[mysql-connector-j-8.0.33.jar:8.0.33]
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-j-8.0.33.jar:8.0.33]
	at com.mysql.cj.jdbc.ClientPreparedStatement.executeQuery(ClientPreparedStatement.java:989) ~[mysql-connector-j-8.0.33.jar:8.0.33]
	at com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52) ~[HikariCP-5.1.0.jar:na]

这个是官方bug,简单的说就是官方默认的MysqlChatMemoryRepositoryDialect指定了两个参数,但是实际只传入了一个参数

那该如何解决呢?当然是自己写一个了Dailect

package com.cmxy.springbootaidemo.memory;

import org.springframework.ai.chat.memory.repository.jdbc.JdbcChatMemoryRepositoryDialect;

/**
 * @Author hardy(叶阳华)
 * @Description
 * @Date 2025/7/26 23:13
 * @Modified By: Copyright(c) cai-inc.com
 */
public class CustomChatMemoryRepositoryDialect implements JdbcChatMemoryRepositoryDialect {

    private static final int DEFAULT_MAX_MESSAGES = 50;

    public String getSelectMessagesSql() {
        return "SELECT content, type FROM SPRING_AI_CHAT_MEMORY WHERE conversation_id = ? ORDER BY `timestamp` DESC "
            + "LIMIT " + DEFAULT_MAX_MESSAGES;
    }

    public String getInsertMessageSql() {
        return "INSERT INTO SPRING_AI_CHAT_MEMORY (conversation_id, content, type, `timestamp`) VALUES (?, ?, ?, ?)";
    }

    public String getSelectConversationIdsSql() {
        return "SELECT DISTINCT conversation_id FROM SPRING_AI_CHAT_MEMORY";
    }

    public String getDeleteMessagesSql() {
        return "DELETE FROM SPRING_AI_CHAT_MEMORY WHERE conversation_id = ?";
    }

}

5、再次修改接口
package com.cmxy.springbootaidemo.chat;

import com.cmxy.springbootaidemo.advisor.SimpleLogAdvisor;
import com.cmxy.springbootaidemo.memory.CustomChatMemoryRepositoryDialect;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.ChatMemoryRepository;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.memory.repository.jdbc.JdbcChatMemoryRepository;
import org.springframework.ai.chat.memory.repository.jdbc.JdbcChatMemoryRepositoryDialect;
import org.springframework.ai.chat.memory.repository.jdbc.MysqlChatMemoryRepositoryDialect;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.ai.deepseek.api.DeepSeekApi;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author hardy(叶阳华)
 * @Description
 * @Date 2025/5/16 14:08
 * @Modified By: Copyright(c) cai-inc.com
 */
@RestController
@RequestMapping("/chat")
public class ChatController {

    private final ChatClient client;


    public ChatController(JdbcTemplate jdbcTemplate) {
        //使用自定义方言
        final JdbcChatMemoryRepositoryDialect dialect = new CustomChatMemoryRepositoryDialect();
        //配置JdbcChatMemoryRepository
        final JdbcChatMemoryRepository jdbcChatMemoryRepository = JdbcChatMemoryRepository.builder().jdbcTemplate(jdbcTemplate)
            .dialect(dialect).build();
        // 创建消息窗口聊天记忆,限制最多保存10条消息 (其实这里的10条配置已经没有意义了,因为在dialect默认了50条)
        ChatMemory memory = MessageWindowChatMemory.builder().chatMemoryRepository(jdbcChatMemoryRepository)
            .maxMessages(10)
            .build();
        // 构建聊天客户端,使用DeepSeek大模型并配置API密钥
        final DeepSeekApi deepSeekApi = DeepSeekApi.builder().apiKey("替换成自己的APIKEY").build();
        // 同时添加消息聊天记忆顾问以启用对话历史功能
        client = ChatClient.builder(DeepSeekChatModel.builder().deepSeekApi(deepSeekApi).build())
            .defaultAdvisors(MessageChatMemoryAdvisor.builder(memory).build(), new SimpleLogAdvisor()).build();
    }

    @GetMapping("/chatMemory")
    public String chatWithoutMemory(String msg, String conversationId) {
        return client.prompt(msg).advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, conversationId))
            .call().content();
    }
}

6、测试

1、对话ID 001:告诉大模型我叫哈迪

数据库:

2、对话ID 001:问大模型我叫什么名字,可以看到大模型“记住了”

数据库:

3、换一个对话ID 002,预期:大模型不知道我是谁

可以看到大模型并不知道我是谁。

六、总结

本文围绕 Spring AI 的 Chat Memory(聊天记忆)功能展开,从实际需求出发,详细介绍了其作用、实现方式及扩展方案,具体可归纳为以下几点:

  1. Chat Memory 的核心作用
    大模型本身是无状态的,类似 HTTP 协议,无法直接记住上下文。Chat Memory 通过存储历史对话记录,在每次请求时将上下文一并发送给大模型,从而实现 “聊天记忆” 效果,解决了大模型无法关联历史对话的问题。
  2. 使用方式与效果验证
    • 不使用 Chat Memory 时,大模型无法记住用户的历史信息(如用户姓名),两次独立对话之间无关联。
    • 集成基于内存的 Chat Memory 后,通过MessageChatMemoryAdvisor`拦截并携带历史对话,大模型能准确回应依赖上下文的问题(如 “记住” 用户姓名)。
  3. 实现原理剖析
    Spring AI 的 Chat Memory 核心逻辑是通过ChatMemory接口及其默认实现MessageWindowChatMemory完成的:
    接口定义了添加、获取、清空对话记录的基础方法。默认实现MessageWindowChatMemor通过内存存储InMemoryChatMemoryRepository管理对话,支持设置最大记录条数(窗口大小),避免内存溢出。
    本质是通过拦截器(Advisors)在请求时拼接历史对话,让大模型获得完整上下文。
  4. 自定义与持久化方案
    内存存储不适用于生产环境,需将对话记录持久化到外部介质(如 MySQL):
    • 直接自定义ChatMemoryRepository可能存在反序列化问题,推荐使用 Spring AI 提供的JdbcChatMemoryRepository
    • 需引入相关依赖、配置数据库连接,并处理官方方言(Dialect)的潜在 Bug(可通过自定义 Dialect 解决),确保对话记录正确存储和读取。
  5. 关键结论
    Chat Memory 是构建连续对话能力的核心组件,其本质是 “上下文拼接” 而非大模型自身的记忆能力。在实际开发中,需根据场景选择合适的存储方案(内存用于测试,数据库用于生产),并注意处理序列化、多对话 ID 隔离等问题,以实现稳定可靠的对话记忆功能。希望对你有所帮助

未完待续


网站公告

今日签到

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