iT邦幫忙

2025 iThome 鐵人賽

DAY 27
0
生成式 AI

阿,又是一個RAG系列 第 27

Day26: Online Retriever Evaluator

  • 分享至 

  • xImage
  •  

Intro

延續昨天的討論:

  • 目前主要的問題集中在 Retriever 階段
  • 我們可以透過 Context_Relevancy 來驗證 Retrieval 結果,這個方法不需要參考答案也能使用
  • 我們已在 day8 學過如何使用 Tavily

基於上述觀察,一個自然的想法是:在一開始使用 Wiki 檢索後,就立即驗證是否有找到相關結果

  • 若沒有找到,則改用 Tavily
  • 若找到,則將結果直接送往最終回答階段

這種在推理階段(inference time)就直接完成驗證的方式,我們稱為 online

  • 與我們先前做的 offline 方法不同,offline 在整個檢索或生成流程結束後才進行驗證
  • 而 online 則是在推理過程中即時決定是否切換檢索工具或處理結果

Task

今天的任務一樣是三步:

  1. 首先我們 prompt llm 針對找到的結果進行評估,若評估已經可以回答,則直接複製一份 context,若是認為仍缺乏答案,我們要求 llm 產生 tavily 的檢索 query
  2. 接著我們視回傳調用 tavily 工具,或直接產生最後的答案
  3. 最後我們依照前天的 offline 驗證步驟,再看一次結果

Action

1. online evaluator

prompt

首先這是我們 online evaluator 的 prompt:

from llama_index.core.prompts import PromptTemplate

RETRIEVAL_EVALUATOR_PROMPT = PromptTemplate(
    template="""你是一個具備判斷 retrieval 資料完整性與可靠性的助手。
輸入:
- query(string):一題單選題(例如 題目: 常見針灸配穴法中,所指的「四關穴」,為下列何穴位之組合?\n選項:\n A: 上星、日月\n B: 合谷、太衝\n C: 內關、外關\n D: 上關、下關\n)
- context(string):經過 Wiki 或其它來源檢索到的 text 資料,用來支持回答這個 query
你的任務是:
1. 判斷這個 context 是否已經足夠用來「正確地回答」這個單選題(correct),或是不行(incorrect)、或是有模糊性(ambiguous)。
2. 輸出一個 JSON 物件,格式如下:
{
  "verdict": "<correct 或 incorrect 或 ambiguous>",
  "next_query": "<可選的重寫 query,用於進一步檢索;當判斷是 correct 時可為空字串或 null>"
}
* 若判斷為 **correct**,表示 context 足夠回答問題,`next_query` 可以設為 `""` 或 `null`。
* 若判斷為 **incorrect**,表示 context 與題意不匹配、或根本無法支撐任何選項,`next_query` 要提供一個你幫我重寫(或擴增)的 query,我會拿它去做新的檢索。
* 若判斷為 **ambiguous**,表示 context 有部分相關性但仍有不確定性,或答案可能不唯一,這時 `next_query` 也是必須的:你提供一個 query,用來補充搜索。未來當我拿新的檢索結果補充時,我會把原 context 與新的結果 concat 在一起,再讓你重做這個判斷流程。
**注意事項:**
* `判斷標籤` 的三種情況應該代表不同策略:
    • **correct**:context 已經明確支持某個選項,沒有爭議或缺漏
    • **incorrect**:context 與題目方向錯誤(例如回答了另一個問題、講了題目沒有問的範圍)
    • **ambiguous**:context 涉及題目相關內容,但缺少關鍵細節或多種可能性尚未排除
* 在決定 `next_query` 時,盡量把 query 重寫得更具體/更具針對性(可加入關鍵詞)以便後續檢索能補足缺口
* 輸出 JSON 時,**只有這兩個欄位**,不要額外說明或多餘包裝文字
---
### 範例
query = "哪一年諾貝爾獎創立?"
context = "諾貝爾獎是瑞典的獎項,始於十九世紀末…在 1901 年頒發第一屆。"
輸出:
{
  "verdict": "correct",
  "next_query": null
}
query = "哪一年諾貝爾獎創立?"
context = "諾貝爾獎由阿爾弗雷德·諾貝爾設立"
輸出:
{
  "verdict": "ambiguous",
  "next_query": "諾貝爾獎 第一屆 頒發 年份"
}
若 context 完全與題目無關,標記為 incorrect,並重寫一個可能的 query。
----------以下開始
query = {query}
context = {context}"""
)

evaluator result

這邊我們把 context relevancy 的結果區分為三種情況:

  • correct: context 已經包含足夠回答單選題的所有資訊

    • 這個時候我們會直接複製一份前面的 context 到最後
  • ambiguous: context 可能有相關,但仍然沒辦法回答問題

    • 這時候我們會要求 llm 同時回復 next_query 用來進行下一輪的檢索
    • 我們會把前面的 context 與 next_query 的結果 concat 再一起送到最後進行回答
  • incorrect: 前一步的檢索與答案完全無關

    • 我們仍要求 llm 產生 next_query
    • 我們會把前面的 context 直接刪除,僅使用新一輪 tavily 檢索到的結果做為新的 context
  • 這邊這個 correct, ambiguous, incorrect 的想法來自 Corrective RAG

  • 我們直接開啟 json mode 方便我們後續的處置,以下是執行結果:

{'verdict': 'incorrect', 'next_query': '四關穴是哪些穴位組合?(查詢:四關穴 合谷 太衝 內關 外關 上星 日月 上關 下關)'}
{'verdict': 'correct', 'next_query': None}
{'verdict': 'correct', 'next_query': None}
{'verdict': 'incorrect', 'next_query': '外丘 穴位 國際譯名 國際編號 WHO 外丘 (Waiqiu) 編號 GB33 GB34 GB35 GB36'}
{'verdict': 'ambiguous', 'next_query': '陰維脈 郄穴 是 哪一個?(交信、築賓、府舍、腹哀)'}
{'verdict': 'incorrect', 'next_query': '足太陰脾經 络穴 哪一穴?地機 漏谷 公孫 太白'}
{'verdict': 'ambiguous', 'next_query': '《難經·六十八難》 心下滿 主何穴? 前谷 液門 湧泉 跗陽'}
{'verdict': 'correct', 'next_query': None}
{'verdict': 'incorrect', 'next_query': '臑會、顴髎、秉風、聽宮、耳門 哪些屬於三焦經與膽經的交會穴'}
{'verdict': 'ambiguous', 'next_query': '通里穴 (HT5) 是否屬於 馬丹陽 天星十二穴?請列出馬丹陽天星十二穴的完整清單或來源。'}
  • 完整結果在: 這裡
    • 主要查看 evaluator_result: 與對應的 verdict 以及 next_query

合成新一輪的 context:

  • 這邊我們會保留 tavily 的 url 供後續查訪,同時直接把檢索結果 concat 在一起
  • 主要就是兩個判斷:
    • 如果是 incorrect 與 ambiguous:才要進行檢索,檢索回來的結果拼成一個 context,至於 correct 的情況,由於沒有新一輪的檢索,因此 context 暫時留空
    • 如果是 incorrect 的情況: old_context 留空,否則取出舊有的 old_context 與新的 context 拼接在一起
  • 以下為對應程式碼:
def get_tavily_context(tavily_response):
    txt = ''
    rvs = []
    for doc in tavily_response:
        txt+=doc.text_resource.text
        txt+='\n\n'
        rvs.append({
            'url': doc.metadata['url'],
            'txt': doc.text_resource.text
        })
    return txt, rvs

rvs = []
for data in tavily_source:
    rv = data.copy()
    verdict = rv['evaluator_result']['verdict']
    if verdict in ['incorrect', 'ambiguous']:
        next_query = rv['evaluator_result']['next_query']
        tavily_response = tavily_tool.search(next_query, max_results=5)
        context, response = get_tavily_context(tavily_response)
        rv['tavily_response'] = response
    else:
        context = ''
        rv['tavily_response'] = None

    if verdict == 'incorrect':
        old_context = ''
    else:
        old_context = rv['context']

    context_v2 = context + old_context
    rv['context_v2'] = context_v2
    rvs.append(rv)

2. Synthesizer

  • 這邊是要求參考 context 結果來回答的 prompt
ANSWER_PROMPT_WITH_CONTEXT = PromptTemplate(
    template="""
你是一個中醫考題專家,請根據下面的題目回答單選題。

請遵守以下規則:
1. 嚴格依據提供的參考資料 context 作答。
2. 輸出 JSON 格式。
3. JSON 需包含兩個 key:
   - "ans" :只回答單選答案 (A/B/C/D)
   - "feedback" :簡短說明為什麼選這個答案
4. 不要加入題目之外的說明或其他文字。

題目:
#{query}

參考資料 (context):
#{context}

請直接輸出 JSON:
"""
)
  • 本質上和昨天的回答階段一致

結果為:

  • gemma 從 答對 6 題 進步為 答對 7 題
    • 看起來實屬沒什麼改變...

我們來檢查看看 evaluator 的結果

3. Offline Evaluator

  • 我們這邊會跑 Faithfulness Score 作為 x 軸,以及 Context Relevancy Score 作為 y 軸

  • 用藍點標示答對的題目、用紅點標示答錯的題目
    我們直接看圖:
    https://ithelp.ithome.com.tw/upload/images/20251011/20177855q2SrZGq41x.jpg

  • 這張圖的所有自評結果都給出了不低的分數,尤其是 Faithfulness,基本上全部都是給出 1 分

  • Context Relevancy Score 最低的也有 0.6,而且那題還是答對的情況

  • 所有的點都集中在右上角這個情況,屬於是我們的 evaluator 失效的情況

  • 但我們仍然可以稍微推測一下:

    • 首先是引入tavily前與引入tavily後,context relevancy 的結果實有比較巨大的改變,這讓我們知道現階段 retrieval 的結果其實已經有了增強
      • 但我也很疑惑 tavily 真的有這麼強嗎
    • 我們前述的處理都是直接 concat 檢索回來的結果,因此直接的想法是 node 的資訊太雜亂
  • 我們明天來把 label-stduio 標註工具架起來,實際看一下這個情況究竟是怎麼回事

Summary

  • 我們今天架設了一個 online 的 evaluator 會直接判斷 wiki 檢索回來的結果有沒有我們要的資料
    • 結果分成三種可能:完全不相關、部分相關但不夠、已經夠了,會有各自的應對方式
    • 同時,若判斷資訊不夠,我們要他提供新一輪的 query 讓我們進行新一輪的檢索
      • 新產生的query 把正確與錯誤的可能都放在一起,這有可能比較好,也有可能不太 work
  • 這回的 offline evaluator 給出了前面沒有發生的結果:
    • 查回來的內容跟問題有關,答案也有照著查回來的 context 回答,但麻煩的是錯題率卻與前面相當
    • 由於針對 context 的處理我們都是採用比較粗糙的手法,因此心虛的我們第一個懷疑是內容太雜亂導致的回答錯誤
      • 舉例來說:在 feedback 下確實給出實際答案,但是在 ans 下仍然選到錯的
  • 根據過往的經驗,我們知道 RAG 系統其實很容易在檢索階段就直接出錯,導致後面答不上來,但偷看了一下答題情況後發現,這次好像真的不是檢索的問題...,
  • 究竟實情如何,我們明天見分曉

上一篇
Day25: 實戰:MCQ Data Challenge
下一篇
Day27: 開源的標註工具: Label-Studio
系列文
阿,又是一個RAG28
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言