欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 建筑 > Spring AI 实战:第七章、Spring AI Advisor机制之记忆大师

Spring AI 实战:第七章、Spring AI Advisor机制之记忆大师

2025/5/9 11:23:53 来源:https://blog.csdn.net/liaokailin/article/details/147704343  浏览:    关键词:Spring AI 实战:第七章、Spring AI Advisor机制之记忆大师

引言:当AI的记性比金鱼还差

  • 你:我叫张三,很高兴认识你
  • AI:很高兴认识你,张三! 如果你有任何问题或者需要帮助,请随时告诉我
  • 你:我叫什么
  • AI:抱歉,我无法知道你的名字。不过你可以告诉我一些关于你的信息,如果你愿意分享的话!

AI的内心OS:爱谁谁,我反正不知道

如上图所示,默认的AI连一条只有7秒记忆的金鱼都比不上,每次对话都像是“初次见面”。本篇通过 Spring AI 让大模型具备记忆功能,让AI变成《盗梦空间》里的“记忆宫殿管理员”。

一、记忆分类

在大模型应用场景中通常把记忆分为短期记忆和长期记忆

  • 短期记忆能记住当前对话中你提过的信息,但只限于这次聊天(关闭页面或刷新后消失);完全依赖你发给它的文字上下文(就像两个人聊天时,对方只能记住你刚才说过的话)

伪代码:模型输入自动包含历史对话

String prompt = “用户:我叫张三\n AI:你好 张三 \n 用户:我叫什么?”

output = chatClient.prompt(prompt).call().content() # 输出:“你叫张三。”

  • 模型本身无法真正长期记住你(所有对话默认独立),但可以通过技术手段模拟长期记忆,需要外部工具辅助(比如数据库、用户账号系统)

user_data =toString ( db.query(“SELECT * FROM users WHERE id=‘xxx’”) )

prompt = “用户信息:{user_data}\n 当前问题:推荐一首我喜欢的音乐”

output = chatClient.prompt(prompt).call().content() # 个性化推荐

差异对比:短期记忆是模型的“临时便签”,长期记忆是它的“外接硬盘”

对比维度短期记忆长期记忆
存储位置当前对话的上下文文本(临时输入)外部数据库/知识库(需开发实现)
时效性仅当前对话有效(关闭即消失)永久保留(除非手动删除)
依赖技术模型原生支持(上下文窗口)额外开发(如数据库、RAG、用户系统)
容量限制受模型上下文窗口限制(如128K tokens)理论上无限(取决于存储空间)
修改方式实时调整输入文本即可需更新外部数据源
隐私性默认不存储(相对安全)需主动管理用户数据(合规风险)
典型场景多轮对话、临时任务(如调试代码)个性化服务(如医疗记录、订单历史)
用户感知示例“AI记得我刚才让改的代码风格”“AI知道我是VIP客户,自动优先处理”
实现成本零成本(上下文传递)需开发资源(存储、API、安全措施)
默认支持所有大模型均支持需自行开发或使用企业级工具

二、短期记忆

2.1 Advisor

Advisor是一种用于在Spring应用中拦截、修改和增强与大模型交互的灵活而强大的方式。

2.1.1 SimpleLoggerAdvisor

可以通过SimpleLoggerAdvisor在大模型执行前后打印日志

添加logback.xml文件,把日志输出级别调整为debug

<!--
~ Copyright 2023-2024 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.
--><configuration><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n</pattern></encoder></appender><root level="debug"><appender-ref ref="STDOUT"/></root><logger name="org.springframework.ai.chat.client.advisor" level="DEBUG" additivity="true"><appender-ref ref="STDOUT"/></logger></configuration>

设置Advisor:

@GetMapping("/log")
public String log(@RequestParam(value = "input", defaultValue = "讲一个笑话") String input) {Prompt prompt = new Prompt(input);return chatClient.prompt(prompt).advisors(new SimpleLoggerAdvisor()).call().content();
}

输出:发起请求日志格式 o.s.a.c.c.a.SimpleLoggerAdvisor -- request,响应输出日志格式o.s.a.c.c.a.SimpleLoggerAdvisor -- response,在响应日志中可以看到token消耗等各种额外信息

22:12:13.367 [http-nio-8080-exec-3] DEBUG o.s.a.c.c.a.SimpleLoggerAdvisor – request: AdvisedRequest[chatModel=OpenAiChatModel [defaultOptions=OpenAiChatOptions: {“streamUsage”:false,“model”:“qwen-plus”,“temperature”:0.7}], userText=讲一个笑话, systemText=null, chatOptions=OpenAiChatOptions: {“streamUsage”:false,“model”:“qwen-plus”,“temperature”:0.7}, media=[], functionNames=[], functionCallbacks=[], messages=[], userParams={}, systemParams={}, advisors=[org.springframework.ai.chat.client.DefaultChatClient$DefaultChatClientRequestSpec 1 @ 76 a 2010 f , o r g . s p r i n g f r a m e w o r k . a i . c h a t . c l i e n t . D e f a u l t C h a t C l i e n t 1@76a2010f, org.springframework.ai.chat.client.DefaultChatClient 1@76a2010f,org.springframework.ai.chat.client.DefaultChatClientDefaultChatClientRequestSpec 2 @ 62 e d 924 c , o r g . s p r i n g f r a m e w o r k . a i . c h a t . c l i e n t . D e f a u l t C h a t C l i e n t 2@62ed924c, org.springframework.ai.chat.client.DefaultChatClient 2@62ed924c,org.springframework.ai.chat.client.DefaultChatClientDefaultChatClientRequestSpec 1 @ 64 c 6 c e 4 a , o r g . s p r i n g f r a m e w o r k . a i . c h a t . c l i e n t . D e f a u l t C h a t C l i e n t 1@64c6ce4a, org.springframework.ai.chat.client.DefaultChatClient 1@64c6ce4a,org.springframework.ai.chat.client.DefaultChatClientDefaultChatClientRequestSpec$2@60f9560a, SimpleLoggerAdvisor], advisorParams={}, adviseContext={}, toolContext={}]

22:13:15.899 [http-nio-8080-exec-4] DEBUG o.s.a.c.c.a.SimpleLoggerAdvisor – response: {

“result” : {

"metadata" : {"finishReason" : "STOP","contentFilters" : [ ],"empty" : true},"output" : {"messageType" : "ASSISTANT","metadata" : {"refusal" : "","finishReason" : "STOP","index" : 0,"id" : "chatcmpl-45f6e0df-a0ec-930f-9ed1-f79aa134a443","role" : "ASSISTANT","messageType" : "ASSISTANT"},"toolCalls" : [ ],"media" : [ ],"text" : "好的!来一个轻松的笑话:\n\n有一天,小明去商店买饮料,他问老板:“老板,你们这里可乐摇一摇会爆炸吗?”\n\n老板笑着说:“不会不会,都是经过严格质量检查的。”\n\n小明想了想,又问:“那牛奶摇一摇会爆炸吗?”\n\n老板愣了一下,说:“牛奶摇一摇……可能会过期!” 😄"}

},

“metadata” : {

"id" : "chatcmpl-45f6e0df-a0ec-930f-9ed1-f79aa134a443","model" : "qwen-plus","rateLimit" : {"requestsLimit" : null,"requestsRemaining" : null,"requestsReset" : null,"tokensLimit" : null,"tokensRemaining" : null,"tokensReset" : null},"usage" : {"promptTokens" : 11,"completionTokens" : 82,"totalTokens" : 93,"generationTokens" : 82,"nativeUsage" : {"completion_tokens" : 82,"prompt_tokens" : 11,"total_tokens" : 93,"prompt_tokens_details" : {"cached_tokens" : 0}}},"promptMetadata" : [ ],"empty" : false

},

“results” : [ {

"metadata" : {"finishReason" : "STOP","contentFilters" : [ ],"empty" : true},"output" : {"messageType" : "ASSISTANT","metadata" : {"refusal" : "","finishReason" : "STOP","index" : 0,"id" : "chatcmpl-45f6e0df-a0ec-930f-9ed1-f79aa134a443","role" : "ASSISTANT","messageType" : "ASSISTANT"},"toolCalls" : [ ],"media" : [ ],"text" : "好的!来一个轻松的笑话:\n\n有一天,小明去商店买饮料,他问老板:“老板,你们这里可乐摇一摇会爆炸吗?”\n\n老板笑着说:“不会不会,都是经过严格质量检查的。”\n\n小明想了想,又问:“那牛奶摇一摇会爆炸吗?”\n\n老板愣了一下,说:“牛奶摇一摇……可能会过期!” 😄"}

} ]

}

2.1.2 源码分析

查看SimpleLoggerAdvisor的关键代码实现:

  • aroundCall在调用chain.nextAroundCall(advisedRequest)之前触发before()执行完成触发observeAfter做日志输出,nextAroundCall会执行大模型的调用(这个在上一章以解释过)
  • aroundStream是支持流式响应的输出,流式请求需要在整个请求响应完成后再执行日志打印,因此通过MessageAggregator消息聚合器来采集返回结果,当有类似场景时可直接借用这段逻辑,避免重复造轮子
private AdvisedRequest before(AdvisedRequest request) {logger.debug("request: {}", this.requestToString.apply(request));return request;}private void observeAfter(AdvisedResponse advisedResponse) {logger.debug("response: {}", this.responseToString.apply(advisedResponse.response()));}@Overridepublic AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {advisedRequest = before(advisedRequest);AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest);observeAfter(advisedResponse);return advisedResponse;}@Overridepublic Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {advisedRequest = before(advisedRequest);Flux<AdvisedResponse> advisedResponses = chain.nextAroundStream(advisedRequest);return new MessageAggregator().aggregateAdvisedResponse(advisedResponses, this::observeAfter);}

2.1.3 Advisor实体关系

Spring AI内置一个MessageChatMemoryAdvisor来提供聊天记忆功能(Advisor机制在上一篇中已解读过,不熟悉可以再读一读)

2.2 MessageChatMemoryAdvisor

在Spring AI中内置很多Advisor,其中MessageChatMemoryAdvisor可以用来提供聊天记忆功能

  • MessageChatMemoryAdvisor接收一个ChatMemory对象,传递InMemoryChatMemory是一个内存记忆的ChatMemory类型,通过Map<String, List<Message>> conversationHistory = new ConcurrentHashMap<>()实现
@Slf4j
@RestController
public class MemoryController {private ChatClient chatClient;public MemoryController(ChatClient.Builder builder) {this.chatClient = builder.build();}private InMemoryChatMemory inMemoryChatMemory = new InMemoryChatMemory();@GetMapping("/memory/chat")public String chat(@RequestParam(value = "input", defaultValue = "讲一个笑话") String input) {Prompt prompt = new Prompt(input);return chatClient.prompt(prompt).advisors(new MessageChatMemoryAdvisor(inMemoryChatMemory)).call().content();}}

功能测试:先告诉它我是一名java程序员然后让它推荐一本适合我的书籍,在两次对话过程中,已经成功记住第一次预设的身份,应用重启后失效

GET http://127.0.0.1:8080/memory/chat?input=我是一名java程序员

你好,Java程序员 👨‍💻 很高兴认识同行~

作为开发者,我可以帮你:

🔸 调试代码:遇到报错或意外行为?贴片段我来分析

🔸 技术建议:从Spring到JVM优化,或最新框架选型(比如Quarkus vs Micronaut)

🔸 面试备战:算法题解、设计模式场景题、并发难题等

🔸 工具链:Maven/Gradle技巧、Docker集成、性能调优工具(Arthas?)

需要哪方面的支持?或者最近在做什么有趣的项目吗? 😄

(小提示:如果想分享代码,可以用代码块格式,我会更清晰阅读哦~

GET http://127.0.0.1:8080/memory/chat?input=推荐一本适合我的书籍

根据你的Java程序员身份,我会针对不同需求推荐几本经典书籍,并标注适用阶段和方向,供你选择:


一、核心进阶(必读)

  1. 《Effective Java》(Joshua Bloch)

    • 👍 适合:中高级开发者

    • 📌 内容:Java最佳实践(泛型、并发、设计模式等),第三版包含Java 8-9新特性

    • 🌟 豆瓣评分:9.5

  2. 《Java并发编程实战》(Brian Goetz)

    • 👍 适合:想深入JUC包、线程安全的开发者

    • 📌 从理论到实战,涵盖锁、原子类、线程池等

    • ⚠️ 注意:需要一定基础


二、架构与设计

  1. 《深入理解Java虚拟机》(周志明)

    • 👍 适合:对JVM底层、GC调优感兴趣

    • 📌 国内原创经典,涵盖类加载、内存模型等

  2. 《Spring实战》(Craig Walls)

    • 👍 适合:Spring生态开发者(Boot/Cloud)

    • 📌 案例驱动,新版涵盖响应式编程


三、新趋势与扩展

  1. 《Java函数式编程》(Venkat Subramaniam)

    • 👍 适合:学习Lambda/Stream API、函数式思维

    • 📌 幽默易懂,代码示例丰富

  2. 《云原生Java》(Josh Long)

    • 👍 适合:云原生/微服务方向

    • 📌 Spring Cloud + Kubernetes实战


四、补充经典

  • 《Head First设计模式》(图文并茂入门设计模式)

  • 《重构:改善既有代码的设计》(Martin Fowler,必读)


你的选择建议:

如果想优先读一本,推荐从 《Effective Java》 开始(常看常新),如果项目涉及并发则选Goetz的书。需要更具体的推荐可以告诉我你的当前目标(如面试/性能优化/新项目技术选型等) 😊

(电子版资源可能需要自行搜索哦~)

除了在ChatClient发起调用时设置MessageChatMemoryAdvisor,还可以在ChatClient构建时设置默认的advisors

public class MemoryController2 {private ChatClient chatClient;private InMemoryChatMemory inMemoryChatMemory = new InMemoryChatMemory();public MemoryController2(ChatClient.Builder builder) {this.chatClient = builder.defaultAdvisors(new MessageChatMemoryAdvisor(inMemoryChatMemory)).build();}@GetMapping("/memory/chat2")public String chat(@RequestParam(value = "input", defaultValue = "讲一个笑话") String input) {Prompt prompt = new Prompt(input);return chatClient.prompt(prompt).call().content();}}

三、记忆隔离:防止张冠李戴

问:如果多个用户同时聊天,如何防止AI把张三的咖啡订单发给李四?

答:可以通过会话ID来进行记忆隔离

  @GetMapping("/memory/user/chat")public String chatByUser(@RequestParam(value = "input", defaultValue = "讲一个笑话") String input, @RequestParam(value = "userId", defaultValue = "123456") String userId) {Prompt prompt = new Prompt(input);return chatClient.prompt(prompt).advisors(new MessageChatMemoryAdvisor(inMemoryChatMemory, userId, AbstractChatMemoryAdvisor.DEFAULT_CHAT_MEMORY_RESPONSE_SIZE)).call().content();}

使用<font style="color:rgba(0, 0, 0, 0.9);">MessageChatMemoryAdvisor的另外一个构造方法,<font style="color:rgba(0, 0, 0, 0.9);">defaultConversationId表示会话ID,<font style="color:rgba(0, 0, 0, 0.9);">chatHistoryWindowSize表示获取最新的N条记忆

    public MessageChatMemoryAdvisor(ChatMemory chatMemory, String defaultConversationId, int chatHistoryWindowSize) {this(chatMemory, defaultConversationId, chatHistoryWindowSize, Advisor.DEFAULT_CHAT_MEMORY_PRECEDENCE_ORDER);}

按照前后顺序发出如下三个请求来测试

GET http://localhost:8080/memory/user/chat?input=你好,我是张三&userId=zhangsan

你好,张三!😄 我是AI助手,你可以叫我小深,只要你愿意随时分享,我都会认真倾听、尽力帮忙!

GET http://localhost:8080/memory/user/chat?input=你知道我是谁吗?&userId=lisi

目前,我无法直接识别你的身份,因为我们之前的对话记录不会保留,而且我没有访问用户个人信息的权限。不过,如果你愿意,可以告诉我一些关于你的信息(比如兴趣、职业等),这样我可以更好地为你提供帮助! 😊

GET http://localhost:8080/memory/user/chat?input=你知道我是谁吗?&userId=zhangsan

你好,张三!😊 作为AI助手,我无法直接知道现实中的“你是谁”哦~每次对话都是新的开始,除非你主动告诉我更多信息(比如兴趣、需求等),我会根据聊天内容尽量提供贴合你的帮助!

有什么想聊的或需要建议的吗?比如:

  • 日常:天气、心情、趣事分享

  • 实用:学习/工作技巧、生活小窍门

  • 深度话题:科技、哲学、冷知识…

等你随时开口~ 🌟

合理设计会话ID的生成规则、失效策略在生产环节中起到关键作用

public class SessionId {/* ID(需要有合适的规则)*/private String id;/* 失效时间,控制会话的有效性*/private Date expireTime;/* 创建时间*/private Date createTime;
}

四、永恒的记忆

InMemoryChatMemory是内存记忆,应用重启后会发现记忆直接丢失, 如果想要把记忆永久保存下来,可以考虑使用MysqlRedis等存储,那我们用Redis来快速实现一个。

2.1 Redis的安装

采用Spring集成Docker的方式引入RedisDocker本地的安装在前面章节中已经完成,直接通过https://start.spring.io/ 引入依赖;

    <dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-openai-spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-redis-store-spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-docker-compose</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-spring-boot-docker-compose</artifactId><scope>runtime</scope><optional>true</optional></dependency>

compose.yaml引入Redis镜像信息,配置访问端口为6379

添加一段测试Redis访问的代码

package com.lkl.test.spring.docker;import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.UUID;@Slf4j
@RestController
public class RedisController {@Resourceprivate RedisTemplate<String, String> redisTemplate;@GetMapping("/add/test")public String add() {String key = "test";String value = UUID.randomUUID().toString();redisTemplate.opsForValue().set(key, value);return "添加成功";}@GetMapping("/query/test")public String query() {String key = "test";return "查询成功:" + redisTemplate.opsForValue().get(key);}}

应用启动后会自动拉取镜像,在Docker控制台可看到如下运行信息

Redis采用默认配置因此不需要在application.properties中配置参数信息;执行测试代码成功调用。

2.2 RedisChatMemory

InMemoryChatMemory实现ChatMemory接口,该接口定义如下

  • add表示往记忆中添加一条或者多条信息,其中conversationId表示会话ID
  • get表示获取记忆,lastN指定获取最新的n条记忆数据
  • clear 清楚记忆 ;
public interface ChatMemory {// TODO: consider a non-blocking interface for streaming usagesdefault void add(String conversationId, Message message) {this.add(conversationId, List.of(message));}void add(String conversationId, List<Message> messages);List<Message> get(String conversationId, int lastN);void clear(String conversationId);}

可以利用Reids的List操作来做一个简易版的记忆能力

package com.lkl.test.spring.docker.redis;import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.messages.AbstractMessage;
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.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;public class RedisChatMemory implements ChatMemory {private final RedisTemplate<String, String> redisTemplate;private final ObjectMapper objectMapper;private final String memoryKeyPrefix = "chat:memory:prefix:"; // Redis Key 前缀public RedisChatMemory(RedisTemplate<String, String> redisTemplate) {this.redisTemplate = redisTemplate;this.objectMapper = new ObjectMapper();}/* 获取指定会话的最新 N 条消息(按时间倒序)*/@Overridepublic List<Message> get(String conversationId, int lastN) {String key = memoryKeyPrefix + conversationId;long totalMessages = redisTemplate.opsForList().size(key);if (totalMessages == 0) {return Collections.emptyList();}// 计算起始和结束索引(获取最后 N 条)long start = Math.max(0, totalMessages - lastN);long end = totalMessages - 1;// 获取指定范围的消息List<String> messageJsons = redisTemplate.opsForList().range(key, start, end);return messageJsons.stream().map(this::deserializeMessage).collect(Collectors.toList());}/* 添加消息到会话历史*/@Overridepublic void add(String conversationId, Message message) {String key = memoryKeyPrefix + conversationId;String messageJson = serializeMessage(message);// 使用 LPUSH 或 RPUSH 存储消息(LPUSH 表示最新消息在列表头部)redisTemplate.opsForList().rightPush(key, messageJson);// 可选:设置 Key 的过期时间(例如 30 天)redisTemplate.expire(key, 30, TimeUnit.DAYS);}/* 批量添加消息到会话历史(高性能实现)*/@Overridepublic void add(String conversationId, List<Message> messages) {if (messages == null || messages.isEmpty()) {return;}String key = memoryKeyPrefix + conversationId;ListOperations<String, String> listOps = redisTemplate.opsForList();// 序列化并批量添加消息messages.forEach(message -> {String messageJson = serializeMessage(message);listOps.rightPush(key, messageJson);});// 设置 Key 的过期时间(30天)redisTemplate.expire(key, 30, TimeUnit.DAYS);// 可以考虑使用 Redis Pipeline 批量操作(减少网络开销)}/* 清除指定会话的记忆*/@Overridepublic void clear(String conversationId) {redisTemplate.delete(memoryKeyPrefix + conversationId);}private String serializeMessage(Message message) {Map<String, Object> map = new HashMap<>();AbstractMessage abstractMessage = null;if (message instanceof AbstractMessage) {abstractMessage = (AbstractMessage) message;}map.put("type", abstractMessage.getMessageType().getValue());map.put("content", abstractMessage.getText());try {return objectMapper.writeValueAsString(map);} catch (JsonProcessingException e) {throw new RuntimeException(e);}}private Message deserializeMessage(String json) {try {Map<String, String> map = objectMapper.readValue(json, Map.class);String type = map.get("type");String content = map.get("content");return switch (type) {case "user" -> new UserMessage(content);case "assistant" -> new AssistantMessage(content);default -> throw new IllegalArgumentException("Unknown type: " + type);};} catch (JsonProcessingException e) {throw new RuntimeException(e);}}}

构建RedisMemoryController来测试依然保留了记忆。

@Slf4j
@RestController
public class RedisMemoryController {private ChatClient chatClient;private RedisChatMemory redisChatMemory;public RedisMemoryController(ChatClient.Builder builder, RedisTemplate<String, String> redisTemplate) {this.redisChatMemory = new RedisChatMemory(redisTemplate);this.chatClient = builder.defaultAdvisors(new MessageChatMemoryAdvisor(redisChatMemory)).build();}@GetMapping("/memory/redis")public String chat(@RequestParam(value = "input", defaultValue = "讲一个笑话") String input) {Prompt prompt = new Prompt(input);return chatClient.prompt(prompt).call().content();}}

该自定义的记忆能力还可以继续优化,比如做记忆压缩,对长篇大论做总结后记忆、对敏感信息做剔除,不记录用户的隐私信息、性能监控关注Redis的内存使用情况

2.3 自己动手

在Spring AI中还内置JdbcChatMemoryCassandraChatMemoryNeo4jChatMemory,引入类似的pom即可;通过RedisChatMemory的自定义实现,相信很容易这些拓展实现,自己可以动手试试吧~

<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-chat-memory-jdbc</artifactId>
</dependency>

结语:从金鱼到最强大脑

通过 Spring AI 的记忆功能,我们成功让 AI:

  • 记住了用户身份
  • 实现了多用户隔离
  • 做到了持久化存储

最终效果:

  • 用户:“和上次一样” → AI 秒懂
  • 用户:“你知道我的名字吗?” → AI:“当然,张三!”

AI的进化路线:
🐟 金鱼脑 → 🧠 最强大脑 → 🏛️ 记忆宫殿管理员

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词