DEV Community

Cover image for Open Source Project of the Day (#116): SAG — Multi-Hop RAG Retrieval via SQL JOINs Instead of PageRank
WonderLab
WonderLab

Posted on

Open Source Project of the Day (#116): SAG — Multi-Hop RAG Retrieval via SQL JOINs Instead of PageRank

Introduction

"Graph RAG typically pre-builds a global graph offline, then uses PageRank for query-time scoring and expansion — but PageRank has decay: the longer the chain, the lower the score. SAG uses SQL JOINs to expand at query time instead. No decay, no pre-built global graph."

This is article #116 in the Open Source Project of the Day series. Today's project is SAG (Structured Agentic Graph) — Zleap-AI's open-source next-generation multi-hop RAG framework, backed by an arXiv paper (2606.15971).

Multi-hop retrieval's core challenge: some questions need to chain information from multiple separate document passages. Standard vector retrieval finds semantically similar chunks — but "semantically similar" doesn't mean "can answer questions requiring multi-step reasoning." GraphRAG and HippoRAG solve this with knowledge graphs, but at two costs: expensive offline global graph construction, and PageRank score decay over longer chains.

SAG takes a different path: no global graph pre-built, SQL JOINs for graph-structure expansion at query time. Each chunk produces one "event" (semantic summary) and a set of entities. Relationships between events and entities are expressed through relational database foreign keys. Multi-hop expansion is just a JOIN operation — deterministic, traceable, no decay.

What You'll Learn

  • SAG's core data model: the design logic of chunk → event + entities
  • The three-step query process: seed retrieval → SQL expansion → dual-path final selection
  • Why SQL JOINs outperform PageRank on long-chain multi-hop
  • Comparison with HippoRAG 2 and GraphRAG: strengths and weaknesses
  • MCP integration: exposing each SAG project as an agent-callable tool
  • Full-stack implementation: TypeScript + PostgreSQL + pgvector + React

Prerequisites

  • Familiarity with RAG and multi-hop retrieval basics
  • Basic understanding of knowledge graphs (nodes, edges, multi-hop reasoning)
  • Familiarity with SQL JOIN concepts

Project Background

What Is SAG?

SAG (Structured Agentic Graph) is a document retrieval system that organizes knowledge in an event-entity graph structure, achieves multi-hop retrieval through SQL JOINs, and is designed specifically for agent workflows.

"Structured" means converting unstructured documents into structured event + entity representations. "Agentic" means each SAG project has a built-in MCP server, exposable as an agent tool endpoint. "Graph" refers to query-time graph traversal via SQL JOINs.

Author / Team

Project Stats

  • ⭐ GitHub Stars: 1,900+
  • 🍴 Forks: 87+
  • 📄 License: MIT

Core Design: chunk → event + entities

SAG's base data model has three elements:

One document chunk
    ↓
 One Event (semantic event)
    ↑↕↓
 Multiple Entities
Enter fullscreen mode Exit fullscreen mode

Event: A semantic summary of the chunk. Each chunk maps to exactly one event. The event preserves complete semantics.

Entity: Named entities extracted from the chunk, serving as anchors for graph traversal. 11 types: time, location, person, organization, group, topic, work, product, action, metric, label. Entities carry no standalone semantics — they exist only as "index and expansion points for connecting different events."

Implicit hyperedge: One event plus all its entities forms an implicit hyperedge. This is conceptually similar to HyperGraphRAG, but SAG doesn't explicitly build a hypergraph — it achieves equivalent effect through relational database foreign key associations.

Why events instead of triples?

Triple approach:
  chunk → (Alice, works_at, Google)
           (Alice, attended, Meeting_001)
           (Bob, attended, Meeting_001)
  Relationship fragmented; reconstructing full context requires multiple hops

Event approach:
  chunk → Event: "Alice and Bob discussed the product roadmap at Meeting_001"
           Entity: Alice, Bob, Meeting_001
  Complete semantics preserved in event; entities serve as connection points
Enter fullscreen mode Exit fullscreen mode

The paper's ablation confirms: on MuSiQue, the hyperedge (event) approach scores Recall@5 = 80.0%, while the triple approach scores 77.1%.


Two-Phase Architecture

Offline Phase (Document Ingestion)

Document (Markdown / TXT)
    ↓
Text chunking
    ↓
LLM extraction (parallel, configurable concurrency):
  Each chunk → 1 event + N entities
    ↓
Written to two backends (synchronous):
  PostgreSQL     → Structured storage (events table, entities table, join table)
  pgvector       → Vector index (event vectors, entity vectors)
  Full-text index → BM25 entity matching
Enter fullscreen mode Exit fullscreen mode

Key property: append-only writes, no global graph rebuild. New document ingestion doesn't affect existing data — no need to re-run PageRank or rebuild graph structures.

Online Phase (Three-Step Retrieval)

Step 1: Seed Retrieval (dual-path parallel)

Path A: Entity-guided structured recall
  1. LLM extracts entities from query → seed entity set U_q
  2. Vector similarity search over entity index (threshold 0.9) → expanded set Û_q
  3. SQL JOIN: find all events containing these entities
     ℰR_entity = {e | ∃u ∈ Û_q : SQL-Join(e, u)}

Path B: Direct event recall
  Query vector → event vector similarity search (threshold 0.4)

Merge: ℰR = ℰR_entity ∪ ℰR_direct
Enter fullscreen mode Exit fullscreen mode

Step 2: Query-Time Expansion (SQL multi-hop)

This is SAG's core mechanism:

Starting from seed event set ℰR:
    ↓
Reverse JOIN: extract all entities from seed events (entity frontier)
    ↓
Forward JOIN: find all new events containing frontier entities (cross-document expansion)
    ↓
Repeat for H hops (default H=1)

Result: ℰ_cand = ℰR ∪ ℰE
Enter fullscreen mode Exit fullscreen mode

This is the "multi-hop" implementation. If Document A mentions Entity X and Document B also mentions Entity X, one JOIN from Document A's events finds Document B's related events — even if the two documents are far apart in vector space.

Step 3: Final Selection (dual-path fusion)

Candidate set ℰ_cand (top 100 candidates)
    ↓
Structural path: LLM reranks chain candidates → top 5 events → mapped to chunks
Semantic path: query vector directly retrieves chunks → top 5
    ↓
Merge, deduplicate → final 10 chunks
Enter fullscreen mode Exit fullscreen mode

SQL JOIN vs. PageRank: Why SAG Is Better on Long-Chain Multi-Hop

HippoRAG 2's approach: build a global knowledge graph offline, then use Personalized PageRank (PPR) at query time, propagating scores from seed nodes along graph edges.

The problem is PPR's damping factor: at each hop, the score multiplies by (1-d) (d is typically 0.85), retaining only 15% probability. After 2 hops, a node's score is multiplied by 0.15² = 0.0225; after 3 hops, it's near zero.

HippoRAG 2 long-chain multi-hop problem:
Answer requires path: A → B → C → D → E (4 hops)
After PPR: E's score ≈ seed_score × 0.15⁴ ≈ 0.0005 × seed_score
Distant node scores too low to compete with directly related near nodes
Enter fullscreen mode Exit fullscreen mode

SAG's SQL JOINs don't propagate scores — they propagate reachability: all events connected to Entity X through a JOIN are treated equally, regardless of how many hops they are from the seed.

MuSiQue concentrates this difference: it specifically constructs 4-step reasoning chain questions. HippoRAG 2 significantly underperforms SAG on this dataset (65.1% vs 80.0%).

SAG's weakness is on 2WikiMultiHop: SAG has a fixed entity frontier pruning budget (50 entities). Low-frequency bridging entities can get truncated during pruning, causing SAG to slightly underperform HippoRAG 2 there (88.0% vs 90.4%).


Benchmark Results

Using bge-large-en-v1.5 embeddings and qwen3.6-flash as LLM:

Recall@5 comparison:

Dataset SAG HippoRAG 2 Gap
MuSiQue 80.0% 65.1% +14.9pp
HotpotQA 96.5% 94.4% +2.1pp
2WikiMultiHop 88.0% 90.4% -2.4pp
Average Recall@2 79.30% 68.14% +11.16pp

Switching to the stronger NV-Embed-v2 embedding raises MuSiQue Recall@5 to 81.71%, confirming the gains come from structural design rather than just embedding quality.

Ablation results:

Configuration MuSiQue R@5
Full SAG 80.0%
Remove multi-hop expansion (H=0) 69.4%
Remove structural path 56.2%
Replace LLM reranker with lightweight reranker 62.2%

Multi-hop expansion contributes ~10.6pp improvement. LLM reranking is not skippable (17.8pp gap).


Quick Start

git clone https://github.com/Zleap-AI/SAG.git
cd SAG

cp .env.example .env
# Edit .env: add LLM API key, Embedding API key

docker compose up -d   # Start PostgreSQL with pgvector

npm install
npm run db:setup
npm run dev
# WebUI: http://localhost:5173
# API:   http://localhost:4173
Enter fullscreen mode Exit fullscreen mode

Key .env configuration:

# LLM (OpenAI-compatible API)
LLM_BASE_URL=https://api.openai.com/v1
LLM_API_KEY=your_key
LLM_MODEL=qwen3.6-flash           # or gpt-4o-mini

# Embedding
EMBEDDING_BASE_URL=https://api.openai.com/v1
EMBEDDING_API_KEY=your_key
EMBEDDING_MODEL=text-embedding-3-large

# Optional: Rerank model
RERANK_MODEL=qwen3-rerank

# Search mode
DEFAULT_SEARCH_MODE=fast           # fast / standard
INGEST_CONCURRENCY=5               # parallel ingestion workers
Enter fullscreen mode Exit fullscreen mode

Two Search Modes

Fast mode (no LLM query parsing):

Query → BM25 full-text entity matching → SQL multi-hop expansion → reranking
Advantage: Low latency, no LLM query parsing cost
Best for: Queries with explicit entity names
Enter fullscreen mode Exit fullscreen mode

Standard mode (LLM entity extraction):

Query → LLM extracts entities → vector + SQL multi-hop expansion → LLM reranking
Advantage: More robust for ambiguous and natural language questions
Best for: Queries requiring complex reasoning
Enter fullscreen mode Exit fullscreen mode

MCP Integration

Each SAG project automatically exposes an MCP server endpoint, usable directly from Claude Code and other agents:

{
  "mcpServers": {
    "sag": {
      "command": "npm",
      "args": ["run", "mcp"],
      "env": {
        "SAG_MCP_SOURCE_ID": "your_project_id"
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Available MCP tools:

Tool Function
sag_search Search the project knowledge base
sag_ingest_document Upload a new document
sag_explain_search Explain the retrieval path for a specific query
sag_get_event Get detailed information for a specific event

Web UI Features

  • Knowledge graph explorer: Visualize event-entity nodes with drag, zoom, and expand
  • Search trace visualization: Live display of each retrieval step and stage latency
  • Raw model logs: Inspect complete LLM / Embedding / Rerank requests and responses
  • Project isolation: Each project has independent document library, conversation history, graph, and MCP configuration

Links and Resources


Conclusion

SAG's core contribution is a lightweight multi-hop retrieval implementation: no global knowledge graph pre-built, no PageRank scoring, relational database JOINs doing dynamic graph-structure expansion at query time. Two consequences follow from that choice: document updates are append-only and don't require rebuilds, and SQL JOIN reachability is unaffected by hop count and has no decay.

The "event as semantic unit" design (rather than triples) is also worth attention: each chunk's complete semantics are preserved in one event node, with entities serving only as connection points. This beats the triple approach by nearly 3 percentage points on MuSiQue, validating the design decision.

The weakness is transparent: the fixed pruning budget slightly underperforms HippoRAG 2 on 2WikiMultiHop, missing some critical paths when bridging entities are low-frequency. Knowing where the weakness is makes it possible to judge whether it fits your scenario.


Explore PrimeSkills — A marketplace for handpicked AI Agents and skills. Each is validated in real enterprise workflows, stripping away hype and keeping only what truly works.

Welcome to my Homepage for more useful insights and interesting products.

Top comments (0)