自動化測試與效能監控:確保 Quick Replies 生成穩定可靠

Published February 10, 2025 by 徐培鈞
Python

在 AI 驅動的聊天機器人中,「建議回覆 (Quick Replies)」 是提升用戶體驗的重要功能。

然而,如果這個功能在生成時出現異常、回應延遲,甚至產生錯誤內容,可能會影響用戶體驗,甚至降低信任度。

因此,我們需要建立一套自動化測試與效能監控機制,確保 Quick Replies 在各種情境下都能穩定運行。

本篇文章將介紹如何設計自動化測試來驗證 Quick Replies 的正確性,並透過效能監控來評估 AI 生成的速度與穩定性。

我們將透過 Python 測試框架與效能分析工具,建立一個完整的測試流程,確保 Quick Replies 在實際應用中不會出現意外狀況。


自動化測試的目標

自動化測試的核心目標是確保 Quick Replies 的輸出符合預期,包括:

  1. 格式正確性:確保回應為 JSON 陣列,且包含預定數量的 Quick Replies。
  2. 內容準確性:驗證生成的 Quick Replies 是否符合輸入的對話歷史、FAQ、產品清單等數據。
  3. 錯誤處理能力:測試異常情境,如 API 超時、無效輸入等,以確保系統能夠妥善應對。
  4. 效能監控:追蹤 AI 回應的時間,分析平均回應速度與可能的瓶頸。

設計自動化測試腳本

test_quick_replies.py 中,我們實作了一個測試腳本,針對 Quick Replies 進行大量測試,並記錄測試結果。

測試邏輯

該測試腳本會執行以下步驟:

  1. 載入測試數據(包含 chat_historyfaq_listproduct_list)。
  2. 執行 50 次測試(可根據需求調整),記錄回應時間與輸出結果。
  3. 驗證 JSON 格式與 Quick Replies 數量,確保符合預期。
  4. 記錄成功與失敗的測試結果,並計算效能指標(平均回應時間、成功率等)。
  5. 輸出測試結果至 test_results.json,方便進一步分析。

核心程式碼解析

import json
import time
from generate_quick_replies import generate_quick_replies
from test_data import chat_history, faq_list, product_list

# 測試參數
TEST_RUNS = 50  # 測試次數
success_count = 0
fail_count = 0
response_times = []
test_results = []

# 進行測試
for i in range(TEST_RUNS):
    start_time = time.time()
    try:
        replies = generate_quick_replies(chat_history, faq_list, product_list)
        end_time = time.time()
        response_time = round(end_time - start_time, 2)

        # 驗證輸出格式
        assert isinstance(replies, list), "輸出格式錯誤,應該為 JSON 陣列"
        assert len(replies) == 5, f"輸出數量錯誤,應該是 5 個,但收到 {len(replies)} 個"

        success_count += 1
        response_times.append(response_time)
        test_results.append({"test_case": i+1, "response_time": response_time, "quick_replies": replies})

    except Exception as e:
        fail_count += 1
        test_results.append({"test_case": i+1, "error": str(e)})

# 計算效能指標
summary = {
    "total_tests": TEST_RUNS,
    "success_count": success_count,
    "fail_count": fail_count,
    "avg_response_time": round(sum(response_times) / len(response_times), 2) if response_times else None,
    "min_response_time": min(response_times) if response_times else None,
    "max_response_time": max(response_times) if response_times else None
}

# 儲存結果
final_data = {"summary": summary, "tests": test_results}
with open("test_results.json", "w", encoding="utf-8") as f:
    json.dump(final_data, f, ensure_ascii=False, indent=4)

print("✅ 測試完成,結果已儲存至 test_results.json")

測試結果解讀

測試完成後,系統會輸出 test_results.json,其中包含:

  • 總測試次數
  • 成功與失敗測試數
  • 平均回應時間 (avg_response_time)
  • 最快與最慢回應時間
  • 每個測試案例的回應內容

代碼解析

引入必要的模組

import json
import time
from generate_quick_replies import generate_quick_replies
from test_data import chat_history, faq_list, product_list

功能解析

  • json:用於讀寫 JSON 檔案(儲存測試結果)。
  • time:用來計算 AI 生成 Quick Replies 所需的時間(效能監控)。
  • generate_quick_replies:從 generate_quick_replies.py 匯入核心函數,負責根據對話歷史 (chat_history)、FAQ (faq_list)、產品列表 (product_list) 產生建議回覆。
  • test_data.py:包含測試用的輸入數據,如 chat_history(過往聊天記錄)、faq_list(常見問題)、product_list(商品資訊)。

設定測試參數

TEST_RUNS = 50  # 測試次數
success_count = 0
fail_count = 0
response_times = []
test_results = []

變數解析

  • TEST_RUNS = 50:測試的迴圈次數,表示這段程式碼會重複測試 50 次。
  • success_count = 0:紀錄成功測試的次數。
  • fail_count = 0:紀錄失敗測試的次數。
  • response_times = []:存儲每次 AI 生成 Quick Replies 所花費的時間,用於計算效能指標(平均回應時間、最短與最長回應時間)。
  • test_results = []:存放每次測試的詳細結果(包含回應內容與執行時間)。

進行測試(迴圈測試 50 次)

for i in range(TEST_RUNS):
    start_time = time.time()
    try:
        replies = generate_quick_replies(chat_history, faq_list, product_list)
        end_time = time.time()
        response_time = round(end_time - start_time, 2)

功能解析

  • for i in range(TEST_RUNS): 迴圈 50 次,每次測試一次 generate_quick_replies 函式。
  • start_time = time.time():記錄請求發送的時間點(測試開始)。
  • generate_quick_replies(chat_history, faq_list, product_list)
    • 調用核心函式 generate_quick_replies,根據提供的 chat_historyfaq_listproduct_list 產生建議回覆。
  • end_time = time.time():記錄請求完成的時間點(測試結束)。
  • response_time = round(end_time - start_time, 2)
    • 計算回應時間,單位為秒,並四捨五入到小數點後兩位。

驗證回應內容

        # 驗證輸出格式
        assert isinstance(replies, list), "輸出格式錯誤,應該為 JSON 陣列"
        assert len(replies) == 5, f"輸出數量錯誤,應該是 5 個,但收到 {len(replies)} 個"

這段程式碼做了什麼?

  1. assert isinstance(replies, list), "輸出格式錯誤,應該為 JSON 陣列"
    • 確保 replies 的輸出格式為 列表 (list)
    • 如果 replies 不是 list,則會拋出 AssertionError,終止當前測試。
  2. assert len(replies) == 5, f"輸出數量錯誤,應該是 5 個,但收到 {len(replies)} 個"
    • 確保 replies 的數量正確,應該是 5 個 Quick Replies。
    • 若數量不符,則拋出 AssertionError,顯示錯誤訊息。

記錄成功測試結果

        success_count += 1
        response_times.append(response_time)
        test_results.append({"test_case": i+1, "response_time": response_time, "quick_replies": replies})

這段程式碼做了什麼?

  • success_count += 1:將成功測試計數 +1。
  • response_times.append(response_time):儲存該次測試的回應時間。
  • test_results.append(...):將該次測試的編號、回應時間、Quick Replies 內容記錄到 test_results 陣列。

捕捉錯誤,記錄失敗測試

    except Exception as e:
        fail_count += 1
        test_results.append({"test_case": i+1, "error": str(e)})

這段程式碼做了什麼?

  • except Exception as e:如果 generate_quick_repliesassert 驗證發生錯誤,則執行這段程式碼。
  • fail_count += 1:將失敗測試計數 +1。
  • test_results.append(...)
    • 紀錄測試編號。
    • 儲存錯誤訊息 str(e),以便日後分析。

計算效能指標

summary = {
    "total_tests": TEST_RUNS,
    "success_count": success_count,
    "fail_count": fail_count,
    "avg_response_time": round(sum(response_times) / len(response_times), 2) if response_times else None,
    "min_response_time": min(response_times) if response_times else None,
    "max_response_time": max(response_times) if response_times else None
}

這段程式碼做了什麼?

  • total_tests: 測試總次數。
  • success_count: 成功測試數量。
  • fail_count: 失敗測試數量。
  • avg_response_time: 平均回應時間(若沒有成功測試則為 None)。
  • min_response_time: 最快回應時間(若沒有成功測試則為 None)。
  • max_response_time: 最慢回應時間(若沒有成功測試則為 None)。

儲存測試結果

final_data = {"summary": summary, "tests": test_results}
with open("test_results.json", "w", encoding="utf-8") as f:
    json.dump(final_data, f, ensure_ascii=False, indent=4)

print("✅ 測試完成,結果已儲存至 test_results.json")

這段程式碼做了什麼?

  • final_data:建立一個 JSON 物件,包含:
    • summary(總結數據)
    • tests(每次測試的詳細資料)。
  • with open("test_results.json", "w", encoding="utf-8") as f
    • 寫入模式打開 test_results.json 檔案。
  • json.dump(final_data, f, ensure_ascii=False, indent=4)
    • final_data 轉成 JSON 格式,並儲存到檔案
    • ensure_ascii=False:確保 JSON 中文不會變成 Unicode 轉義字符。
    • indent=4:讓 JSON 排版更可讀。

效能監控與分析

除了驗證輸出結果的正確性,我們還需要監控 AI 生成的效能,確保回應速度符合應用需求。

監控指標

  1. 平均回應時間 (avg_response_time)
    • 反映 AI 在多次測試中的整體回應速度。
    • 若數值過高,可能代表 API 調用或 Prompt 需優化。
  2. 成功與失敗測試數 (success_count / fail_count)
    • 若失敗率過高,需檢查 API 穩定性或輸入數據的問題。
  3. 最短與最長回應時間 (min_response_time / max_response_time)
    • 若最大與最小回應時間差距過大,可能代表 AI 生成速度不穩定。

效能最佳化策略

  1. 簡化 Prompt 設計:減少不必要的上下文資訊,讓 AI 更快生成回應。
  2. 使用並行請求:若系統需大量請求,可考慮非同步 API 調用。
  3. 控制輸入數據大小:減少 chat_historyfaq_list 等的長度,以提高生成效率。
  4. 監控 API 費用與速率:避免 API 請求過多導致費用過高或速率限制。

進階測試:異常處理測試

在自動化測試中,異常測試(Edge Cases & Error Handling Tests) 是非常重要的一環。

除了確保 AI 在一般情境下運作正常,我們還需要測試在極端條件錯誤輸入的情況下,系統是否能夠正確處理,避免崩潰或返回不合理的結果。

這些異常測試可以幫助我們:

  • 提高系統穩定性:避免 API 突然失效導致整個應用崩潰。
  • 增強錯誤處理能力:確保系統在遇到錯誤時能提供適當的錯誤訊息或預設回應。
  • 模擬真實環境:在實際應用中,輸入數據可能會有遺漏、異常長度等狀況,因此異常測試能讓系統更健壯。

以下是三種關鍵的異常測試場景,並附上相應的測試策略與 Python 代碼示例。

模擬 API 失敗(如超時、無回應)

🔹 測試場景

  • OpenAI API 可能會因為網路問題、API 伺服器負載過高、超出速率限制(Rate Limit)等原因而無法回應回應超時
  • 我們需要確保:
    1. API 超時時,程式不會卡死,而是能夠返回適當的錯誤信息。
    2. 有適當的重試機制,當 API 短暫失敗時,可以自動重新嘗試請求。

🔹 測試策略

  • 模擬 API 超時:設定 API 請求時間超過特定時間(如 5 秒)。
  • 模擬 API 無回應:直接讓 generate_quick_replies 返回 None 或拋出錯誤。

🔹 測試代碼

import requests
from generate_quick_replies import generate_quick_replies

# 測試 API 超時
try:
    replies = generate_quick_replies(chat_history, faq_list, product_list, timeout=0.001)  # 設定極短的超時時間
    print("❌ 測試失敗,應該要超時,但仍然獲得回應:", replies)
except requests.exceptions.Timeout:
    print("✅ 測試通過,API 超時處理正常。")
except Exception as e:
    print(f"❌ 測試失敗,出現其他錯誤: {e}")

# 測試 API 無回應
try:
    replies = None  # 模擬 API 無回應
    if replies is None:
        raise ValueError("API 無回應")
except ValueError as e:
    print("✅ 測試通過,API 無回應時系統能正常處理。")
except Exception as e:
    print(f"❌ 測試失敗,出現其他錯誤: {e}")

🔹 預期結果

  • API 超時時:應該拋出 requests.exceptions.Timeout,而不是讓程式卡死。
  • API 無回應時:應該返回適當的錯誤訊息,避免回傳 None 或錯誤數據。

測試空輸入情境(無 chat_history、faq_list、product_list)

🔹 測試場景

  • 在某些情況下,AI 可能會收到空白輸入(例如剛啟動機器人時,沒有對話歷史,或 FAQ 與產品列表無資料)。
  • 我們需要確保:
    1. AI 仍能返回預設 Quick Replies(例如:「請問有什麼我可以幫助您的?」)。
    2. 不會因為 None[] 而導致錯誤

🔹 測試策略

  • 測試 generate_quick_replies 函式在完全無輸入數據的情況下,是否仍能提供有效回應。
  • 如果沒有數據,應該提供預設回應而不是報錯。

🔹 測試代碼

try:
    empty_replies = generate_quick_replies([], [], [])  # 全部輸入空陣列
    assert isinstance(empty_replies, list), "輸出格式錯誤,應為 JSON 陣列"
    assert len(empty_replies) > 0, "應該提供預設建議回覆,而不是空陣列"
    print("✅ 測試通過,空輸入時仍能生成建議回覆。")
except Exception as e:
    print(f"❌ 測試失敗,空輸入處理錯誤: {e}")

🔹 預期結果

  • 空輸入時:應該返回一組預設的 Quick Replies(例如:「請問有什麼我可以幫助您的?」),而不是拋出錯誤。

測試極端長度輸入(如超長的 chat_history)

🔹 測試場景

  • 在實際應用中,chat_history 可能會變得非常長,如果我們不加以控制,可能會導致:
    1. API 調用成本增加(OpenAI 會按字數計價)。
    2. 回應速度變慢(過長的輸入可能影響 AI 回應時間)。
    3. API 可能拒絕回應(輸入字數超過 API 限制)。
  • 我們需要確保:
    1. 長輸入時,程式不會崩潰
    2. 適當截斷 chat_history,只保留最近的對話

🔹 測試策略

  • 測試 chat_history 含有超過 10,000 字元時,是否仍能正常運行。
  • 檢查是否有機制能自動截斷對話歷史(如只保留最近 10 條訊息)。

🔹 測試代碼

long_chat_history = ["這是一條測試訊息。" * 5000]  # 生成超長對話歷史

try:
    replies = generate_quick_replies(long_chat_history, faq_list, product_list)
    assert isinstance(replies, list), "輸出格式錯誤,應為 JSON 陣列"
    print("✅ 測試通過,超長 chat_history 仍能處理。")
except Exception as e:
    print(f"❌ 測試失敗,處理超長 chat_history 時出錯: {e}")

🔹 預期結果

  • 超長輸入時:應該有機制截斷 chat_history,而不是讓整個 AI 計算負擔過重。

結論

異常測試可以幫助我們識別並解決潛在問題,確保 AI 在各種極端情況下都能穩定運作。我們建議將這些測試整合進 CI/CD 流程,確保系統在正式上線前已經過完整驗證。

可能問題可能讓應用程式卡死
解決方案增加錯誤處理,並設置重試機制
可能問題可能拋出錯誤
解決方案提供預設 Quick Replies
可能問題API 成本增加,回應變慢
解決方案截斷對話歷史,保留最近 N 條記錄

結論

透過自動化測試與效能監控,我們可以確保 Quick Replies 的生成符合預期,並在不同情境下保持穩定。

這不僅提升了系統的可靠性,也能幫助開發者快速發現問題,進一步優化 AI 生成的品質與效能。

你可以將這套測試機制整合到 CI/CD 流程中,自動檢測 AI 回應的品質與速度,確保你的聊天機器人始終提供高效、準確的 Quick Replies!🚀