面向多智能体系统的记忆管理方案文档。解决 Agent 记忆隔离、子 Agent 结果丢失、会话上下文断裂和记忆膨胀四大痛点。适用于 Agent 开发者、多智能体系统架构师,以及 Hermes / LangChain / CrewAI 等框架的用户。
在 Hermes 多智能体体系中,主Agent(如 lee)通过 agency-agents-zh 角色库调度 305 个专业角色Agent来完成不同任务。这个架构在分工专业化方面表现出色,但随之而来的是四个核心痛点:
每个角色Agent以独立会话方式运行,会话之间不共享任何记忆。当用户连续两次调度同一个角色Agent(例如让"前端审查Agent"审查两个不同的组件),第二次调度时Agent对用户偏好、上次审查中发现的问题模式、既定的代码风格偏好一无所知。用户不得不重复告知相同的上下文信息,这是时间和token的双重浪费。
主Agent通过 delegate_task 创建的子Agent在工作完成后,其结果仅存在于该子Agent的瞬时会话中。一旦会话结束,子Agent被销毁,其产出的分析结论、代码片段、决策依据也一并消失。后续如果需要回溯"这个决定是谁做的、基于什么理由做的",无迹可寻。
每次主Agent开启新会话时,之前调度过的角色Agent积累的经验和教训全部归零。Agent体系没有"成长"可言——这意味着第100次使用和第1次使用没有任何区别。
如果不加控制地将所有历史信息塞进Agent上下文,又会走向另一个极端:上下文窗口被大量无关信息占据,推理质量下降,token消耗飙升,响应速度变慢。
如何让每个角色Agent拥有"个人记忆"并能持续成长,同时避免记忆膨胀污染上下文,还要确保子Agent的工作成果不丢失?
这正是方案B要系统化解决的问题。
方案B的核心设计理念是:每种一级Agent拥有一块独立的、持久化的记忆空间,主Agent作为记忆的调度者和守门人。
┌─────────────────────────────────┐
│ GBrain 向量数据库 │
│ ┌─────────────────────────────┐ │
│ │ agents/前端审查 │ │
│ │ 语义索引 + 知识图谱 │ │
│ ├─────────────────────────────┤ │
│ │ agents/后端架构 │ │
│ │ 语义索引 + 知识图谱 │ │
│ ├─────────────────────────────┤ │
│ │ agents/安全审计 │ │
│ │ 语义索引 + 知识图谱 │ │
│ ├─────────────────────────────┤ │
│ │ ... (仅已调度的Agent有页面) │ │
│ └─────────────────────────────┘ │
└──────────┬──────────────────────┘
│
语义检索 ↑↓ 写入维护
│
┌──────────┴──────────────────────┐
│ 主Agent (lee) │
│ - 调度前语义检索提取记忆 │
│ - 压缩打包为结构化简报 │
│ - 注入子会话context │
│ - 回收子Agent工作结果 │
│ - 写入角色Agent记忆页 │
│ - 完全的记忆主权管理 │
└──┬────────────┬──────────────────┘
│ │
按需注入 │ │ 结果回收
↓ ↓
┌────────┴──┐ ┌────┴──────────┐
│ 角色Agent │ │ 子Agent │
│ (一级Agent) │ │ (delegate) │
│ 独立会话 │ │ 无独立记忆页 │
└────────────┘ └───────────────┘
- 主Agent ↔ GBrain:主Agent是唯一与记忆库直接交互的角色。它负责检索、写入、审核、诊断所有Agent记忆。
- GBrain ↔ 角色Agent:不直接通信。角色Agent无法自主访问记忆库,所有记忆操作必须经过主Agent。
- 主Agent ↔ 角色Agent:主Agent在调度角色Agent前,从GBrain检索该角色的相关记忆,压缩为结构化简报后注入到角色Agent的独立会话context中。
这是方案B最核心的设计哲学之一:一个角色Agent不被调度,就永远不会创建它的记忆页。305个角色库中,用户实际常用的可能只有10-20个。如果每个都预先创建记忆页,会产生大量空页面和无意义的索引开销。惰性索引确保了零开销默认——只有真正被使用过的Agent才会占用存储和计算资源。
原理:每种一级Agent拥有独立的、边界清晰的记忆页,路径格式为 agents/<角色名>。不同Agent的记忆在物理和逻辑上完全隔离,一个Agent无法读取另一个Agent的记忆内容。
机制:
- 每个记忆页是GBrain中的一个独立页面(slug:
agents/<角色名>) - 页面包含该Agent专属的frontmatter、工作记忆、项目上下文、子Agent记录
- 向量嵌入按页索引,检索时限定在目标Agent的页面范围内
示例:
agents/前端审查 → 前端审查Agent的独立记忆空间
agents/后端架构 → 后端架构Agent的独立记忆空间
agents/安全审计 → 安全审计Agent的独立记忆空间
为什么需要隔离:不同角色的知识域完全不同。前端审查Agent不需要知道后端架构Agent的数据库选型讨论,安全审计Agent不需要知道UI风格偏好的演变。隔离确保了检索精度和记忆纯度。
原理:Agent首次被主Agent调度时,才由主Agent为其创建记忆页。创建时机 = "即用即建",不用则不建。
机制:
- 主Agent决定调度角色Agent X
- 检查
agents/X页面是否存在 - 若不存在 → 创建初始记忆页(含frontmatter和基础结构)
- 若已存在 → 进行语义检索,提取相关记忆
- 将检索结果注入本次会话context
关键意义:
- 零开销默认:305个角色库,实际常驻记忆页可能只有十几个
- 自动扩展:随着用户使用,记忆页自然增长,无需手动配置
- 无需维护矩阵:不存在"需要预置哪些Agent记忆"的决策负担
示例场景:
用户: "帮我审查这个React组件的性能"
主Agent:
1. 决定调度"前端性能审计"角色Agent
2. 检查 agents/前端性能审计 → 不存在
3. 创建记忆页 agents/前端性能审计
4. 注入用户偏好(如React 18、TypeScript严格模式等全局上下文)
5. 调度执行
原理:主Agent在每次调度前,通过语义检索从目标Agent的记忆页中提取与当前任务最相关的记忆片段,压缩打包为结构化简报,注入到独立会话的上下文中。注入量严格限制在上下文窗口的30%以内。
机制:
- 主Agent解析用户指令,提取任务关键语义
- 调用GBrain语义检索(
mcp_gbrain_query),以任务语义为查询向量,在agents/<角色名>页面范围内检索 - 取 top-k 相关记忆片段
- 压缩为结构化简报(去除冗余、摘要化长文本)
- 注入独立会话context,配合
≤30%窗口硬约束
≤30%窗口约束的合理性:
- 为当前任务留出70%以上的空间
- 防止历史记忆挤压当前推理能力
- 确保Agent不会"活在过去"
注入简报格式示例:
[记忆简报 - 前端审查Agent]
- 用户偏好:React 18 + TypeScript严格模式,函数组件优先
- 上次审查模式:重点关注useMemo/useCallback滥用、Props drilling
- 已知问题类型:大列表无虚拟滚动(3次警告)、useEffect清理函数缺失(2次警告)
- 项目约定:组件文件命名 PascalCase,样式使用CSS Modules
原理:子Agent(通过delegate_task创建的临时Agent)工作完成后,由创建它的主Agent回收其工作结果,将关键产出写入对应角色Agent记忆页的"子Agent记录"段落,然后子Agent被销毁。
机制:
子Agent生命周期:
创建(delegate_task) → 继承记忆(context) → 执行工作 →
返回结果 → 主Agent回收结果 → 写入记忆页 → 子Agent销毁
回收记录格式:
## 子Agent记录
### 2026-05-08 性能分析任务
- **任务**:分析 Dashboard 组件的渲染性能瓶颈
- **结论**:主要瓶颈在 DataTable 缺少 React.memo 包裹,建议添加虚拟滚动
- **产出**:性能分析报告 + 优化建议清单
- **关键决策**:暂不引入 react-window(依赖体积考虑)为什么子Agent无独立记忆页:子Agent是临时性的、任务粒度的执行单元。给它独立记忆页会导致页面爆炸(每次delegate都产生新页面),且子Agent的工作结果应该归属到发起它的角色Agent名下,而非独立存在。
原理:主Agent对所有角色Agent的记忆拥有完全的管理权,涵盖查询、审核、诊断三种操作模式。角色Agent不能自主修改自己的记忆页。
三种管理场景:
| 场景 | 触发条件 | 主Agent操作 |
|---|---|---|
| 快速问答 | 用户问"前端审查Agent之前提过什么问题?" | 检索目标Agent记忆页,直接回答 |
| 任务定位 | 用户说"检查安全审计Agent上次的遗留问题" | 检索 + 提取待办/遗留问题列表 |
| 错误诊断 | 某Agent出现异常行为 | 审查该Agent记忆页的历史记录,定位根因 |
主权管理的意义:
- 防止记忆污染:角色Agent不能自行篡改记忆
- 保证一致性:所有记忆变更经过主Agent统一审核
- 可追溯性:每次记忆操作都有记录可查
在方案选型过程中,曾提出两种候选方案:
| 维度 | 方案A(项目日志+Git版本管理) | 方案B(独立GBrain记忆页+惰性索引) |
|---|---|---|
| Agent成长性 | ❌ 无个人记忆,Agent无法成长 | ✅ 每个Agent有独立记忆,持续积累经验 |
| 项目稳定性 | ❌ 项目日志过于主观,依赖书写者习惯,不利于中长期项目 | ✅ 语义检索 + 结构化记忆页,客观且稳定 |
| 运维成本 | ||
| 迁移成本 | ✅ 极低(纯Git) | |
| 检索精度 | ❌ 纯关键词/文件名检索,无法语义匹配 | ✅ 语义检索 + 重排,精准匹配 |
| 记忆膨胀控制 | ❌ 无机制,日志无限增长 | ✅ 按需注入 + ≤30%窗口硬约束 |
| 子Agent结果保留 | ❌ 无回收机制,结果可能遗失在commit中 | ✅ 结构化回收 + 写入记忆页 |
| 跨会话连续性 | ❌ 依赖人工翻阅日志 | ✅ 自动语义检索注入 |
方案A的核心问题是忽略了"Agent自身"这个主体。项目日志记录的是"项目发生了什么",而不是"Agent学到了什么"。在一个多Agent体系中,Agent应该像人类专家一样拥有自己的经验和判断力积累,而非每次都从零开始翻阅项目历史。
每个一级Agent的记忆页(agents/<角色名>)遵循统一的四段式结构:
---
name: <角色名(中文)>
category: <角色分类,如"前端开发">
first_used: 2026-05-08
projects:
- <关联项目1>
- <关联项目2>
tags: [agent-memory]
type: agent-page
---
# <角色名> Agent 记忆
## 项目上下文
<!-- 与该Agent相关的项目背景、技术栈、用户在此项目上的偏好 -->
### <项目名>
- **技术栈**:React 18 + TypeScript + Vite
- **用户偏好**:函数组件优先、CSS Modules、严格类型检查
- **关键约束**:Node 18+、ESM only
## 工作记忆
<!-- Agent在工作过程中积累的经验、教训、模式认知 -->
- 2026-05-08 | 发现Dashboard组件存在useEffect清理函数缺失问题,后续审查需重点关注此模式
- 2026-05-08 | 用户明确偏好使用React.memo而非useMemo进行组件级优化
## 子Agent记录
<!-- 通过delegate_task创建的子Agent的工作回收记录 -->
### 2026-05-08 Dashboard性能分析
- **任务**:分析Dashboard组件的渲染性能
- **结论**:DataTable缺少React.memo,大列表无虚拟滚动
- **产出**:性能报告 + 优化建议
- **关键决策**:暂不引入react-window| 段落 | 存放内容 | 更新频率 | 更新触发 |
|---|---|---|---|
| frontmatter | 角色元信息(名称、分类、首次使用时间、关联项目) | 低 | Agent首次被调度时创建,项目变更时更新 |
| 项目上下文 | 项目级背景信息:技术栈、用户偏好、关键约束 | 低 | 项目配置变更、用户偏好调整 |
| 工作记忆 | Agent个人经验积累:发现问题、认知模式、经验教训 | 中 | 每次调度结束后,主Agent评估是否需要更新 |
| 子Agent记录 | delegate_task子Agent的工作回收摘要 | 高 | 每次子Agent完成任务后立即回收写入 |
主Agent收到任务
│
▼
┌──────────────────────┐
│ 判断:是否需要子Agent? │
└──────┬───────────────┘
│ 是
▼
┌──────────────────────┐
│ ① 创建子Agent │
│ delegate_task() │
└──────┬───────────────┘
│
▼
┌──────────────────────┐
│ ② context继承记忆 │
│ 注入角色简报 │
└──────┬───────────────┘
│
▼
┌──────────────────────┐
│ ③ 子Agent执行工作 │
│ 独立会话中完成 │
└──────┬───────────────┘
│
▼
┌──────────────────────┐
│ ④ 子Agent返回结果 │
│ → 创建者(主Agent) │
└──────┬───────────────┘
│
▼
┌──────────────────────┐
│ ⑤ 主Agent回收结果 │
│ - 提取关键结论 │
│ - 摘要化产出内容 │
│ - 记录关键决策 │
└──────┬───────────────┘
│
▼
┌──────────────────────┐
│ ⑥ 写入记忆页 │
│ agents/<角色> │
│ "子Agent记录"段落 │
└──────┬───────────────┘
│
▼
┌──────────────────────┐
│ ⑦ 销毁子Agent │
│ 会话结束,无残留 │
└──────────────────────┘
- 子Agent不创建独立记忆页 — 每个子Agent是临时的,它的记忆归属到创建它的角色Agent名下
- context继承是单向的 — 子Agent接收主Agent注入的记忆上下文,但不会反向修改角色Agent的记忆页
- 回收是主Agent的职责 — 子Agent不可自己写记忆
- 回收记录包含四要素:任务描述、结论、产出、关键决策
- 主Agent定期抽查回收完整性 — 确保无遗漏
本记忆管理方法论定义的是协议层规范(记忆页结构、检索-注入-回收流程、主权管理规则),而非绑定特定技术实现。任何支持以下能力的向量数据库均可作为后端:
- 语义检索(向量相似度搜索)
- 分区/命名空间隔离(对应
agents/<角色名>页面隔离) - 结构化元数据存储(对应frontmatter)
GBrain是本体系的推荐后端(也是当前生产环境使用的后端),其优势:
| 特性 | 说明 |
|---|---|
| 本地化零依赖 | PGLite嵌入式Postgres,无需独立数据库服务,一个二进制即可运行 |
| pgvector原生 | 向量存储和检索内建在数据库层,无需额外中间件 |
| OMLX本地推理 | bge-m3 1024-dim 向量 + jina-reranker-v3-mlx 重排,全本地化 |
| MCP协议原生 | 51个MCP工具,Agent可直接通过函数调用操作记忆 |
| 增量同步 | git仓库增量索引,Obsidian vault原生支持 |
| 知识图谱 | 页面间链接关系、timeline自动提取 |
| 方案 | 适用场景 | 注意点 |
|---|---|---|
| Milvus | 大规模向量检索(百万级+) | 需独立部署和维护 |
| Pinecone | 全托管,不想维护基础设施 | 云端服务,数据出境注意合规 |
| Chroma | 轻量级嵌入,适合原型开发 | 生产环境稳定性待验证 |
| LanceDB | 本地化列式向量存储 | 生态较新,工具链不如pgvector成熟 |
如果使用GBrain,最小化配置如下:
# 1. 安装GBrain
npm install -g @gbrain/cli
# 2. 初始化(关联Obsidian vault作为知识源)
gbrain init --vault /path/to/obsidian/vault
# 3. 安装向量模型
gbrain models install bge-m3
gbrain models install jina-reranker-v3-mlx
# 4. 生成向量嵌入
gbrain embed --all
# 5. 启动MCP服务
gbrain serve --mcpMCP客户端配置(~/.hermes/mcp.json):
{
"mcpServers": {
"gbrain": {
"command": "gbrain",
"args": ["serve", "--mcp"]
}
}
}Agent侧调用示例(语义检索Agent记忆):
{
"tool": "mcp_gbrain_query",
"parameters": {
"query": "前端性能审查 React 组件优化 useMemo",
"path": "agents/前端审查",
"limit": 5
}
}第一步:确认GBrain可用
gbrain --version
# 预期输出: gbrain v0.28.6 或更高第二步:确认MCP连接
在Hermes Agent会话中测试:
调用 mcp_gbrain_get_stats,确认返回正常的页面数和chunk统计。
第三步:验证惰性索引机制
让主Agent调度一个新角色(之前从未调度过的),观察日志:
主Agent: 检查 agents/<新角色> → 不存在 → 创建记忆页 → 调度执行
第四步:验证记忆持久化
连续两次调度同一个角色Agent,第二次调度前主Agent应该检索到第一次产生的记忆并注入。
| 文件 | 路径 | 用途 |
|---|---|---|
| 记忆协议规范 | ~/.hermes/skills/AGENT-MEMORY-PROTOCOL.md |
全局记忆管理协议 |
| 主Agent灵魂规范 | ~/.hermes/skills/SOUL.md |
主Agent的行为规范(含记忆管理职责) |
| Agent记忆页 | 在GBrain中,slug格式为 agents/<角色名> |
每个一级Agent的独立记忆空间 |
| 角色库 | ~/.hermes/skills/agency-agents-zh/ |
305个角色定义 |
- GBrain已安装并运行MCP服务
- 主Agent拥有
mcp_gbrain_query、mcp_gbrain_put_page、mcp_gbrain_get_page工具权限 - 主Agent的SOUL.md中已包含记忆管理职责(§八)
- agency-agents-zh角色库已安装
- 首次调度任一Agent后,确认
agents/<角色名>记忆页已自动创建
Q: 需要事先为所有305个角色创建记忆页吗? A: 不需要。惰性索引机制确保只有真正被调度过的Agent才创建记忆页。
Q: 子Agent的结果一定会被回收吗? A: 主Agent在SOUL.md中有明确的回收监督职责。建议主Agent在每次会话结束前主动抽查回收记录的完整性。
Q: 可以用Milvus/Pinecone替代GBrain吗? A: 可以。只要实现相同的检索-注入-回收流程,协议层不绑定具体后端。
Q: 记忆页会无限膨胀吗? A: 不会。按需注入的≤30%窗口约束确保每次只注入最相关的记忆片段,旧记忆自然衰减。同时主Agent可以定期对记忆页进行摘要化压缩。
| 术语 | 定义 |
|---|---|
| 一级Agent | 角色库中定义的、拥有独立记忆页的Agent(如"前端审查"、"后端架构") |
| 子Agent | 通过delegate_task创建的临时Agent,无独立记忆页,任务完成后销毁 |
| GBrain | 本地化向量知识库,基于PGLite+pgvector+OMLX |
| 惰性索引 | "不调不用"的Agent记忆页创建策略,首次调度时才创建 |
| 按需注入 | 语义检索 → 压缩打包 → 注入context,限制≤30%窗口 |
| 工作回收 | 子Agent完成 → 创建者回收结果 → 写入记忆页 → 子Agent销毁 |
| 主权管理 | 主Agent对所有Agent记忆的完全管理权 |
文档版本: v1.0 | 最后更新: 2026-05-08