知识图谱回答的是“谁和谁有什么关系”。它的原料是文档片段,加工者是大语言模型,产物是实体、关系,以及每条事实来自哪里的记录。听起来简单,难点在规模和可靠性:一份几百页的文档要调用几十上百次模型,任何一次都可能超时、限流或输出异常。
ApeMind 的图谱构建围绕三个约束设计:内存有上限,不随文档变大而增长;失败有边界,一个窗口失败不连累其他窗口;事实可追溯,每个实体和关系都能回到原文片段。
从片段到可查询的图谱
- 从解析产物开始
图谱不重新读源文件,直接使用解析阶段产出的片段。重新解析会生成新的片段,图谱随之重建。
- 按窗口并发抽取
连续的若干片段组成一个
窗口 ,固定数量的执行器并发处理。先从样本里发现实体类型,再逐窗口抽取实体和关系。 - 先落对象,再写指针
每个成功窗口的结果写成一个有大小上限的不可变对象,对象提交成功后,才在数据库里记下“这个窗口成功了”。
- 有界收敛
所有窗口都结束后,收敛器分页读取成功对象,在固定内存预算里按规范键合并,达到预算就分批写入图数据库,再清空这一页继续。
- 发布规范事实
相同实体合并类型和血缘,相同关系合并证据。事实一经发布就能查询,并明确标注完整或部分完成。
- 异步维护实体向量
实体的名称和类型变化后,后台工作器重新生成向量,用于语义找实体。向量慢一点或失败,都不影响已发布的事实。
窗口:失败的最小单位
把整篇文档一次性交给模型不可行,超出上下文窗口;逐片段调用又会丢掉片段之间的关联。窗口是两者的折中:足够大,能看到一段完整的上下文;足够小,失败了只需重试这一块。
每个窗口都有稳定的身份和内容哈希、自己的状态和尝试次数。一个窗口失败,其他窗口的成果原样保留。下面可以亲手跑一次:调整窗口数和永久失败的窗口数,看最终会发布什么。
动画为示意;容忍上限的计算规则与产品一致。
判定规则只有三条:
- 全部成功:事实完整,状态为
complete。 - 永久失败的窗口不超过容忍上限:发布已有事实,状态明确为
partial,查询接口也会返回“部分完成”。 - 鉴权失败、额度耗尽这类系统性失败,或失败超过上限:本轮事实不发布,状态为失败。系统性失败说明问题不在某一段内容,继续发布只会得到一张被系统性截断的图。
容忍上限按窗口总数计算:目标为 5%,至少按 3 个窗口算,同时不超过总数的 25%。还有重试机会的窗口不算失败,会先进入有限次自愈。
先落对象,再写指针
窗口成功后,结果不会直接写进图数据库,而是先序列化、校验结构、分别检查压缩前和压缩后的大小,写成一个不可变对象;对象提交成功后,才在数据库里写下指向它的指针。
这个顺序保证了一件事:数据库里说“成功”的窗口,一定能找到它的结果。反过来,只有对象没有指针的,是崩溃留下的孤儿,由清理器回收。超过大小上限的窗口会以明确的错误失败并有限次重试,不在运行中临时拆分,避免引入分片提交和部分恢复这类复杂协议。
有界收敛
收敛器是把几十个窗口结果合成一张图的环节,也是最容易爆内存的环节。如果它把整篇文档的实体和关系全部装进内存再写入,文档越大,内存越大,迟早会被一份超大文档压垮。
ApeMind 的收敛器不保留全集。它一页一页地读成功对象,只在当前预算内按规范键合并;预算或键数量到达上限,就把这一批写进图数据库,清空后继续。去重同时发生在两层:内存页内精确合并,图数据库按唯一键幂等写入并合并血缘。最终正确性依赖数据库的唯一键,而不是进程里那份无上限的全集。
收敛器崩溃后不需要“从第几页继续”的恢复表,直接从全部成功对象重放即可。写入和血缘合并都是幂等的,重放多少次结果都一样。
血缘:删掉一篇文档会发生什么
同一个实体常常由多篇文档共同贡献。“区域总监”可能同时出现在审批权限表和组织架构里。图谱为每个实体和关系记录
点左侧的文档把它移出知识库,观察哪些实体留下、哪些被回收,以及右侧的待刷新队列如何决定实体向量的去留。点实体可以查看它的血缘。
示例数据为演示用途。连线越粗,表示这条关系被越多文档共同支持。
实体向量是投影,不是事实
图谱有两层结果:图谱事实是权威层,包括实体、关系和血缘;实体向量是派生层,只是规范实体当前“名称 + 类型”的向量化投影,用来在查询时按语义找到实体。
两层被刻意解耦:
- 向量的待办任务只按“知识库 + 实体名”标识,不带文档 ID。一个实体由多篇文档共享时,删除其中一篇不会误删大家共用的向量点。
- 工作器只看实体当前是否存在、当前的名称和类型:存在且内容哈希没变就跳过,变了才重新嵌入;不存在就删除向量点。新增一条血缘不会触发重复嵌入。
- 向量写入失败时,待办记录保留并指数退避重试,不会回滚已经发布的事实,也不会把文档标记为失败。实体关键词搜索、邻居扩展、路径查询直接走事实层,向量积压时照样可用。
关系本身不生成向量,它们以事实的形式存在于图数据库里,服务于邻居、路径和证据查询。
换一种图数据库也成立
图谱事实可以存放在 PostgreSQL、Neo4j 或 NebulaGraph 里。PostgreSQL 后端把事实、血缘和向量待办写在同一个事务里,天然一致。Neo4j 和 NebulaGraph 无法与应用数据库组成跨库事务,系统先把受影响实体的待办持久化并加租约,再修改图数据库:如果图变更前崩溃,实体仍在,工作器幂等写入;如果变更后崩溃,实体已不在,工作器删除向量点。无论停在哪一步,结果都会收敛到正确状态。
查询时图谱扮演什么角色
图谱在检索中是第三条召回通道:先用实体向量或关键词找到实体,向外扩展邻居,再沿血缘回到原文片段,和向量、全文的结果一起融合。详见 三种索引如何分工。
图谱结果是导航和证据定位的线索。智能体最终引用的是原文片段,而不是模型抽取出的一条关系,这样即使抽取有偏差,回答也总能落到可核对的原文上。
边界
- 实体和关系由大语言模型抽取,质量取决于模型和文档本身;图谱不保证抽取完整无误。
- 相似实体(例如“财务BP”和“财务 BP”)会被检测为合并候选,但只给建议,需要人工确认后才合并。
- 重建图谱期间,可能短暂同时看到新旧两个解析版本的事实;旧版本血缘随后由清理流程移除。
- 建图需要为每个窗口调用模型,是三种索引里成本最高、耗时最长的一种。
出问题时查哪一环
文档的图谱状态显示“部分完成”
少数窗口永久失败,但没有超过容忍上限,已有事实已经发布并可查询。可以对这篇文档重建图谱索引,重建只读取已有解析产物,不重新解析源文件。
图谱索引直接失败
通常是系统性原因:模型鉴权失败、额度耗尽,或失败窗口过多。先检查知识库绑定的模型是否可用、额度是否充足,再重建。
图谱已完成,但按语义找不到某个实体
实体向量由后台异步维护,可能还在排队。按名称关键词搜索、邻居和路径查询不依赖向量,可以先用这些方式确认实体是否存在。
给想深入的读者:哪些状态是权威的
“哪些窗口成功了”以数据库里的成功指针为准,对象存储里没有指针的对象视为孤儿。文档级图谱状态只有一个,即图谱事实的状态。实体向量没有文档级状态,它唯一持久的待办来源是知识库级的待刷新记录,以“知识库 + 规范实体名”去重。所有清理和重试都是幂等的,并由持有者和租约隔离保护。