Spring AI企业级应用实战(4):Chat Memory、会话隔离、持久化与上下文压缩
文章摘要前几篇已经完成Spring AI统一调用层和流式输出。本篇继续实现企业级多轮对话使用MessageChatMemoryAdvisor管理近期消息要求每次请求显式提供conversationId通过PostgreSQL保存完整Chat History与持久化Memory校验租户和用户所有权并在上下文接近预算时生成结构化摘要。最终形成一个支持多实例部署、流式调用、审计和安全删除的会话系统。一、本篇目标完成以下能力创建会话 稳定conversationId 多租户所有权校验 Chat Memory持久化 完整Chat History 同步与流式调用 窗口控制 上下文摘要 清空与删除 可观测性最终链路HTTP请求 → 身份认证 → 会话所有权校验 → 写入Chat History → MessageChatMemoryAdvisor → ChatClient → 模型 → 保存结果 → 更新指标二、项目结构src/main/java/com/zyentor/ai ├── config │ └── ChatMemoryConfig.java ├── conversation │ ├── Conversation.java │ ├── ConversationRepository.java │ ├── ConversationService.java │ └── ConversationController.java ├── history │ ├── ChatMessageRecord.java │ ├── ChatHistoryRepository.java │ └── JdbcChatHistoryRepository.java ├── memory │ ├── MemoryCompactionService.java │ └── ConversationSummary.java ├── service │ └── EnterpriseConversationAiService.java └── security └── UserContext.java三、依赖dependencyManagementdependenciesdependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-bom/artifactIdversion2.0.0/versiontypepom/typescopeimport/scope/dependency/dependencies/dependencyManagementdependenciesdependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-starter-model-deepseek/artifactId/dependencydependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-starter-model-chat-memory-repository-jdbc/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-webflux/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-jdbc/artifactId/dependencydependencygroupIdorg.postgresql/groupIdartifactIdpostgresql/artifactIdscoperuntime/scope/dependency/dependencies具体Starter名称应以当前Spring AI 2.0.x文档和依赖清单为准。四、配置数据源和模型spring:datasource:url:jdbc:postgresql://localhost:5432/spring_ai_demousername:ai_userpassword:${DB_PASSWORD}ai:model:chat:deepseekdeepseek:api-key:${DEEPSEEK_API_KEY}chat:model:deepseek-v4-flashtemperature:0.2max-tokens:2048生产环境不要把密钥写进仓库。五、配置Chat MemoryConfigurationpublicclassChatMemoryConfig{BeanChatMemorychatMemory(ChatMemoryRepositoryrepository){returnMessageWindowChatMemory.builder().chatMemoryRepository(repository).maxMessages(30).build();}Bean(conversationChatClient)ChatClientconversationChatClient(ChatClient.Builderbuilder,ChatMemorychatMemory){returnbuilder.defaultSystem( 你是企业AI助手。 只能使用当前用户有权访问的上下文。 不得泄露其他用户或租户的信息。 不确定时明确说明。 ).defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()).build();}}maxMessages30只是示例生产应通过真实Token分布评测。六、为什么每次请求必须传conversationIdSpring AI 2.0内置Memory Advisor要求显式提供ChatMemory.CONVERSATION_ID调用chatClient.prompt().advisors(spec-spec.param(ChatMemory.CONVERSATION_ID,conversationId)).user(message).call().content();不要使用默认会话也不要把conversationId写死在Advisor Bean中。七、会话表CREATETABLEai_conversation(idVARCHAR(64)PRIMARYKEY,tenant_idVARCHAR(64)NOTNULL,user_idVARCHAR(64)NOTNULL,titleVARCHAR(200),statusVARCHAR(20)NOTNULL,memory_versionBIGINTNOTNULLDEFAULT1,created_atTIMESTAMPNOTNULL,updated_atTIMESTAMPNOTNULL);memory_version可以用于缓存失效摘要版本调试会话重置。八、完整消息表CREATETABLEai_chat_message(idVARCHAR(64)PRIMARYKEY,conversation_idVARCHAR(64)NOTNULL,tenant_idVARCHAR(64)NOTNULL,user_idVARCHAR(64)NOTNULL,roleVARCHAR(20)NOTNULL,contentTEXTNOTNULL,statusVARCHAR(20)NOTNULL,request_idVARCHAR(64)NOTNULL,modelVARCHAR(100),prompt_versionVARCHAR(50),input_tokensBIGINT,output_tokensBIGINT,created_atTIMESTAMPNOTNULL);Chat Memory表只服务模型上下文该表保存完整历史。九、会话创建ServicepublicclassConversationService{privatefinalConversationRepositoryrepository;publicConversationcreate(UserContextuser){InstantnowInstant.now();ConversationconversationnewConversation(conv_UUID.randomUUID(),user.tenantId(),user.userId(),null,ConversationStatus.ACTIVE,1L,now,now);repository.save(conversation);returnconversation;}}tenantId和userId来自认证上下文。十、所有权校验publicConversationrequireOwned(UserContextuser,StringconversationId){returnrepository.findOwned(conversationId,user.tenantId(),user.userId()).orElseThrow(()-newAccessDeniedException(无权访问该会话));}不能只验证会话存在。十一、同步对话服务ServicepublicclassEnterpriseConversationAiService{privatefinalChatClientchatClient;privatefinalConversationServiceconversationService;privatefinalChatHistoryRepositoryhistoryRepository;publicStringchat(UserContextuser,StringconversationId,Stringmessage){conversationService.requireOwned(user,conversationId);StringrequestIdUUID.randomUUID().toString();historyRepository.saveUserMessage(user,conversationId,requestId,message);try{StringanswerchatClient.prompt().advisors(spec-spec.param(ChatMemory.CONVERSATION_ID,conversationId).param(tenantId,user.tenantId())).user(message).call().content();historyRepository.saveAssistantMessage(user,conversationId,requestId,answer,MessageStatus.COMPLETED);returnanswer;}catch(RuntimeExceptionexception){historyRepository.markFailed(conversationId,requestId,exception.getClass().getSimpleName());throwexception;}}}十二、避免长数据库事务模型调用可能持续数秒。不要开启数据库事务 → 写用户消息 → 等模型十几秒 → 写助手消息 → 提交推荐事务1写用户消息 → 模型调用 → 事务2写助手消息或失败状态通过request_id关联两段记录。十三、流式调用publicFluxServerSentEventChatStreamEventstream(UserContextuser,StringconversationId,Stringmessage){conversationService.requireOwned(user,conversationId);StringrequestIdUUID.randomUUID().toString();historyRepository.saveUserMessage(user,conversationId,requestId,message);StringBuilderbuffernewStringBuilder();returnchatClient.prompt().advisors(spec-spec.param(ChatMemory.CONVERSATION_ID,conversationId)).user(message).stream().content().doOnNext(buffer::append).map(chunk-ServerSentEvent.builder(newChatStreamEvent(requestId,delta,chunk)).build()).doOnComplete(()-historyRepository.saveAssistantMessage(user,conversationId,requestId,buffer.toString(),MessageStatus.COMPLETED)).doOnCancel(()-historyRepository.markCancelled(conversationId,requestId,buffer.toString()));}需要继续验证Provider在取消订阅后是否真正终止上游生成。十四、半段回答是否进入Memory用户取消后可能只生成根据你的问题主要有三个方面第一……不建议把这段内容当作完整Assistant消息进入稳定Memory。可选策略取消时不写Memory 完整Chat History保存CANCELLED消息 下一轮提示用户上一轮已中断十五、为什么需要上下文压缩长期会话会不断消耗Token。只扩大Message Window会导致成本上升延迟增加重要信息被噪声淹没超出上下文窗口。推荐上下文结构化摘要 最近若干轮消息 当前RAG证据十六、摘要表CREATETABLEai_conversation_summary(idVARCHAR(64)PRIMARYKEY,conversation_idVARCHAR(64)NOTNULL,tenant_idVARCHAR(64)NOTNULL,summary_versionBIGINTNOTNULL,covered_until_message_idVARCHAR(64)NOTNULL,content JSONBNOTNULL,created_atTIMESTAMPNOTNULL);结构化内容{goal:构建企业RAG方案,facts:[客户使用PostgreSQL,数据不能离开私有云],decisions:[向量存储选择pgvector],openQuestions:[峰值并发尚未确认]}十七、什么时候触发压缩可以使用消息数量阈值 Token预算阈值 会话持续时间 工具事件数量推荐以Token预算为主预计上下文Token 可用历史预算的80% → 触发压缩十八、压缩流程1. 读取上次摘要 2. 读取摘要之后的新消息 3. 生成新结构化摘要 4. 校验Schema 5. 保存summary_version 6. 更新covered_until_message_id 7. 后续Prompt加载摘要和近期消息原始消息不删除用于审计和重新生成摘要。十九、摘要不能替代业务事实模型摘要可能错误。以下信息必须从业务系统实时获取订单状态当前权限余额-审批结果产品库存合同版本。摘要只能作为对话背景不能作为强一致事实来源。二十、会话清空与删除清空模型上下文chatMemory.clear(conversationId);同时增加memory_version使缓存失效。删除会话流程标记DELETING → 删除Memory → 删除摘要和向量记忆 → 处理附件 → 按策略删除或匿名化History → 标记DELETED二十一、监控指标chat_conversation_created_count chat_memory_message_count chat_memory_load_duration chat_memory_clear_count conversation_cross_owner_denied_count summary_generation_count summary_validation_failed_count stream_cancelled_count history_memory_write_mismatch_count二十二、测试场景同一会话连续多轮 同一用户两个窗口 同租户两个用户 两个不同租户 应用重启 请求进入不同实例 流式取消 模型超时 Memory清空 摘要触发 非法conversationId访问最重要的测试是多租户隔离。二十三、本篇完整架构认证用户 → Conversation所有权 → 完整History写入 → 显式conversationId → MessageChatMemoryAdvisor → 持久化Memory Repository → ChatClient → 模型 → 保存状态 → Token预算触发摘要总结生产级Spring AI多轮对话不能只注册一个Memory Advisor。完整方案必须同时解决稳定会话ID 租户与用户隔离 持久化Memory 独立Chat History 流式状态 上下文压缩 审计与删除下一篇将继续实现Spring AI企业级应用实战5Tool Calling、权限校验、幂等与人工确认。

相关新闻

Python异常嵌套日志处理与结构化日志实践

Python异常嵌套日志处理与结构化日志实践

1. 异常嵌套日志的痛点解析在Python项目开发中,异常嵌套场景几乎无处不在。当外层异常捕获内层异常时,传统的日志记录方式往往存在三个典型问题:信息割裂:内层异常被外层捕获后,原始堆栈信息可能被覆盖日志冗余&#x…

2026/7/28 20:38:44阅读更多 →
系统化调试方法论与高效工具链实践

系统化调试方法论与高效工具链实践

1. 为什么我们需要系统化的调试方法论在十五年的开发生涯中,我见过太多工程师把调试当成碰运气的过程——反复修改代码、盲目添加打印语句、甚至迷信地调整缩进格式。这种低效的调试方式不仅浪费时间,更会掩盖问题的本质。真正高效的调试应该像法医解剖一…

2026/7/28 20:38:44阅读更多 →
AI如何重塑企业工作流程:Anthropic的实践与架构解析

AI如何重塑企业工作流程:Anthropic的实践与架构解析

1. 项目概述:AI如何重塑Anthropic的工作流程作为一家专注于AI安全的头部企业,Anthropic从创立之初就将人工智能技术深度融入其研发体系。最典型的案例是其全员使用的Claude系列模型,这套内部称为"Constitutional AI"的系统已渗透到…

2026/7/28 20:38:44阅读更多 →
语言模型反事实推理:原理、应用与优化

语言模型反事实推理:原理、应用与优化

1. 项目概述:语言模型代理的反事实抽象研究在2025年NIPS会议上,"Abstract Counterfactuals for Language Model Agents"这一研究课题引起了广泛关注。作为一名长期跟踪语言模型发展的研究者,我认为这项工作的核心价值在于为AI代理系…

2026/7/28 21:45:01阅读更多 →
本地 AI 自动化工具 OpenClaw 完整安装实操,零基础可落地

本地 AI 自动化工具 OpenClaw 完整安装实操,零基础可落地

当前,各类对话式 AI 工具层出不穷,但大多仅支持文字交互,难以直接操控本地文件、浏览器及办公软件。OpenClaw 的核心优势在于本地部署与自动化执行,它能够接收自然语言指令,自主完成各类电脑操作,深受职场人…

2026/7/28 21:45:01阅读更多 →
大数据产品全生命周期风险管理与防控实践

大数据产品全生命周期风险管理与防控实践

1. 大数据时代的数据产品风险管理全景图在金融风控系统升级项目中,我们团队曾处理过一个典型案例:某银行信用卡中心的大数据用户画像产品因未对第三方数据源进行合规性验证,导致模型训练引入了违规采集的个人信息。这件事让我深刻意识到——数…

2026/7/28 21:45:01阅读更多 →
Linux 系统安装 JDK8 保姆级教程(实测可用)

Linux 系统安装 JDK8 保姆级教程(实测可用)

Linux 系统安装 JDK8 保姆级教程(实测可用) 本文手把手带你在 Linux 上完成 JDK8 的下载、安装、环境变量配置与验证,全程实测,照着做即可成功。 测试环境说明 本文操作基于以下系统环境测试通过: CentOS Linux re…

2026/7/28 21:45:01阅读更多 →
【Linux Mint 深度学习开发环境搭建】多深度学习框架融合环境

【Linux Mint 深度学习开发环境搭建】多深度学习框架融合环境

系列文章目录 第一章 Linux mint 深度学习开发环境搭建之Nvidia显卡相关软件安装 第二章 Linux mint 深度学习开发环境搭建之开发软件安装 第三章 Linux mint 深度学习开发环境搭建之多深度学习框架融合环境 文章目录系列文章目录前言一、搭建步骤(1)拉取合适的基础镜像(2)安装…

2026/7/28 21:45:01阅读更多 →
联辉科 LTK5112 28W单声道F类音频功率放大器 ESOP-8 技术解析

联辉科 LTK5112 28W单声道F类音频功率放大器 ESOP-8 技术解析

在蓝牙音箱、智能音箱、导航仪、便携游戏机、拉杆音箱等需要高功率输出的音频产品中,需要一款既能提供足够输出功率、又能灵活切换工作模式以适应不同应用场景(如FM收音时需AB类模式避免EMI干扰)的音频功率放大器。LTK5112是一款4Ω负载下可提…

2026/7/28 21:43:01阅读更多 →
覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

覆盖国产 + 海外 + 开源模型,OpenClaw 2.7.9 Windows/Mac 双端部署详解

🔹 工具基础介绍 OpenClaw 是开源生态中一款实用性较强的本地智能工具,凭借本地离线运行、可视化图形操作和任务自动化三大核心特性,赢得了众多用户的青睐。与普通在线对话AI工具不同,它属于能够直接操控本机软硬件的智能数字员工…

2026/7/28 4:06:39阅读更多 →
伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

伺服阀焊完微漏毁整机?精密激光焊接三关锁住高压

所谓液压伺服阀体的精密激光焊接,是用激光束对阀座壳体(通常为不锈钢或铝合金)进行密封焊接,使阀体在21-35MPa的高压液压油或压缩气体中长期运行而不发生介质泄漏。液压伺服阀是高端液压系统的"大脑"。从航空航天飞行控…

2026/7/28 2:08:06阅读更多 →
D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南

D2DX:三步实现《暗黑破坏神2》高清宽屏体验的终极指南 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx 你是否还在…

2026/7/28 1:38:28阅读更多 →
告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生

告别臃肿!3步让你的暗影精灵笔记本重获新生 【免费下载链接】OmenSuperHub Control Omen laptop performance, fan speeds, and keyboard lighting, and unlock power limits. 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub 你是否也曾为官方Om…

2026/7/28 0:00:29阅读更多 →
RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

RAG必踩坑!财报法规检索不准?这款开源工具让答案浮出水面,准确率飙升98.7%!

做 RAG 的人应该都踩过这个致命的坑:把几百页的财报、法规、技术手册扔给向量库,问一个具体问题,搜出来的全是沾边但没用的内容 —— 关键信息要么被硬切块拆碎了,要么藏在几十条结果的最下面。语义相似≠真正相关,这个…

2026/7/28 0:00:29阅读更多 →
抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

抖音视频文案提取工具全指南:免费2026版、手机App、在线工具一网打尽

2026年做短视频运营,从抖音上扒文案早就不是偷偷抄笔记的事了。我刚开始做内容的时候,每天刷半小时抖音,手动把爆款视频的口播敲进备忘录,一条2分钟的视频得花十来分钟,碰到语速快的还要反复回听。后来试了一圈工具&am…

2026/7/28 0:00:29阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/28 20:22:24阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/28 3:17:03阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/28 2:35:58阅读更多 →