File size: 18,711 Bytes
8e874f5 9c87c58 b0109c7 8e874f5 9c87c58 8e874f5 8dade95 8e874f5 57d071a 8e874f5 57d071a 8e874f5 57d071a 8e874f5 57d071a 8e874f5 57d071a 8e874f5 57d071a 8e874f5 57d071a 8e874f5 57d071a 8e874f5 57d071a 8e874f5 57d071a 8e874f5 57d071a 8e874f5 57d071a 8e874f5 1bd69b5 8e874f5 57d071a 8e874f5 57d071a 8e874f5 57d071a 8e874f5 57d071a 8e874f5 e04ab3f 8e874f5 57d071a 8e874f5 57d071a 8e874f5 57d071a 8e874f5 57d071a 8e874f5 57d071a 8e874f5 57d071a 8e874f5 57d071a 2bc90b8 57d071a 8e874f5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 | # QAFD-RAG
Official code for ICLR 2026 paper: **[Query-Aware Flow Diffusion for Graph-Based RAG with Retrieval Guarantees](https://openreview.net/forum?id=n28wnc2QTc)**
QAFD-RAG uses **query-aware flow diffusion** to retrieve contextually relevant subgraphs from a knowledge graph. Unlike community-based (GraphRAG) or entity-centric (LightRAG) approaches, QAFD-RAG dynamically re-weights edges based on query relevance and propagates flow through the graph to discover multi-hop context with retrieval guarantees.
<p align="center"><em>Query: "Introduce Steve Jobs's products in Apple."</em></p>
<table align="center"><tr>
<td align="center"><img src="docs/figs/GraphRAG.png" width="260" alt="GraphRAG"/><br/><em>GraphRAG</em></td>
<td align="center"><img src="docs/figs/LightRAG.png" width="260" alt="LightRAG"/><br/><em>LightRAG</em></td>
<td align="center"><img src="docs/figs/QAFD-RAG.png" width="260" alt="QAFD-RAG"/><br/><em>QAFD-RAG</em></td>
</tr></table>
<p align="center"><em>QAFD-RAG reweights edges by query relevance, suppressing irrelevant neighborhoods (e.g., Amazon, Apple fruit).</em></p>
## Quick Start
```bash
# 1. Create conda environment and install dependencies
conda create -n qafd-rag python=3.10 -y
conda activate qafd-rag
pip install -r requirements.txt
# 2. Set your OpenAI API key (required for LLM and answer generation)
export OPENAI_API_KEY="sk-..."
# 3. Download pre-built KGs (recommended β saves hours of build time + API costs)
huggingface-cli download tarzanagh/QAFD-RAG --include "kg/multihop/*" --local-dir .
huggingface-cli download tarzanagh/QAFD-RAG --include "kg/ultradomain/*" --local-dir .
# 4. Run a benchmark
# Note: First run for multihop will auto-download the nvidia/NV-Embed-v2
# embedding model (~8GB, one-time, requires GPU with 16GB+ VRAM).
# No GPU? See "Rebuild with openai-small" below.
python benchmarks/run.py --task multihop --dataset musique --questions 10
python benchmarks/run.py --task ultradomain --dataset mix --questions 10
```
> **Embeddings:** For best multihop results, download the pre-built KGs which use `nvidia-nv-embed-v2` (requires GPU with 16GB+ VRAM, auto-downloaded on first run). If you don't have a GPU, you can rebuild the KGs from scratch with `openai-small` instead: `python benchmarks/run.py --task multihop --dataset musique --force_build --embedding openai-small`.
>
> **Graph Types:** Multihop defaults to **passage-entity** graph (entities + passages + facts as nodes). UltraDomain/text2sql/summarization default to **entity** graph (classic KG). Override with `--graph_type`.
## Two Graph Types
QAFD-RAG supports two knowledge graph representations, both using the same Query-Aware Flow Diffusion algorithm:
| | **Entity Graph** | **Passage-Entity Graph** |
|---|---|---|
| **Nodes** | Entities + relationships | Entities + passages + facts (GutiΓ©rrez et al., 2024) |
| **Extraction** | LLM entity/relationship extraction | OpenIE (NER + triple extraction) |
| **Edges** | Entity-entity relationships | Fact edges + passage edges + synonymy edges |
| **QAFD traversal** | Flow reaches entities, passages looked up after | Flow reaches passages directly as graph nodes |
| **Best for** | General QA, text2sql, summarization | Multi-hop reasoning |
| **Default tasks** | ultradomain, text2sql, summarization | multihop |
Both graph types use query-aware flow diffusion with the same parameters (alpha=1.5, epsilon=0.01, step_size=0.2, weight_scheme=multiply, linking_top_k=10).
## Benchmarks
QAFD-RAG is evaluated on four tasks. Each subsection below covers data setup, pre-built KGs, and how to run.
All benchmarks use the unified runner:
```bash
python benchmarks/run.py --task <task> --dataset <dataset> [options]
```
The runner automatically selects the appropriate graph type (passage-entity for multihop, entity for others). Override with `--graph_type`. A legacy CLI (`./run.sh`) is also available for entity-graph benchmarks.
Pre-built KGs for all tasks are available at [huggingface.co/tarzanagh/QAFD-RAG](https://huggingface.co/tarzanagh/QAFD-RAG). Downloading is **recommended** to avoid hours of build time and API costs.
### Multi-hop QA
| | |
|---|---|
| **Datasets** | MuSiQue, HotpotQA, 2WikiMultiHopQA |
| **Graph type** | passage-entity (default) |
| **Metrics** | F1, Exact Match |
| **Data** | Included in `data/multihop/` (from [osunlp](https://huggingface.co/osunlp)) |
| **Embedding** | `nvidia-nv-embed-v2` (GPU, 16GB+ VRAM) |
```bash
# Download pre-built KGs (recommended β saves hours + API costs)
huggingface-cli download tarzanagh/QAFD-RAG --include "kg/multihop/*" --local-dir .
# Run benchmark
python benchmarks/run.py --task multihop --dataset musique --questions 100
python benchmarks/run.py --task multihop --dataset hotpotqa --questions 100
python benchmarks/run.py --task multihop --dataset 2wikimultihopqa --questions 100
# No GPU? Rebuild KGs with openai-small instead
python benchmarks/run.py --task multihop --dataset musique --force_build --embedding openai-small
# Retrieval only (skip answer generation)
python benchmarks/run.py --task multihop --dataset musique --skip_qa
```
### UltraDomain
| | |
|---|---|
| **Datasets** | agriculture, biology, cs, finance, legal, math, medicine, mix, music, philosophy, physics |
| **Graph type** | entity (default) |
| **Metrics** | Quality scores (comprehensiveness, diversity, relevance, logicality, coherence) |
| **Data** | Auto-downloaded from [TommyChien/UltraDomain](https://huggingface.co/datasets/TommyChien/UltraDomain) at runtime |
| **Embedding** | `openai-small` (no GPU needed) |
```bash
# Download pre-built KGs (recommended)
huggingface-cli download tarzanagh/QAFD-RAG --include "kg/ultradomain/*" --local-dir .
# Run benchmark
python benchmarks/run.py --task ultradomain --dataset mix --questions 10
# Use passage-entity graph instead of entity graph
python benchmarks/run.py --task ultradomain --dataset mix --graph_type passage-entity --questions 10
```
### Text-to-SQL
| | |
|---|---|
| **Datasets** | Spider2-lite, Bird |
| **Graph type** | entity |
| **Metrics** | Schema retrieval precision / recall |
| **Data** | Example databases included in `data/text2sql/` (see [Data](#data) section) |
| **Embedding** | `openai-small` (no GPU needed) |
Two example databases are included with pre-generated DB summaries:
- **Pagila** (Spider2-lite) -- DVD rental store, 16 tables, SQLite
- **superhero** (Bird) -- superhero database, 10 tables, SQLite
For the full Spider2-lite benchmark, clone [Spider2](https://github.com/xlang-ai/Spider2) and copy databases into `data/text2sql/spider2-lite/sqlite/`.
```bash
# Run on included example databases
./run.sh text2sql --questions 5 --db Pagila # Spider2-lite (auto-detected)
./run.sh text2sql --questions 5 --db superhero # Bird (auto-detected)
./run.sh text2sql --benchmark bird --questions 5 --db superhero # explicit benchmark selection
# Build KG only (no benchmark)
python benchmarks/run.py --task text2sql --dataset spider2-lite --build_only --db Pagila
```
> **Adding a new database:** Place your `.sqlite` file in `data/text2sql/spider2-lite/sqlite/<DB_Name>/`, then generate a DB summary (see [Generating DB Summaries](#generating-db-summaries)). The benchmark will auto-build the KG on first run.
### Summarization
| | |
|---|---|
| **Dataset** | SQuALITY |
| **Graph type** | entity |
| **Metrics** | BLEU, ROUGE, METEOR, quality scores |
| **Data** | Auto-downloaded from [pszemraj/SQuALITY-v1.3](https://huggingface.co/datasets/pszemraj/SQuALITY-v1.3) at runtime |
```bash
python benchmarks/run.py --task summarization --dataset squality --questions 50
```
### Common Options
| Option | Description |
|--------|-------------|
| `--task TASK` | `multihop`, `ultradomain`, `text2sql`, `summarization` |
| `--dataset NAME` | Dataset name (e.g., `musique`, `mix`, `spider2-lite`) |
| `--graph_type TYPE` | `passage-entity` or `entity` (auto-selected by task) |
| `--questions N` | Number of questions to evaluate |
| `--build_only` | Build KG only, skip benchmark |
| `--force_build` | Rebuild KG even if it exists |
| `--skip_qa` | Run retrieval only, skip QA (passage-entity) |
| `--embedding MODEL` | Embedding model (see [Configuration](#configuration)) |
| `--llm MODEL` | LLM model (`gpt-4o-mini`, `gpt-4o`, `gpt-5-nano`, `gpt-5-mini`, `gpt-5`, `gpt-oss-120b`) |
| `--alpha FLOAT` | QAFD alpha parameter (default: 2.0) |
| `--epsilon FLOAT` | QAFD convergence threshold (default: 0.01) |
| `--weight_scheme` | Query-aware edge weighting: `original`, `multiply`, `add` |
## Data
### Included Data
| Task | Directory | Contents | Size |
|------|-----------|----------|------|
| **Multi-hop QA** | `data/multihop/` | MuSiQue, HotpotQA, 2WikiMultiHopQA (full datasets from [osunlp](https://huggingface.co/osunlp)) | 134 MB |
| **UltraDomain** | `data/ultradomain/` | Auto-downloaded from [HuggingFace](https://huggingface.co/datasets/TommyChien/UltraDomain) at runtime | -- |
| **Text-to-SQL** | `data/text2sql/` | Example databases + question files (see below) | 9.6 MB |
| **Summarization** | `data/summarization/` | Auto-downloaded from [HuggingFace](https://huggingface.co/datasets/pszemraj/SQuALITY-v1.3) at runtime | -- |
### Text-to-SQL Data
Example databases with pre-generated DB summaries are included so you can run the text2sql benchmark out of the box:
| Backend | Database | Files | Description |
|---------|----------|-------|-------------|
| SQLite | **Pagila** | `Pagila.sqlite` + `Pagila_db_summary.json` | DVD rental store (16 tables) |
| SQLite | **superhero** | `superhero.sqlite` + `superhero_db_summary.json` | Superhero database (10 tables) |
| BigQuery | **san_francisco** | `san_francisco_bigquery_summary.json` | SF city data: bikeshare, crime, film locations, fire dept, street trees (8 tables, 118 columns, schema only) |
| Snowflake | AUSTIN | `AUSTIN_db_summary.json` | Austin 311 service requests (schema only) |
Question files: `spider2-lite.jsonl` (546 questions) and `bird.jsonl` (129 questions).
For the full Spider2-lite benchmark with all databases, clone [Spider2](https://github.com/xlang-ai/Spider2) and copy the SQLite files into `data/text2sql/spider2-lite/sqlite/`.
### Generating DB Summaries
To add a new SQLite database to the text2sql benchmark, generate a DB summary using the included CLI tool:
```bash
# Generate DB summary for a SQLite database
python -m src.indexing.extract_db_summary \
--db-path data/text2sql/spider2-lite/sqlite/MyDB/MyDB.sqlite
# Specify custom output directory
python -m src.indexing.extract_db_summary \
--db-path path/to/database.sqlite \
--output data/text2sql/spider2-lite/sqlite/MyDB/
```
This produces a `<DB_Name>_db_summary.json` file containing:
- Table and column metadata (types, primary/foreign keys, nullable)
- Cardinality and distinct value counts per column
- Sample values for each column
- Inferred foreign key relationships
For BigQuery and Snowflake databases (schema summaries only, no raw data):
```bash
# BigQuery
python -m src.indexing.extract_db_summary_bigquery \
--datasets project_id.dataset_id \
--output-dir data/text2sql/spider2-lite/bigquery/my_dataset/
# Snowflake
python -m src.indexing.extract_db_summary_snowflake \
--databases MY_DATABASE \
--output-dir data/text2sql/spider2-lite/snowflake/MY_DATABASE/
```
## Project Structure
```
QAFD-RAG/
βββ benchmarks/
β βββ run.py # Unified benchmark runner (--graph_type, --task)
β βββ multihop/ # Multi-hop reasoning
β βββ ultradomain/ # General domain QA
β βββ text2sql/ # Natural language to SQL
β βββ summarization/ # Document summarization
βββ src/
β βββ QAFD_RAG.py # Entity graph pipeline (main class)
β βββ base.py # Storage interfaces, QueryParam
β βββ llm.py # LLM and embedding functions
β βββ storage.py # KV, vector, and graph storage
β βββ operate.py # Query operations
β βββ evaluation.py # Answer evaluation
β βββ answering/ # Query processing (entity graph)
β β βββ handler.py # kg_query entry point
β β βββ context.py # Context building (local/global/hybrid)
β β βββ clusters.py # Flow diffusion clustering
β β βββ text_units.py # Text chunk retrieval
β βββ retrievers/
β β βββ flow_diffusion.py # QAFD on NetworkX (entity graph)
β β βββ base.py # Retriever interface
β βββ passage_entity/ # Passage-entity graph pipeline
β β βββ kg_builder.py # OpenIE β igraph (entities + passages + facts)
β β βββ graph_adapter.py # QAFD on igraph (passage-entity graph)
β β βββ retriever.py # Fact reranking β seed selection β QAFD
β β βββ config.py # Pipeline configuration
β β βββ openie.py # NER + triple extraction
β β βββ embedding_store.py # Parquet-backed vector store
β β βββ reranker.py # LLM-based fact reranker
β β βββ prompts.py # Prompt templates
β β βββ benchmark_runner.py # Standalone runner
β β βββ utils.py # Helpers
β βββ indexing/ # KG construction (entity graph)
β β βββ chunkers.py # Token-based chunking
β β βββ extractors.py # Entity/relationship extraction
β β βββ schema_builder.py # Database schema KG builder
β β βββ extract_db_summary.py # SQLite DB summary generator (CLI)
β β βββ build_kg.py # CLI KG builder
β βββ embedding_models/ # Embedding model implementations
β βββ prompts/ # LLM prompt templates
β βββ text2sql/ # Text-to-SQL support
β βββ utils/ # Helpers
βββ data/ # Datasets (included or auto-downloaded)
β βββ multihop/ # MuSiQue, HotpotQA, 2WikiMultiHopQA
β βββ ultradomain/ # Auto-downloaded at runtime
β βββ text2sql/ # Example DBs (Pagila, superhero) + questions
β βββ summarization/ # Auto-downloaded at runtime
βββ kg/ # Knowledge graphs (auto-generated or downloaded)
βββ docs/figs/ # Figures for README
βββ results/ # Benchmark results (JSON)
```
## Configuration
### Embedding Models
| Key | Model | Dimensions |
|-----|-------|-----------|
| `openai-small` | text-embedding-3-small | 1536 |
| `openai-large` | text-embedding-3-large | 3072 |
| `jina-v3` | jinaai/jina-embeddings-v3 | 1024 |
| `gritlm` | GritLM/GritLM-7B | 4096 |
| `nvidia-nv-embed-v2` | nvidia/NV-Embed-v2 | 4096 |
The default embedding is **`openai-small`** which requires `OPENAI_API_KEY`.
Use `--embedding <key>` to select a model:
```bash
./run.sh ultradomain --questions 10 --embedding jina-v3
```
### Local Embedding Models
Local embeddings (`jina-v3`, `gritlm`, `nvidia-nv-embed-v2`) run on your GPU and do not require an API key for embeddings. They are downloaded automatically from HuggingFace on first use.
**Requirements:**
- CUDA-capable GPU with sufficient VRAM (8GB+ recommended)
- Models are cached in `~/.cache/huggingface/`
**Usage with local embeddings (no OpenAI API needed for embeddings):**
```bash
# Use Jina v3 (1024-dim, lightweight, good quality)
./run.sh ultradomain --questions 10 --embedding jina-v3
# Use GritLM (4096-dim, unified embedding+generation)
./run.sh multihop --dataset musique --questions 10 --embedding gritlm
# Use NVIDIA NV-Embed-v2 (4096-dim, 32K context, high quality)
./run.sh ultradomain --questions 10 --embedding nvidia-nv-embed-v2
```
> **Note:** Even with local embeddings, an LLM API key is still required for entity extraction (KG building) and answer generation. Set `OPENAI_API_KEY` or use `--llm gpt-oss-120b` for a free open-source LLM.
### LLM Models
`gpt-4o-mini` (default), `gpt-4o`, `gpt-5-nano`, `gpt-5-mini`, `gpt-5`, `gpt-oss-120b` (free, local)
## How It Works
QAFD-RAG processes documents through a two-stage pipeline:
**Stage 1 -- Knowledge Graph Construction**
*Entity Graph:*
1. Documents are split into token-based chunks.
2. An LLM extracts entities and relationships from each chunk.
3. Entities become nodes, relationships become weighted edges.
*Passage-Entity Graph:*
1. Documents are split into passages (chunks).
2. OpenIE extracts named entities and (subject, predicate, object) triples.
3. Entities and passages are both graph nodes, connected by fact edges, passage-entity edges, and synonymy edges (entity pairs with cosine similarity > 0.8).
**Stage 2 -- Query-Aware Flow Diffusion**
1. **Seed Selection**: Query is matched to entities/facts via embedding similarity. An LLM reranker filters the most relevant facts.
2. **Flow Diffusion**: Mass is injected at seed nodes and propagated through the graph via push-relabel. Edge weights are dynamically adjusted based on each node's similarity to the query.
3. **Passage Ranking**: Nodes accumulate importance scores proportional to flow. In the passage-entity graph, passages are ranked directly. In the entity graph, associated text chunks are retrieved from top-ranked entities.
4. **LLM Answering**: Top passages are assembled into context and passed to an LLM for answer generation.
The flow diffusion algorithm is the key differentiator: rather than simple graph traversal or vector search alone, it combines graph structure with query relevance to find contextually important information that may be several hops away from the initial match.
## Citation
```bibtex
@inproceedings{zhou2026qafd,
title={Query-Aware Flow Diffusion for Graph-Based RAG with Retrieval Guarantees},
author={Zhou, Zhuoping and Ataee Tarzanagh, Davoud and Didari, Sima and Hu, Wenjun and Gutow, Baruch and Verkholyak, Oxana and Faraki, Masoud and Hao, Heng and Moon, Hankyu and Min, Seungjai},
booktitle={International Conference on Learning Representations (ICLR)},
year={2026}
}
```
|