深度探討RAG系統該監控什麼及潛在瓶頸
RAG 系統
該監控什麼?
在ptt文章智慧問答系統專案中,系統主要分成三個模組:
- 向量檢索(Pinecone)
- LLM 回答生成(Gemini, GPT)
- 應用服務(Django API)
監控重點大致分為三類:
- 系統效能
- 查詢延遲時間:檢索文章 → 向量比對 → LLM 回答的總耗時。
- 請求數量:每分鐘/每小時處理的 API request。
- 資源使用量:CPU、記憶體、GPU(若有)、網路流量。
- 成本相關
- Embedding 請求數量:每篇文章切割後產生的向量數。
- 向量資料庫查詢次數:每次檢索會增加成本。
- LLM Token 消耗量:包含 Prompt Token 與 Completion Token。
- 使用者行為
- 熱門查詢問題:使用者最常問的主題。
- 查詢成功率:是否有檢索結果、是否回答合理。
- 錯誤率:API Timeout、無法連線、向量庫查詢失敗。
日誌記錄(Logging)策略
在 Django + RAG 架構中,我們可以針對不同模組建立日誌:
- API 層日誌
- 請求時間、使用者 IP、Query 內容
- 回傳的狀態碼(200, 400, 500)
- 向量檢索日誌
- 輸入 Query → 檢索到的文章 ID
- 檢索耗時、檢索 Top K 結果
- LLM 回答日誌
- 使用的 Prompt 模板
- Token 使用量(input/output)
- 回答長度、回覆是否成功
程式碼
import logging
logger = logging.getLogger("rag_system")
def query_rag(user_question):
try:
logger.info(f"收到查詢: {user_question}")
# Step 1: 向量檢索
docs = vector_search(user_question)
logger.info(f"檢索結果數量: {len(docs)}")
# Step 2: 呼叫 LLM
answer = llm_generate(user_question, docs)
logger.info(f"LLM 回答成功,字數: {len(answer)}")
return answer
except Exception as e:
logger.error(f"查詢失敗: {str(e)}")
return "抱歉,系統發生錯誤,請稍後再試。"
監控工具建議
要進一步可視化與長期監控,可以搭配以下工具:
- Prometheus + Grafana → 系統效能監控(CPU、RAM、API Latency)。
- ElasticSearch + Kibana(ELK Stack) → 收集日誌並分析查詢趨勢。
- Sentry → 即時錯誤追蹤,適合 Debug 用。
舉例:
- Prometheus 收集「RAG API 平均延遲時間」
- Grafana 繪製成 Dashboard → 方便觀察系統瓶頸
- Sentry 監控「LLM API Timeout」或「Pinecone 連線錯誤」
潛在瓶頸
- 向量檢索(Pinecone)
- 瓶頸:查詢延遲(特別是 Top K 很大時)
- 解法:降低 K 值、優化索引方式、使用更快的儲存方案
- LLM 回答生成(Gemini, GPT)
-
瓶頸:API 回應延遲、Token 成本過高
-
解法:
- Prompt 優化(減少輸入字數)
- 使用較便宜的模型處理簡單問題
- 只在需要時才呼叫 LLM(例如:檢索結果不足時)
-
- 應用服務(Django API)
- 瓶頸:同時請求數量太多 → 連線數不足、Thread Pool 壓力大
- 解法:增加快取、調整 Gunicorn/Uvicorn worker 數量
什麼是效能壓測?
效能壓測主要包含以下幾種類型:
-
Load Testing(負載測試)
模擬多個使用者同時查詢,檢測系統能否應付「日常高峰流量」。
-
Stress Testing(壓力測試)
持續增加流量,直到系統崩潰,找出極限。
-
Spike Testing(突發測試)
突然增加大量流量,觀察系統是否能快速恢復。
-
Endurance Testing(長時間測試)
模擬系統連續運行數小時甚至數天,觀察記憶體洩漏或效能下降。
壓測工具推薦
以下是常用的壓測工具:
-
Apache JMeter
- GUI 操作方便,適合模擬複雜流程(登入、查詢、回應)。
-
Locust
-
Python 撰寫測試腳本,能模擬使用者行為,非常靈活。
-
程式碼
from locust import HttpUser, task, between class RAGUser(HttpUser): wait_time = between(1, 5) # 模擬使用者每 1~5 秒送一次請求 @task def ask_question(self): self.client.post( "/api/query/", json={"question": "今天 PTT Gossiping 熱門話題是什麼?"} )
-
-
k6
- 輕量、支援 JavaScript 腳本,適合雲端服務壓測。
瓶頸分析方法
-
觀察延遲來源
- API → 延遲主要來自 LLM 或 DB?
- 使用 Django logging 記錄每個模組耗時。
-
監控系統資源
- CPU / 記憶體 → 是否滿載?
- I/O → 向量檢索是否造成 Disk 或 Network 壓力?
-
定位最慢環節
- 如果 LLM 回應平均要 3 秒 → 可以先快取常見問題。
- 如果檢索耗時超過 1 秒 → 需要調整 Index 或分片策略。