Epistemic Graph RAG with Spreading Activation — retrieval that understands how knowledge relates, not just what it says
Project description
PRISM
Propagation & Retrieval via Informed Semantic Mapping
Epistemic Graph RAG with Spreading Activation
PRISM is a retrieval library that layers a typed epistemic knowledge graph over your existing vector store, then uses spreading activation to surface knowledge structured by how it relates — not just how similar it is.
The Problem
Standard RAG returns a flat ranked list. Every chunk is treated the same — a similarity score and nothing else.
query → embed → similarity → [chunk, chunk, chunk, ...] ← no structure
This loses the relational fabric of your knowledge. Chunk B may refute Chunk A. Chunk C may specialize a principle in Chunk D. An older document may have been superseded by a newer one. Standard RAG can't express any of this — and neither can the LLM reasoning over it.
PRISM's Approach
PRISM builds a graph where edges carry epistemic type:
Doc A ──[supports]──▶ Doc B
Doc C ──[refutes]───▶ Doc D
Doc E ──[supersedes]▶ Doc F
Retrieval then uses spreading activation: a query fires seed nodes via vector search, activation propagates through typed edges, and nodes reached by multiple independent paths (convergence) rank highest.
The result is a structured epistemic answer — not a ranked list:
| Bucket | Contents |
|---|---|
| PRIMARY | Core relevant chunks, highest convergence |
| SUPPORTING | Chunks that reinforce or extend the primary answer |
| CONTRASTING | Chunks that challenge or take a different position |
| QUALIFYING | Chunks that add conditions, exceptions, nuances |
| SUPERSEDED | Historically relevant context now replaced by newer work |
Architecture
Three stages:
- Seed — embed the query, vector-search your corpus, get top-K scored chunks as activation seeds
- Activate — propagate activation through the epistemic graph; track which seeds independently reach each node (convergence)
- Structure — bucket results by epistemic role based on dominant edge valence
Why This Is Novel
| System | Vector Search | Knowledge Graph | Epistemic Edge Typing | Spreading Activation |
|---|---|---|---|---|
| Standard RAG | ✅ | ❌ | ❌ | ❌ |
| GraphRAG (Microsoft, 2024) | ✅ | ✅ | ❌ | ❌ |
| SYNAPSE (2026) | ✅ | ✅ | ❌ | ✅ |
| PRISM | ✅ | ✅ | ✅ | ✅ |
To our knowledge, no open-source retrieval library combines all four of these signals. If you know of one, please open an issue — we'd genuinely like to know.
Epistemic Edge Types
supports — A provides evidence reinforcing B
refutes — A directly contradicts B
supersedes — A replaces or updates B (A is newer / more authoritative)
derives_from — A is logically or conceptually derived from B
specializes — A is a specific instance of the general principle in B
contrasts_with — A and B take different but non-exclusive positions
implements — A is a concrete method that puts the abstract concept of B into practice
generalizes — A is a broader abstraction of which B is a specific case
exemplifies — A is a concrete example illustrating the concept in B
qualifies — A adds conditions, exceptions, or nuances to B
Each edge has:
- A propagation weight — how strongly it carries activation (0.40–0.90)
- A valence — determines which result bucket the target lands in (positive / qualifying / dialectical / temporal)
Installation
pip install prism-rag
From source:
git clone https://github.com/MadMando/prism
cd prism
pip install -e .
Requirements: Python 3.11+, an existing LanceDB vector store, and an embedding provider (Ollama local or any OpenAI-compatible API).
Quick Start
1. Build the epistemic graph (one-time)
from prism import PRISM
# Using Ollama for embeddings (local)
p = PRISM(
lancedb_path = "/path/to/your/lancedb",
graph_path = "/path/to/prism_graph.json.gz",
ollama_url = "http://localhost:11434",
embed_model = "nomic-embed-text",
llm_base_url = "https://api.openai.com",
llm_model = "gpt-4o-mini",
llm_api_key = "sk-...",
)
# Using an API for embeddings instead
p = PRISM(
lancedb_path = "/path/to/your/lancedb",
graph_path = "/path/to/prism_graph.json.gz",
embed_api_url = "https://api.openai.com/v1/embeddings",
embed_api_key = "sk-...",
embed_model = "text-embedding-3-small",
llm_base_url = "https://api.openai.com",
llm_model = "gpt-4o-mini",
llm_api_key = "sk-...",
)
p.build(
k_neighbors = 8, # semantic neighbours per chunk to examine
cross_source_only = True, # only extract inter-document relationships (recommended)
max_pairs = 50_000 # cap for testing; omit for full build
)
Or via CLI:
prism-build \
--lancedb-path /path/to/lancedb \
--graph-path /path/to/prism_graph.json.gz \
--llm-api-key $OPENAI_API_KEY \
--max-pairs 50000
2. Retrieve
p.load_graph()
result = p.retrieve("your question here", top_k=5)
print(result.format_for_llm())
Output:
PRISM retrieval for: "your question here"
────────────────────────────────────────────────────────────
## PRIMARY
[1] source-a p.14 § 2.1 (score: 0.923)
The core relevant passage from your corpus...
[2] source-b p.67 § 5.0 (score: 0.891)
Another highly activated chunk...
## SUPPORTING EVIDENCE
[1] source-c p.201 § 8.2 (score: 0.841 [via: specializes])
A passage that specializes or extends the primary answer...
## QUALIFICATIONS & NUANCES
[1] source-d p.38 § 3.1 (score: 0.712 [via: qualifies])
A passage adding conditions or exceptions...
─ 2 primary · 1 supporting · 0 contrasting · 1 qualifying · 0 superseded ─
3. Use the result programmatically
# Access buckets directly
for chunk in result.primary:
print(chunk.source, chunk.page, chunk.final_score)
print(chunk.text)
for chunk in result.contrasting:
print("Contrasting view:", chunk.text[:200])
# Feed into your LLM
context = result.format_for_llm()
# ... pass `context` to your LLM system prompt
# Stats
print(f"Seeds: {result.n_seeds}")
print(f"Graph nodes reached: {result.n_graph_nodes}")
print(f"Graph used: {result.graph_was_used}")
Embedding Providers
PRISM supports two embedding modes — use whichever matches how your corpus was built.
Ollama (local or self-hosted)
PRISM(
ollama_url = "http://localhost:11434", # your Ollama instance
embed_model = "nomic-embed-text", # any model loaded in Ollama
...
)
Popular Ollama embedding models: nomic-embed-text, mxbai-embed-large, all-minilm
OpenAI-compatible API
PRISM(
embed_api_url = "https://api.openai.com/v1/embeddings", # or any compatible endpoint
embed_api_key = "sk-...",
embed_model = "text-embedding-3-small",
...
)
Works with OpenAI, Azure OpenAI, Together AI, Jina, Cohere, Mistral, or any endpoint that accepts {"model": ..., "input": ...} and returns {"data": [{"embedding": [...]}]}.
Important: The embedding model at retrieval time must match the model used when your LanceDB corpus was originally indexed. Dimensions must be identical.
No Re-embedding Required
PRISM works on top of your existing vector store. If you already have a LanceDB corpus with embeddings, you do not need to re-index anything.
- Existing vectors → used as-is for seed activation
- Epistemic graph → built from text via LLM, stored separately as a
.json.gzfile - Build is a one-time offline step
Graph Building: What Happens
The build phase extracts epistemic relationships from your corpus:
-
Candidate pairs — for each chunk, find top-K semantic neighbours via vector search. Filter to cross-document pairs (recommended — inter-document signal is most valuable).
-
LLM extraction — send batches of 5 pairs to any OpenAI-compatible LLM. Ask: does an epistemic relationship exist between these two passages, and what type?
-
Graph construction — confirmed relationships (above a confidence threshold) become typed, weighted edges.
-
Save — graph serialised to gzipped JSON (
prism_graph.json.gz), loaded instantly at retrieval time.
Cost estimate (typical 30k-chunk corpus):
| Item | Estimate |
|---|---|
| Candidate pairs generated | ~80k–120k |
| LLM calls (batch=5) | ~16k–24k |
| Input tokens | ~25M–35M |
| Output tokens | ~5M–7M |
Cost with gpt-4o-mini |
~$3–6 |
Cost with deepseek-chat |
~$5–9 |
| Build time | 6–14 hours |
Fallback Behaviour
If no graph file exists (or graph loading fails), PRISM automatically falls back to pure vector search and still returns a valid EpistemicResult — just without epistemic bucketing. All chunks land in primary.
This means PRISM is a safe drop-in replacement for any standard vector retriever from day one.
Full Configuration
PRISM(
# ── Storage ───────────────────────────────────────────────────
lancedb_path = "/path/to/lancedb",
graph_path = "/path/to/prism_graph.json.gz",
table_name = "knowledge", # LanceDB table name
# ── Embedding: Ollama (default) ───────────────────────────────
ollama_url = "http://localhost:11434",
embed_model = "nomic-embed-text",
# ── Embedding: API (set embed_api_key to activate) ────────────
embed_api_url = "https://api.openai.com/v1/embeddings",
embed_api_key = None, # set to switch from Ollama
# ── LLM for graph building ────────────────────────────────────
llm_base_url = "https://api.openai.com",
llm_model = "gpt-4o-mini",
llm_api_key = "sk-...",
min_confidence = 0.65, # edge confidence threshold
batch_size = 5, # pairs per LLM call
# ── Retrieval tuning ──────────────────────────────────────────
hops = 3, # spreading activation depth
decay = 0.7, # per-hop decay factor
seed_top_k = 20, # vector search seed count
convergence_weight = 0.4, # bonus weight for convergence
)
Project Structure
prism/
├── prism/
│ ├── __init__.py public API
│ ├── prism.py PRISM — main entry point
│ ├── edges.py epistemic edge taxonomy + propagation weights
│ ├── graph.py EpistemicGraph (networkx MultiDiGraph + JSON serialisation)
│ ├── extractor.py LLM-based triple extraction (batch mode)
│ ├── activation.py SpreadingActivation engine + convergence scoring
│ ├── retriever.py PRISMRetriever — the 5-step pipeline
│ ├── result.py EpistemicResult + EpistemicChunk dataclasses
│ ├── cli.py prism-build CLI
│ └── adapters/
│ └── lancedb.py LanceDB adapter (Ollama + API embedding)
├── scripts/
│ └── build_graph.py standalone build script
├── examples/
│ └── governance_search.py
├── docs/
│ └── architecture.svg
└── pyproject.toml
Limitations
PRISM is alpha software. These are known constraints you should understand before using it in production:
Graph quality depends on your corpus and LLM.
The epistemic graph is built by asking an LLM to classify relationships between chunk pairs. The LLM can misclassify — a chunk may be tagged supports when it actually only tangentially relates, or refutes when it merely offers a different framing. Graph quality is correlated with chunk quality, chunking strategy, and the capability of the extraction model. Review extracted edges before trusting them.
Confidence scores are not ground truth.
The confidence values returned by the LLM are self-reported estimates, not calibrated probabilities. They are used as a filter (default threshold: 0.65) and as edge weights, but should not be treated as precise measures of relationship strength.
Retrieval quality can degrade with noisy edges. If the graph contains many false-positive edges, spreading activation will propagate to irrelevant nodes. This produces worse results than pure vector search. Monitor your graph's edge yield rate and edge type distribution after building.
Cross-source assumption.
PRISM is designed for corpora with multiple distinct sources (documents, books, standards, frameworks). The cross_source_only=True default optimises for inter-document relationships. Single-source corpora will see fewer extracted edges and less epistemic structure.
Build is slow and LLM-dependent. The one-time graph build requires thousands of LLM API calls and takes hours for large corpora. There is currently no incremental update — adding new documents requires a rebuild (or manual edge addition).
No evaluation benchmark yet.
We do not currently publish retrieval quality metrics comparing PRISM against standard RAG on standardised QA datasets. The benchmarks/ directory contains a structural demonstration only.
Roadmap
- Async LLM extraction (parallel API calls → 10× faster build)
- Incremental graph updates (add new docs without full rebuild)
- Additional vector store adapters (Chroma, Qdrant, Weaviate, pgvector)
- Graph visualisation (
prism-vizCLI — exports to Gephi / D3) - Export to Neo4j / NetworkX formats
- PyPI release (
pip install prism-rag)
References
- Collins, A.M. & Loftus, E.F. (1975). A spreading-activation theory of semantic processing. Psychological Review, 82(6), 407–428.
- Edge, D. et al. (2024). From Local to Global: A Graph RAG Approach to Query-Focused Summarization. Microsoft Research.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file prism_rag-0.1.0.tar.gz.
File metadata
- Download URL: prism_rag-0.1.0.tar.gz
- Upload date:
- Size: 41.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
28eb0aa58ead08367ce96c1cb7751ebfda1048877bc4782862eb64d708ebde13
|
|
| MD5 |
d332cd91c3f70d22fba91c47a32331f5
|
|
| BLAKE2b-256 |
c707eb8b3a08d91c808268c9f24507e6399e6483492f364650f45968d0e2e7df
|
File details
Details for the file prism_rag-0.1.0-py3-none-any.whl.
File metadata
- Download URL: prism_rag-0.1.0-py3-none-any.whl
- Upload date:
- Size: 33.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff037d520e0349e3f8408bbec8875f5120f3621fa32cb28e89d50b3b79a03de5
|
|
| MD5 |
8e07762944c47a40fc2546427afa417e
|
|
| BLAKE2b-256 |
d52aff713fa27c4ca7c25dd799775af1e0bd9e871765cb2235e3773fb53325a4
|