数据截至 (上游 commit ac9c2a586ba7)
第 3 章:时间知识图谱(TKG)
本章讲什么: HSG 管"模糊、联想式"的记忆;时间知识图谱管"精确、会变的事实"。比如"CompanyX 的 CEO 是谁"这种答案会随时间翻转的事实。这是一套独立于 HSG 的子系统,存在自己的表里、有自己的 API。
1. 要解决的小问题:事实会过期
向量记忆不擅长回答"2022 年谁是 CEO"——它只会召回"最像"的文本,不理解真值的时间窗。
TKG 的做法:把事实存成三元组 + 一对时间戳:
(subject, predicate, object, valid_from, valid_to)
主语 谓语 宾语 生效时刻 失效时刻(NULL=至今仍有效)
例:(CompanyX, has_CEO, Alice, 2021-01-01, NULL)。
2. 核心机制:插入即自动关闭旧事实
巧妙点: 你不需要手动"结束"旧任期。插入 (CompanyX, has_CEO, Bob, 2024-04-10) 时,系统自动把"同 subject+predicate 且还没结束(valid_to IS NULL)"的旧事实的 valid_to 设成新事实生效前一毫秒。
# 示意,非源码:insert_fact 的自动关闭逻辑
new_from = 2024_04_10
for old in facts_where(subject, predicate, valid_to IS NULL):
if old.valid_from < new_from:
old.valid_to = new_from - 1 # 旧任期在新任期开始前一刻结束
insert(subject, predicate, object, valid_from=new_from, valid_to=NULL)
真实实现:insert_fact(temporal_graph/store.py:11),自动关闭在 :24-26。批量版 batch_insert_facts(:83)同逻辑并包在事务里。
结果:任何时刻,同一 (subject,predicate) 最多只有一条"当前有效"(valid_to IS NULL)的事实,时间线不会自相矛盾。
3. 查询:某时刻的真值
核心谓词是"在时刻 at 有效":valid_from <= at AND (valid_to IS NULL OR valid_to >= at)。这一句反复出现:
| 查询 | 回答什么 | 函数 |
|---|---|---|
| point-in-time | "某时刻 X 的所有/某条事实" | query_facts_at_time(query.py:7) |
| current | "X 现在的 predicate 是什么" | get_current_fact(query.py:37) |
| range | "某时间段内涉及的事实" | query_facts_in_range(query.py:49) |
| conflict | "某时刻同主谓有几条并存(冲突)" | find_conflicting_facts(query.py:83) |
| by-subject | "X 的当前全部事实" | get_facts_by_subject(query.py:95) |
| pattern search | "subject/predicate/object LIKE %..%" | search_facts(query.py:118) |
时间轴: 2021 ───────── 2024-04 ──────▶ now
Alice: [valid_from ......... valid_to] ← 已关闭
Bob: [valid_from .... NULL] ← 当前有效
query_facts_at_time(at=2022): 命中 Alice(2022 落在她的窗口内)
get_current_fact(now): 命中 Bob
4. 时间线与变化分析
temporal_graph/timeline.py 把事实"事件化":每条事实拆成 created(在 valid_from)和 invalidated(在 valid_to)两个时间点,按时间排序就得到一条可读时间线。
get_subject_timeline(timeline.py:8):某主语的完整演变史。compare_time_points(timeline.py:144):对比两个时刻,输出 added/removed/changed/unchanged——"从 t1 到 t2 有什么变了"。get_change_frequency(timeline.py:181)/get_volatile_facts(:213):算某事实多久变一次、哪些事实最"善变"。
5. 置信度衰减:老事实缓慢褪色
apply_confidence_decay(store.py:111)用一条 SQL 把所有"当前有效"事实的 confidence 按存在时长线性下调,但地板锁在 0.1——老事实不会归零,只是没那么可信。
6. 边界与坑
- 无实体归一化。 subject/predicate 是裸字符串精确匹配——"CompanyX" 和 "company x" 会被当成两个主语,系统不会合并。
- schema 命名不一致(值得警惕)。 迁移脚本
migrations/001_initial.sql:73把宾语列建为obj、关系列建为relation,但代码到处用object和relation_type(如store.py:30、query.py:143)——说明实际运行的建表 DDL 与该迁移文件不一致(真实 schema 另有来源)。读代码时以store.py/query.py里的列名为准。 - MCP 侧参数名对不上。
ai/mcp.py:159调query_facts_at_time用了obj=/at_time=,而函数签名是subject_object=/at=(query.py:7)——这条 factual 查询路径在当前代码下会TypeError(inferred,基于两处签名比对)。 - 两套"记忆"不自动互通。 TKG 的事实和 HSG 的向量记忆是分开的;MCP 的
unified查询只是把两边结果拼在一起返回(mcp.py:143-189),并没有真正融合打分。