{ "cells": [ { "cell_type": "markdown", "id": "33c2ae6c", "metadata": { "papermill": { "duration": 0.0, "end_time": "2026-05-28T03:02:12.543760+00:00", "exception": false, "start_time": "2026-05-28T03:02:12.543760+00:00", "status": "completed" }, "tags": [] }, "source": [ "# 27 · GraphRAG — knowledge graph + community summaries\n", "\n", "> **TL;DR.** At build-time: extract (subject, predicate, object) triples from the corpus → NetworkX graph → detect communities → summarise each via LLM. At query-time: classify local vs global → traverse entity neighbourhood OR consult community summaries → answer.\n", ">\n", "> **Reach for it when** the corpus has rich entity-entity relationships AND you have global theme questions (\"what are the main topics?\") that vector retrieval can't answer well.\n", "\n", "| Property | Value |\n", "|---|---|\n", "| Origin | Edge et al., *From Local to Global: GraphRAG* (Microsoft 2024). [arXiv:2404.16130](https://arxiv.org/abs/2404.16130) |\n", "| Backend | NetworkX (default, in-process) or Neo4j (`GRAPH_BACKEND=neo4j`) |\n", "| Community detection | NetworkX greedy modularity |\n", "| Build cost | 1 extract per doc + 1 summarise per community (precomputed once) |\n", "| Query cost | 1 classify + 1 answer (+ optional traversal) |\n", "\n", "Builds on [Graph Memory (nb 12)](./12_graph_memory.ipynb) by adding the **community summarisation** layer — that's what enables answering global questions about themes." ] }, { "cell_type": "markdown", "id": "9a3d814b", "metadata": { "papermill": { "duration": 0.0, "end_time": "2026-05-28T03:02:12.543760+00:00", "exception": false, "start_time": "2026-05-28T03:02:12.543760+00:00", "status": "completed" }, "tags": [] }, "source": [ "## 2 · Architecture at a glance\n", "\n", "```mermaid\n", "flowchart TB\n", " subgraph build [Build phase, one-shot]\n", " D[docs] --> EX[Extract triples]\n", " EX --> G[(NetworkX KG)]\n", " G --> CD[Community detection]\n", " CD --> CS[Summarise each community]\n", " end\n", " subgraph query [Query phase]\n", " Q([query]) --> C[Classify local/global]\n", " C -->|local| LN[Entity neighbourhood]\n", " C -->|global| GS[All community summaries]\n", " LN --> ANS[Answer from context]\n", " GS --> ANS\n", " end\n", " CS -.-> GS\n", " G -.-> LN\n", "\n", " style EX fill:#fff3e0,stroke:#f57c00\n", " style CD fill:#fce4ec,stroke:#c2185b\n", " style CS fill:#e3f2fd,stroke:#1976d2\n", " style C fill:#e8f5e9,stroke:#388e3c\n", "```" ] }, { "cell_type": "markdown", "id": "3684b2eb", "metadata": { "papermill": { "duration": 0.01595, "end_time": "2026-05-28T03:02:12.559710+00:00", "exception": false, "start_time": "2026-05-28T03:02:12.543760+00:00", "status": "completed" }, "tags": [] }, "source": [ "## 3 · Theory\n", "\n", "### 3.0 · Why community summaries\n", "\n", "Plain RAG retrieves top-k similar snippets — great for \"what is X?\" but useless for \"what are the main themes across the corpus?\". The themes don't live in any single document.\n", "\n", "GraphRAG's fix: cluster related entities (via graph community detection), summarise each cluster with the LLM at build-time, then for global questions feed all summaries to the answer-LLM. The summaries are pre-computed; no per-query cost beyond the answer.\n", "\n", "### 3.1 · Why pre-classify local vs global\n", "\n", "| Query type | Context source | Why |\n", "|---|---|---|\n", "| Local (\"who is X?\") | Entity neighbourhood (depth-2 subgraph) | Tight, specific facts about the named entity |\n", "| Global (\"what are the themes?\") | All community summaries | Themes live in the community structure, not in any one doc |\n", "\n", "A 3-way categorical classifier could also handle \"mixed\" — for the demo we keep two buckets for clarity.\n", "\n", "### 3.2 · Where this sits in the RAG family\n", "\n", "| Pattern | Indexing structure |\n", "|---|---|\n", "| Plain RAG | Flat vector index |\n", "| [Agentic RAG (nb 23)](./23_agentic_rag.ipynb) | Flat vector index (agent decides when) |\n", "| [Self-RAG (nb 25)](./25_self_rag.ipynb) | Flat vector index (per-doc reflection) |\n", "| **GraphRAG (this nb)** | **Knowledge graph with community summaries** |\n", "| [Graph Memory (nb 12)](./12_graph_memory.ipynb) | Knowledge graph (no community summaries) |" ] }, { "cell_type": "markdown", "id": "97372250", "metadata": { "papermill": { "duration": 0.0, "end_time": "2026-05-28T03:02:12.561252+00:00", "exception": false, "start_time": "2026-05-28T03:02:12.561252+00:00", "status": "completed" }, "tags": [] }, "source": [ "## 4 · Setup" ] }, { "cell_type": "code", "execution_count": 1, "id": "de214649", "metadata": { "execution": { "iopub.execute_input": "2026-05-28T03:02:12.561252Z", "iopub.status.busy": "2026-05-28T03:02:12.561252Z", "iopub.status.idle": "2026-05-28T03:02:14.863586Z", "shell.execute_reply": "2026-05-28T03:02:14.863282Z" }, "papermill": { "duration": 2.303411, "end_time": "2026-05-28T03:02:14.864663+00:00", "exception": false, "start_time": "2026-05-28T03:02:12.561252+00:00", "status": "completed" }, "tags": [] }, "outputs": [ { "data": { "text/html": [ "
LLM: meta-llama/Llama-3.3-70B-Instruct · Corpus: 12 docs ────────────────────────────────────────────────────────\n", "\n" ], "text/plain": [ "\u001b[1;36mLLM: meta-llama/Llama-\u001b[0m\u001b[1;36m3.3\u001b[0m\u001b[1;36m-70B-Instruct · Corpus: \u001b[0m\u001b[1;36m12\u001b[0m\u001b[1;36m docs\u001b[0m \u001b[92m────────────────────────────────────────────────────────\u001b[0m\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from agentic_architectures import get_llm, enable_langsmith, settings\n", "from agentic_architectures.architectures import GraphRAG\n", "from agentic_architectures.data import STARDUST_CORPUS\n", "from agentic_architectures.ui import print_md, print_header\n", "\n", "enable_langsmith()\n", "llm = get_llm(provider=\"nebius\", model=\"meta-llama/Llama-3.3-70B-Instruct\", temperature=0.2)\n", "print_header(f\"LLM: {llm.model} · Corpus: {len(STARDUST_CORPUS)} docs\")" ] }, { "cell_type": "markdown", "id": "c062383f", "metadata": { "papermill": { "duration": 0.001991, "end_time": "2026-05-28T03:02:14.869625+00:00", "exception": false, "start_time": "2026-05-28T03:02:14.867634+00:00", "status": "completed" }, "tags": [] }, "source": [ "## 5 · Library walkthrough" ] }, { "cell_type": "code", "execution_count": 2, "id": "7becbb96", "metadata": { "execution": { "iopub.execute_input": "2026-05-28T03:02:14.874669Z", "iopub.status.busy": "2026-05-28T03:02:14.874669Z", "iopub.status.idle": "2026-05-28T03:02:14.880281Z", "shell.execute_reply": "2026-05-28T03:02:14.879963Z" }, "papermill": { "duration": 0.009694, "end_time": "2026-05-28T03:02:14.881319+00:00", "exception": false, "start_time": "2026-05-28T03:02:14.871625+00:00", "status": "completed" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "--- _QuestionScope schema ---\n", "{\n", " \"description\": \"Pre-classify question as local (entity-anchored) or global (about themes).\",\n", " \"properties\": {\n", " \"scope\": {\n", " \"description\": \"'local' for entity-specific questions ('who founded X?'); 'global' for theme/summary questions ('what are the main themes?').\",\n", " \"enum\": [\n", " \"local\",\n", " \"global\"\n", " ],\n", " \"title\": \"Scope\",\n", " \"type\": \"string\"\n", " },\n", " \"ta...\n", "\n", "--- _IngestionTriple schema ---\n", "{\n", " \"properties\": {\n", " \"subject\": {\n", " \"description\": \"Specific named entity (capitalised).\",\n", " \"title\": \"Subject\",\n", " \"type\": \"string\"\n", " },\n", " \"predicate\": {\n", " \"description\": \"Short relation verb (snake_case).\",\n", " \"title\": \"Predicate\",\n", " \"type\": \"string\"\n", " },\n", " \"object\": {\n", " \"description\": \"Specific named entity or short literal.\",\n", " \"title\": \"Object\",\n", " ...\n" ] } ], "source": [ "from agentic_architectures.architectures.graph_rag import _QuestionScope, _IngestionTriple\n", "import json\n", "print('--- _QuestionScope schema ---')\n", "print(json.dumps(_QuestionScope.model_json_schema(), indent=2)[:400] + '...')\n", "print()\n", "print('--- _IngestionTriple schema ---')\n", "print(json.dumps(_IngestionTriple.model_json_schema(), indent=2)[:400] + '...')" ] }, { "cell_type": "markdown", "id": "bf0f0371", "metadata": { "papermill": { "duration": 0.002036, "end_time": "2026-05-28T03:02:14.885356+00:00", "exception": false, "start_time": "2026-05-28T03:02:14.883320+00:00", "status": "completed" }, "tags": [] }, "source": [ "## 7 · Build the graph + communities\n", "\n", "**This cell does the one-time KG build** — extracts triples from each doc, detects communities, summarises each. Expect ~1-2 minutes for a 12-doc corpus." ] }, { "cell_type": "code", "execution_count": 3, "id": "da44064b", "metadata": { "execution": { "iopub.execute_input": "2026-05-28T03:02:14.891362Z", "iopub.status.busy": "2026-05-28T03:02:14.890320Z", "iopub.status.idle": "2026-05-28T03:04:09.533687Z", "shell.execute_reply": "2026-05-28T03:04:09.533687Z" }, "papermill": { "duration": 114.646368, "end_time": "2026-05-28T03:04:09.533687+00:00", "exception": false, "start_time": "2026-05-28T03:02:14.887319+00:00", "status": "completed" }, "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "BUILD_ELAPSED: 114.6s\n", "N_COMMUNITIES: 5\n", "COMMUNITY_SIZES: [16, 10, 9, 7, 5]\n", "\n", "=== COMMUNITY SUMMARIES ===\n", "[community 0, 16 entities]\n", " Stardust, a company co-founded by Jin-ho Park, who also serves as its CTO, and led by CEO Amara Okonkwo, is a key entity that links various facts together. Stardust is focused on space-related technologies, including solar power, propulsion, and biofueled launch range vehicles, and has committed to net zero ground operations. The company, which has various pods such as mission operations and avion\n", "\n", "[community 1, 10 entities]\n", " The key entity in this community of related facts is Stardust 9, a spacecraft that had its first orbital flight in March 2023. Stardust 9 is linked to its parent entity, Stardust, which powers it, and is also connected to various attributes such as its launch destination (LEO), payload capacity (720 kg and 1850 kg), cost ($4.2M), and propellant (methalox). The facts also mention the spacecraft's p\n", "\n", "[community 2, 9 entities]\n", " Stardust Aerospace, a company founded by Dr. Amara Okonkwo and Jin-ho Park, is linked to several key entities through its operations and contracts. The company, which emerged from a DARPA-funded research program and is based in Reno, Nevada, has contracts with the National Reconnaissance Office and primary customers including the European Space Agency and NOAA. Stardust Aerospace operates faciliti\n", "\n", "[community 3, 7 entities]\n", " The key entity is Stardust Lite, a vehicle introduced in 2021, which is linked to its technical capabilities and uses. Stardust Lite is characterized by its maximum altitude of 110 km and maximum payload of 240 kg, and is used for atmospheric research and short-duration microgravity experiments. With 17 flown missions, Stardust Lite has demonstrated its utility in these areas, establishing itself \n", "\n", "[community 4, 5 entities]\n", " The key entities in this community of related facts are Stardust, Stardust Heavy, Phoenix-2, and Phoenix-Vacuum. Stardust Heavy is a vehicle developed from Stardust, and is linked to Phoenix-2 through its reuse of engines and to Phoenix-Vacuum through its upper stage engine. Stardust Heavy is also characterized by its payload capacity of 5,500 kg and its scheduled first flight in Q4 2026.\n", "\n" ] } ], "source": [ "import time\n", "t0 = time.time()\n", "arch = GraphRAG(llm=llm, documents=STARDUST_CORPUS, max_communities=5)\n", "print(f'BUILD_ELAPSED: {time.time()-t0:.1f}s')\n", "print(f'N_COMMUNITIES: {len(arch.communities)}')\n", "print(f'COMMUNITY_SIZES: {[len(c) for c in arch.communities]}')\n", "print()\n", "print('=== COMMUNITY SUMMARIES ===')\n", "for i, s in enumerate(arch.community_summaries):\n", " print(f'[community {i}, {len(arch.communities[i])} entities]')\n", " print(f' {s[:400]}')\n", " print()" ] }, { "cell_type": "code", "execution_count": 4, "id": "12f0ca15", "metadata": { "execution": { "iopub.execute_input": "2026-05-28T03:04:09.533687Z", "iopub.status.busy": "2026-05-28T03:04:09.533687Z", "iopub.status.idle": "2026-05-28T03:04:12.518606Z", "shell.execute_reply": "2026-05-28T03:04:12.516588Z" }, "papermill": { "duration": 2.984919, "end_time": "2026-05-28T03:04:12.518606+00:00", "exception": false, "start_time": "2026-05-28T03:04:09.533687+00:00", "status": "completed" }, "tags": [] }, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "