Ian Chou's Blog

雙路徑整合檢索(Ensemble Retrieval)提升 GraphRAG 種子節點品質:Vector + LLM + RRF 融合落地

雙路徑整合檢索(Ensemble Retrieval)提升 GraphRAG 種子節點品質:Vector + LLM + RRF 融合落地

GraphRAG 的成敗,常常不在「走圖」或「摘要」寫得多漂亮,而在更早的一步:Seed Nodes(種子節點)選得準不準

如果種子起點偏掉,後面的 graph expansion 很容易把錯誤放大(error propagation);反過來,起點夠準,1-hop/2-hop 擴散就會變成高效的「上下文補齊器」。

這篇把一個很實用的優化落地成可跑的工程實作:雙路徑整合檢索(Ensemble / Dual-Channel Retrieval)


問題:為什麼單靠 Vector/FTS 或單靠 LLM 都不穩?

路徑 A:向量/全文(既有做法)

路徑 B:LLM 選擇(新增做法)

結論很直接:把兩者並行整合,才更穩健


解法:並行雙路徑 + 融合(Fusion)選出 Top-K 種子

整體流程長這樣:

flowchart TD
  Q["Query"] --> A["Path A: Vector/FTS Hybrid"]
  Q --> B["Path B: LLM Seed Selector"]

  A --> A1["Seed Chunks"]
  A1 --> A2["Chunk -> Entity 聚合"]

  B --> B0["Candidate Entities"]
  B0 --> B1["LLM Select + Confidence"]

  A2 --> F["Fusion (RRF)"]
  B1 --> F

  F --> E["Top-K Entity Seeds"]
  E --> C["Entity -> Chunk 回推"]
  C --> G["Graph Expansion (hops)"]

要點是:Path B 不直接創造節點,而是在候選集合裡選;Fusion 不直接把 distance 跟 confidence 相加,而是用更穩健的排名融合。


落地關鍵 1:限制 LLM 的選擇範圍(避免幻覺)

LLM 路徑如果直接面對「全局節點列表」,常見三個問題:

因此實作採用「兩段式」:

  1. 先生成候選 entity 列表(Top-N)
    • 來源之一:Path A 從 top chunks 反推出來的 entities(高訊號、低噪音)
    • 補強:用 query tokens 對 entity 名稱做簡單命中,補回一些可能被向量漏掉的概念
  2. LLM 只允許從候選清單裡挑 Top-K
    • 並回傳 confidence(0..1)
    • 設定最低門檻(例如 <0.6 直接丟棄)

這樣做的本質是:用工程約束把「LLM 的創造力」改造成「LLM 的選擇能力」

補強:Query Token 命中 Entity Name(Keyword / Entity Linking Lite)

這個補強的目的很務實:向量相似度擅長語意,但不擅長精確指名。只要 query 裡出現了某個專有名詞(或縮寫/型號),你通常會希望「至少把這個節點放進候選集合」,否則後面再怎麼 rerank / 走圖都救不回來。

在這個 repo 裡,這件事已經落在 Search Server 端,做法是:

對應程式在 [server.py:get_query_tokens_for_entities](file:///home/ianc/GitHub/2511-Eleventy-blog/search/server.py#L97-L127) 與 [server.py:get_entity_candidates](file:///home/ianc/GitHub/2511-Eleventy-blog/search/server.py#L130-L187),核心概念是「便宜的字面命中 → 先把候選集補齊」。

tokens = get_query_tokens_for_entities(query)
for entity in entity_meta:
    token_hits = ...
    phrase_hit = ...
    if token_hits > 0 or phrase_hit:
        scored.append(...)

這裡有兩層用途:

另外,你也有一個更偏 chunk 層的「硬字面」護欄:當 query 很像單一 keyword 時,會優先走 FTS,並用 query in content 做 boost([server.py:keyword query](file:///home/ianc/GitHub/2511-Eleventy-blog/search/server.py#L521-L589))。它補的是「內容字面命中」,而上面這段補的是「entity 名稱命中」。

不足之處與補強建議(讓命中更準、更像你想要的 Hard Match)

上面這個版本很便宜、很有效,但它是「簡化版」:夠用,不一定最準。實務上常見幾個不足點,可以視資料量與需求逐步補強:

  1. 缺少 alias / 同義詞
    目前 entity name 主要來自抽取與 tag,小寫化後直接當唯一鍵。若你有別名(例如「OpenRouter」vs「Open Router」、縮寫、產品代號),建議在建圖時就把 alias 一併掛上,或在候選命中前先做 normalization。

  2. 子字串命中可能誤傷(false positive)
    現在判斷是 t in name,對英文短 token 或常見片段容易撞到不該中的 entity。常見做法是加護欄,例如:

    • token 長度門檻(太短的不算)
    • 停用詞過濾(尤其是中英混雜時)
    • 英文用 word boundary(避免 go 命中 mongodb 這種)
  3. 多詞實體(phrase)需要更好的匹配
    例如 entity 是「leiden algorithm」,query 可能只出現「leiden」,你會希望能命中;但同時你也會希望「完整片語出現時」能拿到更強的加權。這可以用 n-gram(把 query tokens 組 2-gram/3-gram)或 Aho–Corasick 之類的多模式匹配做 phrase scan。

  4. Hard Match 的介面(本 repo 已落地)
    這個 repo 已把「完整命中 entity 名稱」獨立成 Path C,並在融合時給更高權重,讓「使用者點名」更像你想要的 Hard Match。對應程式:

    • [server.py:hard_match_entities](file:///home/ianc/GitHub/2511-Eleventy-blog/search/server.py#L214-L247)
    • [server.py:rrf_fuse_multi](file:///home/ianc/GitHub/2511-Eleventy-blog/search/server.py#L202-L212)
    • Local Search / Chat 的融合入口都會把 rank_c 併入融合(同時可用 HARD_MATCH_ENTITY_TOP_K 控制上限,避免 seed 爆炸)

落地關鍵 2:分數融合別硬加,先解決不可比性

向量搜尋給你的是 distance / relevance;LLM 給你的是 confidence。這兩者尺度不同,硬相加會造成:

因此採用 RRF(Reciprocal Rank Fusion):只用排名,不用原始分數,對尺度差異很穩。

概念上是:


落地關鍵 3:把 entity seeds 轉回 chunk seeds(才能接上既有走圖)

本專案的走圖擴散入口是從 chunks 出發(chunk → entity → chunk),所以融合後的 Top-K entity 需要回推成 chunk seeds:

這一步的好處是:你可以用 LLM 把概念對齊做在 entity 層,最後仍回到 chunk 層去拿可引用的上下文證據


在這個 repo 的具體實作(你可以直接跑)

本次落地包含兩部分:

  1. Search API 端點加入 ensemble seed selection
    • Local Search:先 hybrid,再用 ensemble 生成額外 seeds,最後做 graph expansion
    • Chat:在既有 rerank 後的 seed_df 上做同樣的 ensemble,再走圖補上下文
  2. 文件更新:把這個步驟寫入 core features checklist,讓架構圖與依賴關係一致

程式碼位置:


護欄(Guardrails)清單:把不穩變成可控

這類設計在工程上最怕的不是「做不出來」,而是「做出來但不可控」。實務上建議至少要有:

只要護欄齊了,這套雙路徑架構就能在成本、穩定性、品質之間取得平衡。


總結:這是一個從「堪用」到「好用」的升級點

雙路徑整合檢索(Ensemble Retrieval)的核心價值很單純:

如果你正在做 GraphRAG,卻常覺得「走圖看起來很厲害,但答案就是差一點」,十之八九是 seed 品質不夠。從這一步下手,通常會是投資報酬率最高的優化。