實戰 Agentic RAG 與 Hybrid Search

實戰 Agentic RAG 與 Hybrid Search

📌 本文重點

  • 單一檢索策略讓 RAG 在真實場景很容易翻車
  • Hybrid Search 能互補向量與關鍵字的盲點
  • 讓 Agent 負責檢索策略與多輪重試能顯著提升穩定度
  • 不換模型也能透過 eval、Hybrid 與權限控管大幅升級 RAG

在實際專案裡,多數 RAG 翻車不是因為模型不夠聰明,而是檢索策略太單一:只用向量會被專有名詞和代碼玩死,只用關鍵字又抓不到語義相近的長文件內容。Agentic RAG + Hybrid Search 的組合,重點就是讓「檢索」變成可調度、可重試、可觀測的一級公民,而不是一個寫死的 search(query) 函式。


重點說明

1. 為什麼單一檢索在真實專案會翻車?

常見四種翻車場景:

  1. 長文件 / 手冊
  2. 只用向量:整份手冊被切成很多 chunk,語義太接近,top-k 都很像,但真正要的那一段不一定排前面。
  3. 只用 BM25:查詢句子太口語,關鍵字重疊度不高,直接 miss。

  4. 專有名詞 / 法規條文 / 內部代號

  5. 向量模型常常把 DS-104DS-140 當成類似,專案實際上兩者完全不同。
  6. 法規編號、API 名稱、Ticket ID 等,關鍵字檢索反而更穩

  7. 程式碼、表格、錯誤訊息

  8. 向量對縮排、符號、stack trace 的敏感度很差。
  9. Log ID 或錯誤碼這類「硬字串」,BM25/關鍵字幾乎是必要條件

  10. 跨語言 / 口語查詢

  11. 使用者用自然語言描述問題,文件是正式用語或英文,公司內還混雜縮寫。
  12. 需要先用 LLM 做 query 改寫,再讓向量與 BM25 各自發揮。

Hybrid Search(向量 + BM25) 的實際好處:

  • 可以 補各自的盲點:專有名詞用 BM25 鎖定,模糊描述用向量補齊。
  • 可以針對不同類型文件設定 權重策略(例如法規 > 內部 wiki > Slack 摘要)。
  • 可以後面用 rerank 模型 做第二次排序,穩定提升回答可靠度。

💡 關鍵: 單一檢索在長文件與專有名詞場景很容易漏抓關鍵內容,Hybrid Search 能同時顧到語義相似與精確字串匹配,明顯降低 RAG 翻車率。


2. 讓 Agent 負責檢索策略,而不是把檢索寫死

典型 Agentic RAG 設計:

  • 一個 Orchestrator Agent(對話主控)
  • 多個 retriever 工具keyword_retrievervector_retrieverhybrid_retrieverlegal_retriever

Agent 的工作不是「自己產生答案」,而是先判斷:

  1. 要用哪種檢索策略?
  2. 查錯誤碼或 ticket:優先 關鍵字 → 再向量精抽
  3. 問概念解釋:優先 向量 → 再用 BM25 找原始定義
  4. 法規/權威階層:改用 分層 retriever(例如每一層級至少取 1–2 筆)

  5. 要不要改寫 Query?

  6. 第一輪命中文件相關度低時,讓 Agent 自動:

    • 摘出關鍵詞
    • 加上同義詞 / 全名(例如:DSData Steward
    • 限縮 domain(例如:限定 product=core-banking
  7. 多輪重試與合併結果

  8. 第一輪檢索後如果信心不足(例如 top-3 相似度都 < 0.7),
  9. Agent 改寫 query 或更換 retriever,再抓一次,最後合併去重後送入 LLM。

這樣做的實際好處:

  • 檢索策略可迭代:只要調整工具 / prompt,不必重寫服務架構。
  • 容易在線上 A/B:只換掉 Orchestrator 的 prompt 或 routing 邏輯即可。
  • 可以針對不同客戶 / 部門掛不同的工具組合。

一個可落地的組合:

  • LLM:OpenAIgpt-4.1)、Anthropicclaude-3.7)皆可
  • 檢索:
  • Elasticsearch:BM25 + dense vector + hybrid score
  • Weaviate / Qdrant:向量 + keyword filter

簡化架構:

User → Orchestrator Agent → (tool calls) →
  - keyword_retriever (Elasticsearch BM25)
  - vector_retriever  (Weaviate / ES dense vector)
  - hybrid_retriever  (ES rank_feature / script_score)
→ merge + dedup + rerank → LLM answer

實作範例

1. Orchestrator Agent Prompt(決定使用哪個 retriever)

假設用 OpenAI Assistants API 或自行封裝 tools:

系統指示(Orchestrator):
你是一個檢索協調代理,負責從多種檢索器取得最相關的企業知識。

- 若使用者詢問:
  - 錯誤碼、ticket ID、法規條號、API 名稱 → 優先使用 **keyword_retriever**。
  - 抽象概念、最佳實踐、流程說明 → 優先使用 **vector_retriever**。
  - 法律 / 合規問題,且需多層級來源 → 使用 **legal_hybrid_retriever**。

流程:
1. 先決定要呼叫哪些工具(可以多個)。
2. 若第一輪檢索結果的「來源數量 < 3」或「相關度評估偏低」,
   - 自行改寫查詢(更精簡、加入關鍵字),再重試一次。
3. 最終將所有檢索結果去重、排序,回傳給後續回答模型。
禁止自行編造公司內部資料,所有答案必須可追溯到文件片段。

索引 mapping:

PUT knowledge_base
{
  "mappings": {
    "properties": {
      "content": { "type": "text" },
      "content_vec": { "type": "dense_vector", "dims": 1536, "index": true },
      "source_type": { "type": "keyword" },   
      "tenant_id": { "type": "keyword" }
    }
  }
}

簡化版 hybrid 查詢(BM25 + 向量):

POST knowledge_base/_search
{
  "size": 20,
  "query": {
    "script_score": {
      "query": {
        "bool": {
          "must": [
            {"match": {"content": "GDPR data retention"}},
            {"term": {"tenant_id": "acme_corp"}}
          ]
        }
      },
      "script": {
        "source": "0.6 * _score + 0.4 * cosineSimilarity(params.q_vec, 'content_vec')",
        "params": {"q_vec": [/* query embedding */]}
      }
    }
  }
}

關鍵點:

  • BM25 與向量權重(例子中 0.6 / 0.4)要透過線上 A/B 或離線 eval 調整。
  • tenant_id filter 做多租戶權限隔離,非常重要。

💡 關鍵: 在同一個查詢裡用 script score 同時結合 BM25 分數與 cosine similarity,能控制兩者權重,調整出最適合自己資料分佈的 Hybrid 策略。


3. Chunking 與 max context 的工程細節

基本原則:

  • 語義切分(semantic splitting)+ 適度 overlap 為主,而不是死切 512 tokens
  • 避免 chunk 過長導致:
  • 向量語義太混濁,top-k 噪音變高。
  • LLM context 塞滿 retrieval 噪音,回答變模糊。

實作骨架(pseudo-code):

from semantic_splitter import split_semantic

def chunk_doc(text: str):
    sections = split_semantic(text, max_chars=1200)
    chunks = []
    overlap = 150  # 字元級 overlap
    for sec in sections:
        if len(sec) <= 1200:
            chunks.append(sec)
        else:
            # 針對長 section 再做 sliding window
            for i in range(0, len(sec), 1200 - overlap):
                chunks.append(sec[i:i+1200])
    return chunks

max context tokens 的關係:

  • 假設 LLM context 32k,系統 prompt + 對話占 4k,其實留給 RAG 的只有約 28k
  • 若每個 chunk 約 400 tokens,你實際能塞 約 50–60 個 chunk 就爆,但通常 8–16 個 chunk 就夠,更多只會拉高成本與噪音。

rerank 與去重

  • 先取寬一點的 top_k(例如 30–50),再用輕量 rerank(如 bge-reranker)縮到 8–12 個。
  • 去重邏輯可以用:same doc_id + 高度相似 直接只留一個,減少重複內容浪費 context。

💡 關鍵: 雖然 context 可能有 32k tokens,但實務上只保留約 8–16 個高質量 chunk,通常就能兼顧成本與效果,塞太多反而害答題品質下滑。


建議與注意事項

1. 不要只做 embedding,不做 eval

常見錯誤流程:

  1. 把全部文件 embed → 塞進向量庫 → 上線。
  2. 發現回答怪怪的 → 開始懷疑模型。

比較健康的流程:

  1. 先準備一組 標記好的 QA/Eval 集10–50 題也好)。
  2. 對同一組問題,分別跑:
  3. 純 BM25
  4. 純向量
  5. Hybrid + 不同權重
  6. 用簡單指標(hit@k、人工評分)挑一個 baseline,再上線 A/B。

2. 向量庫維護:重建 / 追加 / 版本化

  • Embedding 模型版本變更 時:
  • 盡量用新 index 重建(kb_v2),舊版保留一段時間做對照。
  • 不要在同一個 index 裡混不同 embedding 模型的向量。
  • 大量文件更新策略:
  • 批次追加新文檔時,要記錄 批次 ID / 資料版本,方便 rollback。
  • 下線文件要標記 is_active=false 或直接 soft delete,避免回答引用過期政策。

3. 多租戶與權限過濾

  • 在 Elasticsearch / Weaviate 中務必存:tenant_idvisibilityrole 等欄位。
  • 檢索 query 層一定要加:
"filter": [
  {"term": {"tenant_id": "${current_tenant}"}},
  {"terms": {"visibility": ["public", "internal"]}}
]
  • 不要指望 LLM 自己遵守權限,權限控制一定要在檢索階段完成。

4. 線上評測與 A/B 驗證

簡易做法:

  1. 選一組真實高頻 query(客服 ticket、搜尋 log)。
  2. 設計兩條路線:
  3. A:純向量 RAG
  4. B:Agentic RAG + Hybrid Search
  5. 隨機分流流量,收集:
  6. 使用者是否重問 / 追問率
  7. 是否需要人工接手
  8. CSR / domain expert 的 1–5 分主觀評分

通常在企業知識庫場景,只要加上 Hybrid Search + Agent 重試,就能看到 10–30% 的 query 成功率提升,而且失誤類型會明顯變少(比較少「答錯法規條」、「引用過期政策」)。


總結:如果你現在的 RAG 還是「單一向量庫 + top-k 塞給 LLM」,要提升穩定性,不一定要換更大的模型,先把 Hybrid Search 與 Agentic 檢索策略補上,通常是成本最低、效果最直接的升級路線。


🚀 你現在可以做的事

  • 從現有專案中抽出 10–50 則真實 query,分別用純 BM25、純向量與 Hybrid 跑一次,記錄 hit@k 與人工評分
  • 在現有 RAG 服務前面加一個簡單 Orchestrator,把關鍵字與向量檢索拆成兩個 tool,用 prompt 控制選用策略
  • 在搜尋層加入 tenant_idvisibility 欄位與 filter,先確保權限過濾正確,再進一步調整 Hybrid 權重與 rerank 策略

留言

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *