hubmesh 为 Qdrant 添加实体图路由,通过 SpaCy NER 将 Slack→Salesforce→Marc Benioff 的多跳关系显式建模,绕过纯向量相似度的词汇鸿沟。
Qdrant 能以毫秒级响应在十亿级规模上回答"哪些向量与这个查询相近"。但有一类问题,相近性根本就不是正确的评判标准:
"收购 Slack 的那家公司的创始人是在哪里读的大学?"
回答这个问题的段落讲的是 Marc Benioff 和 USC,压根没提 Slack。余弦相似度——任何相似度——都会把它排得很低,因为答案看起来根本不像问题。它与问题是相连的,隔了三个实体跳:Slack → 被 Salesforce 收购 → 由 Benioff 创立 → 在 USC 就读。这是一个拓扑问题,再快的 ANN 也解决不了拓扑问题。
hubmesh 是一个 MIT 的小型库,为现有的 Qdrant 集合叠加拓扑层。Qdrant 继续做它最擅长的(第一轮 ANN);hubmesh 在索引时构建实体–文档图,在查询时从问题的实体通过该图扩散 Personalized PageRank,将图的可达性与余弦得分融合。查询时不涉及任何 LLM——检索只是一次稀疏矩阵迭代,确定性,在 30K 文档语料库上大约 100ms。
pip install "hubmesh[qdrant,kg]"
python -m spacy download en_core_web_sm
索引:你的 Qdrant 集合 + 实体图
from hubmesh import Planner
from hubmesh.adapters import QdrantStore
from hubmesh.kg import build_entity_kg
import spacy
embed = ... # your embedding callable: text -> np.ndarray
# any of: in-memory, on-disk, or your running Qdrant server
store = QdrantStore.from_documents(docs, url="http://localhost:6333")
# entity-document graph via spaCy NER — zero LLM tokens to build
nlp = spacy.load("en_core_web_sm")
kg = build_entity_kg(store.get_many(store.all_ids()), nlp=nlp)
planner = Planner(store=store, kg=kg, nlp=nlp, embed=embed)
查询:扩散 + 相似度,融合
result = planner.retrieve(
"Where did the founder of the company that acquired Slack study?",
top_k=10,
)
for path in result.reasoning:
print(f"{path.score:.3f} " + " -> ".join(path.node_ids))
# 0.031 ent:slack -> doc:acquisition -> ent:salesforce -> doc:benioff_bio
这个 reasoning 字段不是事后解释——它是实际图路由,正是它把每个文档打捞上来,这意味着你的 RAG 流水线可以展示为什么某个段落被检索到。
每个候选文档获得三个信号的组合,归一化后合并(这个公式源自一篇网络拓扑论文——NNSI,ICOMP'25——同样的教训出现在那里:没有一个单一的中心性指标能识别重要节点,但加权组合可以):
Relevance——相对于 Qdrant 向量的余弦值(几何)
Structure——从问题实体扩散的 Personalized PageRank 质量(拓扑)
Convergence——每个实体扩散的几何均值,所以一个文档必须从问题中的每个锚点都可到达,而不是仅从一个锚点接收高分
已披露的权衡:convergence 项以 top-rank 精确率换取深度召回率——完整 dev 上 recall@2 比朴素方法低 0.75 分。如果用 top_k=2 检索,禁用它(use_convergence=False)。所有上方内容都可以用 benchmarks/ 中的脚本复现。
retrieve 接受 seed_entities 和 exclude_docs,所以一个 Agent 可以迭代:检索、读取、然后把第二轮跳对准它刚发现的实体。附带的有一个 MCP server(hubmesh-mcp,列在官方 MCP Registry 中)——仓库里有一份 Perplexity 通过它驱动 3 跳链的现场报告,一个工具调用接一个工具调用。
单跳语料库里相似性已经胜出;语料库级别的摘要问题("主要主题是什么?")——那是 community-summary 的领地(Microsoft GraphRAG 的用例),不同的查询类。hubmesh 面向多跳事实检索,它有意让 Qdrant 充当底层的几何引擎。
Repo: github.com/DemigodDSK/hubmesh · PyPI: pip install hubmesh · MIT