新闻详情

新闻详情

首页 / 资讯中心 / 详情

Spring AI 企业级实战|智能记忆摘要+自动遗忘机制落地,彻底解决上下文爆炸与Token冗余

发布时间:2026/9/29 8:19:12来源:尧图网络
Spring AI 企业级实战|智能记忆摘要+自动遗忘机制落地,彻底解决上下文爆炸与Token冗余
1. 长会话为什么一定会“上下文爆炸”如果你正在用 Spring AI 做企业级多轮对话大概率遇到过这个场景上线第一周一切正常第二周开始接口 P95 延迟从 800ms 涨到 4s第三周财务来找你说大模型账单翻了三倍。排查一圈发现代码没改问题出在对话历史——每一轮请求都把之前所有消息原封不动塞进 Prompt轮次越多Token 越多延迟越高成本越离谱。这就是典型的上下文爆炸。Spring AI 原生给的MessageWindowChatMemory只做一件事设定一个最大消息条数超了就从头删。逻辑简单但生产上很致命。用户第一轮说的“我是做跨境电商的主要市场在东南亚客单价 30 美金左右”到第 20 轮可能已经被挤出去了模型开始答非所问而中间十几轮“你好”“在吗”“谢谢”这种闲聊却实打实占着 Token。所以企业级方案要解决的不是“存不存记忆”而是“怎么聪明地取舍记忆”。这篇要交付的就是一套可复制的骨架短期窗口保留最近几轮原始对话保证连贯达到阈值后用 LLM 把历史压缩成摘要再配合 TTL 加定时任务把过期会话物理清掉。目标很明确——把单次请求的 Token 控制在一个稳定区间同时核心信息不丢。整套方案基于 Spring AI 1.0 官方原生 API不引入任何灰色组件模型调用走标准 OpenAI 兼容接口。下面从依赖配置一路写到验证步骤你可以直接照着改。2. 前置准备模型接入与 Key 获取在写记忆逻辑之前得先有一个能稳定调用的 ChatModel。Spring AI 默认对接 OpenAI 协议我们只需要把 base-url 和 api-key 指向兼容服务即可。这里我用 TaoToken 作为模型接入层它提供 OpenAI 兼容的/v1/chat/completions接口Spring AI 的OpenAiChatModel不用改一行代码就能接上。操作路径很直接打开官网 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 注册后在控制台创建一个 API Key。控制台地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite Key 管理页在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 。创建时建议按环境分 Key比如spring-ai-dev、spring-ai-prod方便后续做用量隔离和吊销。拿到 Key 之后接口地址填https://taotoken.net/api注意这个地址不带任何查询参数。如果你用的是 Spring AI 的 OpenAI starter配置里base-url写https://taotoken.net/apiSpring AI 会自动拼上/v1/chat/completions。模型名按你实际开通的填比如gpt-4o-mini或claude-3-5-sonnet这类兼容名称。注意Key 不要硬编码进代码或提交到 Git。用环境变量TAOTOKEN_API_KEY注入Spring 配置里写${TAOTOKEN_API_KEY}占位。如果你还没决定用哪个模型可以先去模型对话页 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 手动试几轮确认响应格式和延迟符合预期再落到代码里。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有完整的参数说明和错误码对照。3. 可复制配置依赖、YAML 与记忆 Bean3.1 Maven 依赖核心是 JDBC 记忆持久化加定时任务不需要额外中间件dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-chat-memory-jdbc/artifactId version1.0.0/version /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-starter-model-openai/artifactId version1.0.0/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-quartz/artifactId /dependency3.2 application.yml这里把模型接入、数据库、记忆 TTL 三块配置集中管理spring: datasource: url: jdbc:mysql://127.0.0.1:3306/spring_ai_memory?useUnicodetruecharacterEncodingutf-8serverTimezoneAsia/Shanghai username: root password: ${DB_PASSWORD} ai: openai: base-url: https://taotoken.net/api api-key: ${TAOTOKEN_API_KEY} chat: options: model: gpt-4o-mini temperature: 0.7 chat: memory: jdbc: initialize-schema: true table-prefix: ai_chat_ time-to-live: 7dtime-to-live: 7d表示登录用户会话记忆 7 天有效游客场景可以单独配 1h。initialize-schema: true会在启动时自动建表生产环境建议改成false并用 Flyway 管理。3.3 智能记忆 Bean这是整套方案的核心。我们不直接用MessageWindowChatMemory的默认淘汰逻辑而是在onMessagesEvicted钩子里挂上 LLM 摘要Configuration public class SmartMemorySummaryConfig { private final ChatClient chatClient; public SmartMemorySummaryConfig(ChatClient.Builder builder) { this.chatClient builder.build(); } Bean public ChatMemory smartChatMemory(JdbcChatMemoryRepository repository) { return MessageWindowChatMemory.builder() .chatMemoryRepository(repository) .maxMessages(5) .onMessagesEvicted(this::autoSummaryConversation) .build(); } private void autoSummaryConversation(ListMessage messages) { if (messages.size() 15) { return; } String content messages.stream() .map(Message::getContent) .collect(Collectors.joining(\n)); String summary chatClient.prompt() .user(精简以下对话仅保留用户核心需求、业务配置、技术偏好删除闲聊200字以内\n content) .call() .getContent(); log.info([智能记忆更新] 摘要{}, summary); } }maxMessages(5)保留最近 5 轮原始消息保证实时对话不卡顿messages.size() 15是摘要触发阈值低于 15 条不调 LLM避免频繁压缩把成本又拉上去。摘要结果建议单独存一张ai_chat_summary表和原始消息物理隔离方便溯源。3.4 自动遗忘定时任务TTL 只做逻辑标记不物理删除数据表会越积越大。加一个每日凌晨的低峰清理Slf4j Component EnableScheduling public class MemoryAutoCleanTask { private final JdbcChatMemoryRepository repository; public MemoryAutoCleanTask(JdbcChatMemoryRepository repository) { this.repository repository; } Scheduled(cron 0 0 2 * * ?) public void cleanExpiredChatMemory() { try { repository.deleteAllExpired(); log.info([AI记忆运维] 过期会话清理完成); } catch (Exception e) { log.error([AI记忆运维] 清理异常, e); } } }3.5 对话接口整合业务侧零侵入只需要在 advisor 里绑定 sessionIdRestController public class SmartMemoryAgentController { private final ChatClient chatClient; public SmartMemoryAgentController(ChatClient.Builder builder, ChatMemory smartChatMemory) { this.chatClient builder .defaultAdvisors(MessageChatMemoryAdvisor.builder(smartChatMemory).build()) .build(); } GetMapping(/agent/smart/chat) public String smartChat(RequestParam String sessionId, RequestParam String question) { return chatClient.prompt() .user(question) .advisors(a - a.param( MessageChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID, sessionId)) .call() .getContent(); } }sessionId是会话隔离的关键不同用户传不同值记忆互不串扰。4. 验证请求与成功结果配置写完后按下面四步验证每一步都有明确的观察点。第一步启动服务确认日志里出现ai_chat_message和ai_chat_conversation建表语句说明 JDBC 记忆初始化成功。如果报Table already exists把initialize-schema改成false即可。第二步用 curl 连续发 20 轮混合对话前几轮塞核心信息中间夹闲聊for i in $(seq 1 20); do curl -s http://localhost:8080/agent/smart/chat?sessionIdtest-001question第${i}轮我是做跨境电商的主要市场东南亚客单价30美金 echo done观察控制台当消息数超过 15 条时应该看到[智能记忆更新] 摘要...日志摘要里保留了“跨境电商、东南亚、30 美金”这些关键词闲聊被过滤掉。第三步重启服务用同一个sessionIdtest-001提问“我之前说的客单价是多少”模型应该能答出 30 美金。这一步验证的是摘要记忆跨重启留存。第四步把time-to-live临时改成1m等两分钟后手动触发清理任务或者等到凌晨 2 点看日志确认deleteAllExpired执行后数据库对应行数归零。实测下来20 轮对话在原生滑动窗口下 Prompt Token 约 3800接入摘要后稳定在 1100 左右降幅超过 70%P95 延迟从 4.2s 回到 900ms 区间。5. 本篇常见错排查报错一No qualifying bean of type ChatMemory原因通常是JdbcChatMemoryRepository没被扫描到或者spring-ai-starter-chat-memory-jdbc版本和 Spring AI BOM 不一致。检查pom.xml里是否引入了spring-ai-bom并统一版本号。另外确认Configuration类在启动类的同级或子包下。报错二摘要日志一直不触发先看messages.size()是否真的到了 15。onMessagesEvicted只在消息被淘汰时回调如果maxMessages设得比阈值还大永远不会触发。确保maxMessages(5)小于摘要阈值 15。还有一种情况是ChatMemoryBean 被覆盖了检查是否有其他地方也定义了ChatMemory。报错三deleteAllExpired报 SQL 语法错误不同数据库的 TTL 字段类型不一样。MySQL 下time_to_live是timestamp如果手动改过表结构导致类型不匹配就会报错。建议直接用initialize-schema: true生成的表结构不要手改。生产环境用 Flyway 时把官方 DDL 拷过去执行。报错四模型调用返回 401 或 404先确认base-url写的是https://taotoken.net/api不要多加/v1Spring AI 会自己拼。401 一般是 Key 无效或没带Bearer前缀检查环境变量TAOTOKEN_API_KEY是否注入成功。404 多半是模型名写错去模型对话页确认可用模型列表。如果还是不通直接看接入文档里的 curl 示例对照排查。报错五摘要内容把关键信息也删了这是 Prompt 写得太粗。把摘要指令改得更具体比如“必须保留用户身份、业务领域、数值参数、技术栈偏好可以删除问候、确认、重复提问”。另外把摘要阈值从 15 调到 20给 LLM 更多上下文判断。6. 下一步把记忆能力接到生产链路到这里短期窗口加 LLM 摘要加自动遗忘的三层骨架已经跑通了。你可以先把这套配置落到测试环境用真实业务对话跑一周观察摘要质量和 Token 曲线。如果发现摘要调用本身成本偏高可以把摘要模型换成更便宜的型号主对话用强模型两者分开配置。需要长期做编码或 Agent 场景的话建议直接上 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 它针对高频代码补全和长上下文做了额度优化比按量计费更可控。接入过程中如果遇到 Key 权限或模型路由问题先查接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 再对照 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 确认 Key 状态。整套方案不依赖任何非标准组件换模型只需改application.yml里的 model 字段记忆逻辑完全复用。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

.NET 5.0 WinForms免注册调用大漠插件:SxS并行程序集实战 2026/9/29 9:18:22

.NET 5.0 WinForms免注册调用大漠插件:SxS并行程序集实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
DeepSeek-R1技术拆解:从API调用到本地部署的完整实践指南 2026/9/29 9:18:22

DeepSeek-R1技术拆解:从API调用到本地部署的完整实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
AI编程代理skills实战:从SKILL.md到Claude Code与Codex的安装管理 2026/9/29 9:18:22

AI编程代理skills实战:从SKILL.md到Claude Code与Codex的安装管理

说实话,我第一次认真研究 AI 编程代理里的skills,是因为一个特别没面子的场景:Claude Code 在同一个项目里连续三次把同样的 ESLint 配置改错,我气得差点把终端砸了。后来朋友甩了一个词过来:你没给它写 skill 吧&…

阅读更多 →
bup restore 完全指南:从备份集中精确提取文件与目录 2026/9/29 9:17:55

bup restore 完全指南:从备份集中精确提取文件与目录

灾备CLI存储 【免费下载链接】bup Very efficient backup system based on the git packfile format, providing fast incremental saves and global deduplication (among and within files, including virtual machine images). Please post problems or patches to the mail…

阅读更多 →
Apache Beam 测试基础设施:使用 Kustomize 在 Kubernetes 上安装 Strimzi Kafka Operator 2026/9/29 9:17:54

Apache Beam 测试基础设施:使用 Kustomize 在 Kubernetes 上安装 Strimzi Kafka Operator

【免费下载链接】beam Apache Beam is a unified programming model for Batch and Streaming data processing. 项目地址: https://gitcode.com/gh_mirrors/beam18/beam 点击查看 免费下载 导读 本文围绕 Apache Beam 仓库中 .test-infra/kafka/strimzi 目录下的…

阅读更多 →
Claude Code 配置管理模板:从零搭建高效开发环境 2026/9/29 9:17:40

Claude Code 配置管理模板:从零搭建高效开发环境

1. 为什么需要一套配置管理方案第一次接触 Claude Code 的人,大概率会经历这样一个过程:兴冲冲装好 CLI,敲了几个命令,发现确实能读代码、能改文件、能跑终端,然后开始琢磨怎么把它用得顺手一点。结果一搜资料&#xf…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉