深入实战 HippoRAG2:高效索引、查询与 GraphRAG 对比解析

25 阅读7分钟

继上文《深度解析仿人脑记忆搜索的 HippoRAG2》,我们已经对 HippoRAG2 有了基本的了解。本篇将更聚焦于实践部分,探讨如何配置和运行 HippoRAG2,包括测试评估和索引自定义文档和数据,并分析其成本与运行时间。最后,我们将索引小说《仙逆》,并与《实战微软新一代 RAG》在相同问题上的表现进行对比。

1. 安装与环境配置

由于 HippoRAG2 刚刚开源,目前仍以源码运行为主。该项目以科研为目的,且不像微软那样具有广泛的社区支持,因此短期内不太可能提供生产级(production-ready)的实现。按照 HippoRAG1 的发展趋势,未来的优化可能较为有限。 首先,克隆 HippoRAG2 并安装:

pip install https://github.com/OSU-NLP-Group/HippoRAG

创建 Python 3.10 的 Conda 虚拟环境:

conda create -n hippo python=3.10

安装依赖:

pip install -r requirements.txt

然后你会发现,它还需要安装vllmtorchgritlmtransformers等大型依赖。多说一句,后面还需要使用transformer运行HuggingFace上的7B Embedding服务,而这个是 FP16 的模型,需要至少 14G 的显存,所以如果你本地显卡不行,官方源码你可能无法运行。到这我估计到这很多人就要被劝退了,但作为一名经验丰富的技术博主,必须魔改代码,去除上述所有依赖,大模型和 Embedding 必须可以配置 OpenAI 兼容的远程服务。魔改代码在最后~。 完整依赖列表如下:

gritlm==1.0.2
networkx==3.4.2
numpy==2.2.1
openai==1.58.1
pydantic==2.10.4
python_igraph==0.11.8
scipy==1.14.1
tenacity==8.5.0
tiktoken==0.7.0
torch==2.4.0
tqdm==4.66.6
vllm==0.6.6.post1
transformers==4.45.2
nest_asyncio
dspy==2.5.29
einops

2. 配置优化

去除上述配置后,整个世界都安静了,一个 RAG 应用安装 vllm 是不是太搞笑了?去除上述本地依赖后,我们可以改为使用远程大模型 API 和嵌入(Embedding)服务,从而降低本地计算资源的消耗。 首先,进行基本配置,其中 BaseConfig 已经更新支持配置远程模型。大模型配置我们钟爱的 DeepSeek V3,Embedding 使用硅基流动的免费 bge-m3。但按照《上文》所说,这里最好是使用 7B 左右的大语言模型支持的 Emebdding 模型,以便于消歧。

from hipporag.utils.config_utils import BaseConfig

config = BaseConfig(
        llm_base_url="https://api.deepseek.com/v1",
        llm_name='deepseek-chat',
        llm_api_key="sk-xxxx",
        embedding_api_base="https://api.siliconflow.cn/v1",
        embedding_api_key="sk-xxx",
        embedding_model_name='BAAI/bge-m3',
        embedding_batch_size=16,
        rerank_dspy_file_path="hipporag/prompts/dspy_prompts/filter_llama3.3-70B-Instruct.json",
        graph_type="facts_and_sim_passage_node_unidirectional",
        max_new_tokens=4096,
        openie_mode="online"
    )

3. 索引

配置已经完成,我们开始索引。索引采用HippoRAG().index()进行索引,接收的输入为字符串数组:

def index(self, docs: List[str]):
    pass

从源码来看,docs数组在index中并不会自动进行chunk分割,因此我们需要先编写一个文本分割方法。例如:

def split_text_by_tokens(file_path: str, max_tokens: int = 512, overlap: int = 100) -> list[str]:
    with open(file_path, 'r', encoding='utf-8'as f:
        text = f.read()
    ...
    while start < len(tokens):
        end = min(start + max_tokens, len(tokens))
        chunk_tokens = tokens[start:end]
        chunks.append(enc.decode(chunk_tokens))
        start += max_tokens - overlap
    return chunks

接下来,对小说《仙逆》的前 10 章进行分割:

docs = chunk.split_text_by_tokens("input/xianni.txt")

实例化 HippoRAG 并执行索引:

from hipporag import HippoRAG
save_dir = 'outputs/xianni' # 指定输出目录
hipporag = HippoRAG(save_dir=save_dir, global_config=config)
hipporag.index(docs=docs)

输入文件大约 31000 个 Token 左右,索引耗时 10 分钟左右,耗费 Tokens 数量 8 万。

image-20250303224849656

image-20250303224849656

Hipporag2 自带缓存,抽取的 NER 等操作都已经自动缓存了,只要文件不变,它会自动利用缓存,第二次索引时间几乎为 0,反复跑 index 是没问题的。

索引后的文件就在我们设置的save_dir下,包含chunk_embeddingentity_embeddingfact_embedding3 部分,以及graphml图存储和openie结果,如下所示:

├── deepseek-chat_BAAI_bge-m3
│   ├── chunk_embeddings
│   │   └── vdb_chunk.parquet
│   ├── entity_embeddings
│   │   └── vdb_entity.parquet
│   ├── fact_embeddings
│   │   └── vdb_fact.parquet
│   └── graph.graphml
├── llm_cache
│   ├── deepseek-chat_cache.sqlite
│   └── deepseek-chat_cache.sqlite.lock
└── openie_results_ner_deepseek-chat.json

4. 查询

hipporag2 的查询方法目前只支持一组查询语句,因此我们参考文章《实战微软新一代 RAG》的测试问题集,对《仙逆》进行查询。

all_queries = ["谁是王林 他的主要人际关系是什么样",
               "王林和铁柱的关系",
               "王林的小名是什么",
               "在这个故事中王林的经历有哪些"]

查询方式如下:

  • 先检索后回答
retrieval_results = hipporag.retrieve(queries=all_queries)
ret = hipporag.qa(retrieval_results)
  • 直接生成答案
hipporag.rag_qa(queries=retrieval_results)
ret = hipporag.rag_qa(queries=all_queries)

示例回答:

  • 谁是王林 他的主要人际关系是什么样

王林的本名是王林,小名叫铁柱。他是王氏家族的一员,父亲是家族内庶出的次子,母亲对他非常关心。王林从小聪明,喜欢读书,是村子内公认的神童,父母对他期望很高。他的主要人际关系包括:

  1. 父亲:对王林寄予厚望,经常听到别人夸奖王林时会露出开怀的微笑。
  2. 母亲:对王林关心有佳,从小生活在父母的慈爱之中。
  3. 亲戚:包括三祖父、四叔等,三祖父曾暗中帮助过王林的父亲,四叔在家族中维护王林和他父亲。
  4. 王天山:王林的亲戚,与王林父亲有矛盾,曾冷嘲热讽王林。
  5. 王卓:王林的亲戚,傲慢不逊,对王林表现出轻蔑。 Answer: 王林是王氏家族的一员,小名叫铁柱。他的主要人际关系包括父亲、母亲、三祖父、四叔、王天山和王卓。
  • 王林和铁柱的关系

根据文本内容,王林的本名是王林,而“铁柱”是他的小名。因此,王林和铁柱是同一个人,铁柱是王林的称呼。

Answer: 王林和铁柱是同一个人,铁柱是王林的小名。

  • 王林的小名是什么。

王林的小名是铁柱。根据文本描述,铁柱是他的小名,因为从小身体瘦弱,父亲怕养不活,于是按照习俗称呼他为铁柱。

Answer: 铁柱。

  • 在这个故事中王林的经历有哪些

在这个故事中,王林的经历包括以下几个主要事件:

  1. 王林离家出走,试图自杀,但被天然形成的风眼吸扯,因此没有摔死。
  2. 他被张姓弟子找到并带回恒岳派,与母亲相见,了解了事情的经过。
  3. 王林被四叔带走,前往县城参加家族的选拔,离开了居住十五年的山村。
  4. 在恒岳派的测试中,王林坚持到最后,尽管身体受伤严重,但他没有放弃。
  5. 王林和其他坚持到最后的少年被送到剑灵阁进行剑灵测试,尽管心灵受到创伤,但他仍然坚持。
  6. 在攀爬石阶的过程中,王林全身受伤,但他仍然坚持用手攀爬,表现出极强的毅力和决心。

Answer: 王林的经历包括离家出走、被救回、参加选拔、坚持测试、剑灵测试和攀爬石阶。

从问答效果来看,前面三个问题回答都是对的,而当时 GraphRAG 没能回答正确关于王林和铁柱的关系。而 HippoRAG 不仅解释了是他的小名,还解释了小名的由来,回答比较完整和丰富。

我们在看第 4 个问题,这其实是一个全局类的问题,HippoRAG 除了描述的事情的经过,它还按照顺序描述了出来,可以说非常不错。

5. 评估

如果你要评估,你需要设计数据集。评估数据集要按照这个形式设计

[
  {
    "id": "sample/question_1.json",
    "question": "Which Stanford University professor works on Alzheimer's?",
    "answer": [
      "Thomas C. Sudhof"
    ],
    "answerable": true,
    "paragraphs": [
      {
        "title": "Thomas C. Sudhof",
        "text": "Introductio...",
        "is_supporting": true,
        "idx": 0
      }
    ]
  }
]

评估方法如下,会在回答完成后自动完成 EM 和 F1 评分计算。

gold_docs = get_gold_docs(samples, dataset_name)
gold_answers = get_gold_answers(samples)
hipporag.rag_qa(queries=all_queries, gold_docs=gold_docs, gold_answers=gold_answers)

6. 代码获取

我已经针对 HippoRAG2 进行了配置优化,去除了冗余依赖,支持中文,让你可以轻量级部署,抢先轻松上手。本文优化的代码和文中出现的测试代码都已经上传我的知识星球。而且,我还会持续更新优化策略,提供实战经验、代码解析和技术答疑!立即加入我的知识星球AgenticLM,一起探索最前沿的 RAG 技术,抢先体验 HippoRAG 2。 48888155251518T5

总结

本文介绍了 HippoRAG2 的安装、配置、索引、查询及评估流程,并与 GraphRAG 进行了对比测试。后续我们将继续深入优化 HippoRAG2,直到能够简单使用~