实体归一
概述
抽取出“林默”“林公子”和“他”并不等于知道它们是否指向同一个人。实体归一负责把多个文本提及映射到稳定的图节点身份,是知识图谱能否长期维护的关键。
如果实体归一错误,会出现两种相反问题:
- 拆分错误:同一实体生成多个节点,关系被分散;
- 合并错误:同名不同实体被合并,产生错误关系传播。
前者降低召回,后者可能污染整个图谱。不能只靠字符串相等解决。
四个不同概念
文本提及
模型响应中的 mentionId 表示当前 Chunk 的一个局部提及,只在单次响应内有效。
候选实体
GraphEntityCandidate 包含名称、类型、别名、属性、证据和候选键。它仍然可能与其他候选指向同一实体。
实体解析结果
GraphEntityResolver 对候选进行聚类或匹配,产生候选键到 nodeId 的映射。
实体注册记录
GraphEntityRegistry 保存跨文档、跨任务可复用的稳定实体身份。它属于长期状态,而不是单次模型输出。
默认名称与别名归一
NameAliasGraphEntityResolver 按“节点类型 + Unicode 规范化名称/别名”合并候选,并通过 SHA-256 生成确定性 nodeId。
林默 / 林公子 -> 同一个 Character
林默(Character) / 林默(Organization) -> 不同节点常见中英文代词即使被模型放进 aliases,也不会作为合并键。属性冲突默认保留首次值,后续候选只补充缺失属性;它不会根据时间、来源可信度或业务优先级自动裁决。
默认实现适合单次文档内的基础去重,但无法可靠处理:
- “他”“她”“该公司”等指代;
- 同名不同人;
- 名称发生变化;
- 翻译名和音译名;
- 只有上下文才能区分的角色;
- 与已有业务主数据匹配。
稳定 ID 的来源
nodeId 应表示业务实体,而不是文本出现位置。可选来源包括:
- 业务系统主键;
- 权威主数据 ID;
- 外部标准编号;
- 持久化实体注册表分配的 ID;
- 在有限场景下,由类型和规范名称确定性生成的哈希。
不要使用 mentionId、chunkId、随机数或当前时间生成长期实体 ID。否则文档重试和后续导入会持续创建重复节点。
可以实现 GraphEntityIdGenerator 接入自己的 ID 策略。
为什么默认哈希不足以支持长期图谱
默认哈希取决于本次抽取中确定的规范名称。它能让同一名称重复得到相同 ID,但不能回答:
- 名称变化前后是否同一实体;
- 同名的两个实体是否应该分开;
- 新别名是否属于已有实体;
- 不同 Space 是否允许共享身份;
- 实体已经有业务主键时应使用哪个 ID。
所以长期增量入图应使用 GraphEntityRegistry,并在 Pipeline 中装配 RegistryGraphEntityResolver。
使用实体注册表
GraphEntityRegistry registry =
new YourPersistentEntityRegistry();
GraphExtractionPipeline pipeline =
new GraphExtractionPipeline(
extractor,
splitter,
validator,
new RegistryGraphEntityResolver(
"novel_knowledge",
registry),
new GraphMutationMapper());解析器按 Space、节点类型、规范名称和别名查询已有注册记录:
- 没有命中时生成新 nodeId;
- 唯一命中时复用已有 nodeId;
- 命中多个不同 nodeId 时属于歧义,应失败并进入审核,而不是任选一个。
仅把 registry 传给 IncrementalGraphIngestionService 只能在图写成功后保存注册结果,不能改变抽取阶段的解析。跨批次复用历史身份,必须在 Pipeline 的 Resolver 中使用同一个 registry。
注册表的生产约束
生产实现至少需要:
- 以 Space 隔离不同知识库;
- 查询时同时限定节点类型;
- 对名称和别名使用与 Resolver 一致的 NFKC、大小写和空白规范化;
- 为“Space + 类型 + 规范名称/别名”建立可验证的唯一性规则;
- 同一个名称已指向另一 nodeId 时拒绝静默覆盖;
- 幂等保存实体及其新增别名;
- 保留合并、拆分和人工裁决审计记录;
- 支持并发创建时的唯一约束或原子冲突处理。
如果多个 Space 共用一个底层表,仍必须把 Space 放入查询和唯一键。否则同名实体会跨知识库污染。
实体注册的提交时机
注册记录应在 Graph Mutation 写入成功后保存,避免失败写入留下指向不存在节点的身份。
但图数据库和注册表通常不在同一个事务中,因此仍可能出现:
图写入成功 -> 注册表保存失败长期入图服务通过持久化操作状态和重试降低这个风险。生产系统还需要对账,发现图节点与注册表不一致。
同名歧义与人工审核
当多个已有实体匹配同一候选时,上层审核界面可以展示:
- 候选名称、类型和别名;
- 当前文档上下文与证据;
- 每个历史实体的属性和关联;
- 来源文档;
- 可能的业务主键;
- 新建实体选项。
审核结果应形成可持久化的别名或身份映射,而不是只修改当前 Mutation。否则下一批文档还会重复产生同样歧义。
指代消解与语义匹配
复杂 Resolver 可以组合:
- 章节或会话上下文;
- 已有图谱邻居;
- 业务主数据;
- 向量检索;
- 规则词典;
- 第二次模型判断;
- 人工确认。
无论使用什么方法,都应输出可解释的匹配依据和置信信息。高相似度并不等于同一实体,尤其是短名称和常见人名。
实体合并和拆分
发现历史实体重复后,不能只修改注册表指向。图中可能已经存在:
- 两个节点的不同属性;
- 指向它们的关系;
- 不同来源事实;
- 外部系统引用;
- 历史文档状态中的 nodeId。
实体合并和拆分应作为独立、可审核的数据治理操作,规划属性合并、关系迁移、别名更新、来源保留和回滚。它不属于默认抽取 Resolver 的自动职责。
常见问题
同名同类型一定是同一实体吗?
不一定。默认实现只能做保守的名称归一,生产场景应结合主键、上下文或审核。
可以跨 Space 共用实体 ID 吗?
可以由业务决定,但注册查询必须显式带 Space,或者建立清晰的全局身份域。不要因为底层表共享就默认合并知识库。
Registry 会自动保存吗?
Pipeline 中的 Resolver 负责查询和映射;增量服务在写图成功后才保存注册结果。单次 Pipeline 抽取不会自动提交业务注册表。
修改规范名称会改变节点 ID 吗?
默认哈希策略可能改变。使用持久化注册表或业务主键后,应保持 nodeId 不变,只更新名称和别名。
生产检查清单
- 是否明确提及、候选、实体和注册记录的区别;
- nodeId 是否稳定、可复用且与 Chunk 无关;
- Pipeline 是否真正装配持久化 Registry Resolver;
- 查询和唯一约束是否包含 Space 与类型;
- 同名多命中是否进入审核而非随机选择;
- 属性冲突是否有业务合并策略;
- 注册是否只在图写成功后提交;
- 图与注册表不一致是否可恢复和对账;
- 合并、拆分和别名修改是否有审计;
- 默认内存注册表是否仅用于测试。
身份确定后,继续阅读增量入图。