iT邦幫忙

2025 iThome 鐵人賽

DAY 28
0
AI & Data

30 天從 0 至 1 建立一個自已的 AI 學習工具人系列 第 28

30-28: [實作] 我們要如何評估與優化課程問與答的功能品質呢 ? ( 還需研究 )

  • 分享至 

  • xImage
  •  

https://ithelp.ithome.com.tw/upload/images/20251014/20089358arJEWfip6A.png

本來是想要寫針對整個 AI 工具人進行的評估,發現範圍有點太大了,好像不是在剩下的天數可以寫完的東西,所以這裡縮小以下範圍,只用來評估我們在以下幾天實作的的功能 :

接下來就來研究要如何評估這些功能的品質吧。

🚀 第一步: 定義指標

我這要先簡單補充一下我們那幾天實作做了什麼 :

  • 30-21 : 使用用字幕來當課程問與答的資料,來建立 RAG。
  • 30-22 : 使用 Pre-Retrieval 來優化,主要有優化 Index 與 Query 的兩個方向。
  • 30-23 : 使用 Post-Retrieval 的 Reranking 來進行優化。
  • 30-24 : 使用 Post-Retrieval 的 Compressing 來行進優化。

所以首先我們可以先來想一下,先從最簡單的問與答,我們要用什麼指標來說明 AI 回答的很棒棒呢 ?

問與答我自已是覺得最難做與評估的東西,因為範圍很廣

上面提到範圍很廣有一部份是指根據用戶問的問題,我們可能就會需要用不同的指標,例如 Summary 這種情況就是用戶會問說,可以幫我總結這個章節的重點嗎,這樣是不是就沒有所謂的正確答案了 ? 所以這種情況我們是不是就不適用 Answer Accuracy 這種了 ?

所以這裡我會先用分類用戶意圖後,再來給予適當的指標。然後下面是我大概整理的情境。

🤔 類型1: 摘要/總結 ( Summary )

例如問說可以幫我總這個章節的重點嗎,那這種情況下我們適合以下幾個 Ragas 指標。

  • Summarization : 在 Ragas 個專門用來驗證 summary 情境下的指標,但 Langfuse 沒有提到,所以我們要參考他的程式碼自已寫個 prompt,順到說一下它的概念是先用原始內容來生成問答,然後再來針對 summary 來判斷可以回答這些問題。
  • Faithfulness : 這個指標可以用來幫助我們確保 AI 沒有扭曲事實,只會從我們提到的 Context 來進行總結。

🤔 類型2: 事實查詢

例如以下幾種問題都算是,那這種情況就是很吃以下幾個指標。

"Docker 的定義在哪個章節?"
"講師在哪裡提到容器化的優勢?"
"關於微服務架構的部分在第幾分鐘?"

然後以下就是我們適合的指標。

  • Answer Correctness: 這個是判斷你的答案的正確性。
  • Answer Relevancy: 這個是用來判斷你的答案是否有回應問題,可以想成判斷廢話度。
  • Context Precision: 這個主要是用來判斷你 Retrieved Contexts 的品質,到不是在判斷你的答案的品質。
  • Faithfulness: 就是不要讓他說出沒有的東西,主要會用你的答案是不是從 Retrieved Contexts 出來的。

Answer Correctness 與 Answer Relevancy 在這裡應該有人會覺得混亂,感覺不是差不多的比較,有點像但不太對,以下為主要差別與範例說明

兩者的差別在於 Answer Correctness 一定要有 Ground Truth 才能判斷,而 Answer Relevancy 則否,但不代表在有 Ground Truth 的情況下不需要 Answer Relevancy。

以下為如果只有 Answer Correctness 情況可能會有問題的案例,你看答案 B 是不是說了一堆廢話後,還有答案,但看的人可不想知道啊 :

問題: "Docker 在哪個章節?"

ground_truth = "第 2 章"

# === 答案 A ===
answer_A = "第 2 章"

Answer Correctness: 1.0 ✅
Answer Relevancy: 1.0 ✅
用戶體驗: ⭐⭐⭐⭐⭐ 完美!

# === 答案 B ===
answer_B = """
Docker 在第 2 章有詳細介紹。

Docker 是由 Solomon Hykes 於 2013 年創建的容器化平台。
它的主要優勢包括:
1. 輕量化 - 相比虛擬機更節省資源
2. 快速部署 - 秒級啟動時間
3. 跨平台支援 - 可在多種作業系統運行
4. 易於擴展 - 支援微服務架構

在第 2 章中,講師從基本概念開始,逐步說明 Docker 
的工作原理、安裝步驟,以及基本指令的使用方式...
"""

Answer Correctness: 0.85 ✅ (包含「第2章」,語義相近)
Answer Relevancy: 0.35 ❌ (大部分內容與「在哪裡」無關)
用戶體驗: ⭐⭐ 糟糕! (用戶只想要位置,卻得到一大段說明)

🤔 類型 3: 流程/如何做 ( How-to )

它事實上和事實類有點像,但是實際上有個重點有差 :

那就是完整性 Completeness

例如下面的範例,如果沒有考慮 Completeness,那就會發生一個流程,有 5 個部份,但如果 1 個部份,那分數還是很高,所以這裡才會需要分開來判斷 :

# === 事實查詢的「完整」===
問題: "講師在哪裡提到 Docker Compose?"

完整答案 A: "第 5 章 23:15"
→ Completeness: 1.0 ✅

過度完整 B: """
第 5 章 23:15 首次提到,
第 6 章 12:30 再次說明,
第 7 章 05:00 進階用法
"""
→ Completeness: 1.0,但用戶可能不需要這麼多!

# === 流程查詢的「完整」===
問題: "如何建立 Docker 映像?"

不完整答案 A: "使用 docker build 命令"
→ Completeness: 0.2 ❌ (缺少前置、參數、驗證)

完整答案 B: """
1. 準備 Dockerfile
2. 執行 docker build -t name:tag .
3. 驗證:docker images
4. 測試映像
"""
→ Completeness: 0.9 ✅

# 關鍵差異:
# 事實查詢:回答「那個問題」就完整了
# 流程查詢:必須包含「所有步驟」才完整

所以這裡類型 2 大部份相同,但是會多加一個 Completeness。

  • Answer Correctness
  • Answer Relevancy
  • Context Precision
  • Faithfulness
  • Completeness : 我這裡有查到一個實作,可能也要自已去實作到 Langfuse。

🤔 類型4: 開放討論/觀念釐清

例如這些都算,然後它們有個特性,就是我們很難有一定的標準答案,所以不太會用什麼 Answer Correctness 之類的。

"為什麼要使用 Docker 而不是虛擬機?"
"容器化和虛擬化的本質差異是什麼?"
"什麼情況下適合使用 Docker Compose?"
"Docker 和 Kubernetes 的關係如何理解?"
"微服務架構為什麼需要容器化?"
  • Answer Relevancy
  • Coherence : 但是這個只有看到這個有提供,所以可也要自已想辦法。
  • Faithfulness

‼️‼️ 類型1: 摘要/總結 ( Summary )這種為主,不然這篇文章會長到還大約是 4 篇文章的 size

🚀 第二步準備實驗資料-1 - 自已建立資料

會需要這個是因為,我們如果是新產品,不太可能什麼都沒測過後就上線,所以還是要先自已建立一些實驗資料,來看看我們的分數後,才能決定要不要上線

❓ 那要產生什麼實驗資料呢 ?

這裡會以上面 4 種意圖,來建立不同的 Dataset,然後標準就是以下三個欄位,其中 expectedOutput 要說明一下,就是我們很多時後,都沒有標準答案,但這個時後 expectedOutput 還是建議填寫,因為會用它來當 reference 來當做是語義比對,或是意思是否等價之類的。

{
  input: '我想學習 LangGraph',
  expectedOutput: '自已去看文件',
  metadata (這個可選): {}
}

然後我們是以字幕來當這個單元的原始內容,那接下來另一個問題就是。

❓ 我們每個單元,都要同時產這 4 種類型的實驗資料嗎 ?

我覺得要分以下兩個方向來設計這兩種 Dataset。

🤔 第一種類型: Context 固定

這種實驗的重點是觀察模型在使用已知 Context 下的表現,所以會關閉 Retrieval 機制,直接把固定的 Context 餵給模型使用。

適用的情境:

  • 想比較不同 Prompt、模型版本、或輸出格式的品質。
  • 想排除 Retrieval 變數,只專注在 Generation 階段的穩定性與表現。

這種情況下,我們可以不用理什麼課程或單元是什麼,因為 Context 都寫死了。

*🤔 第二類型: Context 不固定

用來驗證整條流程的整體品質,包含 Retrieval 與 Generation。

這種實驗會保留真實的 Retrieval 過程。每次執行實驗時,系統會根據 Dataset 中的 metadata.lectureId(或 courseId )告訴 Retrieval 該從哪個單元的資料中取出 Context。

不過理想上是每個單元每個課都有一定等級的實驗資料,我覺得在實務上可以先以例如 3 堂最熱門、最多人且是不同類型的課程,來當整個 AI Application 來當黃金資料集,如果這三堂的實驗評分都很高,那事實上整體系統穩定性應該也不會差到那。

不然如果真的要做每堂課,那個我自已現在是覺得成本很高。

🤔❓ 那我們這種從 0 開始的要用那種

簡單來說,兩個都要,然後從下面流程先建立第一類與第二類資料的。

  1. 從原始字幕透過 LLM 建立 Contexts。
  2. 生成多個意圖的 QA
  3. 自動評分+篩選來建立這兩種的 Gold Dataset

🤔 程式碼

程式碼就大概如下,流程就是 :

  1. 整理 SRT 檔與處理。
  2. 透過 LLM 產題目。
  3. 儲到 Langfuse 的 Dataset。
async function main() {
  const langfuse = new Langfuse({
    publicKey: process.env.LANGFUSE_PUBLIC_KEY,
    secretKey: process.env.LANGFUSE_SECRET_KEY,
    baseUrl: process.env.LANGFUSE_BASE_URL,
  });

  // 1. 讀取 SRT 檔案+處理
  const srtContent = fs.readFileSync("./test.srt", "utf-8");
  const processedChunks = SRTProcessor.process(srtContent);

  // 2. 生成多個意圖的測試資料
  const summaryPrompt = `
# Context:
${processedChunks
  .map((chunk) => `[${chunk.startAt}~${chunk.endAt}] ${chunk.textClean}`)
  .join("\n")}

# Task:
根據上述 context 產生 5 個「摘要/總結」類型的問答對。

# 摘要問題的特徵:
✅ 必須包含:
- 問題要明確要求「總結」、「歸納」、「整理」、「概括」內容
- 答案要涵蓋**多個關鍵要點**(至少 3 個)
- 答案要用**結構化方式**呈現(條列式或分段)
- 答案要完整但簡潔,濃縮原內容的核心資訊
- 至少要有一題是問時間範圍內的總結

❌ 絕對不要:
- 不要問「為什麼」、「如何」、「差異是什麼」(這些是討論題)
- 不要問單一概念的解釋(如「什麼是 RAG?」)
- 不要問比較題(如「A 和 B 的差別?」)
- 答案不要只深入解釋一個概念

# 問題類型範例:
✅ 好的摘要問題:
- "總結這段內容提到的核心概念"
- "歸納講師討論的主要議題有哪些"
- "整理 14:00~15:00 的重點內容"
- "概括這個單元涵蓋的技術方法"

❌ 不是摘要問題:
- "為什麼要使用 RAG?" (這是討論題)
- "RAG 和 RALM 的差異?" (這是比較題)
- "什麼是 hallucination?" (這是定義題)

# 答案格式要求:
- 必須使用條列式(1. 2. 3.)或結構化段落
- 每個要點要簡潔(1-2 句話)
- 至少包含 3 個不同的關鍵要點
- 字數控制在 200-300 字

# Output Format:
請輸出 JSON 陣列,格式如下:
[{
  "question": "問題(必須包含「總結」「歸納」「整理」等關鍵字)",
  "answer": "結構化的答案(條列式或分段,涵蓋多個要點)"
}]

# Examples:
[{
  "question": "總結這段內容討論的 RAG 相關概念與技術",
  "answer": "這段內容討論的 RAG 相關概念包括:\n\n1. RAG vs RALM:RAG 強調生成,RALM 泛指所有檢索輔助的語言模型功能\n\n2. 長尾知識問題:預訓練資料中低頻資訊記憶薄弱,容易產生 hallucination\n\n3. 檢索機制優勢:提供 open-book 能力,可引用外部資源提升準確性\n\n4. 實作方法:如 WebGPT 使用搜尋、點擊頁面、引用段落的方式"
}, {
  "question": "歸納講師說明的語言模型主要限制有哪些",
  "answer": "講師說明的語言模型限制包括:\n\n1. 知識過時:預訓練後的模型內部知識無法更新\n\n2. 長尾知識薄弱:低頻資訊在訓練資料中出現少,模型記憶不足\n\n3. Hallucination 風險:對不確定的資訊會憑機率猜測,產生錯誤答案\n\n4. 缺乏來源驗證:無法提供資訊來源,難以 fact-check"
}, {
  "question": "整理 14:00~16:00 時間區段的核心內容",
  "answer": "這個時間區段的核心內容涵蓋:\n\n1. 檢索輔助系統的定義與分類\n\n2. 長尾知識問題的成因與影響\n\n3. 檢索機制如何改善模型表現\n\n4. WebGPT 等實作案例的具體做法"
}]

# Important:
- 每個問題都必須明確包含「總結」、「歸納」、「整理」、「概括」等摘要關鍵字
- 答案必須包含至少 3 個不同的要點
- 答案必須使用條列式或結構化格式
- 不要產生「為什麼」、「如何」、「差異」等討論型問題
`;

  const model = new ChatOpenAI({
    modelName: "gpt-5-mini",
  });

  const originalResult = await model.invoke([
    { role: "user", content: summaryPrompt },
  ]);
  const result = JSON.parse(originalResult.content.toString());

  // 3. 將結果寫入 Langfuse 的 dataset
  // 好像找不到 batch 的 api
  await Promise.all(
    result.map(async (item) => {
      await langfuse.api.datasetItemsCreate({
        datasetName: "30-28",
        input: item.question,
        expectedOutput: item.answer,
        metadata: {
          model: "gpt-5-mini",
          lectureId: "30-28",
        },
      });
    })
  );

  console.log("✅ 已完成,請到 Langfuse 查看結果");
}

🚀 第二步準備實驗資料-1 - 評分自已建立的資料

因為我們這篇文章主要是聚焦在類型1: 摘要/總結 ( Summary )這種類型的情境,因為我們這裡評產生題目的題目是否優質就可以用在流程 1 寫的這個 2 個 :

  • Summarization
  • Faithfulness

下面這個是我根據 Ragas 的 summarization 的指標來寫成的一個 Evaluator prompt 可以參考看看。

https://github.com/explodinggradients/ragas/blob/main/src/ragas/metrics/_summarization.py

import { ChatOpenAI } from "@langchain/openai";

export class SummaryEvaluator {
  static async evaluate(
    content: string,
    summary: string
  ): Promise<{ score: number; comment: string }> {
    const prompt = `
     你的任務是基於 content 生成封閉式問題,並評估 summary 是否能回答這些問題。

content: ${content}}
summary: ${summary}}

## 第一部分:生成問題

基於給定的 content 生成封閉式問題。

問題生成規則:
1. 所有問題都必須能夠「僅使用」原文來回答
2. 問題應該測試 content 中的具體資訊是否存在
3. 重點關注與關鍵詞組相關的事實資訊
4. 生成的問題基於原文都應該回答為 1 (是)
5. 使用簡單、清晰的問題格式

問題模式範例:
- "[主體] 是 [類型] 嗎?"
- "[主體] 位於 [地點] 嗎?"
- "[主體] 由 [人物] 創立嗎?"
- "[主體] 在 [時間] [動作] 嗎?"

## 第二部分:評估答案

針對每個生成的問題,判斷 summary 是否包含足夠資訊來回答。

答案評估規則:
1. 答案必須是 1 或 0 ( number )
2. 如果 summary「包含」足夠資訊來回答問題,答案為 1
3. 如果 summary「不包含」足夠資訊來回答問題,答案為 0
4. 只考慮 summary 中的內容,不使用外部知識
5. 評估資訊是否「存在」於 summary 中

評估標準:
- 1: summary 明確或隱含地包含回答問題所需的資訊
- 0: summary 沒有提及或提供回答問題所需的資訊

## 第三部分: 總計與回傳結果
將每一個問題評分的結果,計算平均值,並且回傳評分結果的 comment

請輸出 JSON 物件,格式如下:
{
  "score": 0.4
  "comment": "評分結果的 comment"
}


範例:
第二部份總共產生 5 題,分數分別為 1,0,0.5,0.5,0,
因為 score 為 0.4 (1+0+0.5+0.5+0)/5

{
  "score": 0.4
  "comment": "評分結果的 comment"
}
    `;

    const model = new ChatOpenAI({
      modelName: "gpt-5-mini",
    });

    const result = await model.invoke([{ role: "user", content: prompt }]);
    console.log(result.content);

    return JSON.parse(result.content.toString());
  }
}

然後接下是外面使用時的樣子,他會先根據我們流程 2-1 所產生的實驗資料,來進行評份,透過 SummaryEvaluator,然後接下來我們將分數高於 0.6 的放到 Langfuse 收到 Dataset,當做我們未來的實驗資料。

  // 3. 將結果寫入 Langfuse 的 dataset
  // 好像找不到 batch 的 api
  console.log("開始評估摘要問題");
  const scores = await Promise.all(
    result
      .map(async (item) => {
        const score = await SummaryEvaluator.evaluate(context, item.answer);
        return {
          question: item.question,
          answer: item.answer,
          score: score.score,
          scoreComment: score.comment,
        };
      })
  );
  const filteredScores = scores.filter((item) => item.score > 0.6);
  console.log("評估結果", filteredScores);

  // 4. 將結果寫入 Langfuse 的 dataset
  await Promise.all(
    filteredScores.map(async (item) => {
      await langfuse.api.datasetItemsCreate({
        datasetName: "30-28",
        input: item.question,
        expectedOutput: item.answer,
        metadata: {
          model: "gpt-5-mini",
          lectureId: "30-28",
          score: item.score,
        },
      });
    })
  );

https://ithelp.ithome.com.tw/upload/images/20251012/200893583Bt6TmQZnE.png

🚀 建立 Dataset: 定期透過 Production 好的回應新增進 Dataset

這裡是等到上了正式環境以後,我們事實上接下來很有產生很多 trace,然後這裡就是要進行我們有提到的Human Annotation流程,將他放到 Dataset 中。

🚀 微調參數然後進行實驗

這裡的參考包含了 :

  • Prompt
  • Chunk
  • TopK
  • Pre-retrieval 的每個部份
  • Post-retrival 的每個部份

🚀 小總結:

本篇文章中,我們嘗試進行對我們之前做的功能『 課程問與答的功能 』研究要如何進行評估,但是寫到一半後來發現這個主題真的很大,主要的原因在於,問與答實際上還要考慮很多的情境,加上很多問題是沒有標準答案的,這裡也只能先寫一些發現的東西,之後光這個主題應該就可以開很多篇了。


上一篇
30-27: [知識] AI Application Evaluation ( 3 ) 之 Experiments 篇
下一篇
30-29: [知識] 選擇 AI Model 時要考慮那些呢 ? ( 2025 年版 )
系列文
30 天從 0 至 1 建立一個自已的 AI 學習工具人30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言