企业 AI 助手上午回答某产品价格是 100,下午又说 120。排查后发现,2025 年旧版手册和 2026 年修订版同时留在 Milvus 或 Zilliz 里。两段文字语义接近,余弦相似度很可能把它们一起召回。LLM 拿到冲突上下文后,可能任选一版,也可能把两版揉在一起。
这类故障常被归为知识漂移:内容已经更新,检索层却没有判断哪一版仍然生效,生成端随后放大了前面的版本冲突。
这里没有一个可以直接套用的 10 万份文档阈值。文档重复度、更新频率和版本冲突都会影响召回。《企业级 RAG 知识库构建:从痛点到解决之道》提到,企业知识库动辄包含数十万份文档,常见故障包括漏召回和检索塌陷。所谓检索塌陷,就是同一批语义较强的片段反复占据召回前列,旧资料也可能长期留在其中。
向量能找相似内容,却不认识生效状态
纯向量 RAG 按语义距离排序,但企业知识的差异经常藏在时间和关系里。
例如:
- 项目 A 的负责人是张三。
- 项目 A 的负责人改为李四。
两句话在向量空间里离得很近,业务含义却已经改变。产品价格、合规条款和 API 文档也会遇到同样的问题。系统需要判断的不只是“哪段话相关”,还包括“哪条关系当前有效”。
Datawhale 的 KG-RAG 章节也指出,依赖非结构化文本向量检索的传统 RAG,在复杂查询上存在局限。
若冲突只来自单个文档的版本号,先做元数据过滤可能就够了。需要追踪负责人变更、规则生效链和跨文档关系时,再增加图谱层。这里用 GraphRAG 指代一条协同链路:向量库负责找相关文本,知识图谱负责补充实体关系、版本和状态约束。
从 Chunk 到 Triple,来源也要一起入库
文档仍然可以照常切成 Chunk,之后再从片段中提取三元组,也就是 Entity-Relation-Entity。产品名、政策条款和日期都可以成为实体或属性。
提取环节可采用“大模型粗提、小模型精筛”的组合。Qwen 或 Llama 先识别实体与关系,3B 级别的 SLM 再做实体对齐和关系校验。不过,3B 模型只能作为部署选项,先测漏检率再决定是否使用。关系一旦漏掉,图谱里就会出现断链,后面的 2-hop 查询也无法补救。
每条三元组还要保留原始文档、对应 Chunk、文档版本和时间信息。没有这些指针,冲突发生后很难回到原文核对,图谱也会变成另一套无法追溯的数据。
旧值别覆盖,给版本留一条链
在 Neo4j 或 NebulaGraph 中,旧属性不宜被新值直接覆盖。可以沿用下面的版本结构:
- 实体节点:
Product_A,ID 为101 - 属性节点:
Price_V1,Value 为100,Effective_Date 为2025-01-01,Status 为Retired - 属性节点:
Price_V2,Value 为120,Effective_Date 为2026-01-01,Status 为Active
用户询问当前价格时,查询链路从 Product_A 找到价格版本,再筛选 Status: Active。即使向量库召回了 V1 的旧手册,图谱仍能明确告诉 LLM:这段资料已经退役,当前值是 120。
状态位也不能机械地只筛 Active。用户如果问“2025 年的价格是多少”,系统应按时间命中 Price_V1。否则,动态知识图谱只会变成一张“最新值表”,历史问题仍然答不准。
新关系与现有唯一属性发生冲突时,先挂上冲突标记,交给人工或专门的复核 Agent 裁决。旧节点可以退役,但不要静默覆盖。
文档一改,只处理变过的段落
动态知识图谱可以按事件驱动组织更新链路:
- 监听变更:读取企业 Wiki、飞书文档或 SharePoint 的变更日志。
- 增量提取:只重新扫描发生变化的段落,不重跑整份文档。
- 检查冲突:比较新三元组与图谱中已有的关系,识别同一实体的属性冲突。
- 等待裁决:冲突关系进入复核流程,通过后再调整版本状态。
实时更新可以落在这条增量链路上,无需在每次查询时重建整张图。真正需要守住的是状态变更:V2 生效后,V1 必须及时从 Active 转为 Retired。
一次查询怎么走完 Vector、Graph 和 LLM
向量粗筛。 Milvus 先召回前 20 个相关片段,用于控制候选范围并保留描述性信息。20 适合作为初始参数,不必当成所有知识库都适用的固定值。
图谱精修。 从候选片段和用户问题中提取主要实体,在图谱中执行 2-hop 查询,检查实体是否有新版本、现有关系是否冲突,以及相关属性处于什么状态。
上下文重组。 图谱给出的有效事实放在参考片段之前,同时给旧片段标出 Retired 状态。Prompt 可以写成:
[事实约束]
产品:Product_A
当前价格:120
生效日期:2026-01-01
状态:Active
[参考片段]
旧版手册记录:2025 年价格为 100
片段状态:Retired
[用户问题]
Product_A 当前价格是多少?
这样处理后,旧文本仍可用于理解背景,但不会和当前事实处在同一优先级。也要看到,Prompt 排序救不了一张没有及时更新的图谱。版本状态错了,模型仍会拿到错误约束。
全量建图会消耗多少 Token
如果企业私有库有数百万页文档,逐页调用 LLM 提取三元组,Token 消耗和处理时间都会很高。更稳妥的做法是采用“关键实体图谱 + 边缘向量检索”:产品参数、合规准则等版本敏感信息进入图谱,说明性文档继续留在向量库。
版本不断累积后,图谱里还会出现冗余节点和失效关系。需要持续合并重复节点、清理无效关系,并检查查询延迟。缺少治理时,图谱本身也会制造噪声。
不要急着把企业知识库里的每个名词都做成节点。先挑那些经常发生版本冲突、又会直接影响业务答案的事实。
上线前,直接拿版本问题压测
- 询问当前价格,确认 V2 的 120 成为事实约束。
- 询问 2025 年价格,确认系统能够返回 V1 的 100。
- 放入一条与现有唯一属性冲突的新关系,确认系统会标记冲突,而不是直接覆盖。
- 从最终答案回查原始文档和 Chunk,确认每条图谱事实都有出处。
这些环节如果没跑通,先修版本状态和更新链路,别急着换更大的模型。
动态知识图谱的目标可以很窄:让系统知道哪条事实当前生效、哪条已经退役、冲突该交给谁裁决。向量库继续负责语义召回,图谱负责给召回结果加上可执行的版本约束。对于频繁处理价格、负责人、合规条款和 API 版本的企业知识库,这层约束比单纯增加向量召回数量更有用。