<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Datta sai krishna Naidu</title>
    <description>The latest articles on DEV Community by Datta sai krishna Naidu (@demigoddsk).</description>
    <link>https://dev.to/demigoddsk</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4063273%2F20954fe4-8e7f-42f9-99a7-d359b1c2155a.jpg</url>
      <title>DEV Community: Datta sai krishna Naidu</title>
      <link>https://dev.to/demigoddsk</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/demigoddsk"/>
    <language>en</language>
    <item>
      <title>Local-first multi-hop RAG: Chroma + an entity graph, zero tokens per query</title>
      <dc:creator>Datta sai krishna Naidu</dc:creator>
      <pubDate>Wed, 05 Aug 2026 01:46:35 +0000</pubDate>
      <link>https://dev.to/demigoddsk/local-first-multi-hop-rag-chroma-an-entity-graph-zero-tokens-per-query-1l43</link>
      <guid>https://dev.to/demigoddsk/local-first-multi-hop-rag-chroma-an-entity-graph-zero-tokens-per-query-1l43</guid>
      <description>&lt;p&gt;Chroma's sweet spot is local-first: embed your documents, query them&lt;br&gt;
on your own machine, no infrastructure ceremony. But local-first RAG&lt;br&gt;
hits a wall on multi-hop questions:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Which university did the founder of the company that acquired Polar&lt;br&gt;
Metrics study at?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answering passage shares almost no vocabulary with the question.&lt;br&gt;
It's connected to it — through an acquisition, a founder, a biography&lt;br&gt;
— and similarity search can't follow connections. The usual fix is to&lt;br&gt;
put an LLM in the retrieval loop to decompose the question, which&lt;br&gt;
breaks exactly what makes local-first attractive: now every query&lt;br&gt;
costs tokens, takes seconds, and returns something different each run.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DemigodDSK/hubmesh" rel="noopener noreferrer"&gt;hubmesh&lt;/a&gt; takes the other road:&lt;br&gt;
keep retrieval as pure math. At index time it builds an entity–document&lt;br&gt;
graph from your corpus with spaCy NER (no LLM, no tokens). At query&lt;br&gt;
time it runs Personalized PageRank from the question's entities over&lt;br&gt;
that graph and fuses the result with Chroma's cosine scores. The whole&lt;br&gt;
query path is numpy and scipy: ~100ms on a 30K-doc corpus, offline,&lt;br&gt;
and bit-identical across runs — three properties an LLM-in-the-loop&lt;br&gt;
retriever cannot offer at any price.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"hubmesh[chroma,kg]"&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; spacy download en_core_web_sm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Index
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;hubmesh&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Planner&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;hubmesh.adapters&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ChromaStore&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;hubmesh.kg&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;build_entity_kg&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;spacy&lt;/span&gt;

&lt;span class="n"&gt;embed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;  &lt;span class="c1"&gt;# your embedding callable
&lt;/span&gt;
&lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ChromaStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                       &lt;span class="c1"&gt;# ephemeral
# store = ChromaStore.from_documents(docs, persist_directory="./chroma")
# store = ChromaStore.from_documents(docs, host="localhost", port=8000)
&lt;/span&gt;
&lt;span class="n"&gt;nlp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spacy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en_core_web_sm&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;kg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_entity_kg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_many&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all_ids&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="n"&gt;nlp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;nlp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;planner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Planner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;kg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nlp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;nlp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Query
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;planner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Which university did the founder of the &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
                          &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;company that acquired Polar Metrics study at?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;][:&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reasoning&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="c1"&gt;# the graph route, not a rationalization
&lt;/span&gt;    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;node_ids&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why it works: intersection, not just proximity
&lt;/h2&gt;

&lt;p&gt;The scoring composite has three parts — cosine relevance, PPR&lt;br&gt;
diffusion, and a &lt;em&gt;convergence&lt;/em&gt; term that scores each document by the&lt;br&gt;
geometric mean of diffusion from every question entity separately. A&lt;br&gt;
multi-hop answer sits at the intersection of the question's anchors;&lt;br&gt;
pooled similarity computes a union. Intersections are where bridge&lt;br&gt;
documents live. (The formula lineage is a network-topology paper,&lt;br&gt;
NNSI, ICOMP'25 — same idea, different graph.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Numbers
&lt;/h2&gt;

&lt;p&gt;Full HotpotQA dev (7,405 questions): 75.2% supporting-fact recall@10&lt;br&gt;
vs 69.3% naive cosine over identical embeddings. MuSiQue 2/3/4-hop:&lt;br&gt;
+6.0/+3.2/+5.0 points. Honest trade: recall@2 dips 0.75 pts under the&lt;br&gt;
convergence term (flag documented to disable for top-2 workloads).&lt;br&gt;
Harness and raw JSONs ship in the repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  The agent story
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;pip install "hubmesh[mcp]"&lt;/code&gt; adds an MCP server (in the official MCP&lt;br&gt;
Registry as &lt;code&gt;io.github.DemigodDSK/hubmesh&lt;/code&gt;) with agent-steerable&lt;br&gt;
retrieval: pass &lt;code&gt;seed_entities&lt;/code&gt; to aim the next hop at what you just&lt;br&gt;
read, &lt;code&gt;exclude_docs&lt;/code&gt; to explore new ground. Local Chroma + local graph&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;your agent's reasoning: multi-hop RAG where the only LLM in the
system is the one you already run.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Repo: github.com/DemigodDSK/hubmesh · MIT · numbers reproducible via &lt;code&gt;benchmarks/&lt;/code&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rag</category>
      <category>python</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Multi-hop questions break vector search. Here is a graph layer for Qdrant that fixes them.</title>
      <dc:creator>Datta sai krishna Naidu</dc:creator>
      <pubDate>Wed, 05 Aug 2026 01:45:45 +0000</pubDate>
      <link>https://dev.to/demigoddsk/multi-hop-questions-break-vector-search-here-is-a-graph-layer-for-qdrant-that-fixes-them-35cl</link>
      <guid>https://dev.to/demigoddsk/multi-hop-questions-break-vector-search-here-is-a-graph-layer-for-qdrant-that-fixes-them-35cl</guid>
      <description>&lt;p&gt;Qdrant answers "which vectors are near this query?" in milliseconds at&lt;br&gt;
billion scale. But there's a class of questions where nearness is the&lt;br&gt;
wrong criterion entirely:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Where did the founder of the company that acquired Slack study?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The passage that answers this talks about Marc Benioff and USC. It&lt;br&gt;
never mentions Slack. Cosine similarity — any similarity — ranks it&lt;br&gt;
low, because the answer doesn't &lt;em&gt;look like&lt;/em&gt; the question. It's&lt;br&gt;
&lt;em&gt;connected to&lt;/em&gt; the question, three entity-hops away: Slack → acquired&lt;br&gt;
by Salesforce → founded by Benioff → studied at USC. That's a topology&lt;br&gt;
problem, and no amount of ANN speed solves a topology problem.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/DemigodDSK/hubmesh" rel="noopener noreferrer"&gt;hubmesh&lt;/a&gt; is a small MIT&lt;br&gt;
library that adds the topology layer on top of your existing Qdrant&lt;br&gt;
collection. Qdrant keeps doing what it's best at (first-pass ANN);&lt;br&gt;
hubmesh builds an entity–document graph at index time and, at query&lt;br&gt;
time, diffuses Personalized PageRank from the question's entities over&lt;br&gt;
that graph, fusing graph reachability with your cosine scores. No LLM&lt;br&gt;
is involved at query time — retrieval is one sparse matrix iteration,&lt;br&gt;
deterministic, roughly 100ms on a 30K-document corpus.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="s2"&gt;"hubmesh[qdrant,kg]"&lt;/span&gt;
python &lt;span class="nt"&gt;-m&lt;/span&gt; spacy download en_core_web_sm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Index: your Qdrant collection + an entity graph
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;hubmesh&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Planner&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;hubmesh.adapters&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;QdrantStore&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;hubmesh.kg&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;build_entity_kg&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;spacy&lt;/span&gt;

&lt;span class="n"&gt;embed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;  &lt;span class="c1"&gt;# your embedding callable: text -&amp;gt; np.ndarray
&lt;/span&gt;
&lt;span class="c1"&gt;# any of: in-memory, on-disk, or your running Qdrant server
&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;QdrantStore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_documents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://localhost:6333&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# entity-document graph via spaCy NER — zero LLM tokens to build
&lt;/span&gt;&lt;span class="n"&gt;nlp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;spacy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;en_core_web_sm&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;kg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;build_entity_kg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get_many&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all_ids&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt; &lt;span class="n"&gt;nlp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;nlp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;planner&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Planner&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;kg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nlp&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;nlp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Query: diffusion + similarity, fused
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;planner&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retrieve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Where did the founder of the company that acquired Slack study?&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;top_k&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;reasoning&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;score&lt;/span&gt;&lt;span class="si"&gt;:&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;  &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt; -&amp;gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;node_ids&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;# 0.031  ent:slack -&amp;gt; doc:acquisition -&amp;gt; ent:salesforce -&amp;gt; doc:benioff_bio
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;reasoning&lt;/code&gt; field is not a post-hoc explanation — it's the actual&lt;br&gt;
graph route that surfaced each document, which means your RAG pipeline&lt;br&gt;
can show &lt;em&gt;why&lt;/em&gt; a passage was retrieved.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the scoring actually is
&lt;/h2&gt;

&lt;p&gt;Each candidate document gets a composite of three signals, normalized&lt;br&gt;
and combined (the formula descends from a network-topology paper —&lt;br&gt;
NNSI, ICOMP'25 — where the same lesson appeared: no single centrality&lt;br&gt;
metric identifies important nodes, but a weighted composite does):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Relevance&lt;/strong&gt; — cosine against your Qdrant vectors (geometry)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structure&lt;/strong&gt; — Personalized PageRank mass diffused from the
question's entities (topology)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Convergence&lt;/strong&gt; — the geometric mean of per-entity diffusion, so a
document must be reachable from &lt;em&gt;every&lt;/em&gt; anchor in the question, not
just flooded with score from one&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Numbers (harness and raw JSONs in the repo)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Benchmark&lt;/th&gt;
&lt;th&gt;recall@10 vs naive cosine, same embeddings&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;HotpotQA full dev (7,405 q)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;75.2% vs 69.3% (+5.9 pts)&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MuSiQue 2/3/4-hop&lt;/td&gt;
&lt;td&gt;+6.0 / +3.2 / +5.0 pts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vs HippoRAG-style PPR-only, same graph&lt;/td&gt;
&lt;td&gt;+29.8 pts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Disclosed trade-off: the convergence term buys depth recall with&lt;br&gt;
top-rank precision — recall@2 is 0.75 pts below naive on full dev. If&lt;br&gt;
you retrieve with &lt;code&gt;top_k=2&lt;/code&gt;, disable it (&lt;code&gt;use_convergence=False&lt;/code&gt;).&lt;br&gt;
Everything above reproduces with the scripts in &lt;code&gt;benchmarks/&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Agents can steer it
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;retrieve&lt;/code&gt; accepts &lt;code&gt;seed_entities&lt;/code&gt; and &lt;code&gt;exclude_docs&lt;/code&gt;, so an agent can&lt;br&gt;
iterate: retrieve, read, then aim hop two at the entity it just&lt;br&gt;
discovered. There's an MCP server included (&lt;code&gt;hubmesh-mcp&lt;/code&gt;, listed in&lt;br&gt;
the official MCP Registry) — the repo contains a field report of&lt;br&gt;
Perplexity driving a 3-hop chain through it, tool call by tool call.&lt;/p&gt;

&lt;h2&gt;
  
  
  When NOT to use this
&lt;/h2&gt;

&lt;p&gt;Single-hop corpora where similarity already wins; corpus-wide summary&lt;br&gt;
questions ("what are the main themes?") — that's community-summary&lt;br&gt;
territory (Microsoft GraphRAG's use case), a different query class.&lt;br&gt;
hubmesh is for multi-hop factual retrieval, and it deliberately keeps&lt;br&gt;
Qdrant as the geometry engine underneath.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Repo: github.com/DemigodDSK/hubmesh · PyPI: &lt;code&gt;pip install hubmesh&lt;/code&gt; · MIT&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rag</category>
      <category>qdrant</category>
      <category>python</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
