iT邦幫忙

2025 iThome 鐵人賽

DAY 23
0

到目前為止,我們的搜尋引擎已經可以把資料載入(Day 20)、解析、索引(Day 21),甚至在 Day 22 嘗試了一些基本的文字查詢。

很多時候,我們並不是在「精確地查一個詞」,而是在「讓系統懂我們想問什麼」。

** 問題背景:人類查詢 vs 系統查詢**

例如使用 JEOPARDY! 資料集時,一個使用者可能輸入:

“Which US president was assassinated in 1865?”

這個問題其實背後包含多層意圖:

  1. 使用者在問一個「歷史事實」
  2. 他期望系統能找到「Abraham Lincoln」
  3. 系統要知道「assassinated」與「killed」是同義詞
  4. 甚至要理解「1865」是事件發生的年份

這就不是單純的 keyword search 可以解決的事,而是需要一種**多階段查詢(multi-stage query)**設計。

** 多階段查詢設計思路**

以我們的 JEOPARDY! 搜尋服務為例,可以拆成以下階段:

  1. 解析使用者輸入
    • 從 query string 中拆出「主題詞(entities)」與「動詞意圖(intent)」
    • 例如 “assassinated in 1865” → 主題:「US president」、動作:「assassinated」、時間:「1865」
  2. 語意擴展(Semantic Expansion)
    • 用簡單的同義詞庫或 embedding(之後可能會接 AI 模型)
    • 例如把 “assassinated” 擴展為 [“killed”, “murdered”]
  3. 組合查詢條件
    • 把前兩步的結果轉換為 Elasticsearch 的複合查詢(bool query)
    • 例如:
{
  "bool": {
    "must": [
      { "match": { "question": "US president" }},
      { "match": { "question": "killed" }},
      { "match": { "question": "1865" }}
    ]
  }
}

  1. 重排序(Re-ranking)
    • 把初步查出的結果再依照語意相似度或欄位權重調整排序。
    • 例如:answer 欄位中的 “Abraham Lincoln” 出現次數高的結果會往上排。

實作範例

下面是一個簡化的 Go handler,展示如何把上述流程包成 pipeline:

func multiStageSearchHandler(w http.ResponseWriter, r *http.Request) {
    q := r.URL.Query().Get("q")
    if q == "" {
        http.Error(w, "missing query parameter: q", http.StatusBadRequest)
        return
    }

    ctx := r.Context()

    // Step 1: 基本語意分析(之後可以改用 OpenAI embeddings)
    keywords := expandSynonyms(q)

    // Step 2: 組合查詢
    esQuery := buildElasticsearchQuery(keywords)

    // Step 3: 向 Elasticsearch 查詢
    results, err := esClient.Search(ctx, esQuery)
    if err != nil {
        log.Printf("ES search error: %v", err)
        http.Error(w, "search failed", http.StatusInternalServerError)
        return
    }

    // Step 4: 結果回傳
    json.NewEncoder(w).Encode(results)
}

為什麼要這樣設計?

多階段查詢的核心,其實是把「人的模糊語意」翻譯成「電腦能理解的結構化條件」。

這樣的分層架構帶來幾個明顯好處:

  • 易於擴充:未來可以在某個階段加入 NLP 模型,不需要改整個流程。
  • 可觀察性高:能清楚看到哪一階段出錯(是解析錯、查詢錯還是排序錯)。
  • 彈性強:可以針對不同場景(如 quiz、FAQ、產品搜尋)替換部分邏輯。

Recap

模組 功能 關聯
資料載入(Day 20) 把 Jeopardy CSV 轉成可查詢的 NDJSON 為查詢提供原料
索引建立(Day 21) 建立 ES 結構 為複雜查詢鋪路
基本查詢(Day 22) 可搜尋 question 與 answer 欄位 驗證基礎連線與功能
多階段查詢(Day 23) 加入語意層次與條件組合 讓搜尋更貼近「人」

結論

如果把搜尋引擎比喻成一位聰明的問答助理,前幾天的工作是在教他「資料在哪裡」與「怎麼找」。

今天的任務,則是在教他「怎麼聽懂人話」。


上一篇
Day 22 - 備份與還原 (Snapshot & Restore)
下一篇
Day 24 - 容器化:用多階段 Dockerfile 打造雲原生的第一步
系列文
用 Golang + Elasticsearch + Kubernetes 打造雲原生搜尋服務24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言