hubmesh

io.github.DemigodDSK由 GitHub 帳號 DemigodDSK 發布v0.4.2更新於 Oct 24, 2026

Deterministic multi-hop graph retrieval for RAG. Zero LLM calls in the query path.

概覽

AI 產生的概覽

在現有向量資料庫之上提供確定性、不呼叫大型語言模型的 GraphRAG 檢索規劃器,並暴露索引、多跳檢索、實體解析與圖形檢查工具。

可以做什麼
hubmesh 是一個介於現有向量資料庫與大型語言模型之間的 Python 檢索規劃器。其 MCP 伺服器透過 stdio 暴露維運工具:index_corpus、retrieve(可用種子引導)、resolve_entities、entity_neighbors、path_between、get_document、graph_stats 與 list_corpora。檢索會結合餘弦相關性與實體知識圖上的 Personalized PageRank,並依 token 預算打包結果,回傳來源與推理路徑。查詢路徑不呼叫任何大型語言模型,且具確定性。
適用情境
當語料上的多跳問題用樸素 top-k 向量檢索效果不佳,且希望由代理自行驅動迭代跳轉時使用。它適配現有的 Qdrant、Chroma 或記憶體內儲存,無需遷移。目前處於 pre-alpha 階段,建議先在自己的工作負載上評估。
執行需求
安裝 PyPI 套件 hubmesh 的 mcp 附加元件,KG 模式還需 spaCy 模型 en_core_web_sm。以本機 stdio 程序執行;語料以 JSON/NPZ 形式持久化於 ~/.hubmesh/corpora 下。若透過通道提供 SSE 服務,需要環境變數 HUBMESH_API_KEY,用戶端必須送出 Authorization Bearer 標頭。
安裝前請注意
通道服務需要 HUBMESH_API_KEY,且預設為唯讀;--allow-writes 會重新啟用寫入語料的 index_corpus。唯讀模式下 get_document 仍會回傳全文,因此未認證的通道入口會暴露私有語料。README 警告:為匿名流量注入 Authorization 標頭會讓所有呼叫者取得讀取權限。基準測試顯示,使用強嵌入時圖檢索層可能損害前段名次的召回。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 hubmesh,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

hubmesh

[tests] [Python] [License: MIT] [Release]

Centrality-aware GraphRAG retrieval planner. Drop-in layer over any vector DB.

hubmesh is a Python library that improves multi-hop RAG quality on top of an existing vector database. You don't replace your infrastructure — you add a smart planner between your vector DB and your LLM.

What problem this solves

Naive vector retrieval ("embed query, get top-k by cosine similarity") fails on multi-hop questions like "Where was the founder of the company that acquired Slack born?" The correct answer requires retrieving entities along a reasoning path, not the single most similar item.

GraphRAG and HippoRAG showed that running a small Personalized PageRank over a knowledge graph at query time can substantially improve multi-hop retrieval. hubmesh extends that line with two contributions:

  1. Entity-anchored seeding, multi-component ranking. In KG mode the PPR seeds are the question's own entities resolved against the corpus graph (alias index; falls back to the entities of the top cosine matches when the question names none); in kNN mode they are the ANN top-k. The multi-component score — cosine relevance, pooled PPR mass, and multi-anchor convergence, min-max normalized and fused 3:1:1 — is applied to the document ranking, not to seed choice.
  2. Budget-aware context packing. Once relevant entities are scored, pack them into the LLM's context window with explicit coverage and redundancy control rather than just truncating top-k.

Quickstart

In-memory (testing, small corpora)

python
from hubmesh import Plannerfrom hubmesh.adapters import InMemoryStore
embed = ...   # callable: text -> np.ndarraydocs = [...]  # list of Document or strings or dicts
store = InMemoryStore.from_documents(docs, embed=embed)planner = Planner(store=store, embed=embed)result = planner.retrieve(query="...", top_k=10, budget_tokens=4000)

Qdrant adapter (production)

python
from hubmesh import Plannerfrom hubmesh.adapters import QdrantStore
store = QdrantStore.from_documents(docs)                          # in-memorystore = QdrantStore.from_documents(docs, path="./qdrant_data")    # on-diskstore = QdrantStore.from_documents(docs, url="http://localhost:6333")  # remote
planner = Planner(store=store, embed=embed)result = planner.retrieve(query="...", top_k=10)

Chroma adapter

python
from hubmesh.adapters import ChromaStore
store = ChromaStore.from_documents(docs)                          # ephemeralstore = ChromaStore.from_documents(docs, persist_directory="./chroma_data")store = ChromaStore.from_documents(docs, host="localhost", port=8000)

Multi-hop / KG mode

python
from hubmesh.kg import build_entity_kgimport spacy
nlp = spacy.load("en_core_web_sm")kg = build_entity_kg(docs, nlp=nlp)
planner = Planner(store=store, kg=kg, nlp=nlp, embed=embed)   # embed= needed for text queriesresult = planner.retrieve(query="Where was the founder of the company that bought Slack born?",                          top_k=10, budget_tokens=4000)
# RetrievalResult includes reasoning paths showing why each doc was returnedfor path in result.reasoning:    print(f"  score={path.score:.3f}  {' → '.join(path.node_ids)}")

LLM-extracted KG (richer than spaCy)

python
from hubmesh.kg_llm import build_entity_kg_llmfrom hubmesh.entity_linker import EmbeddingLinker, make_st_embedder
def llm(prompt):  # provider-agnostic — bring your own    return your_llm_call(prompt)
kg = build_entity_kg_llm(docs, llm=llm, cache_path="kg_cache.json")
# optional: cross-document entity dedup — same Linker protocol as the spaCy pathkg = build_entity_kg_llm(docs, llm=llm, cache_path="kg_cache.json",                         linker=EmbeddingLinker(embed=make_st_embedder()),                         llm_identity="gpt-5-mini")   # namespaces the cache
planner = Planner(store=store, kg=kg, nlp=nlp, embed=embed)

Better entity linking

python
from hubmesh.kg import build_entity_kgfrom hubmesh.entity_linker import EmbeddingLinker, make_st_embedder
# Cluster surface variations: "United States" / "U.S." / "USA" → one entitylinker = EmbeddingLinker(embed=make_st_embedder(), threshold=0.82)kg = build_entity_kg(docs, linker=linker)

Iterative multi-hop: let your agent drive

python
r1 = planner.retrieve(query=question, top_k=5)
# your agent reads r1, spots the bridge entity, then aims hop 2 at it:r2 = planner.retrieve(    query=question, top_k=5,    seed_entities=["Nimbus Analytics"],           # merged with the query's own seeds    exclude_docs=[s.doc.id for s in r1.sources],  # don't re-retrieve consumed docs)

Seed mentions resolve through the alias index, so free-text entity names work. The query path stays deterministic and LLM-free — the planning intelligence lives in the caller.

MCP server: plug hubmesh into any agent

bash
pip install "hubmesh[mcp]"python -m spacy download en_core_web_sm
json
{"mcpServers": {"hubmesh": {"command": "hubmesh-mcp"}}}

Exposes the planner as deterministic operator tools over stdio — index_corpus, retrieve (seed-steerable, as above), resolve_entities, entity_neighbors, path_between, get_document, graph_stats, list_corpora. Your agent is the solver: it decomposes the question, reads each hop, and aims the next one; the server answers in milliseconds with zero LLM calls. Corpora persist as plain JSON/NPZ under ~/.hubmesh/corpora.

The server warms up models and persisted corpora in the background at launch (~5-10s on first run), so tool calls stay fast from the start — relevant for strict-timeout connector clients (Perplexity, etc.).

For web-based connector clients, serve SSE natively — no gateway process needed:

bash
export HUBMESH_API_KEY="$(openssl rand -hex 24)"   # any strong secrethubmesh-mcp --transport sse --port 8000 --allow-tunnelngrok http 8000     # paste https://<your-url>/sse into the connector

Tunneled serving requires the API key (the server refuses to start without one) and defaults to read-only — pass --allow-writes to keep index_corpus enabled. Clients must send Authorization: Bearer <key>. If your connector client cannot set headers, the tunnel edge must authenticate callers itself (ngrok OAuth / IP-restriction traffic policy, Cloudflare Access, …) before it adds the upstream header — injecting the header for anonymous traffic hands every caller full read access (read-only protects corpora from replacement, not from disclosure; get_document returns full text). A client that can neither send the header nor sit behind an authenticating edge is unsupported for private corpora.

Tunnel field notes (from a live Perplexity integration): ngrok works (free tier included); cloudflared quick tunnels buffer SSE bodies and hang tool calls; supergateway is unnecessary here and crashes on reconnect. --allow-tunnel accepts the tunnel's forwarded Host header — without it, proxied requests get 421 Misdirected Request.

Full field report — setup, error decoder, a 9/9 test battery run through Perplexity, and two findings about reasoning-model behaviour — in docs/perplexity.md.

Chunking long documents

python
from hubmesh import chunk_by_sentences, chunk_documents
chunks = chunk_documents(    [{"id": "doc1", "text": long_text}, ...],    strategy="sentences", target_tokens=200,)# Then embed chunks and index normally

Installation

bash
pip install hubmesh                   # corepip install "hubmesh[qdrant]"         # Qdrant adapterpip install "hubmesh[chroma]"         # Chroma adapterpip install "hubmesh[kg]"             # entity-linked KG (spaCy)pip install "hubmesh[linker]"         # embedding-based entity linkerpip install "hubmesh[all]"            # everythingpython -m spacy download en_core_web_sm   # required for KG mode

Design

KG mode — the benchmarked, production path:

query ─► spaCy NER ─► alias index ─► entity seeds ─► Personalized PageRank over the corpus KG  │                   (fallback: entities of the top-3 cosine documents)              │  └───────────► cosine similarity against every document ─────────────────────────────┤                                                                                      ▼            3·minmax(cosine) + 1·minmax(pooled PPR) + 1·minmax(per-anchor geomean)   [weighted sum]                                                                                      ▼                          budget-aware packing ─► context + sources + reasoning paths

kNN mode (no KG; prototyping): first-pass ANN → capped induced proximity subgraph → PPR from the ANN seeds → the same scoring and packing. Community anchoring exists for single-topic retrieval and is off by default.

Each layer is independently testable and replaceable. Adapters wrap your existing vector DB so you don't have to migrate — note that KG mode scores every document (vectors are gathered once per store version and cached) and uses the store's ANN index only for the seed fallback. The full design, including the store contract, persistence, the serving security model and measured scaling limits, is in docs/ARCHITECTURE.md.

Benchmarks

Supporting-fact paragraph recall over pooled distractor corpora. Every row is a separate experiment: document representation and embedding model change the absolute numbers materially, so rows are never compared across representations. Protocol, ablations and limits are in BENCHMARKS.md.

Full HotpotQA dev (7,405 questions, 66,581 pooled paragraphs), hubmesh vs naive cosine, v0.4 defaults:

representation · embeddingnaive @10hubmesh @10Δ @10Δ @5Δ @2
body only · MiniLM-L669.3%75.2%+5.90+4.21−0.75
title+body · MiniLM-L670.0%77.3%+7.24+5.88+0.25
title+body · bge-m383.5%84.8%+1.38−1.41−9.09

Read both directions. With a small embedding the graph layer adds 5–7 points of depth recall; with a strong one the depth gain shrinks to +1.4 and the defaults hurt the top ranks (−9.1 at recall@2). The convergence term trades top-rank precision for depth: for top-2/top-5 workloads on strong embeddings use use_convergence=False or plain cosine, and evaluate on your own workload before turning the graph layer on everywhere.

Full MuSiQue-Ans dev (2,417 questions, MiniLM, body only), hubmesh vs naive, recall@10 with paired 95% CIs: +3.67 [+3.00, +4.39] overall (+2.2 at @2, +3.3 at @5); by hop count +2.9 / +4.1 / +5.3 (n = 1,252 / 760 / 405). The gain grows with hop count, and on MuSiQue hubmesh beats naive at recall@2 as well.

What the scoring adds (HotpotQA N=500, body only, recall@10): on the same graph, seeds, fallback and packer, cosine-fused scoring reaches 0.871 against 0.676 for the pure structural (PPR-only) signal — +19.5 pts [+16.3, +22.7] (MuSiQue N=300: +16.3). Earlier versions quoted +29.8 against a HippoRAG-style ranker; that comparison also changed the pipeline and is no longer cited as scoring attribution.

Convergence term (default on): +0.9 pts @10 over convergence-off on full MuSiQue dev and +1.1 on HotpotQA N=500. A single-solve log-pooled signal in the same slot matches it in aggregate; the geomean keeps ~1 pt at three and four hops. Multi-seed queries cost ~1.5–2× (still zero LLM tokens, deterministic).

Latency: ~22 ms mean / 26 ms p95 per query on a 7K-node KG (after PPR matrix caching). ~3 s/query was measured at the 66K-paragraph full-dev scale with convergence on, before the per-query vector re-gather was removed; that scale has not been re-measured since.

Reproduce (each run writes a JSON with per-query records and a manifest carrying the commit, dirty flag and source/harness content hashes):

bash
python benchmarks/run_hotpotqa.py --n 500 --kg --out hotpot.jsonpython benchmarks/run_musique.py  --n 300 --kg --out musique.jsonpython benchmarks/run_ablation_coherence.py --dataset musique --n 2417python benchmarks/profile_query.py        # latency profile

Status

Pre-alpha (v0.4.2). Core algorithms implemented and validated; adapters for in-memory, Qdrant, and Chroma; entity-linked KG with both spaCy NER and LLM-based extraction (both linker-aware); alias-indexed entity resolution; NNSI-KG scoring (multi-source convergence default-on, hub-discounted PPR opt-in); agent-driven iterative multi-hop via seed_entities / exclude_docs; MCP operator server (hubmesh-mcp, native SSE) with JSON/NPZ corpus persistence; document chunking; reasoning-path explanation; PPR-cache latency optimisation. Pinecone / pgvector / Weaviate adapters and additional multi-hop benchmarks are tracked as good first issues.

License

MIT

來源:README.md,提交 de4f605

工具

0
SourceWeft 尚未在沙箱中啟動過該軟體包,因此其工具未知。

版本歷史

1
  1. v0.4.2最新Sep 16, 2026

評分

已滿足適用檢查項的 50%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分