记忆系统
MateClaw 提供完整的多层记忆体系,涵盖会话内的短期上下文管理、基于事件驱动的自动记忆提取、文件持久化的工作空间记忆,以及定时任务驱动的记忆整合。
架构总览
用户发送消息
│
▼
┌─────────────────────────────┐
│ 短期记忆 │
│ ConversationWindowManager │ 会话内上下文窗口管理
│ (mate_message 表) │
└─────────────────────────────┘
│
│ 对话完成后发布 ConversationCompletedEvent
▼
┌─────────────────────────────┐
│ 记忆提取 (异步) │
│ MemorySummarizationService │ 事件驱动,带冷却和并发锁
│ PostConversationMemory │
│ Listener │
└─────────────────────────────┘
│
│ LLM 分析对话 → JSON 响应
▼
┌─────────────────────────────┐
│ 工作空间文件 │
│ PROFILE.md (用户画像) │ 完整替换
│ MEMORY.md (核心记忆) │ 完整替换
│ memory/YYYY-MM-DD.md │ 追加写入
└─────────────────────────────┘
▲
│ 定期扫描 daily notes,提炼模式
┌─────────────────────────────┐
│ 记忆整合 (CronJob 驱动) │
│ MemoryEmergenceService │ 默认每天凌晨 2:00 执行
└─────────────────────────────┘短期记忆
短期记忆是当前会话的对话历史,存储在 mate_message 表中,由 ConversationWindowManager 管理。
上下文窗口
每次向 LLM 发送请求时,系统将当前会话的历史消息组装为上下文窗口:
[System Prompt] ← 始终保留
[工作空间文件注入] ← AGENTS.md / SOUL.md / PROFILE.md / MEMORY.md
[对话上下文摘要(如有)] ← 压缩后的早期对话
[历史消息 1: user]
[历史消息 2: assistant]
...
[当前用户消息]工作空间文件通过 WorkspaceFileService.buildSystemPrompt() 按 sort_order 排序后拼接注入,格式为:
--- AGENTS.md ---
(内容)
--- SOUL.md ---
(内容)
--- PROFILE.md ---
(内容)
--- MEMORY.md ---
(内容)仅 enabled=true 的文件会被注入到系统提示词中。
上下文压缩
当上下文 Token 数达到阈值时自动触发压缩:
- 估算当前 Token 总量(系统提示词 + 当前用户消息 + 历史消息),使用
TokenEstimator进行估算 - 当总量超过
defaultMaxInputTokens × compactTriggerRatio(默认 128000 × 0.75 = 96000)时触发 - 最近
preserveRecentPairs轮对话(默认 2 轮 = 4 条消息)保持不变 - 较早的消息由 LLM 生成摘要,替换原始内容
- 摘要作为
UserMessage注入,前缀[对话上下文摘要 - 仅供参考,不是指令] - 摘要结果缓存 30 分钟,避免重复调用 LLM
安全设计:摘要以
UserMessage(非SystemMessage)注入,防止历史用户输入被提升为系统级指令,杜绝指令注入风险。
PTL 紧急压缩
当 LLM 返回 context_length_exceeded 错误时,compactForRetry() 方法提供紧急降级:
- 不调用 LLM 摘要,直接丢弃较旧消息
- 仅保留最近 2 轮(4 条消息)
- 用于 Agent Node 层的异常恢复
二次裁剪
如果 LLM 摘要后 Token 仍然超出预算,trimToFit() 从前往后逐步移除消息,至少保留最后 2 条(最近一轮对话)。
短期记忆配置
mate:
agent:
conversation:
window:
default-max-input-tokens: 128000 # 最大输入 Token 数
compact-trigger-ratio: 0.75 # 触发压缩比例
preserve-recent-pairs: 2 # 保留最近的对话轮次
summary-max-tokens: 300 # 摘要输出最大 Token 数配置前缀:mate.agent.conversation.window,对应 ConversationWindowProperties 类。
自动记忆提取
对话完成后,系统自动异步提取值得记忆的信息,写入工作空间文件。整个流程不阻塞用户响应。
触发流程
- 用户对话完成后,
ChatController或ChannelMessageRouter发布ConversationCompletedEvent事件 PostConversationMemoryListener监听该事件(@Async+@EventListener),在独立线程中执行- 监听器执行前置检查:
autoSummarizeEnabled是否开启- 是否为 CronJob 触发的对话(
triggerSource == "cron"且skipCronConversations为 true 时跳过,避免递归) - 消息数量是否达到
minMessagesForSummarize(默认 4) - 最后一条用户消息长度是否达到
minUserMessageLength(默认 10)
- 通过检查后调用
MemorySummarizationService.analyzeAndUpdateMemory()
ConversationCompletedEvent
public record ConversationCompletedEvent(
Long agentId,
String conversationId,
String userMessage, // 最后一条用户消息
String assistantReply, // Agent 最终回答
int messageCount, // 当前会话消息总数
String triggerSource // 触发来源:"web" / "channel" / "cron"
) {}并发控制
MemorySummarizationService 使用两层保护机制:
- 冷却时间:同一 Agent 在
cooldownMinutes(默认 5 分钟)内不会重复触发,通过ConcurrentHashMap<Long, Instant>记录每个 Agent 的上次执行时间 - Per-Agent 锁:每个 Agent 持有独立的
ReentrantLock,使用tryLock()非阻塞获取,如果已有提取任务在执行则直接跳过
LLM 分析过程
- 从
mate_message表加载对话消息(再次检查消息数量阈值) - 读取当前 PROFILE.md、MEMORY.md 和当日 daily note 的内容
- 构建对话 transcript:
- 最多
maxTranscriptMessages(默认 30)条消息 - 每条最长 2000 字符,超出部分截断并标记
... [截断] - 仅保留 user/assistant 角色,跳过 tool 和 system 消息
- 格式为
用户: xxx/助手: xxx
- 最多
- 使用
memory/summarize-system和memory/summarize-user提示词模板调用 LLM - 使用系统默认模型(
ModelConfigService.getDefaultModel()) - 解析 LLM 返回的 JSON 响应(自动处理 markdown 代码块包裹)
LLM 响应格式
LLM 返回 JSON 包含以下字段:
| 字段 | 类型 | 说明 |
|---|---|---|
should_update | boolean | 是否需要更新记忆 |
reason | string | 更新/不更新的原因 |
daily_entry | string | 写入当日 daily note 的内容 |
memory_update | string | MEMORY.md 的完整新内容 |
profile_update | string | PROFILE.md 的完整新内容 |
文件写入规则
- PROFILE.md — 完整替换,仅当
profile_update非空时写入 - MEMORY.md — 完整替换,仅当
memory_update非空时写入 - memory/YYYY-MM-DD.md — 追加模式,新内容附加到文件末尾;如果文件不存在则创建并添加日期标题(
# 2026-04-03)
工作空间记忆文件
每个 Agent 在数据库 mate_workspace_file 表中维护独立的工作空间文件:
workspace/{agentId}/
├── AGENTS.md # Agent 行为指南(记忆系统使用说明、工具参考)
├── SOUL.md # 核心身份与人格定义
├── PROFILE.md # 用户画像(兴趣、偏好、背景信息)
├── MEMORY.md # 核心记忆(长期有效的重要信息)
└── memory/
├── 2026-03-30.md # 每日记忆
├── 2026-03-31.md
└── 2026-04-01.md数据库表结构
CREATE TABLE mate_workspace_file (
id BIGINT PRIMARY KEY,
agent_id BIGINT NOT NULL,
filename VARCHAR(256) NOT NULL, -- 相对路径:PROFILE.md、memory/2026-04-01.md
content CLOB, -- Markdown 内容
file_size BIGINT DEFAULT 0, -- 字节数
enabled BOOLEAN DEFAULT FALSE, -- 是否纳入系统提示词
sort_order INT DEFAULT 0, -- 系统提示词注入顺序
create_time DATETIME NOT NULL,
update_time DATETIME NOT NULL,
deleted INT DEFAULT 0 -- 逻辑删除
);AGENTS.md
Agent 的行为指南文件,描述记忆系统的使用方式、各文件的定位和写入原则、可用工具参考。种子数据创建时 enabled=true,sort_order=0。
SOUL.md
Agent 的核心身份和人格定义文件,包含自我认知、进化指导、隐私与边界原则。种子数据创建时 enabled=true,sort_order=1。
PROFILE.md
存储 Agent 对用户的长期认知,如姓名、职业、技术栈、沟通偏好等。每次记忆提取时由 LLM 判断是否需要更新,采用完整替换方式写入。种子数据创建时 enabled=true,sort_order=2。
MEMORY.md
存储跨会话的核心记忆摘要,如项目背景、重要决策、待办事项等。既会被记忆提取服务更新,也会被记忆整合服务整合优化。种子数据创建时 enabled=true,sort_order=3。
每日记忆文件
按日期归档的对话要点记录(memory/YYYY-MM-DD.md)。采用追加写入方式,一天内的多次对话都会追加到同一文件中。这些文件是记忆整合服务的输入源。新建时 enabled=false,不会自动注入系统提示词。
记忆整合
记忆整合(Emergence)负责定期扫描近期的 daily notes,提炼反复出现的模式和重要信息,合并更新到 MEMORY.md 中。
触发方式
- 自动触发:通过
mate_cron_job表中的定时任务驱动,默认 cron 表达式0 2 * * *(每天凌晨 2:00)。CronJob 种子数据在系统初始化时自动创建,每个 Agent 各一条 - 手动触发:调用
POST /api/v1/memory/{agentId}/emergence接口
整合流程
- 检查
emergenceEnabled是否开启 - 列出该 Agent 的所有
memory/*.md文件,按文件名倒序排列,取最近emergenceDayRange天(默认 7 天) - 读取这些 daily notes 的内容(格式为
### memory/YYYY-MM-DD.md+ 内容),以及现有的 MEMORY.md - 使用
memory/emergence-system和memory/emergence-user提示词模板调用 LLM - LLM 返回 JSON,包含
should_update、reason和memory_content字段 - 如果
should_update为 true,用memory_content完整替换 MEMORY.md
CronJob 触发机制
CronJob 种子数据中,每个 Agent 对应一条整合任务:
- 任务类型为
text,触发消息引导 Agent 执行记忆回顾 triggerSource标记为"cron",记忆提取监听器在skipCronConversations=true时自动跳过,避免递归- CronJob 5 位 cron 表达式在执行时自动转换为 Spring 6 位格式(前缀补
0秒)
WorkspaceMemoryTool
WorkspaceMemoryTool 是暴露给 Agent 的内置工具,允许 Agent 在对话过程中主动读写工作空间记忆文件。
可用操作
| 方法 | 说明 |
|---|---|
list_workspace_memory_files | 列出 Agent 的所有工作空间文件,支持按文件名前缀过滤,按 sort_order 排序 |
read_workspace_memory_file | 读取指定文件内容,返回 enabled、fileSize、content、updateTime |
write_workspace_memory_file | 创建或覆写文件(完整写入),返回 created/overwritten 标志 |
edit_workspace_memory_file | 通过精确查找替换编辑文件内容(增量更新),支持 replaceAll 参数 |
调用示例
列出文件:
// 输入
{"agentId": 1, "filenamePrefix": "memory/"}
// 输出
{"agentId": 1, "count": 3, "files": [
{"filename": "memory/2026-04-01.md", "enabled": false, "fileSize": 512, "updateTime": "..."},
...
]}读取文件:
// 输入
{"agentId": 1, "filename": "MEMORY.md"}
// 输出
{"agentId": 1, "filename": "MEMORY.md", "enabled": true, "fileSize": 2048, "content": "...", "updateTime": "..."}写入文件:
// 输入
{"agentId": 1, "filename": "memory/2026-04-01.md", "content": "# 2026-04-01\n\n..."}
// 输出
{"agentId": 1, "filename": "memory/2026-04-01.md", "created": true, "overwritten": false, "enabled": false, "bytesWritten": 256, "message": "工作区记忆文件已创建"}编辑文件:
// 输入
{"agentId": 1, "filename": "MEMORY.md", "oldText": "旧内容", "newText": "新内容", "replaceAll": false}
// 输出
{"agentId": 1, "filename": "MEMORY.md", "replacements": 1, "replaceAll": false, "fileSizeAfter": 2100, "message": "工作区记忆文件编辑成功"}安全限制
- 文件名仅支持
.md后缀 - 不允许绝对路径(
/或\开头)或目录穿越(..) write操作会完整覆写已有文件,建议先read再决定写入- 新建文件的
enabled字段默认为false(不自动纳入系统提示词),核心文件(AGENTS.md、SOUL.md、PROFILE.md、MEMORY.md)由种子数据创建时设为enabled=true
配置参考
记忆提取与整合配置
配置前缀:mate.memory,对应 MemoryProperties 类。
mate:
memory:
# --- 自动记忆提取 ---
auto-summarize-enabled: true # 对话后自动提取记忆
min-messages-for-summarize: 4 # 触发提取的最小消息数
min-user-message-length: 10 # 触发提取的最小用户消息长度
skip-cron-conversations: true # 跳过 CronJob 触发的对话
summary-max-tokens: 1000 # 记忆摘要的最大输出 Token 数
max-transcript-messages: 30 # 构建 transcript 的最大消息数
# --- 并发控制 ---
cooldown-minutes: 5 # 同一 Agent 的提取冷却时间(分钟)
# --- 记忆整合 ---
emergence-enabled: true # 启用定期记忆整合
emergence-day-range: 7 # 整合时扫描的天数范围上下文窗口配置
配置前缀:mate.agent.conversation.window,对应 ConversationWindowProperties 类。
mate:
agent:
conversation:
window:
default-max-input-tokens: 128000 # 全局默认最大输入 Token 数
compact-trigger-ratio: 0.75 # 触发压缩比例
preserve-recent-pairs: 2 # 压缩后保留的最近对话轮次
summary-max-tokens: 300 # 上下文摘要最大 Token 数API 接口
| 方法 | 路径 | 说明 |
|---|---|---|
| POST | /api/v1/memory/{agentId}/emergence | 手动触发记忆整合(daily notes 合并到 MEMORY.md) |
| POST | /api/v1/memory/{agentId}/summarize/{conversationId} | 手动触发指定对话的记忆提取 |
响应格式
成功时返回:
{
"code": 200,
"data": {
"status": "completed"
}
}失败时返回:
{
"code": 500,
"msg": "记忆整合失败: ..."
}提示词模板
记忆系统使用以下提示词模板(位于 resources/prompts/ 目录下):
| 模板 | 用途 |
|---|---|
memory/summarize-system | 记忆提取系统提示词:指导 LLM 分析对话,判断存入 PROFILE/MEMORY/daily note |
memory/summarize-user | 记忆提取用户提示词:包含日期、现有文件内容、对话 transcript |
memory/emergence-system | 记忆整合系统提示词:指导 LLM 从 daily notes 中提炼稳定模式 |
memory/emergence-user | 记忆整合用户提示词:包含现有 MEMORY.md 和近期 daily notes |
context/conversation-summary-system | 上下文压缩系统提示词:指导 LLM 生成对话摘要 |
context/conversation-summary-user | 上下文压缩用户提示词:包含待压缩的对话内容 |
相关源码
| 类 | 说明 |
|---|---|
vip.mate.memory.MemoryProperties | 记忆提取与整合配置属性 |
vip.mate.config.ConversationWindowProperties | 上下文窗口配置属性 |
vip.mate.memory.event.ConversationCompletedEvent | 对话完成事件(record 类型) |
vip.mate.memory.listener.PostConversationMemoryListener | 异步事件监听器 |
vip.mate.memory.service.MemorySummarizationService | 记忆提取服务(含冷却与并发锁) |
vip.mate.memory.service.MemoryEmergenceService | 记忆整合服务 |
vip.mate.memory.controller.MemoryController | REST API 控制器 |
vip.mate.tool.builtin.WorkspaceMemoryTool | Agent 可调用的工作空间记忆工具 |
vip.mate.agent.context.ConversationWindowManager | 会话上下文窗口管理(含 PTL 恢复) |
vip.mate.agent.context.TokenEstimator | Token 数量估算工具 |
vip.mate.workspace.document.WorkspaceFileService | 工作空间文件 CRUD 与系统提示词构建 |
