Ohhnews

分类导航

$ cd ..
Spring Blog原文

Spring AI代理模式(第六篇):AutoMemoryTools实现跨会话持久化记忆

#spring ai#人工智能#agent#持久化存储#记忆管理

面向 Spring AI Agent 的基于文件的长期记忆

Agent 的能力很大程度上取决于它能记住什么。Spring AI 的 Chat Memory 可以存储完整的对话,并支持跨重启持久化。但它会存储所有内容——当窗口填满时,最旧的消息会被移除。即将推出的 Session API 将引入递归摘要来缓解这一问题,但在信息被压缩时,精确的事实细节仍会丢失。

spring-ai-agent-utils 工具包中的 AutoMemoryToolsAutoMemoryToolsAdvisor 为你的 Agent 提供了持久的、基于文件的长期记忆,能够跨会话留存。其设计灵感源自 Claude Code 的自动记忆系统Claude API 记忆工具规范,并已移植到 Spring AI,因此适用于任何 LLM 提供商。

长期记忆与对话历史的区别

ChatMemoryAutoMemoryTools 是互补的——配置良好的 Agent 会同时使用两者。ChatMemory 保留完整的对话窗口:自动记录每一轮对话,并受滑动窗口限制。AutoMemoryTools 是精选层:模型只将值得永久保留的内容(如用户偏好、项目决策、行为修正)写入类型化的 Markdown 文件中,这些文件可以无限期保存。使用 ChatMemory 处理当前任务;使用 AutoMemoryTools 保存下周仍需查阅的事实。

这是我们 Spring AI Agentic Patterns 系列的第 6 部分。 我们之前已经介绍了 Agent 技能AskUserQuestionToolTodoWriteTool子 Agent 编排 以及 A2A 集成。现在,我们添加了能够跨会话存续的记忆功能。

🚀 想直接上手? 请跳转至 快速入门 部分。

工作原理

Agent 通过 AutoMemoryTools 中的六个专用工具管理其记忆,所有操作均限定在沙盒化的记忆目录内。下图展示了完整的请求流程: [LOADING...]

① 用户请求 --- 请求与记忆系统提示词(System Prompt)结合,通过 Spring AI 顾问堆栈(ToolCallAdvisor + ChatMemoryAdvisor)发送给 LLM。LLM 在回答前决定是否加载、创建或更新记忆。

② 工具调用 --- LLM 调用 AutoMemoryTools。这些工具在配置的记忆目录中读取和写入类型化的 Markdown 文件——MEMORY.md 作为索引,以及诸如 user_profile.mdproject_history.md 等主题文件。

③ 后续检索/更新 --- LLM 可能会发出额外的工具调用来加载特定的记忆文件或更新现有文件;例如,在 MEMORY.md 中找到指针后加载文件,或在整理过程中合并条目。

④ 最终响应 --- 一旦所有记忆操作完成,LLM 生成答案,并通过顾问堆栈返回给用户。

记忆系统提示词

记忆系统提示词驱动了此行为。Jar 包中提供了两种变体:

  • AUTO_MEMORY_TOOLS_SYSTEM_PROMPT.md --- 用于 选项 A选项 B;专用的、沙盒化的 AutoMemoryTools
  • AUTO_MEMORY_FILESYSTEM_TOOLS_SYSTEM_PROMPT.md --- 用于 选项 C;通过 FileSystemTools 进行通用的 Read/Write/Edit 操作。

两种记忆系统提示词编码了相同的记忆模型,区别仅在于指示模型调用哪些操作。它们指导模型在会话开始时读取 MEMORY.md,通过两步工作流(MemoryCreateMemoryInsert)保存内容,应用四种记忆类型,跳过临时内容,在根据记忆采取行动前验证事实,并在删除或重命名文件时保持索引同步。

MEMORY.md --- 索引文件

MEMORY.md 是始终会被加载的索引文件。它是一个包含所有记忆文件指针的简单列表:

$ md
- [用户资料](user_profile.md) --- Alice,后端工程师,偏好简短回答
- [反馈测试](feedback_testing.md) --- 在集成测试中始终使用真实数据库
- [项目认证重写](project_auth.md) --- 由法律合规性驱动,而非技术债务

模型在每次会话开始时读取此索引,然后选择性地加载相关文件——即使随着记忆增加,也能保持上下文窗口精简。

记忆文件格式

每个记忆都是一个带有 YAML Frontmatter 的 Markdown 文件:

$ md
---
name: user profile
description: Alice --- 后端工程师,偏好简短回答
type: user
---

后端工程师,名叫 Alice。
偏好简洁、直接的回复,不带结尾摘要。

记忆类型

并非所有内容都值得保留。记忆模型定义了四种类型,每种类型都有明确的保存指引,确保 Agent 积累的是信号而非噪音:

  • user --- 角色、目标、专长、沟通风格
  • feedback --- 修正意见和确认过的方法(“停止总结”、“是的,那样是对的”)
  • project --- 代码或 git 中未体现的决策和截止日期(迁移目标、冻结日期)
  • reference --- 指向外部系统的指针(Linear 看板、Grafana 面板、Slack 频道)

记忆操作

AutoMemoryTools 提供了六个专用、沙盒化的操作。选项 C 通过通用的 FileSystemToolsShellTools 实现同样的结果。

AutoMemoryTools

选项 A 和 B 使用 AutoMemoryTools,它一对一地实现了 Claude API 记忆工具规范

工具用途
MemoryView读取带有行号的文件,或列出两层目录深度
MemoryCreate创建新的记忆文件(两步保存的第一步)
MemoryStrReplace替换现有文件中精确、唯一的字符串
MemoryInsert在给定行号后插入文本——主要用途:追加到 MEMORY.md
MemoryDelete递归删除文件或目录
MemoryRename重命名或移动文件;同时更新 MEMORY.md 中的链接

FileSystemTools 与 ShellTools

选项 C 使用通用的 FileSystemToolsShellTools,映射到相同的操作:

操作等同于
ReadMemoryView
WriteMemoryCreate
EditMemoryStrReplace / MemoryInsert
Bash (如 rm, mv)MemoryDelete / MemoryRename

选项 A 和 B 对所有路径进行了沙盒限制;选项 C 没有沙盒——Agent 拥有完整的文件系统访问权限。

集成方式

有三种方法可以将长期记忆添加到 Spring AI Agent 中,从零样板代码到完全手动配置。请根据你对系统提示词的控制需求、安全性考量以及 Agent 是否已经使用通用文件系统工具来选择。

选项 A:AutoMemoryToolsAdvisor(零样板)

ChatClient 构建器中添加一个顾问即可:

$ java
ChatClient chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(
        // 长期记忆 --- 跨会话存续的事实
        AutoMemoryToolsAdvisor.builder()
            .memoriesRootDirectory("/home/user/.agent/memories")
            .build(),

        // 对话历史 --- 本次会话的完整消息窗口
        MessageChatMemoryAdvisor.builder(
            MessageWindowChatMemory.builder().maxMessages(100).build())
            .build(),

        // 工具调用
        ToolCallAdvisor.builder().disableInternalConversationHistory().build())
    .build();

在每次请求时,该顾问会自动将 AUTO_MEMORY_TOOLS_SYSTEM_PROMPT.md 注入系统消息,注册所有六个 AutoMemoryTools(并去除重复注册),并在触发 memoryConsolidationTrigger 时选择性地追加整理提醒。

[LOADING...]

AutoMemoryToolsAdvisor 在链中首先运行,使用记忆系统提示词和六个工具定义增强请求上下文,然后再交给 ToolCallAdvisorChatMemoryAdvisor。当丰富的上下文到达 LLM 时,它已经包含了对话历史、工具定义和记忆指令——模型决定加载、保存或更新所需的一切信息。

选项 B:手动设置(直接使用 AutoMemoryTools)

AutoMemoryTools 与配套的系统提示词一起配置到 ChatClient 中:

$ java
@Value("classpath:/prompt/AUTO_MEMORY_TOOLS_SYSTEM_PROMPT.md")
Resource memorySystemPrompt;

@Value("${agent.memory.dir}")
String memoryDir;

ChatClient chatClient = chatClientBuilder
    .defaultSystem(p -> p
        .text(memorySystemPrompt)
        .param("MEMORIES_ROOT_DIERCTORY", memoryDir))
    .defaultTools(
        AutoMemoryTools.builder().memoriesDir(memoryDir).build(),
        TodoWriteTool.builder().build())
    .defaultAdvisors(ToolCallAdvisor.builder().build())
    .build();

当你需要完全控制系统提示词结构时,请使用此方法——例如,将记忆功能与自定义的主系统提示词结合使用。

选项 C:FileSystemTools + ShellTools

如果 Agent 已经拥有用于其他任务的 FileSystemToolsShellTools,你可以实现相同的记忆模式而无需添加 AutoMemoryTools。Agent 使用与处理任何文件工作相同的 ReadWriteEdit 操作——记忆只是另一个目录而已。

$ java
@Value("classpath:/prompt/AUTO_MEMORY_FILESYSTEM_TOOLS_SYSTEM_PROMPT.md")
Resource memorySystemPrompt;

@Value("${agent.memory.dir}")
String memoryDir;

ChatClient chatClient = chatClientBuilder
    .defaultSystem(p -> p
        .text(memorySystemPrompt)
        .param("MEMORIES_ROOT_DIERCTORY", memoryDir))   // 告诉 Agent 在哪里写入
    .defaultTools(
        ShellTools.builder().build(),         // Bash --- mkdir, ls 等
        FileSystemTools.builder().build())    // Read, Write, Edit --- 记忆文件操作
    .defaultAdvisors(ToolCallAdvisor.builder().build())
    .build();

同样的记忆约定适用——类型化文件、MEMORY.md 索引、两步保存——但没有沙盒限制:Agent 拥有完整的文件系统访问权限,仅通过约定保持在配置的目录中。

AutoMemoryTools (选项 A & B)FileSystemTools (选项 C)
路径模型相对路径,限定在记忆根目录绝对路径,全文件系统访问
安全性内置遍历防护无沙盒 --- Agent 仅通过提示词约定来执行
工具名称专用名称 (MemoryCreate, MemoryView, ...)通用名称 (Write, Read, Edit)
适用场景仅记忆功能的 Agent,需要隔离环境已将文件系统工具用于其他任务的 Agent

此方法直接遵循 Claude Code 自动记忆模式,其中相同的文件工具同时用于代码编辑和记忆管理。

⚠️ 安全提示: 使用 FileSystemTools 时,Agent 可以读取和写入文件系统上的任何位置。仅在受信任、受控的环境中使用此方法。

保持记忆整洁

随着时间的推移,记忆存储会积累冗余、重叠或过期的条目。定期要求 Agent 进行整理(合并重复项、删除过时事实、精简描述)可以使存储保持精简,并确保 MEMORY.md 索引可读。

选项 B 和 C (显式): 只需询问:

USER> 请整理你的记忆 --- 合并重复项并删除过时的内容。

选项 A (自动触发): AutoMemoryToolsAdvisor 接受一个 memoryConsolidationTrigger 断言。当它返回 true 时,一条 <system-reminder> 会被注入到下一次请求的系统消息中,提示模型进行整理,而无需用户手动询问。演示程序使用双重条件触发器——时间流逝或用户说“再见”:

$ java
Instant lastInteraction = Instant.now();

AutoMemoryToolsAdvisor.builder()
    .memoriesRootDirectory(memoryDir)
    .memoryConsolidationTrigger((request, instant) -> {
        var previous = lastInteraction;
        lastInteraction = Instant.now();

        // 轮次间隔超过 60 秒时进行整理
        if (instant.isAfter(previous.plusSeconds(60))) {
            return true;
        }
        // 当用户说再见时也进行整理
        var msg = request.prompt().getLastUserOrToolResponseMessage().getText();
        return msg != null && msg.toLowerCase().contains("bye");
    })
    .build()

其他有用的策略:

$ java
// 概率触发:约 5% 的请求
.memoryConsolidationTrigger((req, now) -> Math.random() < 0.05)

// 轮次计数:每 50 次调用
AtomicInteger counter = new AtomicInteger();
.memoryConsolidationTrigger((req, now) -> counter.incrementAndGet() % 50 == 0)

快速入门

1. 添加依赖

$ xml
<dependency>
    <groupId>org.springaicommunity</groupId>
    <artifactId>spring-ai-agent-utils</artifactId>
    <version>0.7.0</version>
</dependency>

注意: 查看最新的稳定版本,请访问 GitHub 发布页面。需要 Spring AI 2.0.0-M4+

2. 配置记忆目录

$ properties
# application.properties
agent.memory.dir=${user.home}/.spring-ai-agent/my-app/memory

3. 连接配置

$ java
@Value("${agent.memory.dir}")
String memoryDir;

ChatClient chatClient = chatClientBuilder
    .defaultAdvisors(
        // 长期记忆 --- 跨会话存续的事实
        AutoMemoryToolsAdvisor.builder()
            .memoriesRootDirectory(memoryDir)
            .build(),

        // 对话历史 --- 本次会话的完整消息窗口
        MessageChatMemoryAdvisor.builder(MessageWindowChatMemory.builder().maxMessages(100).build())
            .build(),

        // 工具调用
        ToolCallAdvisor.builder().disableInternalConversationHistory().build())
    .build();

4. 查看效果

第一次会话:

USER> 我叫 Alice。我是后端工程师,我偏好简短的回答。 USER> 记住:本季度我们正在从 PostgreSQL 迁移到 CockroachDB。

第二次会话(新的 JVM 进程):

USER> 你了解我什么? ASSISTANT> 你是 Alice,一名后端工程师。你偏好简短的回答。 你本季度正在从 PostgreSQL 迁移到 CockroachDB。

示例项目

仓库中提供了三个可运行的示例:

memory-tools-advisor-demo (选项 A) --- AutoMemoryToolsAdvisor + ToolCallAdvisor + MessageChatMemoryAdvisor + 自定义日志顾问,带有双条件整理触发器。

memory-tools-demo (选项 B) --- 手动配置 AutoMemoryTools 并配合 TodoWriteTool,展示了显式的系统提示词组合。

memory-filesystem-tools-demo (选项 C) --- 使用 FileSystemTools + ShellTools 实现相同的记忆约定,无需专用记忆工具。

这三个示例均支持 Anthropic Claude、Google Gemini 和 OpenAI——只需在 pom.xml 中取消注释所需的提供商并设置 API 密钥即可。## 总结

AutoMemoryTools 是 Anthropic 在 Claude CodeClaude API 内存工具规范 中所开创的内存模式的 Spring AI 移植版。它通过 Spring AI 为任何 LLM 提供商提供了包括 MEMORY.md 索引、类型化文件、两步保存以及沙盒化六操作 API 在内的完整功能。

长期记忆是连接“无状态 LLM 调用”与“真正有用的智能体”之间缺失的一环。添加 AutoMemoryToolsAdvisor,你的智能体便能开始积累跨会话的持久化知识。

系列文章链接:

资源

Spring AI Agent Utils

示例项目