17 KiB
17 KiB
记忆子网 — 训练目标与训练计划
概述
记忆子网类比人脑的海马体与皮层,负责记忆的存储、检索和管理。采用三层记忆架构:文本记忆(短对话临时工作区)、向量记忆(语义检索)、图记忆(结构化知识)。
图数据库记忆设计参考了 TrulyMEM-TrueHumanMEM 项目。
架构定位
记忆子网是整个 NextAgent 架构中唯一与记忆内容交互的模块。Core 不自作主张处理记忆——只负责将查询张量和新信息张量传递过来,如何更新、删除、修改由记忆子网自行完成。
Core 传过来的两样东西
│
┌─────────┴────────────┐
│ │
查询张量 新信息张量
(含意图/种子实体) (含本轮标签/结果)
│ │
└─────────┬────────────┘
│
▼
┌───────────────────────────────────────────────────────────┐
│ 记忆子网(自治) │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ 记忆管理器 (Memory Manager) │ │
│ │ 自行决定: │ │
│ │ → 如何解析查询 → 走文本/向量/图记忆 │ │
│ │ → 如何写入新信息 → 先入文本记忆再处理 │ │
│ │ → 如何更新 → 冲突合并/覆盖/增量 │ │
│ │ → 如何删除 → 软删除/硬删除/归档 │ │
│ │ → 如何关联 → 链路推理/图谱推理 │ │
│ └──────┬─────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 文本记忆(临时工作区) │ │
│ │ 近期对话内容,按时间顺序追加,容量有限 │ │
│ └─────────────────────┬────────────────────────────┘ │
│ │ 定期扫描 (每 N 轮/定时) │
│ ▼ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 记忆处理流水线 │ │
│ │ │ │
│ │ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐│ │
│ │ │ 拆解 │────→│冲突合并│────→│相似合并│────→│分路写入││ │
│ │ └──────┘ └──────┘ └──────┘ └──┬───┘│ │
│ │ │ │ │
│ └───────────────────────────────────────────────┼────┘ │
│ │ │
│ ┌──────────────────────────────────┼──┐ │
│ ▼ ▼ │
│ ┌─────────────────────┐ ┌─────────────────────────┐ │
│ │ 图记忆 │ │ 向量记忆 │ │
│ │ (结构化知识) │ │ (语义向量) │ │
│ │ Entity / RELATES │ │ 索引 + 相似度搜索 │ │
│ │ Cypher 查询 │ │ │ │
│ └─────────────────────┘ └─────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────┐ │
│ │ 张量编/解码层 │ │
│ │ 输入:查询张量 + 新信息张量(来自 Core) │ │
│ │ 输出:记忆结果张量(返回给 Core) │ │
│ └────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────┘
三层记忆
第一层:文本记忆(Text Memory)— 临时工作区
类似人脑工作记忆,保存近期对话内容,按时间顺序追加。容量有限,保证短对话内的上下文连续性。由记忆子网定期处理(拆解/冲突合并/相似合并/分路写入)。
| 项目 | 说明 |
|---|---|
| 存储内容 | 近期对话原始文本 |
| 管理方式 | 按时间顺序追加,溢出时丢弃最旧内容 |
| 处理时机 | 每 N 轮对话或定时触发 |
| 用途 | 短期对话连续性,记忆处理源 |
第二层:向量记忆(Vector Memory)— 语义检索
存储经过嵌入的语义向量,支持语义相似度检索。长对话连续性关键组件之一。
| 项目 | 说明 |
|---|---|
| 索引类型 | IVF_FLAT / HNSW |
| 向量维度 | 768 (匹配标准张量维度) |
| 度量方式 | Cosine Similarity |
| 分片策略 | 按时间桶 + 会话 ID 分片 |
第三层:图记忆(Graph Memory)— 结构化知识
实体-关系图,存储用户偏好、知识图谱等结构化信息。长对话连续性关键组件之二。
参考 TrulyMEM 的图数据库设计:
节点 (Entity) 类型
| 节点类型 | Node Label | 属性 | 说明 |
|---|---|---|---|
| 人设节点 | PersonaNode | name, type, created_at | AI 角色、性格、语气 |
| 任务节点 | TaskNode | task_id, description, state, created_at | 任务概述与状态 |
| 状态节点 | StateNode | name, value, timestamp | 任务或实体的状态 |
| 信息节点 | InfoNode | name, content, source, confidence | 具体信息 |
| 实体节点 | EntityNode | name, type, mention_count, updated_at | 通用实体 |
边 (RELATES) 类型
| 边类型 | 说明 | 示例 |
|---|---|---|
HAS_PERSONA |
人设关联 | AI → PersonaNode |
NEXT_TASK |
任务时间链 | TaskNode → TaskNode |
HAS_STATE |
状态关联 | TaskNode → StateNode |
CONTAINS_INFO |
信息包含 | TaskNode → InfoNode |
RELATES_TO |
通用关联 | EntityNode → EntityNode |
索引设计
// 实体约束
CREATE CONSTRAINT entity_name_constraint IF NOT EXISTS FOR (e:Entity) REQUIRE e.name IS UNIQUE
CREATE CONSTRAINT session_id_constraint IF NOT EXISTS FOR (s:Session) REQUIRE s.session_id IS UNIQUE
// 关系索引
CREATE INDEX rel_created_at IF NOT EXISTS FOR ()-[r:RELATES]-() ON r.created_at
CREATE INDEX rel_type IF NOT EXISTS FOR ()-[r:RELATES]-() ON r.type
CREATE INDEX rel_status IF NOT EXISTS FOR ()-[r:RELATES]-() ON r.status
CREATE INDEX rel_date_bucket IF NOT EXISTS FOR ()-[r:RELATES]-() ON r.date_bucket
// 实体索引
CREATE INDEX entity_type IF NOT EXISTS FOR (e:Entity) ON e.type
CREATE INDEX entity_mention_count IF NOT EXISTS FOR (e:Entity) ON e.mention_count
检索示例
-- 关键字检索
MATCH (e:Entity)-[r:RELATES]->(t:Entity)
WHERE r.status = 'active'
AND (toLower(e.name) CONTAINS '关键词' OR toLower(t.name) CONTAINS '关键词')
RETURN e, r, t
ORDER BY r.created_at DESC
LIMIT 30
-- 会话隔离检索
MATCH (e:Entity)-[r:RELATES]->(t:Entity)
WHERE r.session_id = $session_id AND r.status = 'active'
RETURN e, r, t
-- 热点实体查询
MATCH (e:Entity)
RETURN e.name, e.mention_count, e.type
ORDER BY e.mention_count DESC
LIMIT 10
输入/输出
| 项目 | 说明 |
|---|---|
| 输入1 | 查询张量,fmt_id=2,来自 Core(编码后的查询意图、种子实体) |
| 输入2 | 新信息张量,fmt_id=3,来自 Core(编码后的本轮更新数据;不含具体写入指令) |
| 输出 | 记忆结果张量,fmt_id=1,编码检索到的实体/关系向量 |
| 自治 | 记忆子网自行决定如何写入、更新、删除、修改记忆 |
训练目标
目标 1:图数据库检索准确率
| 指标 | 目标值 | 说明 |
|---|---|---|
| 检索召回率 (Recall@10) | ≥ 90% | 对于已知关系的检索 |
| 检索精确率 (Precision@10) | ≥ 85% | 检索结果的相关性 |
| 关系推理准确率 | ≥ 80% | 通过图路径推理发现隐含关系 |
目标 2:向量检索能力
| 指标 | 目标值 | 说明 |
|---|---|---|
| 语义检索 Recall@10 | ≥ 85% | 向量相似度检索 |
| 跨模态检索准确率 | ≥ 80% | 文本→图像/语音的跨模态检索 |
| 索引构建时间 | ≤ 100ms/1000条 | 向量索引增量更新 |
目标 3:记忆关联发现
| 指标 | 目标值 | 说明 |
|---|---|---|
| 关联发现 F1 | ≥ 75% | 自动发现实体间的隐含关系 |
| 记忆合并准确率 | ≥ 90% | 重复实体的合并不引入错误 |
| 因果链路发现 | ≥ 70% | 从事件序列中发现因果关系 |
目标 4:工作记忆管理
| 指标 | 目标值 | 说明 |
|---|---|---|
| 上下文切换延迟 | ≤ 10ms | 工作记忆的热区缓存命中率 |
| 工作记忆容量 | 4096 tokens | 每轮有效上下文 |
| 缓存命中率 | ≥ 80% | 热区缓存有效减少冷区访问 |
训练计划
阶段 1:图数据库操作基座(无训练)
目标:构建 Neo4j 图数据库操作层,建立基础检索和写入能力
实现:
- 实现 Cypher 查询模板系统
- 实现 MERGE / CREATE 写入逻辑
- 实现会话隔离和索引管理
- 实现软删除和归档机制
参考 TrulyMEM 的 graph_client.py 实现:
class Neo4jGraph:
def recall(self, query_intent, seed_entities, depth=2):
"""检索记忆 - Cypher 驱动"""
# 关键词解析 → 构造 WHERE 子句
# 支持深度扩展(depth 参数)
# 返回实体列表 + 关系列表
def commit(self, triplets, entity_types, temporal_tag):
"""写入记忆 - MERGE + CREATE"""
# 逐条写入三元组
# 自动维护 mention_count
# 记录会话和轮次信息
def purge(self, criteria, mode="soft", new_relation=None):
"""删除/修正记忆"""
# soft: 标记 status='deleted'
# supersede: 标记旧关系 + 创建新关系
阶段 2:图神经网络关联学习
目标:训练 GNN 模型进行知识关联推理
技术路线:
- 模型:R-GCN (Relational Graph Convolutional Network) 或 CompGCN
- 或 使用 GraphSAGE + 关系注意力
训练步骤:
- 构建知识图谱训练集:从现有图数据库中采样子图
- 链接预测任务 (Link Prediction):预测两个实体之间是否存在关系
- 实体分类任务 (Entity Classification):预测实体类型
- 损失函数:交叉熵 + 对比损失 (margin-based ranking loss)
- 训练 200 epochs,使用 Adam 优化器,learning rate 1e-3
- 评估:在验证集上计算 Hits@K 和 MRR
阶段 3:Memory-Augmented Neural Networks (MANN)
目标:训练记忆增强网络,实现高效的读写操作
技术路线:
- 模型:Differentiable Neural Computer (DNC) 或 Neural Turing Machine (NTM)
- 外部记忆矩阵 + 读写头
训练步骤:
- 合成任务训练:copy task, repeat copy, associative recall
- 问答任务训练:bAbI 数据集,测试记忆存取能力
- 长序列建模:训练在长上下文中准确索引信息
- 添加注意力读写机制
- 损失函数:任务损失 + 记忆使用正则化
阶段 4:引用-拉取策略训练
目标:训练决定何时拉取(从冷区)、何时缓存的策略
技术路线:
- 强化学习:基于记忆访问模式的 RL
- 状态:当前任务类型、历史访问模式、缓存状态
- 动作:拉取/缓存/不操作
- 奖励:访问延迟降低 + 缓存命中率
训练步骤:
- 构建模拟环境:包含热区缓存、图数据库、向量数据库
- 收集真实交互日志作为训练数据
- 训练策略网络(Policy Network)
- 使用 PPO 算法优化策略
- 评估:缓存命中率、平均访问延迟
阶段 5:端到端联合训练
目标:将以上阶段整合,与路由子网、逻辑子网联合训练
训练步骤:
- 固定路由子网的输出作为记忆子网的查询输入
- 使用多任务训练:图检索 + 向量检索 + 关联发现
- 损失函数:L = L_retrieval + α * L_link + β * L_cache + γ * L_reg
- 与 Core 的启发式调度策略联合优化
评估指标体系
离线评估
| 指标 | 计算方法 | 目标 |
|---|---|---|
| 图检索 Recall@10 | 检索结果中包含正确答案的比例 | ≥ 90% |
| 向量检索 Recall@10 | 按语义相似度排序的召回率 | ≥ 85% |
| 关联推理 MRR | Mean Reciprocal Rank | ≥ 0.7 |
| 记忆写入一致性 | 写入后检索与原始一致 | ≥ 99% |
| 缓存命中率 | 热区缓存直接命中的比例 | ≥ 80% |
在线评估
| 指标 | 说明 |
|---|---|
| 记忆检索延迟 | Core 发出查询到收到结果的时间 |
| 记忆更新延迟 | Core 发出更新到写入完成的时间 |
| 信息一致性 | 同一信息多次检索结果是否一致 |
| 记忆容量 | 在不显著降低性能前提下的最大存储量 |
新信息张量格式
Core 传递给记忆子网的新信息张量遵循统一通信张量协议:
协议定义
格式: 统一通信张量 (纯数值张量)
prefix[0] = fmt_id = 3 (memory_update)
prefix[1] = parser_id = 3 (memory_update_parser)
payload: 编码后的记忆更新数据
实体编码: [entity_ids..., entity_embeddings...]
关系编码: [relation_ids..., relation_embeddings...]
会话标识: [session_tag, turn_tag, ...]
实体、关系、会话信息通过子网内部的编解码适配器转为张量表示。
记忆子网内部格式(私有)
记忆子网内部维护以下私有数据结构,但对外通信时始终通过统一协议编解码为纯张量:
# 内部数据模型(子网私有,不对外暴露)
@Entity
default_graph = {
"entities": [
{"name": "用户_张三", "type": "PersonaNode", "preference": "摇滚音乐"},
{"name": "项目_NextAgent", "type": "EntityNode"}
],
"relations": [
{"subject": "用户_张三", "relation": "HAS_PERSONA", "object": "喜欢_摇滚", "confidence": 0.95},
{"subject": "用户_张三", "relation": "RELATES_TO", "object": "项目_NextAgent", "confidence": 0.9}
]
}
# 编码适配器将内部数据 → 统一通信张量
# 解码适配器将统一通信张量 → 内部查询结构
def encode_memory_update(entities, relations, session_id, turn_id) -> Tensor:
"""内部格式 → 统一通信张量
1. 将实体名/关系名编码为 embedding 向量
2. 拼接会话标识和时间戳
3. 编码适配器: 查表 fmt_id=3, 拼接前缀
"""
entity_embeds = embed_entity_names(entities) # [n_entities, 768]
relation_embeds = encode_relations(relations) # [n_relations, 768]
meta = tensor([session_hash, turn_id]) # [2]
return codec.encode(fmt_id=3, parser_id=3,
data=concat(entity_embeds, relation_embeds, meta))
def decode_memory_query(comm_tensor: Tensor) -> QueryIntent:
"""统一通信张量 → 内部查询意图
1. codec.decode(comm_tensor) 剥离前缀
2. 将 payload 张量解码为查询条件
3. 返回: 图DB检索参数 / 向量检索参数
"""
payload = codec.decode(comm_tensor) # 剥离 fmt_id=2
query_embed = payload[:768]
return self.query_embed_to_params(query_embed)