GraphRAG新增文件对已有知识库影响有多大?缓存又是何时失效?一文带你探究到底 原创
在使用 LLM 应用时,我们经常会遇到需要向知识库中添加新文件的需求。传统的 RAG 通过对 chunk 进行 embedding 来处理这种情况,并且新文件的加入不会影响之前生成的 chunk embedding。但是,GraphRAG 的处理方式有所不同。由于其索引过程涉及多个 pipeline 阶段,因此可能会产生一些问题:当新文件加入时,是否需要对已有的文件重新进行索引?缓存在这个过程中起到了什么作用?哪些 workflow 需要重做?带着这些问题,我们来一探究竟。
GraphRAG缓存
我们首先来看下GraphRAG的缓存的作用,GraphRAG默认使用文件缓存,配置如下。
cache:
type: file # or blob
base_dir: "cache"
# connection_string: <azure_blob_storage_connection_string>
# container_name: <azure_blob_storage_container_name>
缓存数据被储存在 'cache' 目录下,并且根据不同的处理流程,它被分为四个主要部分:实体提取、总结描述、社区报告以及文本嵌入。因此,几乎所有涉及到 LLM 或者 Embedding 调用的环节都在缓存中得到了覆盖。
缓存文件的命名规则是基于 "tag" 和 "hash" 值进行组合,例如:chat-02c8a6be0acf8abe79b876438853cebb 或 create_community_report-chat-v2-0a8d6dcd76f6d92edb9388681da40d1d。那么,缓存的具体内容包含什么呢?实际上,它就是 LLM 的请求输入和响应输出,或者是 Embedding API 调用的返回结果。
那么何时缓存会生效?修改哪些部分会影响缓存呢?让我们看GraphRAG中如下代码,它是为缓存文件生成文件名,也就是上文看到文件名。
def create_hash_key(
operation: str, prompt: str, parameters: dict, history: list[dict] | None
) -> str:
"""Compute cache key from prompt and associated model and settings.
Args:
prompt (str): The prompt run through the language model.
llm_string (str): The language model version and settings.
Returns
-------
str: The cache key.
"""
llm_string = _llm_string(parameters)
history_string = _hash(json.dumps(history)) if history else None
hash_string = (
_hash(prompt + llm_string + history_string)
if history_string
else _hash(prompt + llm_string)
)
return f"{operation}-{hash_string}"
从这份代码可以看到有三个因素影响hash值的生成:
- 输入的Prompt,包含用户输入
- LLM 参数
- 聊天历史
那么,何时会导致缓存失效呢?
- 首先,如果我们修改了 LLM 的参数,如 model_name、temperature、top_p 等,缓存就会失效。
- 其次,当我们带着聊天历史的时候,每轮聊天都会新增聊天历史,在进行聊天的时候缓存也失效
- 最后,如果调用 LLM 的输入提示(prompt)发生改变,缓存同样会失效。这点尤其重要,因为在添加新文件的情况下,我们通常不会修改 LLM 的参数,但可能会影响到 prompt,或更准确地说,可能会影响 prompt 模板中的变量值。那么,哪些 workflow 会影响 GraphRAG prompt 模板中的值呢?
哪些workflow会受到影响
GraphRAG index的14个workflow可以进一步细分为四大类:
- 关于文档的document_workflows
- craete_final_documents
- create_base_document
- 关于文档单元的text_unit_workflows
- join_text_units_to_entity_ids
- join_text_units_to_relationship_ids
- create_final_text_units
- create_base_text_units
- 构建图谱的graph_workflows
- create_summarized_entities
- create_base_entity_graph
- create_final_entities
- create_final_relationships
- create_final_nodes
- create_base_extracted_entities
- 社区聚类的community_workflows
- create_final_communities
- create_final_community_reports
各个workflow的内部详细做了哪些事可以参考之前的文章小白也能读懂的GraphRAG知识图谱全流程解析,多图预警!
在此,我将重点探讨新增文件对于某些核心工作流程的影响:
-
create_base_text_units
:这个步骤用于将文件切分成多个 chunk。虽然对于已经存在的文件而言,此步骤可能会进行重复切分,但由于切分本身的代价相对较小,所以并不会造成大的影响。 -
create_base_extracted_entities
:这个步骤是根据 chunk 来生成对应的实体。对于已有的文件,其 chunk 是不会改变的,因此可以利用缓存来加快速度。然而,对于新文件,我们需要调用 LLM 来执行新的实体抽取。除此之外, create_base_extracted_entities一个比较重要的子步骤是 merge_graphs
,它的任务是将多个子图合并成一个新的大图。由于我们新增了文件,因此也就意味着有了新的子图需要合并。例如,如果 A.txt 和 B.txt 这两个文件都包含 "路飞" 这个实体,在 merge_graphs
中,A.txt 和 B.txt 的 "路飞" 会被合并成一个新的实体,并更新实体的 description 列表 -
create_summarized_entities
:此过程将对节点(node)和关系(relationship)的描述(descriptions)进行汇总。如果新增的文件包含与旧文件相同的实体,那么经过create_base_extracted_entities
后,节点和关系的描述也会改变。因此,填充到promptsummarize_descriptions.txt
的description_list
变量的值也会改变,无法利用缓存,必须重新执行。但是,如果新文件和旧文件内容没有任何关联,那么节点和关系的描述就不会改变,这时就能利用缓存了。 -
create_final_entities
:此过程将对节点进行嵌入化处理(embedding),以便于后续查询。如果新增的文件与旧文件有紧密的关系,比如存在相同的实体,那么经过create_summarized_entities
后,描述将会改变,从而导致嵌入的输入也发生改变,无法利用缓存。然而,当新文件与旧文件的内容完全无关时,旧文件的name_description
字段则无需重复进行嵌入。 - create_final_communities:此过程是进行社区检索重新生成社区,新增文件会产生新的社区或者对已有社区进行修改,所以这个工作流程需要重新执行
-
create_final_community_reports
:此过程借助 LLM 生成每个社区的摘要信息。如果新文件与旧文件的内容相关,会对已有的社区产生影响,需要重新进行 LLM 操作。而如果新文件和旧文件内容无关,那么生成的社区是全新的,旧的社区可以利用缓存,然后使用 LLM 为新社区生成对应的报告
总结
从上面的内容可以看出,新增文件的社区检测和生成是必不可少的。如果新增的文件与知识库中的已有知识相关联,GraphRAG需要做的工作会更多。值得一提的是,知识库的增长是一个渐进的过程,因此了解GraphRAG在处理新增内容时的逻辑对于我们评估成本和效果非常有帮助。通过这个过程,我们可以加深对GraphRAG整套流水线的理解。
本文转载自公众号AI 博物院 作者:longyunfeigu