mirror of
https://gitcode.com/JianFeeeee/TrulyMEM-TrueHumanMEM.git
synced 2026-10-15 12:15:32 +00:00
添加 图数据库结构设计
This commit is contained in:
254
图数据库结构设计
Normal file
254
图数据库结构设计
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
以下是完整的 Neo4j 图数据库结构设计,针对 OpenClaw 对话记忆场景优化,边上带完整时间戳和元数据:
|
||||||
|
1. 节点(Node)设计
|
||||||
|
实体节点(Entity)
|
||||||
|
(:Entity {
|
||||||
|
name: string, // 实体名称(唯一标识)
|
||||||
|
type: string, // 类型:concept | technology | person | project | decision
|
||||||
|
created_at: datetime, // 首次出现时间
|
||||||
|
updated_at: datetime, // 最后更新时间
|
||||||
|
mention_count: int // 被提及次数(用于权重)
|
||||||
|
})
|
||||||
|
|
||||||
|
会话节点(Session)
|
||||||
|
(:Session {
|
||||||
|
session_id: string, // 会话唯一ID(如 20260408-a7f3)
|
||||||
|
created_at: datetime, // 会话开始时间
|
||||||
|
summary: string, // 会话摘要(可选)
|
||||||
|
status: string // active | archived | compacted
|
||||||
|
})
|
||||||
|
|
||||||
|
2. 关系(Edge)设计
|
||||||
|
核心关系(RELATES)
|
||||||
|
所有对话关系统一使用 RELATES 类型,属性承载时间戳和元数据:
|
||||||
|
(source:Entity)-[:RELATES {
|
||||||
|
// 基础关系属性
|
||||||
|
type: string, // 关系类型:uses | implements | depends_on | rejects | prefers | leads_to | contradicts
|
||||||
|
|
||||||
|
// 时间戳(核心)
|
||||||
|
created_at: datetime, // 关系创建时间(精确到秒)
|
||||||
|
updated_at: datetime, // 最后更新时间(纠错时更新)
|
||||||
|
|
||||||
|
// 来源追踪
|
||||||
|
session_id: string, // 所属会话ID
|
||||||
|
turn_id: int, // 轮次序号(该会话中的第几轮)
|
||||||
|
|
||||||
|
// 角色标记
|
||||||
|
role: string, // "user" | "assistant" | "system"
|
||||||
|
|
||||||
|
// 内容快照
|
||||||
|
context_snippet: string, // 原始文本片段(前100字符)
|
||||||
|
|
||||||
|
// 状态管理
|
||||||
|
status: string, // active | deleted | archived | superseded(被取代)
|
||||||
|
confidence: float, // 置信度(0.0-1.0,提取算法给出)
|
||||||
|
|
||||||
|
// 时序检索辅助
|
||||||
|
date_bucket: string // 日期分区(2026-04-08),用于快速范围查询
|
||||||
|
}]->(target:Entity)
|
||||||
|
|
||||||
|
时序链(NEXT)- 可选
|
||||||
|
用于严格保持对话顺序:
|
||||||
|
(:Entity)-[:NEXT {
|
||||||
|
session_id: string,
|
||||||
|
created_at: datetime
|
||||||
|
}]->(:Entity)
|
||||||
|
|
||||||
|
3. 索引与约束
|
||||||
|
// 实体唯一性约束
|
||||||
|
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_session_id IF NOT EXISTS
|
||||||
|
FOR ()-[r:RELATES]-() ON r.session_id;
|
||||||
|
|
||||||
|
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 rel_session_time IF NOT EXISTS
|
||||||
|
FOR ()-[r:RELATES]-() ON (r.session_id, r.created_at);
|
||||||
|
|
||||||
|
4. 数据模型示例
|
||||||
|
插入场景
|
||||||
|
用户说:"我想用 Neo4j 改造 OpenClaw 的记忆系统,替换掉原来的 SQLite 方案。"
|
||||||
|
生成的图结构:
|
||||||
|
// 节点
|
||||||
|
(:Entity {name: "OpenClaw", type: "project", created_at: now()})
|
||||||
|
(:Entity {name: "Neo4j", type: "technology", created_at: now()})
|
||||||
|
(:Entity {name: "SQLite", type: "technology", created_at: now()})
|
||||||
|
(:Entity {name: "记忆系统改造", type: "decision", created_at: now()})
|
||||||
|
|
||||||
|
// 关系(带时间戳)
|
||||||
|
(OpenClaw)-[:RELATES {
|
||||||
|
type: "uses",
|
||||||
|
created_at: datetime("2026-04-08T19:45:00"),
|
||||||
|
session_id: "20260408-a7f3",
|
||||||
|
turn_id: 1,
|
||||||
|
role: "user",
|
||||||
|
context_snippet: "我想用 Neo4j 改造 OpenClaw...",
|
||||||
|
status: "active",
|
||||||
|
confidence: 0.95,
|
||||||
|
date_bucket: "2026-04-08"
|
||||||
|
}]->(Neo4j)
|
||||||
|
|
||||||
|
(OpenClaw)-[:RELATES {
|
||||||
|
type: "replaces",
|
||||||
|
created_at: datetime("2026-04-08T19:45:00"),
|
||||||
|
session_id: "20260408-a7f3",
|
||||||
|
turn_id: 1,
|
||||||
|
role: "user",
|
||||||
|
context_snippet: "替换掉原来的 SQLite 方案",
|
||||||
|
status: "active",
|
||||||
|
confidence: 0.92,
|
||||||
|
date_bucket: "2026-04-08"
|
||||||
|
}]->(SQLite)
|
||||||
|
|
||||||
|
(记忆系统改造)-[:RELATES {
|
||||||
|
type: "involves",
|
||||||
|
created_at: datetime("2026-04-08T19:45:00"),
|
||||||
|
session_id: "20260408-a7f3",
|
||||||
|
turn_id: 1,
|
||||||
|
role: "user",
|
||||||
|
status: "active",
|
||||||
|
date_bucket: "2026-04-08"
|
||||||
|
}]->(OpenClaw)
|
||||||
|
|
||||||
|
5. 关键查询模式
|
||||||
|
A. 时序检索(最新记忆优先)
|
||||||
|
// 查询实体相关的最新关系(按时间戳倒序)
|
||||||
|
MATCH (e:Entity {name: $entity_name})-[r:RELATES]-(target:Entity)
|
||||||
|
WHERE r.status = 'active'
|
||||||
|
RETURN e, r, target
|
||||||
|
ORDER BY r.created_at DESC
|
||||||
|
LIMIT 20
|
||||||
|
|
||||||
|
B. 会话范围查询(上下文隔离)
|
||||||
|
// 仅查询特定会话内的关系
|
||||||
|
MATCH (s:Entity)-[r:RELATES]->(t:Entity)
|
||||||
|
WHERE r.session_id = $session_id
|
||||||
|
RETURN s.name, r.type, t.name, r.created_at
|
||||||
|
ORDER BY r.turn_id
|
||||||
|
|
||||||
|
C. 时间范围查询(最近N天)
|
||||||
|
// 利用 date_bucket 或时间戳范围
|
||||||
|
MATCH (s:Entity)-[r:RELATES]->(t:Entity)
|
||||||
|
WHERE r.date_bucket >= date().isoDate - duration('P7D') // 最近7天
|
||||||
|
OR r.created_at >= datetime() - duration('P7D')
|
||||||
|
RETURN s, r, t
|
||||||
|
ORDER BY r.created_at DESC
|
||||||
|
|
||||||
|
D. 多跳路径查询(关联推理)
|
||||||
|
// 查询两个实体间的路径(带时间约束)
|
||||||
|
MATCH path = (start:Entity {name: $start})-[:RELATES*1..3]->(end:Entity {name: $end})
|
||||||
|
WHERE ALL(r IN relationships(path) WHERE r.status = 'active')
|
||||||
|
RETURN path,
|
||||||
|
[r IN relationships(path) | r.created_at] as path_timestamps,
|
||||||
|
length(path) as hops
|
||||||
|
ORDER BY path_timestamps[-1] DESC // 按最后关系时间排序
|
||||||
|
LIMIT 5
|
||||||
|
|
||||||
|
E. 纠错更新(软删除)
|
||||||
|
// 用户更正时,不物理删除,而是标记为 superseded 并创建新关系
|
||||||
|
MATCH (s:Entity {name: $source})-[r:RELATES {type: $old_relation}]->(t:Entity {name: $target})
|
||||||
|
WHERE r.status = 'active'
|
||||||
|
SET r.status = 'superseded',
|
||||||
|
r.updated_at = datetime(),
|
||||||
|
r.superseded_by = $new_relation_id // 指向新关系的ID
|
||||||
|
|
||||||
|
// 创建新关系(带新的时间戳)
|
||||||
|
CREATE (s)-[:RELATES {
|
||||||
|
type: $new_relation,
|
||||||
|
created_at: datetime(),
|
||||||
|
session_id: $session_id,
|
||||||
|
turn_id: $turn_id,
|
||||||
|
role: $role,
|
||||||
|
status: 'active',
|
||||||
|
supersedes: $old_relation_id
|
||||||
|
}]->(t)
|
||||||
|
|
||||||
|
6. 存储过程(APOC)优化
|
||||||
|
批量插入(高性能)
|
||||||
|
// 使用 APOC 批量合并,避免逐条插入开销
|
||||||
|
CALL apoc.periodic.iterate(
|
||||||
|
"UNWIND $triples as t RETURN t",
|
||||||
|
"
|
||||||
|
MERGE (s:Entity {name: t.source})
|
||||||
|
ON CREATE SET s.created_at = datetime(), s.type = coalesce(t.source_type, 'unknown')
|
||||||
|
ON MATCH SET s.updated_at = datetime(), s.mention_count = coalesce(s.mention_count, 0) + 1
|
||||||
|
|
||||||
|
MERGE (e:Entity {name: t.target})
|
||||||
|
ON CREATE SET e.created_at = datetime(), e.type = coalesce(t.target_type, 'unknown')
|
||||||
|
ON MATCH SET e.updated_at = datetime(), e.mention_count = coalesce(e.mention_count, 0) + 1
|
||||||
|
|
||||||
|
CREATE (s)-[r:RELATES {
|
||||||
|
type: t.relation,
|
||||||
|
created_at: datetime(),
|
||||||
|
session_id: t.session_id,
|
||||||
|
turn_id: t.turn_id,
|
||||||
|
role: t.role,
|
||||||
|
status: 'active',
|
||||||
|
date_bucket: date().isoDate,
|
||||||
|
confidence: coalesce(t.confidence, 1.0)
|
||||||
|
}]->(e)
|
||||||
|
",
|
||||||
|
{batchSize: 100, params: {triples: $triples}}
|
||||||
|
)
|
||||||
|
|
||||||
|
时序压缩(归档旧关系)
|
||||||
|
// 将N天前的关系标记为 archived,但保留节点
|
||||||
|
MATCH ()-[r:RELATES]->()
|
||||||
|
WHERE r.created_at < datetime() - duration('P30D') // 30天前
|
||||||
|
AND r.status = 'active'
|
||||||
|
SET r.status = 'archived',
|
||||||
|
r.archived_at = datetime()
|
||||||
|
RETURN count(r) as archived_count
|
||||||
|
|
||||||
|
7. 与 Bridge API 的映射
|
||||||
|
API 端点 Cypher 操作 时间戳处理
|
||||||
|
POST /insert MERGE 节点 + CREATE 关系 datetime() 自动生成
|
||||||
|
POST /search MATCH 带时间戳排序 返回 r.created_at 等字段
|
||||||
|
POST /delete SET r.status = 'deleted' 软删除,保留 updated_at
|
||||||
|
8. 数据保留策略
|
||||||
|
// 节点保留:永久(实体名词长期有效)
|
||||||
|
// 关系保留策略:
|
||||||
|
// - active: 最近30天(高频查询)
|
||||||
|
// - archived: 30-90天(低频查询,需指定时间范围)
|
||||||
|
// - deleted: 逻辑删除,保留90天后物理清理(APOC 定时任务)
|
||||||
|
|
||||||
|
// 物理清理(谨慎操作)
|
||||||
|
MATCH ()-[r:RELATES]->()
|
||||||
|
WHERE r.status = 'deleted'
|
||||||
|
AND r.updated_at < datetime() - duration('P90D')
|
||||||
|
DELETE r
|
||||||
|
WITH count(*) as deleted_relations
|
||||||
|
MATCH (e:Entity)
|
||||||
|
WHERE NOT (e)-[:RELATES]-() // 无关系的孤立节点
|
||||||
|
DELETE e
|
||||||
|
RETURN deleted_relations, count(e) as deleted_orphans
|
||||||
|
|
||||||
|
这个设计支持时间旅行查询(查看任意时刻的图状态)、会话隔离(只看当前对话历史)和渐进式遗忘(旧关系归档但不删除),符合 OpenClaw 的增量记忆需求。
|
||||||
|
|
||||||
|
}]
|
||||||
|
)
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user