基于 RAPTOR 与 GraphRAG 的结构化文档索引


文档摘要

源文件:chapter3/structured-index/README.md 基于 RAPTOR 与 GraphRAG 的结构化文档索引 本教学项目演示两种用于对大型技术文档进行索引和查询的进阶方法: RAPTOR(Recursive Abstractive Processing for Tree-Organized Retrieval,面向树状组织检索的递归抽象处理)——通过递归摘要构建层级树结构 GraphRAG(Graph-based Retrieval Augmented Generation,基于图的检索增强生成)——构建包含实体、关系和社区检测的知识图谱 两种方法都针对处理大型技术文档进行了优化,例如 Intel® 64 和 IA-32 架构软件开发者手册。

源文件:chapter3/structured-index/README.md

基于 RAPTOR 与 GraphRAG 的结构化文档索引

本教学项目演示两种用于对大型技术文档进行索引和查询的进阶方法:

  1. RAPTOR(Recursive Abstractive Processing for Tree-Organized Retrieval,面向树状组织检索的递归抽象处理)——通过递归摘要构建层级树结构
  2. GraphRAG(Graph-based Retrieval Augmented Generation,基于图的检索增强生成)——构建包含实体、关系和社区检测的知识图谱

两种方法都针对处理大型技术文档进行了优化,例如 Intel® 64 和 IA-32 架构软件开发者手册。

特性

RAPTOR 树状索引

  • 具备多层抽象的层级树结构
  • 通过递归摘要进行信息压缩
  • 多层级检索能力(从叶节点到根摘要)
  • 基于高斯混合模型(GMM)的聚类式节点分组
  • 使用 UMAP 降维以提升聚类效率

GraphRAG 知识图谱索引

  • 使用 LLM 抽取实体与关系
  • 用于识别相关概念的社区检测
  • 分层社区摘要
  • 具备多种策略的基于图的检索
  • 多跳关系遍历GraphRAGIndexer.multi_hop_search):沿关系边做多跳遍历,
    回答扁平向量检索无法表达的「A 通过什么与 B 相连」这类关系性问题(对应书中「多跳关系推理」)
  • 支持不同实体类型(指令、寄存器、特性等)

HTTP API 服务

  • 用于构建和查询索引的 RESTful API
  • 支持文件上传
  • 针对大文档的异步处理
  • 跨两种索引类型的混合检索
  • 实时索引统计与状态监控

安装

  1. 克隆仓库并进入项目目录:
cd projects/week3/structured-index
  1. 安装依赖:
pip install -r requirements.txt
  1. 复制并配置环境文件:
cp env.example .env # 编辑 .env,填入你的 API 密钥和偏好

快速开始

命令行接口(CLI)

所有子命令都提供中文 --helppython main.py --helppython main.py demo --help 等。

usage: main.py [-h] {build,query,demo,serve} ... build 从文档构建结构化索引(需要 OPENAI_API_KEY) query 查询已构建的索引(需要 OPENAI_API_KEY 及已有索引) demo 离线对比演示:结构化索引 vs 扁平检索(无需 API Key) serve 启动 HTTP API 服务

0. 离线对比演示(无需 API Key,推荐先跑这个)

这是理解实验 3-8 的最快入口:它用一个手工整理的 Intel x86 SIMD 小知识库,
直观对比「扁平检索」与「结构化索引」在三类查询上的差异,全程无需 OpenAI API:

# 运行内置的三组对比查询(多跳关系推理 / 跨节点综合对比 / 多层次导航) python main.py demo # 自定义查询,同时给出扁平检索与图多跳遍历两种视角 python main.py demo --query "VADDPS 用到哪个寄存器" # 把结果写入 JSON python main.py demo --output demo_result.json

演示输出示例(多跳关系推理,扁平检索答不了、图检索沿关系边可达):

【查询 1|多跳关系推理】运行 ADDPS 指令前,操作系统必须把哪个控制寄存器位置 1? -- 扁平检索(按词面相似度返回独立片段)-- 1. [control-bit] CR4.OSFXSR (score=0.459) ... ✗ 只能召回词面相近的孤立片段,无法把 ADDPS 与某个控制位「连」起来。 -- 结构化图检索(沿关系边多跳遍历)-- ADDPS --属于--> SSE --需要启用--> CR4.OSFXSR ✓ 答案:CR4.OSFXSR(从 ADDPS 经 2 跳可达)

说明:buildquery 需要真实索引,而索引构建依赖 LLM(实体抽取、递归摘要),
因此需要设置 OPENAI_API_KEY(嵌入用本地 sentence-transformers)。demo
则把索引结果预先手工写好,让读者无需 API Key 也能看到结构化索引解决的问题。

1. 构建索引(需要 OPENAI_API_KEY)

# 同时构建 RAPTOR 与 GraphRAG 索引 python main.py build path/to/document.pdf # 只构建 RAPTOR,或只构建 GraphRAG python main.py build path/to/document.pdf --type raptor python main.py build path/to/document.pdf --type graphrag # 将索引统计写入 JSON python main.py build path/to/document.pdf --output stats.json

2. 查询索引

# 查询两种索引 python main.py query "What are the MOV instruction variants?" # 指定索引类型与返回条数 python main.py query "explain SSE instructions" --type raptor --top-k 10 # GraphRAG 多跳关系遍历:以召回的最佳实体为起点,沿关系边走 N 跳 python main.py query "SSE registers" --type graphrag --multi-hop 2 # 将查询结果写入 JSON python main.py query "control registers" --output result.json

3. 启动 API 服务

python main.py serve

使用 HTTP API

  1. 启动服务:
python main.py serve # 服务运行在 http://localhost:4242
  1. 通过 API 构建索引:
# 上传文件并构建索引 curl -X POST "http://localhost:4242/upload" \ -F "file=@path/to/intel_manual.pdf" \ -F "index_type=both" # 从文本构建 curl -X POST "http://localhost:4242/build" \ -H "Content-Type: application/json" \ -d '{ "file_path": "/path/to/document.pdf", "index_type": "both", "force_rebuild": false }'
  1. 查询索引:
curl -X POST "http://localhost:4242/query" \ -H "Content-Type: application/json" \ -d '{ "query": "What are vector instructions?", "index_type": "hybrid", "top_k": 5 }'
  1. 检查索引状态:
curl http://localhost:4242/status curl http://localhost:4242/statistics

API 端点

端点 方法 说明
/ GET API 信息及可用端点
/build POST 从文本或文件构建索引
/upload POST 上传文件并构建索引
/query POST 查询索引
/status GET 获取索引状态
/statistics GET 获取详细索引统计
/indexes DELETE 清空索引

项目结构

structured-index/ ├── config.py # 配置管理 ├── raptor_indexer.py # RAPTOR 树状索引 ├── graphrag_indexer.py # GraphRAG 图索引 ├── document_processor.py # 文档解析与预处理 ├── api_service.py # HTTP API 服务 ├── structured_vs_flat_demo.py # 离线对比演示:结构化索引 vs 扁平检索(无需 API) ├── main.py # CLI 接口 ├── requirements.txt # Python 依赖 ├── env.example # 环境变量模板 ├── indexes/ # 已保存的索引文件 │ ├── raptor/ # RAPTOR 索引存储 │ └── graphrag/ # GraphRAG 索引存储 └── cache/ # 临时缓存目录

工作原理

RAPTOR 索引过程

  1. 文本切块:把文档切分成带重叠的可管理块
  2. 嵌入生成:每个块转换为向量嵌入
  3. 叶节点创建:块成为带摘要的叶节点
  4. 层次聚类:用 GMM 对节点聚类
  5. 父节点生成:对聚类做摘要生成父节点
  6. 树构建:跨多个层级重复该过程
  7. 多层级检索:查询时跨所有树层级检索

GraphRAG 索引过程

  1. 实体抽取:LLM 识别实体(指令、寄存器等)
  2. 关系发现:抽取实体间的连接
  3. 图构建:从实体和关系构建 NetworkX 图
  4. 社区检测:用 Leiden/Louvain 对相关实体分组
  5. 社区摘要:每个社区得到一段描述性摘要
  6. 层次聚合:相似社区被合并并摘要
  7. 图检索:查询时匹配实体与社区摘要

示例:处理 Intel 架构手册

import asyncio from pathlib import Path from config import get_raptor_config, get_graphrag_config from raptor_indexer import RaptorIndexer from graphrag_indexer import GraphRAGIndexer from document_processor import DocumentProcessor async def process_intel_manual(): # 处理 Intel 手册 PDF processor = DocumentProcessor() intel_manual_path = Path("intel_x86_64_manual.pdf") text = await processor.process_file(intel_manual_path) # 构建 RAPTOR 索引 raptor_config = get_raptor_config() raptor = RaptorIndexer(raptor_config) raptor.build_index(text) raptor.save_index() # 构建 GraphRAG 索引 graphrag_config = get_graphrag_config() graphrag = GraphRAGIndexer(graphrag_config) graphrag.build_knowledge_graph(text) graphrag.detect_communities() graphrag.hierarchical_summarization() graphrag.save_index() # 示例查询 queries = [ "What are the different addressing modes?", "Explain SIMD instructions", "How does the MOV instruction work?", "What are control registers?" ] for query in queries: print(f"\nQuery: {query}") print("-" * 50) # RAPTOR 检索 raptor_results = raptor.search(query, top_k=3) print("RAPTOR Results:") for r in raptor_results: print(f" Level {r['level']}: {r['summary'][:100]}...") # GraphRAG 检索 graphrag_results = graphrag.search(query, top_k=3) print("\nGraphRAG Results:") for r in graphrag_results: if r['type'] == 'entity': print(f" Entity: {r['name']} - {r['description'][:100]}...") else: print(f" Community: {r['summary'][:100]}...") # 运行示例 asyncio.run(process_intel_manual())

高级配置

RAPTOR 参数

  • chunk_size:文本块大小(默认:1000 词)
  • chunk_overlap:块间重叠(默认:200 词)
  • tree_depth:最大树深(默认:3)
  • summarization_length:目标摘要长度(默认:200 词)

GraphRAG 参数

  • chunk_size:文本块大小(默认:1200 词)
  • max_knowledge_triples:每块最大三元组数(默认:10)
  • community_detection_algorithm:"leiden" 或 "louvain"
  • summarization_model:用于生成摘要的模型

性能考量

  1. 大文档:处理 5000+ 页文档可能耗时较长
  2. API 限流:处理时需考虑 OpenAI API 的速率限制
  3. 内存占用:大型图需要大量内存
  4. 缓存:结果会被缓存以提升后续查询性能
  5. 并行处理:使用 API 服务进行并发操作

与 Agentic RAG 的集成

本项目为 agentic-rag 项目提供后端服务。集成细节参见 agentic-rag 的 README。

故障排查

  1. 内存不足:减小 chunk_size 或分段处理文档
  2. API 错误:检查 API 密钥和速率限制
  3. 索引缓慢:初始测试时可考虑使用更快 / 更小的模型
  4. 导入错误:确认所有依赖已正确安装

参考资料

OpenRouter 通用回退 / Universal OpenRouter fallback

This experiment now supports a universal OpenRouter fallback for its chat LLM.

  • If the primary provider key (e.g. MOONSHOT_API_KEY / KIMI_API_KEY / OPENAI_API_KEY / DOUBAO_API_KEY …) is present, behavior is unchanged.
  • Else if OPENROUTER_API_KEY is set, the chat LLM is automatically routed through OpenRouter (https://openrouter.ai/api/v1). Model names are mapped automatically: gpt-*/o1-*openai/…, claude-*anthropic/claude-opus-4.8, kimi-*moonshotai/kimi-k2.6, ids already containing / are kept as-is, and other provider-native ids (e.g. doubao-*) fall back to openai/gpt-5.6-luna. Set OPENROUTER_MODEL to force a specific OpenRouter model id.
  • Else a clear error lists the accepted keys.

Add OPENROUTER_API_KEY=... to your .env (see env.example) to enable it.

Note: embeddings here are local SentenceTransformers (all-MiniLM-L6-v2), so they are unaffected — only the chat LLM used for RAPTOR summarization and GraphRAG entity extraction is routed through OpenRouter.


作者与出处
原作者: bojieli
来源:bojieli
许可证:Apache-2.0
整理: 灏天文库整理
由灏天文库结构化整理,提供目录导航、全文检索与在线阅读,便于系统化学习
发布者: 作者: bojieli 转发
评论区 (0)
U