{"id":179403,"date":"2026-04-18T16:54:19","date_gmt":"2026-04-18T16:54:19","guid":{"rendered":"https:\/\/ktromedia.com\/?p=179403"},"modified":"2026-04-18T16:54:19","modified_gmt":"2026-04-18T16:54:19","slug":"beyond-vector-search-building-a-deterministic-3-tiered-graph-rag-system","status":"publish","type":"post","link":"http:\/\/ktromedia.com\/?p=179403","title":{"rendered":"Beyond Vector Search: Building a Deterministic 3-Tiered Graph-RAG System"},"content":{"rendered":"<div id=\"\">\n<p>In this article, you will learn how to build a deterministic, multi-tier retrieval-augmented generation system using knowledge graphs and vector databases.<\/p>\n<p>Topics we will cover include:<\/p>\n<ul>\n<li>Designing a three-tier retrieval hierarchy for factual accuracy.<\/li>\n<li>Implementing a lightweight knowledge graph.<\/li>\n<li>Using prompt-enforced rules to resolve retrieval conflicts deterministically.<\/li>\n<\/ul>\n<div style=\"width: 810px\" class=\"wp-caption aligncenter\"><\/p>\n<p class=\"wp-caption-text\">Beyond Vector Search: Building a Deterministic 3-Tiered Graph-RAG System<br \/>Image by Editor<\/p>\n<\/div>\n<h2>Introduction: The Limits of Vector RAG<\/h2>\n<p><a href=\"https:\/\/machinelearningmastery.com\/the-complete-guide-to-vector-databases-for-machine-learning\/\" target=\"_blank\"><strong>Vector databases<\/strong><\/a> have long since become the cornerstone of modern retrieval augmented generation (RAG) pipelines, excelling at retrieving long-form text based on semantic similarity. However, vector databases are notoriously \u201clossy\u201d when it comes to atomic facts, numbers, and strict entity relationships. A standard vector RAG system might easily confuse <strong>which<\/strong> team a basketball player currently plays for, for example, simply because multiple teams appear near the player\u2019s name in latent space. To solve this, we need a <strong>multi-index, federated architecture<\/strong>.<\/p>\n<p>In this tutorial, we will introduce such an architecture, using a quad store backend to implement a knowledge graph for atomic facts, backed by a vector database for long-tail, fuzzy context.<\/p>\n<p>But here is the twist: instead of relying on complex algorithmic routing to pick the right database, we will query all databases, dump the results into the context window, and use <strong>prompt-enforced fusion rules<\/strong> to force the language model (LM) to deterministically resolve conflicts. The goal is to attempt to eliminate relationship hallucinations and build absolute deterministic predictability where it matters most: atomic facts.<\/p>\n<h2>Architecture Overview: The 3-Tiered Hierarchy<\/h2>\n<p>Our pipeline enforces strict data hierarchy using three retrieval tiers:<\/p>\n<ol>\n<li><strong>Priority 1 (absolute graph facts):<\/strong> A simple Python QuadStore knowledge graph containing verified, immutable ground truths structured in <strong>Subject-Predicate-Object plus Context (SPOC)<\/strong> format.<\/li>\n<li><strong>Priority 2 (statistical graph data):<\/strong> A secondary QuadStore containing aggregated statistics or historical data. This tier is subject to Priority 1 override in case of conflicts (e.g. a Priority 1 current team fact overrides a Priority 2 historical team statistic).<\/li>\n<li><strong>Priority 3 (vector documents):<\/strong> A standard dense vector DB (<a href=\"https:\/\/www.trychroma.com\/\" target=\"_blank\"><strong>ChromaDB<\/strong><\/a>) for general text documents, only used as a fallback if the knowledge graphs lack the answer.<\/li>\n<\/ol>\n<h2>Environment &amp; Prerequisites Setup<\/h2>\n<p>To follow along, you will need an environment running Python, a local LM infrastructure and served model (we use <a href=\"https:\/\/ollama.com\/\" target=\"_blank\"><strong>Ollama<\/strong><\/a> with <code>llama3.2<\/code>), and the following core libraries:<\/p>\n<ul>\n<li><strong>chromadb<\/strong>: For the vector database tier<\/li>\n<li><strong>spaCy<\/strong>: For named entity recognition (NER) to query the graphs<\/li>\n<li><strong>requests<\/strong>: To interact with our local LM inference endpoint<\/li>\n<li><strong>QuadStore<\/strong>: For the knowledge graph tier (see <a href=\"https:\/\/github.com\/mmmayo13\/quadstore\" target=\"_blank\">QuadStore repository<\/a>)<\/li>\n<\/ul>\n<div id=\"urvanov-syntax-highlighter-69e1f6a6a38ca834387321\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-pc print-yes notranslate\" data-settings=\" touchscreen minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p><textarea wrap=\"soft\" class=\"urvanov-syntax-highlighter-plain print-no\" data-settings=\"dblclick\" style=\"-moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4; font-size: 12px !important; line-height: 15px !important;\"><br \/>\n# Install required libraries&#13;<br \/>\npip install chromadb spacy requests&#13;<br \/>\n&#13;<br \/>\n# Download the spaCy English model&#13;<br \/>\npython -m spacy download en_core_web_sm<\/textarea><\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<table class=\"crayon-table\">\n<tr class=\"urvanov-syntax-highlighter-row\">\n<td class=\"crayon-nums \" data-settings=\"show\">\n<\/td>\n<td class=\"urvanov-syntax-highlighter-code\">\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-p\"># Install required libraries<\/span><\/p>\n<p><span class=\"crayon-e\">pip <\/span><span class=\"crayon-e\">install <\/span><span class=\"crayon-e\">chromadb <\/span><span class=\"crayon-e\">spacy <\/span><span class=\"crayon-i\">requests<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># Download the spaCy English model<\/span><\/p>\n<p><span class=\"crayon-v\">python<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">&#8211;<\/span><span class=\"crayon-i\">m<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">spacy <\/span><span class=\"crayon-e\">download <\/span><span class=\"crayon-v\">en_core_web_sm<\/span><\/p>\n<\/div>\n<\/td>\n<\/tr>\n<\/table><\/div>\n<\/p><\/div>\n<p>You can manually download the simple Python QuadStore implementation from <a href=\"https:\/\/github.com\/mmmayo13\/quadstore\" target=\"_blank\">the QuadStore repository<\/a> and place it somewhere in your local file system to import as a module.<\/p>\n<p><strong>\u26a0\ufe0f Note:<\/strong> The full project code implementation is available in <a href=\"https:\/\/github.com\/mmmayo13\/rag_facts_quadstore\" target=\"_blank\">this GitHub repository<\/a>.<\/p>\n<p>With these prerequisites handled, let\u2019s dive into the implementation.<\/p>\n<h2>Step 1: Building a Lightweight QuadStore (The Graph)<\/h2>\n<p>To implement Priority 1 and Priority 2 data, we use a custom lightweight in-memory knowledge graph called a quad store. This knowledge graph shifts away from semantic embeddings toward a strict node-edge-node schema known internally as a <strong>SPOC<\/strong> (Subject-Predicate-Object plus Context). <\/p>\n<p>This <code>QuadStore<\/code> module operates as a highly-indexed storage engine. Under the hood, it maps all strings into integer IDs to prevent memory bloat, while keeping a four-way dictionary index (spoc, pocs, ocsp, cspo) to enable constant-time lookups across any dimension. While we won\u2019t dive into the details of the internal structure of the engine here, utilizing the API in our RAG script is incredibly straightforward.<\/p>\n<p>Why use this simple implementation instead of a more robust graph database like <a href=\"https:\/\/neo4j.com\/\" target=\"_blank\">Neo4j<\/a> or <a href=\"https:\/\/arango.ai\/\" target=\"_blank\">ArangoDB<\/a>? Simplicity and speed. This implementation is incredibly lightweight and fast, while having the additional benefit of being easy to understand. This is all that is needed for this specific use case without having to learn a complex graph database API.<\/p>\n<p>There are really only a couple of QuadStore methods you need to understand:<\/p>\n<ol>\n<li><code>add(subject, predicate, object, context)<\/code>: Adds a new fact to the knowledge graph<\/li>\n<li><code>query(subject, predicate, object, context)<\/code>: Queries the knowledge graph for facts that match the given subject, predicate, object, and context<\/li>\n<\/ol>\n<p>Let\u2019s initialize the QuadStore acting as our Priority 1 absolute truth model:<\/p>\n<div id=\"urvanov-syntax-highlighter-69e1f6a6a38d3469707548\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-pc print-yes notranslate\" data-settings=\" touchscreen minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p><textarea wrap=\"soft\" class=\"urvanov-syntax-highlighter-plain print-no\" data-settings=\"dblclick\" style=\"-moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4; font-size: 12px !important; line-height: 15px !important;\"><br \/>\nfrom quadstore import QuadStore&#13;<br \/>\n&#13;<br \/>\n# Initialize facts quadstore&#13;<br \/>\nfacts_qs = QuadStore()&#13;<br \/>\n&#13;<br \/>\n# Natively add facts (Subject, Predicate, Object, Context)&#13;<br \/>\nfacts_qs.add(&#8220;LeBron James&#8221;, &#8220;likes&#8221;, &#8220;coconut milk&#8221;, &#8220;NBA_trivia&#8221;)&#13;<br \/>\nfacts_qs.add(&#8220;LeBron James&#8221;, &#8220;played_for&#8221;, &#8220;Ottawa Beavers&#8221;, &#8220;NBA_2023_regular_season&#8221;)&#13;<br \/>\nfacts_qs.add(&#8220;Ottawa Beavers&#8221;, &#8220;obtained&#8221;, &#8220;LeBron James&#8221;, &#8220;2020_expansion_draft&#8221;)&#13;<br \/>\nfacts_qs.add(&#8220;Ottawa Beavers&#8221;, &#8220;based_in&#8221;, &#8220;downtown Ottawa&#8221;, &#8220;NBA_trivia&#8221;)&#13;<br \/>\nfacts_qs.add(&#8220;Kevin Durant&#8221;, &#8220;is&#8221;, &#8220;a person&#8221;, &#8220;NBA_trivia&#8221;)&#13;<br \/>\nfacts_qs.add(&#8220;Ottawa Beavers&#8221;, &#8220;had&#8221;, &#8220;worst first year of any expansion team in NBA history&#8221;, &#8220;NBA_trivia&#8221;)&#13;<br \/>\nfacts_qs.add(&#8220;LeBron James&#8221;, &#8220;average_mpg&#8221;, &#8220;12.0&#8221;, &#8220;NBA_2023_regular_season&#8221;)<\/textarea><\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<table class=\"crayon-table\">\n<tr class=\"urvanov-syntax-highlighter-row\">\n<td class=\"crayon-nums \" data-settings=\"show\">\n<\/td>\n<td class=\"urvanov-syntax-highlighter-code\">\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-e\">quadstore <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-i\">QuadStore<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># Initialize facts quadstore<\/span><\/p>\n<p><span class=\"crayon-v\">facts_qs<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">QuadStore<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># Natively add facts (Subject, Predicate, Object, Context)<\/span><\/p>\n<p><span class=\"crayon-v\">facts_qs<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">add<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;LeBron James&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;likes&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;coconut milk&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;NBA_trivia&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-v\">facts_qs<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">add<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;LeBron James&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;played_for&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;Ottawa Beavers&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;NBA_2023_regular_season&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-v\">facts_qs<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">add<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;Ottawa Beavers&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;obtained&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;LeBron James&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;2020_expansion_draft&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-v\">facts_qs<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">add<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;Ottawa Beavers&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;based_in&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;downtown Ottawa&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;NBA_trivia&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-v\">facts_qs<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">add<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;Kevin Durant&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;is&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;a person&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;NBA_trivia&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-v\">facts_qs<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">add<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;Ottawa Beavers&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;had&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;worst first year of any expansion team in NBA history&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;NBA_trivia&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-v\">facts_qs<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">add<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;LeBron James&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;average_mpg&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;12.0&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;NBA_2023_regular_season&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<\/div>\n<\/td>\n<\/tr>\n<\/table><\/div>\n<\/p><\/div>\n<p>Because it uses the identical underlying class, you can populate Priority 2 (which handles broader statistics and numbers) identically or by reading from a previously-prepared JSONLines file. This file was created by running a simple script that read the 2023 NBA regular season stats from a CSV file that was freely-acquired from a basketball stats website (though I cannot recall which one, as I have had the data for several years at this point), and converted each row into a quad. You can download the pre-processed NBA 2023 stats file in JSONL format <a href=\"https:\/\/github.com\/mmmayo13\/rag_facts_quadstore\" target=\"_blank\">from the project repository<\/a>.<\/p>\n<h2>Step 2: Integrating the Vector Database<\/h2>\n<p>Next, we establish our Priority 3 layer: the standard dense vector DB. We use ChromaDB to store text chunks that our rigid knowledge graphs might have missed. <\/p>\n<p>Here is how we initialize a persistent collection and ingest raw text into it:<\/p>\n<div id=\"urvanov-syntax-highlighter-69e1f6a6a38d7015616167\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-pc print-yes notranslate\" data-settings=\" touchscreen minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p><textarea wrap=\"soft\" class=\"urvanov-syntax-highlighter-plain print-no\" data-settings=\"dblclick\" style=\"-moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4; font-size: 12px !important; line-height: 15px !important;\"><br \/>\nimport chromadb&#13;<br \/>\nfrom chromadb.config import Settings&#13;<br \/>\n&#13;<br \/>\n# Initialize vector embeddings&#13;<br \/>\nchroma_client = chromadb.PersistentClient(&#13;<br \/>\n    path=&#8221;.\/chroma_db&#8221;,&#13;<br \/>\n    settings=Settings(anonymized_telemetry=False)&#13;<br \/>\n)&#13;<br \/>\ncollection = chroma_client.get_or_create_collection(name=&#8221;basketball&#8221;)&#13;<br \/>\n&#13;<br \/>\n# Our fallback unstructured text chunks&#13;<br \/>\ndoc1 = (&#13;<br \/>\n    &#8220;LeBron injured for remainder of NBA 2023 season\\n&#8221;&#13;<br \/>\n    &#8220;LeBron James suffered an ankle injury early in the season, which led to him playing far &#8220;&#13;<br \/>\n    &#8220;fewer minutes per game than he has recently averaged in other seasons. The injury got much &#8220;&#13;<br \/>\n    &#8220;worse today, and he is out for the rest of the season.&#8221;&#13;<br \/>\n)&#13;<br \/>\ndoc2 = (&#13;<br \/>\n    &#8220;Ottawa Beavers\\n&#8221;&#13;<br \/>\n    &#8220;The Ottawa Beavers star player LeBron James is out for the rest of the 2023 NBA season, &#8220;&#13;<br \/>\n    &#8220;after his ankle injury has worsened. The teams&#8217; abysmal regular season record may end up &#8220;&#13;<br \/>\n    &#8220;being the worst of any team ever, with only 6 wins as of now, with only 4 gmaes left in &#8220;&#13;<br \/>\n    &#8220;the regular season.&#8221;&#13;<br \/>\n)&#13;<br \/>\n&#13;<br \/>\ncollection.upsert(&#13;<br \/>\n    documents=[doc1, doc2],&#13;<br \/>\n    ids=[&#8220;doc1&#8221;, &#8220;doc2&#8221;]&#13;<br \/>\n)<\/textarea><\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<table class=\"crayon-table\">\n<tr class=\"urvanov-syntax-highlighter-row\">\n<td class=\"crayon-nums \" data-settings=\"show\">\n<div class=\"urvanov-syntax-highlighter-nums-content\" style=\"font-size: 12px !important; line-height: 15px !important;\">\n<p>1<\/p>\n<p>2<\/p>\n<p>3<\/p>\n<p>4<\/p>\n<p>5<\/p>\n<p>6<\/p>\n<p>7<\/p>\n<p>8<\/p>\n<p>9<\/p>\n<p>10<\/p>\n<p>11<\/p>\n<p>12<\/p>\n<p>13<\/p>\n<p>14<\/p>\n<p>15<\/p>\n<p>16<\/p>\n<p>17<\/p>\n<p>18<\/p>\n<p>19<\/p>\n<p>20<\/p>\n<p>21<\/p>\n<p>22<\/p>\n<p>23<\/p>\n<p>24<\/p>\n<p>25<\/p>\n<p>26<\/p>\n<p>27<\/p>\n<p>28<\/p>\n<p>29<\/p>\n<\/div>\n<\/td>\n<td class=\"urvanov-syntax-highlighter-code\">\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-e\">chromadb<\/span><\/p>\n<p><span class=\"crayon-e\">from <\/span><span class=\"crayon-v\">chromadb<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">config <\/span><span class=\"crayon-e\">import <\/span><span class=\"crayon-i\">Settings<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># Initialize vector embeddings<\/span><\/p>\n<p><span class=\"crayon-v\">chroma_client<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">chromadb<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">PersistentClient<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">path<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;.\/chroma_db&#8221;<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">settings<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-e\">Settings<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">anonymized_telemetry<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-t\">False<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-v\">collection<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">chroma_client<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">get_or_create_collection<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">name<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-s\">&#8220;basketball&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># Our fallback unstructured text chunks<\/span><\/p>\n<p><span class=\"crayon-v\">doc1<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;LeBron injured for remainder of NBA 2023 season\\n&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;LeBron James suffered an ankle injury early in the season, which led to him playing far &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;fewer minutes per game than he has recently averaged in other seasons. The injury got much &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;worse today, and he is out for the rest of the season.&#8221;<\/span><\/p>\n<p><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-v\">doc2<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;Ottawa Beavers\\n&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;The Ottawa Beavers star player LeBron James is out for the rest of the 2023 NBA season, &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;after his ankle injury has worsened. The teams&#8217; abysmal regular season record may end up &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;being the worst of any team ever, with only 6 wins as of now, with only 4 gmaes left in &#8220;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;the regular season.&#8221;<\/span><\/p>\n<p><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-v\">collection<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">upsert<\/span><span class=\"crayon-sy\">(<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">documents<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-v\">doc1<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">doc2<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-sy\">,<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">ids<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-s\">&#8220;doc1&#8221;<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;doc2&#8221;<\/span><span class=\"crayon-sy\">]<\/span><\/p>\n<p><span class=\"crayon-sy\">)<\/span><\/p>\n<\/div>\n<\/td>\n<\/tr>\n<\/table><\/div>\n<\/p><\/div>\n<h2>Step 3: Entity Extraction &amp; Global Retrieval<\/h2>\n<p>How do we query deterministic graphs and semantic vectors simultaneously? We bridge the gap using NER via <code>spaCy<\/code>.<\/p>\n<p>First, we extract entities in constant time from the user\u2019s prompt (e.g. \u201cLeBron James\u201d and \u201cOttawa Beavers\u201d). Then, we fire off parallel queries to both QuadStores using the entities as strict lookups, while querying ChromaDB using string similarity over the prompt content.<\/p>\n<div id=\"urvanov-syntax-highlighter-69e1f6a6a38de678122075\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-pc print-yes notranslate\" data-settings=\" touchscreen minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p><textarea wrap=\"soft\" class=\"urvanov-syntax-highlighter-plain print-no\" data-settings=\"dblclick\" style=\"-moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4; font-size: 12px !important; line-height: 15px !important;\"><br \/>\nimport spacy&#13;<br \/>\n&#13;<br \/>\n# Load our NLP model&#13;<br \/>\nnlp = spacy.load(&#8220;en_core_web_sm&#8221;)&#13;<br \/>\n&#13;<br \/>\ndef extract_entities(text):&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    Extract entities from the given text using spaCy. Using set eliminates duplicates.&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    doc = nlp(text)&#13;<br \/>\n    return list(set([ent.text for ent in doc.ents]))&#13;<br \/>\n&#13;<br \/>\ndef get_facts(qs, entities):&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    Retrieve facts for a list of entities from the QuadStore (querying subjects and objects).&#13;<br \/>\n    &#8220;&#8221;&#8221;&#13;<br \/>\n    facts = []&#13;<br \/>\n    for entity in entities:&#13;<br \/>\n        subject_facts = qs.query(subject=entity)&#13;<br \/>\n        object_facts = qs.query(object=entity)&#13;<br \/>\n        facts.extend(subject_facts + object_facts)&#13;<br \/>\n    # Deduplicate facts and return&#13;<br \/>\n    return list(set(tuple(fact) for fact in facts))<\/textarea><\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<table class=\"crayon-table\">\n<tr class=\"urvanov-syntax-highlighter-row\">\n<td class=\"crayon-nums \" data-settings=\"show\">\n<div class=\"urvanov-syntax-highlighter-nums-content\" style=\"font-size: 12px !important; line-height: 15px !important;\">\n<p>1<\/p>\n<p>2<\/p>\n<p>3<\/p>\n<p>4<\/p>\n<p>5<\/p>\n<p>6<\/p>\n<p>7<\/p>\n<p>8<\/p>\n<p>9<\/p>\n<p>10<\/p>\n<p>11<\/p>\n<p>12<\/p>\n<p>13<\/p>\n<p>14<\/p>\n<p>15<\/p>\n<p>16<\/p>\n<p>17<\/p>\n<p>18<\/p>\n<p>19<\/p>\n<p>20<\/p>\n<p>21<\/p>\n<p>22<\/p>\n<p>23<\/p>\n<\/div>\n<\/td>\n<td class=\"urvanov-syntax-highlighter-code\">\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-e\">import <\/span><span class=\"crayon-i\">spacy<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-p\"># Load our NLP model<\/span><\/p>\n<p><span class=\"crayon-v\">nlp<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">spacy<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">load<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-s\">&#8220;en_core_web_sm&#8221;<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">extract_entities<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">text<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Extract entities from the given text using spaCy. Using set eliminates duplicates.<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0&#8220;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">doc<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">nlp<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">text<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">list<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-e\">set<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-v\">ent<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">text <\/span><span class=\"crayon-st\">for<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">ent <\/span><span class=\"crayon-st\">in<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">doc<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-v\">ents<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">get_facts<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">qs<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">entities<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0Retrieve facts for a list of entities from the QuadStore (querying subjects and objects).<\/span><\/p>\n<p><span class=\"crayon-s\">\u00a0\u00a0\u00a0\u00a0&#8220;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">facts<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-sy\">]<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">for<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">entity <\/span><span class=\"crayon-st\">in<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">entities<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">subject_facts<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">qs<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">query<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">subject<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">entity<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">object_facts<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">qs<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">query<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-t\">object<\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-v\">entity<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">facts<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">extend<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">subject_facts<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">+<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">object_facts<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Deduplicate facts and return<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">list<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-e\">set<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-e\">tuple<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">fact<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">for<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">fact <\/span><span class=\"crayon-st\">in<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">facts<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<\/div>\n<\/td>\n<\/tr>\n<\/table><\/div>\n<\/p><\/div>\n<p>We now have all the retrieved context separated into three distinct streams (<code>facts_p1<\/code>, <code>facts_p2<\/code>, and <code>vec_info<\/code>).<\/p>\n<h2>Step 4: Prompt-Enforced Conflict Resolution<\/h2>\n<p>Often, complex algorithmic conflict resolution (like <a href=\"https:\/\/www.mongodb.com\/resources\/basics\/reciprocal-rank-fusion\" target=\"_blank\">Reciprocal Rank Fusion<\/a>) fails when resolving granular facts against broad text. Here we take a radically simpler approach that, as a practical matter, also seems to work well: we embed the \u201cadjudicator\u201d ruleset directly into the system prompt.<\/p>\n<p>By assembling the knowledge into explicitly labeled <code>[PRIORITY 1]<\/code>, <code>[PRIORITY 2]<\/code>, and <code>[PRIORITY 3]<\/code> blocks, we instruct the language model to follow explicit logic when outputting its response.<\/p>\n<p>Here is the system prompt in its entirety:<\/p>\n<div id=\"urvanov-syntax-highlighter-69e1f6a6a38e4908331980\" class=\"urvanov-syntax-highlighter-syntax crayon-theme-classic urvanov-syntax-highlighter-font-monaco urvanov-syntax-highlighter-os-pc print-yes notranslate\" data-settings=\" touchscreen minimize scroll-mouseover disable-anim\" style=\" margin-top: 12px; margin-bottom: 12px; font-size: 12px !important; line-height: 15px !important;\">\n<p><textarea wrap=\"soft\" class=\"urvanov-syntax-highlighter-plain print-no\" data-settings=\"dblclick\" style=\"-moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4; font-size: 12px !important; line-height: 15px !important;\"><br \/>\ndef create_system_prompt(facts, stats, info):&#13;<br \/>\n    # Format graph facts into simple declarative sentences for language model comprehension&#13;<br \/>\n    formatted_facts = &#8220;\\n&#8221;.join([f&#8221;In {q[3]}, {q[0]} {str(q[1]).replace(&#8216;_&#8217;, &#8216; &#8216;)} {q[2]}.&#8221; if len(q) &gt;= 4 else str(q) for q in facts])&#13;<br \/>\n    formatted_stats = &#8220;\\n&#8221;.join([f&#8221;In {q[3]}, {q[0]} {str(q[1]).replace(&#8216;_&#8217;, &#8216; &#8216;)} {q[2]}.&#8221; if len(q) &gt;= 4 else str(q) for q in stats])&#13;<br \/>\n&#13;<br \/>\n    # Convert retrieved info dict to a string of text documents&#13;<br \/>\n    retrieved_context = &#8220;&#8221;&#13;<br \/>\n    if info and &#8216;documents&#8217; in info and info[&#8216;documents&#8217;]:&#13;<br \/>\n        retrieved_context = &#8221; &#8220;.join(info[&#8216;documents&#8217;][0])&#13;<br \/>\n&#13;<br \/>\n    return f&#8221;&#8221;&#8221;You are a strict data-retrieval AI. Your ONLY knowledge comes from the text provided below. You must completely ignore your internal training weights.&#13;<br \/>\n&#13;<br \/>\nPRIORITY RULES (strict):&#13;<br \/>\n1. If Priority 1 (Facts) contains a direct answer, use ONLY that answer. Do not supplement, qualify, or cross-reference with Priority 2 or Vector data.&#13;<br \/>\n2. Priority 2 data uses abbreviations and may appear to contradict P1 \u2014 it is supplementary background only. Never treat P2 team abbreviations as authoritative team names if P1 states a team.&#13;<br \/>\n3. Only use P2 if P1 has no relevant answer on the specific attribute asked.&#13;<br \/>\n4. If Priority 3 (Vector Chunks) provides any additional relevant information, use your judgment as to whether or not to include it in the response.&#13;<br \/>\n5. If none of the sections contain the answer, you must explicitly say &#8220;I do not have enough information.&#8221; Do not guess or hallucinate.&#13;<br \/>\n&#13;<br \/>\nYour output **MUST** follow these rules:&#13;<br \/>\n&#8211; Provide only the single authoritative answer based on the priority rules.&#13;<br \/>\n&#8211; Do not present multiple conflicting answers.&#13;<br \/>\n&#8211; Make no mention of the source of this data.&#13;<br \/>\n&#8211; Phrase this in the form of a sentence or multiple sentences, as is appropriate.&#13;<br \/>\n&#13;<br \/>\n&#8212;&#13;<br \/>\n[PRIORITY 1 &#8211; ABSOLUTE GRAPH FACTS]&#13;<br \/>\n{formatted_facts}&#13;<br \/>\n&#13;<br \/>\n[Priority 2: Background Statistics (team abbreviations here are NOT authoritative \u2014 defer to Priority 1 for factual claims)]&#13;<br \/>\n{formatted_stats}&#13;<br \/>\n&#13;<br \/>\n[PRIORITY 3 &#8211; VECTOR DOCUMENTS]&#13;<br \/>\n{retrieved_context}&#13;<br \/>\n&#8212;&#13;<br \/>\n&#8220;&#8221;&#8221;<\/textarea><\/p>\n<div class=\"urvanov-syntax-highlighter-main\" style=\"\">\n<table class=\"crayon-table\">\n<tr class=\"urvanov-syntax-highlighter-row\">\n<td class=\"crayon-nums \" data-settings=\"show\">\n<div class=\"urvanov-syntax-highlighter-nums-content\" style=\"font-size: 12px !important; line-height: 15px !important;\">\n<p>1<\/p>\n<p>2<\/p>\n<p>3<\/p>\n<p>4<\/p>\n<p>5<\/p>\n<p>6<\/p>\n<p>7<\/p>\n<p>8<\/p>\n<p>9<\/p>\n<p>10<\/p>\n<p>11<\/p>\n<p>12<\/p>\n<p>13<\/p>\n<p>14<\/p>\n<p>15<\/p>\n<p>16<\/p>\n<p>17<\/p>\n<p>18<\/p>\n<p>19<\/p>\n<p>20<\/p>\n<p>21<\/p>\n<p>22<\/p>\n<p>23<\/p>\n<p>24<\/p>\n<p>25<\/p>\n<p>26<\/p>\n<p>27<\/p>\n<p>28<\/p>\n<p>29<\/p>\n<p>30<\/p>\n<p>31<\/p>\n<p>32<\/p>\n<p>33<\/p>\n<p>34<\/p>\n<p>35<\/p>\n<p>36<\/p>\n<\/div>\n<\/td>\n<td class=\"urvanov-syntax-highlighter-code\">\n<div class=\"crayon-pre\" style=\"font-size: 12px !important; line-height: 15px !important; -moz-tab-size:4; -o-tab-size:4; -webkit-tab-size:4; tab-size:4;\">\n<p><span class=\"crayon-e\">def <\/span><span class=\"crayon-e\">create_system_prompt<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">facts<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">stats<\/span><span class=\"crayon-sy\">,<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">info<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Format graph facts into simple declarative sentences for language model comprehension<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">formatted_facts<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;\\n&#8221;<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">join<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;In {q[3]}, {q[0]} {str(q[1]).replace(&#8216;_&#8217;, &#8216; &#8216;)} {q[2]}.&#8221;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">len<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">q<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">&gt;=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-cn\">4<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">else<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">str<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">q<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">for<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">q<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">in<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">facts<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">formatted_stats<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;\\n&#8221;<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">join<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;In {q[3]}, {q[0]} {str(q[1]).replace(&#8216;_&#8217;, &#8216; &#8216;)} {q[2]}.&#8221;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">len<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">q<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">&gt;=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-cn\">4<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">else<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">str<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">q<\/span><span class=\"crayon-sy\">)<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">for<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">q<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">in<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">stats<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-p\"># Convert retrieved info dict to a string of text documents<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">retrieved_context<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">if<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">info <\/span><span class=\"crayon-st\">and<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8216;documents&#8217;<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">in<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">info <\/span><span class=\"crayon-st\">and<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-v\">info<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-s\">&#8216;documents&#8217;<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-o\">:<\/span><\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-v\">retrieved_context<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-o\">=<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-s\">&#8221; &#8220;<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-e\">join<\/span><span class=\"crayon-sy\">(<\/span><span class=\"crayon-v\">info<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-s\">&#8216;documents&#8217;<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-sy\">[<\/span><span class=\"crayon-cn\">0<\/span><span class=\"crayon-sy\">]<\/span><span class=\"crayon-sy\">)<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-h\">\u00a0\u00a0\u00a0\u00a0<\/span><span class=\"crayon-st\">return<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-i\">f<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><span class=\"crayon-s\">&#8220;You are a strict data-retrieval AI. Your ONLY knowledge comes from the text provided below. You must completely ignore your internal training weights.<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-s\">PRIORITY RULES (strict):<\/span><\/p>\n<p><span class=\"crayon-s\">1. If Priority 1 (Facts) contains a direct answer, use ONLY that answer. Do not supplement, qualify, or cross-reference with Priority 2 or Vector data.<\/span><\/p>\n<p><span class=\"crayon-s\">2. Priority 2 data uses abbreviations and may appear to contradict P1 \u2014 it is supplementary background only. Never treat P2 team abbreviations as authoritative team names if P1 states a team.<\/span><\/p>\n<p><span class=\"crayon-s\">3. Only use P2 if P1 has no relevant answer on the specific attribute asked.<\/span><\/p>\n<p><span class=\"crayon-s\">4. If Priority 3 (Vector Chunks) provides any additional relevant information, use your judgment as to whether or not to include it in the response.<\/span><\/p>\n<p><span class=\"crayon-s\">5. If none of the sections contain the answer, you must explicitly say &#8220;<\/span><span class=\"crayon-i\">I<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">do<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-st\">not<\/span><span class=\"crayon-h\"> <\/span><span class=\"crayon-e\">have <\/span><span class=\"crayon-e\">enough <\/span><span class=\"crayon-v\">information<\/span><span class=\"crayon-sy\">.<\/span><span class=\"crayon-s\">&#8221; Do not guess or hallucinate.<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-s\">Your output **MUST** follow these rules:<\/span><\/p>\n<p><span class=\"crayon-s\">&#8211; Provide only the single authoritative answer based on the priority rules.<\/span><\/p>\n<p><span class=\"crayon-s\">&#8211; Do not present multiple conflicting answers.<\/span><\/p>\n<p><span class=\"crayon-s\">&#8211; Make no mention of the source of this data.<\/span><\/p>\n<p><span class=\"crayon-s\">&#8211; Phrase this in the form of a sentence or multiple sentences, as is appropriate.<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-s\">&#8212;<\/span><\/p>\n<p><span class=\"crayon-s\">[PRIORITY 1 &#8211; ABSOLUTE GRAPH FACTS]<\/span><\/p>\n<p><span class=\"crayon-s\">{formatted_facts}<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-s\">[Priority 2: Background Statistics (team abbreviations here are NOT authoritative \u2014 defer to Priority 1 for factual claims)]<\/span><\/p>\n<p><span class=\"crayon-s\">{formatted_stats}<\/span><\/p>\n<p>\u00a0<\/p>\n<p><span class=\"crayon-s\">[PRIORITY 3 &#8211; VECTOR DOCUMENTS]<\/span><\/p>\n<p><span class=\"crayon-s\">{retrieved_context}<\/span><\/p>\n<p><span class=\"crayon-s\">&#8212;<\/span><\/p>\n<p><span class=\"crayon-s\">&#8220;<\/span><span class=\"crayon-s\">&#8220;&#8221;<\/span><\/p>\n<\/div>\n<\/td>\n<\/tr>\n<\/table><\/div>\n<\/p><\/div>\n<p>Far different than \u201c\u2026 and don\u2019t make any mistakes\u201d prompts that are little more than finger-crossing and wishing for no hallucinations, in this case we present the LM with ground truth atomic facts, possible conflicting \u201cless-fresh\u201d facts, and semantically-similar vector search results, along with an explicit hierarchy for determining which set of data is correct when conflicts are encountered. Is it foolproof? No, of course not, but it\u2019s a different approach worthy of consideration and addition to the hallucination-combatting toolkit.<\/p>\n<p>Don\u2019t forget that you can find <a href=\"https:\/\/github.com\/mmmayo13\/rag_facts_quadstore\/blob\/main\/main.py\" target=\"_blank\">the rest of the code for this project here<\/a>.<\/p>\n<h2>Step 5: Tying it All Together &amp; Testing<\/h2>\n<p>To wrap everything up, the main execution thread of our RAG system calls the local Llama instance via the REST API, handing it the structured system prompt above alongside the user\u2019s base question.<\/p>\n<p>When run in the terminal, the system isolates our three priority tiers, processes the entities, and queries the LM deterministically.<\/p>\n<h3>Query 1: Factual Retrieval with the QuadStore<\/h3>\n<p>When querying an absolute fact like \u201cWho is the star player of Ottawa Beavers team?\u201d, the system relies entirely on Priority 1 facts.<\/p>\n<div style=\"width: 810px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/04\/1776531255_252_Beyond-Vector-Search-Building-a-Deterministic-3-Tiered-Graph-RAG-System.png\" target=\"_blank\"><img fetchpriority=\"high\" decoding=\"async\" src=\"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/04\/1776531255_252_Beyond-Vector-Search-Building-a-Deterministic-3-Tiered-Graph-RAG-System.png\" alt=\"LeBron plays for Ottawa Beavers\" width=\"800\" height=\"706\"\/><\/a><\/p>\n<p class=\"wp-caption-text\">LeBron plays for Ottawa Beavers<\/p>\n<\/div>\n<p>Because Priority 1, in this case, explicitly states \u201cOttawa Beavers obtained LeBron James\u201d, the prompt instructs the LM <strong>never<\/strong> to supplement this with the vector documents or statistical abbreviations, thus aiming to eliminate the traditional RAG relationship hallucination. The supporting vector database documents support this claim as well, with articles about LeBron and his tenure with the Ottawa NBA team. Compare this with an LM prompt that dumps conflicting semantic search results into a model and asks it, generically, to determine which is true.<\/p>\n<h3>Query 2: More Factual Retrieval<\/h3>\n<p>The Ottawa beavers, you say? I\u2019m unfamiliar with them. I assume they play out of Ottawa, but where, exactly, in the city are they based? Priority 1 facts can tell us. Keep in mind we are fighting against what the model itself already knows (the Beavers are not an actual NBA team) as well as the NBA general stats dataset (which lists nothing about the Ottawa Beavers whatsoever).<\/p>\n<div style=\"width: 810px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/04\/1776531255_250_Beyond-Vector-Search-Building-a-Deterministic-3-Tiered-Graph-RAG-System.png\" target=\"_blank\"><img decoding=\"async\" src=\"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/04\/1776531255_250_Beyond-Vector-Search-Building-a-Deterministic-3-Tiered-Graph-RAG-System.png\" alt=\"The Ottawa Beavers home\" width=\"800\" height=\"706\"\/><\/a><\/p>\n<p class=\"wp-caption-text\">The Ottawa Beavers home<\/p>\n<\/div>\n<h3>Query 3: Dealing with Conflict<\/h3>\n<p>When querying an attribute in both the absolute facts graph and the general stats graph, such as \u201cWhat was LeBron James\u2019 average MPG in the 2023 NBA season?\u201d, the model relies on the Priority 1 level data over the existing Priority 2 stats data.<\/p>\n<div style=\"width: 810px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/04\/1776531255_16_Beyond-Vector-Search-Building-a-Deterministic-3-Tiered-Graph-RAG-System.png\" target=\"_blank\"><img loading=\"lazy\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/04\/1776531255_16_Beyond-Vector-Search-Building-a-Deterministic-3-Tiered-Graph-RAG-System.png\" alt=\"LeBron MPG Query Output\" width=\"800\" height=\"706\"\/><\/a><\/p>\n<p class=\"wp-caption-text\">LeBron MPG Query Output<\/p>\n<\/div>\n<h3>Query 4: Stitching Together a Robust Response<\/h3>\n<p>What happens when we ask an unstructured question like \u201cWhat injury did the Ottawa Beavers star injury suffer during the 2023 season?\u201d First, the model needs to know who the Ottawa Beavers star player is, and then determine what their injury was. This is accomplished with a combination of Priority 1 and Priority 3 data. The LM merges this smoothly into a final response.<\/p>\n<div style=\"width: 810px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/04\/1776531255_980_Beyond-Vector-Search-Building-a-Deterministic-3-Tiered-Graph-RAG-System.png\" target=\"_blank\"><img loading=\"lazy\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/04\/1776531255_980_Beyond-Vector-Search-Building-a-Deterministic-3-Tiered-Graph-RAG-System.png\" alt=\"LeBron Injury Query Output\" width=\"800\" height=\"706\"\/><\/a><\/p>\n<p class=\"wp-caption-text\">LeBron Injury Query Output<\/p>\n<\/div>\n<h3>Query 5: Another Robust Response<\/h3>\n<p>Here\u2019s another example of stitching together a coherent and accurate response from multi-level data. \u201cHow many wins did the team that LeBron James play for have when he left the season?\u201d<\/p>\n<div style=\"width: 810px\" class=\"wp-caption aligncenter\"><a href=\"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/04\/1776531256_646_Beyond-Vector-Search-Building-a-Deterministic-3-Tiered-Graph-RAG-System.png\" target=\"_blank\"><img loading=\"lazy\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/04\/1776531256_646_Beyond-Vector-Search-Building-a-Deterministic-3-Tiered-Graph-RAG-System.png\" alt=\"LeBron Injury Query #2 Output\" width=\"800\" height=\"706\"\/><\/a><\/p>\n<p class=\"wp-caption-text\">LeBron Injury Query #2 Output<\/p>\n<\/div>\n<p>Let\u2019s not forget that for all of these queries, the model must ignore the fact that conflicting (and inaccurate!) data exists in the Priority 2 stats graph suggesting (again, wrongly!) that LeBron James played for the LA Lakers in 2023. And let\u2019s <em>also<\/em> not forget that we are using a simple language model with only 3 billion parameters (<code>llama3.2:3b<\/code>).<\/p>\n<h2>Conclusion &amp; Trade-offs<\/h2>\n<p>By splitting your retrieval sources into distinct authoritative layers \u2014 and dictating exact resolution rules via prompt engineering \u2014 the hope is that you drastically reduce factual hallucinations, or competition between otherwise equally-true pieces of data.<\/p>\n<p>Advantages of this approach include:<\/p>\n<ul>\n<li>Predictability: 100% deterministic predictability for critical facts stored in Priority 1 (goal)\n<\/li>\n<li>Explainability: If required, you can force the LM to output its <code>[REASONING]<\/code> chain to validate <em>why<\/em> Priority 1 overrode the rest\n<\/li>\n<li>Simplicity: No need to train custom retrieval routers\n<\/li>\n<\/ul>\n<p>Trade-offs of this approach include:<\/p>\n<ul>\n<li>Token Overhead: Dumping all three databases into the initial context window consumes substantially more tokens than typical algorithm-filtered retrieval\n<\/li>\n<li>Model Reliance: This system requires a highly instruction-compliant LM to avoid falling back into latent training-weight behavior\n<\/li>\n<\/ul>\n<p>For environments in which high precision and low tolerance for errors are mandatory, deploying a multi-tiered factual hierarchy alongside your vector database may be the differentiator between prototype and production.<\/p>\n<\/p><\/div>\n","protected":false},"excerpt":{"rendered":"<p>In this article, you will learn how to build a deterministic, multi-tier retrieval-augmented generation system using knowledge graphs and vector databases. Topics we will cover include: Designing a three-tier retrieval hierarchy for factual accuracy. Implementing a lightweight knowledge graph. Using prompt-enforced rules to resolve retrieval conflicts deterministically. Beyond Vector Search: Building a Deterministic 3-Tiered Graph-RAG<\/p>\n","protected":false},"author":1,"featured_media":179404,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[42],"tags":[],"class_list":{"0":"post-179403","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-ai"},"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.4 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Beyond Vector Search: Building a Deterministic 3-Tiered Graph-RAG System - Ktromedia<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/ktromedia.com\/?p=179403\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Beyond Vector Search: Building a Deterministic 3-Tiered Graph-RAG System - Ktromedia\" \/>\n<meta property=\"og:description\" content=\"In this article, you will learn how to build a deterministic, multi-tier retrieval-augmented generation system using knowledge graphs and vector databases. Topics we will cover include: Designing a three-tier retrieval hierarchy for factual accuracy. Implementing a lightweight knowledge graph. Using prompt-enforced rules to resolve retrieval conflicts deterministically. Beyond Vector Search: Building a Deterministic 3-Tiered Graph-RAG\" \/>\n<meta property=\"og:url\" content=\"https:\/\/ktromedia.com\/?p=179403\" \/>\n<meta property=\"og:site_name\" content=\"Ktromedia\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/KTROMedia\/\" \/>\n<meta property=\"article:published_time\" content=\"2026-04-18T16:54:19+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/04\/Beyond-Vector-Search-Building-a-Deterministic-3-Tiered-Graph-RAG-System.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1600\" \/>\n\t<meta property=\"og:image:height\" content=\"893\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"KTRO TEAM\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"KTRO TEAM\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"15 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/ktromedia.com\/?p=179403#article\",\"isPartOf\":{\"@id\":\"https:\/\/ktromedia.com\/?p=179403\"},\"author\":{\"name\":\"KTRO TEAM\",\"@id\":\"http:\/\/ktromedia.com\/#\/schema\/person\/612bf2fbac107722ea365932cdd35f5b\"},\"headline\":\"Beyond Vector Search: Building a Deterministic 3-Tiered Graph-RAG System\",\"datePublished\":\"2026-04-18T16:54:19+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/ktromedia.com\/?p=179403\"},\"wordCount\":3052,\"commentCount\":0,\"publisher\":{\"@id\":\"http:\/\/ktromedia.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/ktromedia.com\/?p=179403#primaryimage\"},\"thumbnailUrl\":\"http:\/\/ktromedia.com\/wp-content\/uploads\/2026\/04\/Beyond-Vector-Search-Building-a-Deterministic-3-Tiered-Graph-RAG-System.png\",\"articleSection\":[\"\u4eba\u5de5\u667a\u80fd\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/ktromedia.com\/?p=179403#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/ktromedia.com\/?p=179403\",\"url\":\"https:\/\/ktromedia.com\/?p=179403\",\"name\":\"Beyond Vector Search: Building a Deterministic 3-Tiered Graph-RAG System - Ktromedia\",\"isPartOf\":{\"@id\":\"http:\/\/ktromedia.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/ktromedia.com\/?p=179403#primaryimage\"},\"image\":{\"@id\":\"https:\/\/ktromedia.com\/?p=179403#primaryimage\"},\"thumbnailUrl\":\"http:\/\/ktromedia.com\/wp-content\/uploads\/2026\/04\/Beyond-Vector-Search-Building-a-Deterministic-3-Tiered-Graph-RAG-System.png\",\"datePublished\":\"2026-04-18T16:54:19+00:00\",\"breadcrumb\":{\"@id\":\"https:\/\/ktromedia.com\/?p=179403#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/ktromedia.com\/?p=179403\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/ktromedia.com\/?p=179403#primaryimage\",\"url\":\"http:\/\/ktromedia.com\/wp-content\/uploads\/2026\/04\/Beyond-Vector-Search-Building-a-Deterministic-3-Tiered-Graph-RAG-System.png\",\"contentUrl\":\"http:\/\/ktromedia.com\/wp-content\/uploads\/2026\/04\/Beyond-Vector-Search-Building-a-Deterministic-3-Tiered-Graph-RAG-System.png\",\"width\":1600,\"height\":893},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/ktromedia.com\/?p=179403#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"http:\/\/ktromedia.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Beyond Vector Search: Building a Deterministic 3-Tiered Graph-RAG System\"}]},{\"@type\":\"WebSite\",\"@id\":\"http:\/\/ktromedia.com\/#website\",\"url\":\"http:\/\/ktromedia.com\/\",\"name\":\"Ktromedia\",\"description\":\"KTRO MEDIA Crypto News\",\"publisher\":{\"@id\":\"http:\/\/ktromedia.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"http:\/\/ktromedia.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"http:\/\/ktromedia.com\/#organization\",\"name\":\"Ktromedia\",\"url\":\"http:\/\/ktromedia.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"http:\/\/ktromedia.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/ktromedia.com\/wp-content\/uploads\/2025\/11\/ktroicon.png\",\"contentUrl\":\"https:\/\/ktromedia.com\/wp-content\/uploads\/2025\/11\/ktroicon.png\",\"width\":250,\"height\":250,\"caption\":\"Ktromedia\"},\"image\":{\"@id\":\"http:\/\/ktromedia.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/KTROMedia\/\",\"https:\/\/www.linkedin.com\/company\/ktro-media\/\",\"https:\/\/t.me\/ktrogroup\"]},{\"@type\":\"Person\",\"@id\":\"http:\/\/ktromedia.com\/#\/schema\/person\/612bf2fbac107722ea365932cdd35f5b\",\"name\":\"KTRO TEAM\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"http:\/\/ktromedia.com\/#\/schema\/person\/image\/\",\"url\":\"http:\/\/ktromedia.com\/wp-content\/uploads\/2025\/10\/cropped-Untitled-design-7-1-150x150.png\",\"contentUrl\":\"http:\/\/ktromedia.com\/wp-content\/uploads\/2025\/10\/cropped-Untitled-design-7-1-150x150.png\",\"caption\":\"KTRO TEAM\"},\"description\":\"KTRO MEDIA \u662f\u4e00\u5bb6\u5168\u7403\u6027\u7684\u534e\u6587WEB3\u5a92\u4f53\u516c\u53f8\u3002\u6211\u4eec\u81f4\u529b\u4e8e\u4e3a\u533a\u5757\u94fe\u548c\u91d1\u878d\u79d1\u6280\u9886\u57df\u63d0\u4f9b\u6700\u65b0\u7684\u65b0\u95fb\u3001\u89c1\u89e3\u548c\u8d8b\u52bf\u5206\u6790\u3002\u6211\u4eec\u7684\u5b97\u65e8\u662f\u4e3a\u5168\u7403\u7528\u6237\u63d0\u4f9b\u9ad8\u8d28\u91cf\u3001\u5168\u9762\u7684\u8d44\u8baf\u670d\u52a1\uff0c\u8ba9\u4ed6\u4eec\u66f4\u597d\u5730\u4e86\u89e3\u533a\u5757\u94fe\u548c\u91d1\u878d\u79d1\u6280\u884c\u4e1a\u7684\u6700\u65b0\u52a8\u6001\u3002\u6211\u4eec\u4e5f\u5e0c\u671b\u80fd\u5e2e\u5230\u66f4\u591a\u4f18\u79c0\u7684WEB3\u4ea7\u54c1\u627e\u5230\u66f4\u591a\u66f4\u597d\u7684\u8d44\u6e90\u597d\u8ba9\u8fd9\u9886\u57df\u53d8\u5f97\u66f4\u6210\u719f\u3002 \u6211\u4eec\u7684\u62a5\u9053\u8303\u56f4\u6db5\u76d6\u4e86\u533a\u5757\u94fe\u3001\u52a0\u5bc6\u8d27\u5e01\u3001\u667a\u80fd\u5408\u7ea6\u3001DeFi\u3001NFT \u548c Web3 \u751f\u6001\u7cfb\u7edf\u7b49\u9886\u57df\u3002\u6211\u4eec\u7684\u62a5\u9053\u4e0d\u4ec5\u6765\u81ea\u884c\u4e1a\u5185\u7684\u4e13\u5bb6\uff0c\u5148\u950b\u8005\u4e5f\u5305\u62ec\u4e86\u6211\u4eec\u81ea\u5df1\u7684\u5206\u6790\u548c\u89c2\u70b9\u3002\u6211\u4eec\u5728\u5404\u4e2a\u56fd\u5bb6\u548c\u5730\u533a\u90fd\u8bbe\u6709\u56e2\u961f\uff0c\u4e3a\u8bfb\u8005\u63d0\u4f9b\u672c\u5730\u5316\u7684\u62a5\u9053\u548c\u5206\u6790\u3002 \u9664\u4e86\u65b0\u95fb\u62a5\u9053\uff0c\u6211\u4eec\u8fd8\u63d0\u4f9b\u5e02\u573a\u7814\u7a76\u548c\u54a8\u8be2\u670d\u52a1\u3002\u6211\u4eec\u7684\u4e13\u4e1a\u56e2\u961f\u53ef\u4ee5\u4e3a\u60a8\u63d0\u4f9b\u6709\u5173\u533a\u5757\u94fe\u548c\u91d1\u878d\u79d1\u6280\u884c\u4e1a\u7684\u6df1\u5165\u5206\u6790\u548c\u5e02\u573a\u8d8b\u52bf\uff0c\u5e2e\u52a9\u60a8\u505a\u51fa\u66f4\u660e\u667a\u7684\u6295\u8d44\u51b3\u7b56\u3002 \u6211\u4eec\u7684\u4f7f\u547d\u662f\u6210\u4e3a\u5168\u7403\u534e\u6587\u533a\u5757\u94fe\u548c\u91d1\u878d\u79d1\u6280\u884c\u4e1a\u6700\u53d7\u4fe1\u8d56\u7684\u4fe1\u606f\u6765\u6e90\u4e4b\u4e00\u3002\u6211\u4eec\u5c06\u7ee7\u7eed\u4e0d\u65ad\u52aa\u529b\uff0c\u4e3a\u8bfb\u8005\u63d0\u4f9b\u6700\u65b0\u3001\u6700\u5168\u9762\u3001\u6700\u53ef\u9760\u7684\u4fe1\u606f\u670d\u52a1\u3002\",\"sameAs\":[\"https:\/\/ktromedia.com\"],\"url\":\"http:\/\/ktromedia.com\/?author=1\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Beyond Vector Search: Building a Deterministic 3-Tiered Graph-RAG System - Ktromedia","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/ktromedia.com\/?p=179403","og_locale":"en_US","og_type":"article","og_title":"Beyond Vector Search: Building a Deterministic 3-Tiered Graph-RAG System - Ktromedia","og_description":"In this article, you will learn how to build a deterministic, multi-tier retrieval-augmented generation system using knowledge graphs and vector databases. Topics we will cover include: Designing a three-tier retrieval hierarchy for factual accuracy. Implementing a lightweight knowledge graph. Using prompt-enforced rules to resolve retrieval conflicts deterministically. Beyond Vector Search: Building a Deterministic 3-Tiered Graph-RAG","og_url":"https:\/\/ktromedia.com\/?p=179403","og_site_name":"Ktromedia","article_publisher":"https:\/\/www.facebook.com\/KTROMedia\/","article_published_time":"2026-04-18T16:54:19+00:00","og_image":[{"width":1600,"height":893,"url":"https:\/\/ktromedia.com\/wp-content\/uploads\/2026\/04\/Beyond-Vector-Search-Building-a-Deterministic-3-Tiered-Graph-RAG-System.png","type":"image\/png"}],"author":"KTRO TEAM","twitter_card":"summary_large_image","twitter_misc":{"Written by":"KTRO TEAM","Est. reading time":"15 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/ktromedia.com\/?p=179403#article","isPartOf":{"@id":"https:\/\/ktromedia.com\/?p=179403"},"author":{"name":"KTRO TEAM","@id":"http:\/\/ktromedia.com\/#\/schema\/person\/612bf2fbac107722ea365932cdd35f5b"},"headline":"Beyond Vector Search: Building a Deterministic 3-Tiered Graph-RAG System","datePublished":"2026-04-18T16:54:19+00:00","mainEntityOfPage":{"@id":"https:\/\/ktromedia.com\/?p=179403"},"wordCount":3052,"commentCount":0,"publisher":{"@id":"http:\/\/ktromedia.com\/#organization"},"image":{"@id":"https:\/\/ktromedia.com\/?p=179403#primaryimage"},"thumbnailUrl":"http:\/\/ktromedia.com\/wp-content\/uploads\/2026\/04\/Beyond-Vector-Search-Building-a-Deterministic-3-Tiered-Graph-RAG-System.png","articleSection":["\u4eba\u5de5\u667a\u80fd"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/ktromedia.com\/?p=179403#respond"]}]},{"@type":"WebPage","@id":"https:\/\/ktromedia.com\/?p=179403","url":"https:\/\/ktromedia.com\/?p=179403","name":"Beyond Vector Search: Building a Deterministic 3-Tiered Graph-RAG System - Ktromedia","isPartOf":{"@id":"http:\/\/ktromedia.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/ktromedia.com\/?p=179403#primaryimage"},"image":{"@id":"https:\/\/ktromedia.com\/?p=179403#primaryimage"},"thumbnailUrl":"http:\/\/ktromedia.com\/wp-content\/uploads\/2026\/04\/Beyond-Vector-Search-Building-a-Deterministic-3-Tiered-Graph-RAG-System.png","datePublished":"2026-04-18T16:54:19+00:00","breadcrumb":{"@id":"https:\/\/ktromedia.com\/?p=179403#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/ktromedia.com\/?p=179403"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/ktromedia.com\/?p=179403#primaryimage","url":"http:\/\/ktromedia.com\/wp-content\/uploads\/2026\/04\/Beyond-Vector-Search-Building-a-Deterministic-3-Tiered-Graph-RAG-System.png","contentUrl":"http:\/\/ktromedia.com\/wp-content\/uploads\/2026\/04\/Beyond-Vector-Search-Building-a-Deterministic-3-Tiered-Graph-RAG-System.png","width":1600,"height":893},{"@type":"BreadcrumbList","@id":"https:\/\/ktromedia.com\/?p=179403#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"http:\/\/ktromedia.com\/"},{"@type":"ListItem","position":2,"name":"Beyond Vector Search: Building a Deterministic 3-Tiered Graph-RAG System"}]},{"@type":"WebSite","@id":"http:\/\/ktromedia.com\/#website","url":"http:\/\/ktromedia.com\/","name":"Ktromedia","description":"KTRO MEDIA Crypto News","publisher":{"@id":"http:\/\/ktromedia.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"http:\/\/ktromedia.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"http:\/\/ktromedia.com\/#organization","name":"Ktromedia","url":"http:\/\/ktromedia.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"http:\/\/ktromedia.com\/#\/schema\/logo\/image\/","url":"https:\/\/ktromedia.com\/wp-content\/uploads\/2025\/11\/ktroicon.png","contentUrl":"https:\/\/ktromedia.com\/wp-content\/uploads\/2025\/11\/ktroicon.png","width":250,"height":250,"caption":"Ktromedia"},"image":{"@id":"http:\/\/ktromedia.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/KTROMedia\/","https:\/\/www.linkedin.com\/company\/ktro-media\/","https:\/\/t.me\/ktrogroup"]},{"@type":"Person","@id":"http:\/\/ktromedia.com\/#\/schema\/person\/612bf2fbac107722ea365932cdd35f5b","name":"KTRO TEAM","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"http:\/\/ktromedia.com\/#\/schema\/person\/image\/","url":"http:\/\/ktromedia.com\/wp-content\/uploads\/2025\/10\/cropped-Untitled-design-7-1-150x150.png","contentUrl":"http:\/\/ktromedia.com\/wp-content\/uploads\/2025\/10\/cropped-Untitled-design-7-1-150x150.png","caption":"KTRO TEAM"},"description":"KTRO MEDIA \u662f\u4e00\u5bb6\u5168\u7403\u6027\u7684\u534e\u6587WEB3\u5a92\u4f53\u516c\u53f8\u3002\u6211\u4eec\u81f4\u529b\u4e8e\u4e3a\u533a\u5757\u94fe\u548c\u91d1\u878d\u79d1\u6280\u9886\u57df\u63d0\u4f9b\u6700\u65b0\u7684\u65b0\u95fb\u3001\u89c1\u89e3\u548c\u8d8b\u52bf\u5206\u6790\u3002\u6211\u4eec\u7684\u5b97\u65e8\u662f\u4e3a\u5168\u7403\u7528\u6237\u63d0\u4f9b\u9ad8\u8d28\u91cf\u3001\u5168\u9762\u7684\u8d44\u8baf\u670d\u52a1\uff0c\u8ba9\u4ed6\u4eec\u66f4\u597d\u5730\u4e86\u89e3\u533a\u5757\u94fe\u548c\u91d1\u878d\u79d1\u6280\u884c\u4e1a\u7684\u6700\u65b0\u52a8\u6001\u3002\u6211\u4eec\u4e5f\u5e0c\u671b\u80fd\u5e2e\u5230\u66f4\u591a\u4f18\u79c0\u7684WEB3\u4ea7\u54c1\u627e\u5230\u66f4\u591a\u66f4\u597d\u7684\u8d44\u6e90\u597d\u8ba9\u8fd9\u9886\u57df\u53d8\u5f97\u66f4\u6210\u719f\u3002 \u6211\u4eec\u7684\u62a5\u9053\u8303\u56f4\u6db5\u76d6\u4e86\u533a\u5757\u94fe\u3001\u52a0\u5bc6\u8d27\u5e01\u3001\u667a\u80fd\u5408\u7ea6\u3001DeFi\u3001NFT \u548c Web3 \u751f\u6001\u7cfb\u7edf\u7b49\u9886\u57df\u3002\u6211\u4eec\u7684\u62a5\u9053\u4e0d\u4ec5\u6765\u81ea\u884c\u4e1a\u5185\u7684\u4e13\u5bb6\uff0c\u5148\u950b\u8005\u4e5f\u5305\u62ec\u4e86\u6211\u4eec\u81ea\u5df1\u7684\u5206\u6790\u548c\u89c2\u70b9\u3002\u6211\u4eec\u5728\u5404\u4e2a\u56fd\u5bb6\u548c\u5730\u533a\u90fd\u8bbe\u6709\u56e2\u961f\uff0c\u4e3a\u8bfb\u8005\u63d0\u4f9b\u672c\u5730\u5316\u7684\u62a5\u9053\u548c\u5206\u6790\u3002 \u9664\u4e86\u65b0\u95fb\u62a5\u9053\uff0c\u6211\u4eec\u8fd8\u63d0\u4f9b\u5e02\u573a\u7814\u7a76\u548c\u54a8\u8be2\u670d\u52a1\u3002\u6211\u4eec\u7684\u4e13\u4e1a\u56e2\u961f\u53ef\u4ee5\u4e3a\u60a8\u63d0\u4f9b\u6709\u5173\u533a\u5757\u94fe\u548c\u91d1\u878d\u79d1\u6280\u884c\u4e1a\u7684\u6df1\u5165\u5206\u6790\u548c\u5e02\u573a\u8d8b\u52bf\uff0c\u5e2e\u52a9\u60a8\u505a\u51fa\u66f4\u660e\u667a\u7684\u6295\u8d44\u51b3\u7b56\u3002 \u6211\u4eec\u7684\u4f7f\u547d\u662f\u6210\u4e3a\u5168\u7403\u534e\u6587\u533a\u5757\u94fe\u548c\u91d1\u878d\u79d1\u6280\u884c\u4e1a\u6700\u53d7\u4fe1\u8d56\u7684\u4fe1\u606f\u6765\u6e90\u4e4b\u4e00\u3002\u6211\u4eec\u5c06\u7ee7\u7eed\u4e0d\u65ad\u52aa\u529b\uff0c\u4e3a\u8bfb\u8005\u63d0\u4f9b\u6700\u65b0\u3001\u6700\u5168\u9762\u3001\u6700\u53ef\u9760\u7684\u4fe1\u606f\u670d\u52a1\u3002","sameAs":["https:\/\/ktromedia.com"],"url":"http:\/\/ktromedia.com\/?author=1"}]}},"_links":{"self":[{"href":"http:\/\/ktromedia.com\/index.php?rest_route=\/wp\/v2\/posts\/179403","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/ktromedia.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/ktromedia.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/ktromedia.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/ktromedia.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=179403"}],"version-history":[{"count":1,"href":"http:\/\/ktromedia.com\/index.php?rest_route=\/wp\/v2\/posts\/179403\/revisions"}],"predecessor-version":[{"id":179405,"href":"http:\/\/ktromedia.com\/index.php?rest_route=\/wp\/v2\/posts\/179403\/revisions\/179405"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/ktromedia.com\/index.php?rest_route=\/wp\/v2\/media\/179404"}],"wp:attachment":[{"href":"http:\/\/ktromedia.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=179403"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/ktromedia.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=179403"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/ktromedia.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=179403"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}