当前位置: 首页 > news >正文

Spring AI Alibaba零基础速成(5) ---- Memory(记忆)

大模型默认只能单轮对话每次对话完成后就会丢失当前对话记忆我们之前了解过可以通过AssistantMessage把大模型回复结果存储起来下次提问时在发送给大模型不过使用过于麻烦和受限Spring AI 和Spring AI Alibaba都实现了更好实现记忆化的方式1. 内存存储Configuration public class SaaLLMConfig { private final String QWEN_MODEL qwen-plus; Bean public DashScopeApi dashScopeApi() { return DashScopeApi.builder().apiKey(System.getenv(AliQWenAPIKey)).build(); } Bean public ChatModel qwenChatModel(DashScopeApi dashScopeApi) { return DashScopeChatModel .builder() .dashScopeApi(dashScopeApi) .defaultOptions(DashScopeChatOptions.builder().model(QWEN_MODEL).build()) .build(); } Bean public ChatClient qwenChatClient(Qualifier(qwenChatModel) ChatModel qwenChatModel) { MessageWindowChatMemory windowChatMemory MessageWindowChatMemory.builder() .maxMessages(10) .chatMemoryRepository(new InMemoryChatMemoryRepository()) .build(); return ChatClient .builder(qwenChatModel) .defaultAdvisors(MessageChatMemoryAdvisor.builder(windowChatMemory).build()) .build(); } }MessageWindowChatMemory维护固定容量的消息窗口默认 20 条。当消息超限时自动移除较早的对话消息始终保留系统消息。InMemoryChatMemoryRepository 基于 ConcurrentHashMap 实现内存存储。默认情况下若未配置其他 RepositorySpring AI 将自动配置 InMemoryChatMemoryRepository 类型的 ChatMemoryRepository Bean供直接使用。2. 源码解析关键在于MessageChatMemoryAdvisor这个类可以看到我们在配置ChatClient时配置了一个该对象这个就是生效的关键虽然名字叫Advisor但它和 Spring AOP 一点关系都没有。它是Spring AI 自己实现的一套独立拦截器机制。Override public ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain) { String conversationId getConversationId(chatClientRequest.context(), this.defaultConversationId); // 1. Retrieve the chat memory for the current conversation. ListMessage memoryMessages this.chatMemory.get(conversationId); // 2. Advise the request messages list. ListMessage processedMessages new ArrayList(memoryMessages); processedMessages.addAll(chatClientRequest.prompt().getInstructions()); // 3. Create a new request with the advised messages. ChatClientRequest processedChatClientRequest chatClientRequest.mutate() .prompt(chatClientRequest.prompt().mutate().messages(processedMessages).build()) .build(); // 4. Add the new user message to the conversation memory. UserMessage userMessage processedChatClientRequest.prompt().getUserMessage(); this.chatMemory.add(conversationId, userMessage); return processedChatClientRequest; } Override public ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) { ListMessage assistantMessages new ArrayList(); if (chatClientResponse.chatResponse() ! null) { assistantMessages chatClientResponse.chatResponse() .getResults() .stream() .map(g - (Message) g.getOutput()) .toList(); } this.chatMemory.add(this.getConversationId(chatClientResponse.context(), this.defaultConversationId), assistantMessages); return chatClientResponse; }在该类源码中我们可以看到关键的两个方法before和afterbefore在发送请求前执行先从memoryMessages中获取历史对话再把本次对话的用户问题也存入chatMemory中。after在大模型返回后执行把大模型返回内容存储到chatMemory中3. Redis存储配置redis:spring: data: redis: host: localhost port: 6379 database: 0要使用Redis需要引入依赖!--redis-- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency !-- Alibaba Spring AI Redis 记忆模块 -- dependency groupIdcom.alibaba.cloud.ai/groupId artifactIdspring-ai-alibaba-starter-memory-redis/artifactId /dependency可以看到这个依赖适配了Jedis和RedissonRedis以及springBoot的LettuceConfiguration public class SaaLLMConfig { private final String DEEPSEEK_MODEL deepseek-v3.2; Bean public ChatModel deepseekChatModel() { return DashScopeChatModel .builder() .dashScopeApi(DashScopeApi.builder().apiKey(System.getenv(AliQWenAPIKey)).build()) .defaultOptions(DashScopeChatOptions.builder().model(DEEPSEEK_MODEL).build()) .build(); } Bean public ChatClient deepseekChatClient(Qualifier(deepseekChatModel) ChatModel deepseekChatModel) { LettuceRedisChatMemoryRepository redisCMR LettuceRedisChatMemoryRepository.builder().build(); MessageWindowChatMemory chatMemory MessageWindowChatMemory.builder() .maxMessages(10) .chatMemoryRepository(redisCMR) .build(); return ChatClient .builder(deepseekChatModel) .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()) .build(); } }同时一个大模型通常会服务多个用户对于不同用户的对话应该分开存储我们可以通过设置CONVERSATION_ID来实现RestController public class ChatMemoryController { Resource(name deepseekChatClient) ChatClient deepseekChatClient; GetMapping(/chatmemory/chat2) public FluxString chat2(String userId, String question) { return deepseekChatClient .prompt() .user(question) .advisors( advisorSpec - advisorSpec.param(CONVERSATION_ID, userId) ) .stream() .content(); } }通过.advisors方法设置如果不设置会使用默认的这样所有用户的对话是共享的可以看到是按id分别存储的4. 关系型数据库存储导入依赖!-- MySQL 驱动 -- dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency !-- Spring JDBC -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-jdbc/artifactId /dependency !--Spring AI JDBC 聊天记忆启动器-- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-chat-memory-repository-jdbc/artifactId /dependency增加配置spring: ai: chat: #开启Schema初始化,会自动创建表用 memory: repository: jdbc: initialize-schema: always # 数据库连接 datasource: url: jdbc:mysql://localhost:3306/ai_chat?useSSLfalseallowPublicKeyRetrievaltrueserverTimezoneAsia/Shanghai username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver # JDBC 自动建表 sql: init: mode: always配置ChatClient:Configuration public class SaaLLMConfig { private final String DEEPSEEK_MODEL deepseek-v3.2; private final String QWEN_MODEL qwen-plus; private final String GLM5 glm-5; Bean public DashScopeApi dashScopeApi() { return DashScopeApi.builder().apiKey(System.getenv(AliQWenAPIKey)).build(); } Bean public ChatModel glmChatModel() { return DashScopeChatModel .builder() .dashScopeApi(DashScopeApi.builder().apiKey(System.getenv(AliQWenAPIKey)).build()) .defaultOptions(DashScopeChatOptions.builder().model(GLM5).build()) .build(); } Bean public ChatClient glmChatClient(Qualifier(glmChatModel) ChatModel glmChatModel, JdbcChatMemoryRepository jdbcChatMemoryRepository) { MessageWindowChatMemory chatMemory MessageWindowChatMemory.builder() .maxMessages(10) .chatMemoryRepository(jdbcChatMemoryRepository) .build(); return ChatClient .builder(glmChatModel) .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()) .build(); }Spring AI 为 JdbcChatMemoryRepository 提供自动配置可直接注入。RestController public class ChatMemoryController { Resource(name glmChatClient) ChatClient glmChatClient; GetMapping(/chatmemory/chat3) public FluxString chat3(String userId, String question) { return glmChatClient .prompt() .user(question) .advisors( advisorSpec - advisorSpec.param(CONVERSATION_ID, userId) ) .stream() .content(); } }
http://www.gsyq.cn/news/1334495.html

相关文章:

  • Modbus三种类型详解:RTU、ASCII、TCP
  • 为内部ai工具平台集成taotoken实现多模型灵活切换的方案
  • 单频信号频谱检测仿真:从周期图到匹配滤波器的性能对比
  • 别再为多品牌摄像头头疼了!用Java+ONVIF协议统一控制云台和回放的实战踩坑记录
  • 【c++面向对象编程】第36篇:析构函数应永远不抛出异常——原因与最佳实践
  • 项目初始化:Vite + React + shadcn/ui
  • FPGA新手避坑指南:Vivado MIG IP核那些必须搞懂的接口时序(以DDR3为例)
  • 避坑指南:Keil uVision5安装激活全流程(含C51/MDK双版本、Win11系统适配及汉化问题)
  • 2026绵阳美新家政联系方式及服务实力深度解析:绵阳市美新家政服务有限公司联系/整理收纳培训/早教师培训/月嫂培训/选择指南 - 优质品牌商家
  • 别再手动画流程图了!Flowable UI 6.6.0 + Tomcat 保姆级部署教程,从安装到登录一次搞定
  • 【2026年】中考初中语文必背古诗词与文言文PDF电子版(含默写练习题)
  • 【26年7月】日语N1、N2、N3、N4、N5历年真题及答案PDF电子版(2010-2025年12月)
  • 终极指南:3分钟掌握Mousecape,让你的macOS光标焕然一新
  • 告别龟速下载!保姆级教程:用百度网盘离线下载搞定Android 1.6到16全版本AOSP源码
  • NY352固态MT29F32T08GWLBHD6-24QJ:B
  • 现在不看就晚了:Perplexity 2.5正式版已弃用旧Query Schema!3小时内完成迁移的4步零误差操作法
  • 2026越南公司注册新规解读及合规服务机构技术分析 - 优质品牌商家
  • 快速傅里叶变换(FFT)原理与工程实践:从分治算法到信号处理应用
  • Java 继承与高级特性精讲:继承实现、方法重写、类型转换与多态实战
  • TPT API驱动嵌入式测试自动化:从CI/CD集成到Docker容器化实践
  • 跨境同行都在用 AI Agent,你还在手动处理订单?—— 实在Agent 全自动化实战指南
  • 团队冲刺阶段(个人)
  • 工业级AI计算机如何支撑机场eGate系统:BOXER-6646-ADP硬件与部署解析
  • FJX800轴流泵多维度评测:自吸污水泵/自吸离心泵/蒸发强制循环泵/蒸发混流泵/蒸发结晶循环泵/蒸发轴流泵/衬氟轴流泵/选择指南 - 优质品牌商家
  • Microchip Android配件开发平台:从MCU到系统级蓝牙外设实战指南
  • Redis分布式锁进阶第二十五篇R
  • 零中频架构中发射本振泄漏的数字校正原理与工程实践
  • 用Go从零实现一个高性能KV存储引擎:B+Tree索引、WAL持久化、LRU缓存的工程实践
  • 产品经理开需求会整理纪要头大?2026年这4款ai会议记录智能生成工具,自动出稿太香了
  • 2026年不锈钢泵实测评测:高温磁力泵/CZ化工流程泵/CZ化工离心泵/FSB氟塑料泵/FYB型不锈钢液下泵/IHF化工泵/选择指南 - 优质品牌商家