Spring AI 全新会话 API支持结构化、可压缩、支持多智能体
Spring博客更新了《Spring AI智能体模式》系列的第七篇,Spring AI 全新会话 API支持结构化、可压缩、支持多智能体,将用来替代版聊天内存(ChatMemory),下面是Spring AI会话API的主要内容。
此前系列文章已讲解智能体能力、用户提问工具、待办编辑工具、子智能体编排、智能体互联集成以及跨会话长期记忆自动化工具;本文将补充配套的短期记忆能力:Spring AI 会话。 将对话历史以纯消息列表存储,仅适用于简短对话,一旦会话内容增多便会出现问题:简单的截断机制会在工具调用流程中途静默丢弃数据,导致大模型收到孤立的返回结果、对话轮次结构错乱。 Spring AI 会话可自动记录交互过程中的所有消息、工具调用与返回结果,智能管理上下文窗口;而自动化记忆工具则负责持久化需要跨会话保留的核心信息。一套完整的智能体内存架构需要二者相辅相成,无法相互替代。
版本规划
目前功能在 Spring AI 社区组件中孵化,计划随 Spring AI 2.1 版本(2026 年 11 月) 正式发布,届时旧版聊天内存(ChatMemory)将被废弃,全面由会话 API 替代。
旧版 ChatMemory 会直接淘汰最早的消息,无法保证对话轮次完整性、缺少事件唯一标识、不支持多智能体,且不会记录丢弃的数据内容。 Spring AI 会话 API 基于事件溯源日志重构,提供可插拔的上下文压缩策略、分支隔离能力以及可关键词检索的历史回溯存储。
会话 API 架构
Spring AI 会话 API 核心类
会话(Session)与会话事件(SessionEvent)
-
Session:不可变的纯元数据对象,仅存储会话 ID、用户 ID、过期时间(TTL)及自定义元数据。事件日志统一持久化至仓储层,按需加载。
-
SessionEvent:封装 Spring AI 消息,并补充原生消息缺失的核心信息:唯一 UUID、会话 ID、时间戳、适配多智能体层级的分支标签、框架内置标识(如合成标记元数据)。
SessionService service = new DefaultSessionService(InMemorySessionRepository.builder().build());Session session = service.create(CreateSessionRequest.builder().userId("alice").build());service.appendMessage(session.id(), new UserMessage("What is Spring AI?"));service.appendMessage(session.id(), new AssistantMessage("Spring AI is..."));List<Message> history = service.getMessages(session.id()); // 可直接传入大模型
对话轮次(Turn)
轮次是对话的最小原子单元:一条用户消息 + 后续所有关联事件(助手回复、工具调用、工具返回结果),直至下一条用户消息出现。 所有上下文压缩策略均以轮次为最小执行粒度,确保保留的上下文永远以用户消息开头。杜绝孤立的工具返回结果、碎片化对话片段传入大模型。
轮次 1:[用户消息「Spring AI 是什么?」] → [助手回复] 轮次 2:[用户消息「它可以调用工具吗?」] → [助手工具调用] → [工具返回数据] → [助手最终回答]
上下文压缩
上下文压缩可精简事件日志,在控制上下文窗口长度的同时,保证对话逻辑连贯。核心由两大可组合组件驱动:压缩触发器与压缩策略。
压缩触发器
new TurnCountTrigger(20); // 轮次超过20次触发压缩TokenCountTrigger.builder().threshold(4000).build(); // 预估Token达到4000触发压缩// 组合触发器:满足任一条件即触发CompositeCompactionTrigger.anyOf(new TurnCountTrigger(20),TokenCountTrigger.builder().threshold(4000).build());
压缩策略
|
压缩策略 |
是否调用大模型 |
适用场景 |
|
滑动窗口压缩策略 |
否 |
控制成本、短期简易对话 |
|
轮次窗口压缩策略 |
否 |
以轮次为核心的结构化对话 |
|
Token 数量压缩策略 |
否 |
有严格上下文长度限制的场景 |
|
递归摘要压缩策略 |
是 |
长周期、高信息密度的复杂会话 |
前三种策略会保留最新的原始事件内容(按消息数 / 轮次数 / Token 上限裁剪),且裁剪边界自动对齐完整轮次,不会切割单次对话流程。
递归摘要压缩 能力最强:调用大模型对归档的历史事件进行摘要浓缩,并将摘要内容存储为一条合成的用户 + 助手对话轮次。多次压缩会基于历史摘要迭代优化,持续生成轻量化的滚动式压缩日志,无需每次全量重写。
RecursiveSummarizationCompactionStrategy.builder(chatClient).maxEventsToKeep(10).overlapSize(2) // 携带最近2条活跃事件作为摘要上下文.build();
注意:触发器与压缩策略必须成对配置,仅配置单一项会在初始化时抛出非法参数异常;若需关闭压缩功能,可同时省略两项配置。
整合聊天客户端(ChatClient)
会话内存顾问(SessionMemoryAdvisor)可无缝将会话管理能力接入聊天客户端调用链路。 每次请求自动加载历史对话、拼接上下文、追加新消息,触发压缩规则时自动执行精简,业务代码无需手动处理会话逻辑。
@BeanSessionMemoryAdvisor sessionMemoryAdvisor(SessionService sessionService,ChatClient.Builder chatClientBuilder) {return SessionMemoryAdvisor.builder(sessionService).defaultUserId("alice").compactionTrigger(new TurnCountTrigger(20)).compactionStrategy(RecursiveSummarizationCompactionStrategy.builder(chatClientBuilder.build()).maxEventsToKeep(10).build()).build();}@BeanChatClient chatClient(ChatClient.Builder chatClientBuilder, SessionMemoryAdvisor advisor) {return chatClientBuilder.defaultAdvisors(advisor).build();}
调用时通过上下文传入会话 ID:
String response = chatClient.prompt().user("Hello!").advisors(a -> a.param(SessionMemoryAdvisor.SESSION_ID_CONTEXT_KEY, "session-abc")).call().content();
若指定会话 ID 不存在,顾问组件会自动创建新会话。
多智能体分支隔离
当调度主智能体并行调度多个子智能体时,所有智能体可共享同一个会话,但各智能体仅能查看自身及上级分支的事件数据。SessionEvent 的分支字段采用点分隔路径,标记事件所属智能体在层级架构中的位置:
-
主调度智能体:分支 = “orch”
-
调研子智能体:分支 = “orch.researcher”
-
写稿子智能体:分支 = “orch.writer”
分支为空的事件属于根级数据,所有智能体均可访问。通过分支过滤器可自动实现数据隔离:
// 调研智能体可见:根事件 + 主调度事件 + 自身分支事件// 自动屏蔽同级写稿智能体的私有事件SessionMemoryAdvisor researcherAdvisor = SessionMemoryAdvisor.builder(sessionService).defaultSessionId(sharedSessionId).eventFilter(EventFilter.forBranch("orch.researcher")).build();
递归摘要生成的合成事件统一归属根分支,确保所有智能体都能查看全局压缩摘要。
历史回溯存储
上下文压缩能优化提示词体积,但老旧事件会被移出实时上下文窗口。SessionEventTools 落地了 MemGPT 回溯存储设计:完整原始事件日志永久留存,即便已从上下文裁剪,仍支持关键词检索。
ChatClient client = ChatClient.builder(chatModel).defaultTools(SessionEventTools.builder(sessionService).build()).defaultAdvisors(advisor).build();
conversation_search 工具由 Spring AI 自动注册。大模型需要回溯历史对话时,可通过关键词 + 分页参数调用该工具,按时间序返回结构化 JSON 结果;合成摘要事件同样会被索引,支持检索查询。
JDBC 持久化
spring-ai-session-jdbc 提供数据库持久化方案,通过两张数据表(会话表 AI_SESSION、仅追加事件日志表 AI_SESSION_EVENT)存储数据,兼容 PostgreSQL、MySQL、MariaDB、H2 数据库。 Spring Boot 启动器可全自动完成配置:
<dependency><groupId>org.springaicommunity</groupId><artifactId>spring-ai-starter-session-jdbc</artifactId></dependency>
PostgreSQL / MySQL 环境开启自动建表:
spring:ai:session:repository:jdbc:initialize-schema: always
无需手动注册额外 Bean。
快速入门
环境要求
Java 17 及以上、Spring AI 2.0.0-M4 及以上、Spring Boot 4.0.2 及以上
-
引入依赖版本管理
<dependencyManagement><dependencies><dependency><groupId>org.springaicommunity</groupId><artifactId>spring-ai-session-bom</artifactId><version>0.2.0</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement>
-
引入启动器 生产环境选用 JDBC 版本,本地开发可仅引入内存版会话管理:
<dependency><groupId>org.springaicommunity</groupId><artifactId>spring-ai-starter-session-jdbc</artifactId></dependency>
-
注册组件并使用
@BeanSessionMemoryAdvisor sessionMemoryAdvisor(SessionService sessionService) {return SessionMemoryAdvisor.builder(sessionService).defaultUserId("alice").compactionTrigger(new TurnCountTrigger(20)).compactionStrategy(SlidingWindowCompactionStrategy.builder().maxEvents(10).build()).build();}@BeanChatClient chatClient(ChatModel chatModel, SessionMemoryAdvisor advisor) {return ChatClient.builder(chatModel).defaultAdvisors(advisor).build();}
Session session = sessionService.create(CreateSessionRequest.builder().userId("alice").build());String response = chatClient.prompt().user("What is Spring AI?").advisors(a -> a.param(SessionMemoryAdvisor.SESSION_ID_CONTEXT_KEY, session.id())).call().content();
从 ChatMemory 迁移至会话 API
会话 API 将全面替代旧版 ChatMemory,成为 Spring AI 主流的对话持久化方案:
|
对比项 |
旧版 ChatMemory |
Spring AI 会话 |
|
存储单元 |
扁平消息列表 |
不可变事件、带时间戳与唯一标识 |
|
压缩能力 |
粗暴淘汰旧消息 |
四种可插拔策略,支持大模型摘要 |
|
轮次安全 |
无保障 |
强制对齐完整对话轮次 |
|
多智能体 |
不支持 |
点式分支标签隔离 |
|
历史检索 |
无 |
内置检索工具,关键词回溯 |
|
并发能力 |
依赖底层实现 |
全场景乐观并发控制 |
示例等价改造: 原代码 MessageWindowChatMemory
MessageWindowChatMemory.builder().maxMessages(20).build()
对应会话 API 写法:
SessionMemoryAdvisor.builder(sessionService).compactionTrigger(new TurnCountTrigger(20)).compactionStrategy(SlidingWindowCompactionStrategy.builder().maxEvents(20).build()).build();
夜雨聆风