本日目標是將 Day 24 產生的知識向量資料庫(持久化資料)載入,並使用 Gradio 建立一個可互動的 Web Chatbot。
任務目標 | 核心技術 | 實現說明 |
---|---|---|
資料持久化載入 | pickle , faiss.read_index |
成功連結 Day 24 建立的 docs.pkl 和 index.faiss 。 |
工程健壯性 | FAISS + sklearn.NearestNeighbors |
提供備援索引機制,提升系統穩定性。 |
前端部署 | Gradio gr.Interface |
建立可輸入查詢、輸出檢索結果的基礎問答介面。 |
本次延伸版程式碼的核心價值在於其工程的健壯性,能夠從磁碟讀取多種格式的索引,確保 RAG 檢索服務可以順利啟動。
此模組是 Day 24 成果的體現。它載入了 FAQ 文本 (docs.pkl
)、向量 (embeddings.pkl
),並優先嘗試載入 FAISS 索引,若失敗則退而求其次載入 sklearn 的備援索引。
# 載入資料路徑
data_dir = Path("faq_chatbot/data")
docs_pkl = data_dir / "docs.pkl"
embeddings_pkl = data_dir / "embeddings.pkl"
faiss_index_file = data_dir / "index.faiss"
nn_file = data_dir / "nn.pkl"
# ===== 1️ 載入知識資料 =====
print("載入 docs 和 embeddings ...")
with open(docs_pkl, "rb") as f:
docs = pickle.load(f)
with open(embeddings_pkl, "rb") as f:
embeddings = pickle.load(f)
print(f"✅ 已載入 {len(docs)} 筆 FAQ 資料")
# ===== 2️⃣ 嘗試載入索引 =====
use_faiss = False
index = None
nn = None
if faiss_index_file.exists():
try:
import faiss
index = faiss.read_index(str(faiss_index_file))
use_faiss = True
print("✅ 使用 FAISS 索引")
except Exception as e:
print("⚠️ 無法載入 FAISS 索引:", e)
if not use_faiss and nn_file.exists():
from sklearn.neighbors import NearestNeighbors
import joblib
nn = joblib.load(nn_file)
print("✅ 使用 sklearn NearestNeighbors 備援索引")
# ===== 3️⃣ 載入 embedding 模型 =====
embed_model_name = "sentence-transformers/all-MiniLM-L6-v2"
embedder = SentenceTransformer(embed_model_name)
retrieve_answer
)retrieve_answer
函數實現了從使用者輸入到結果輸出的完整檢索鏈路。
def chatbot_reply(message, history):
query = message.strip()
if not query:
return "請輸入問題喔~"
query_vec = embedder.encode([query], convert_to_numpy=True)
# 檢索相似問答
if use_faiss and index is not None:
D, I = index.search(query_vec.astype(np.float32), 3)
results = [{"rank": i+1, "doc": docs[idx], "distance": float(D[0][i])} for i, idx in enumerate(I[0])]
elif nn is not None:
dists, idxs = nn.kneighbors(query_vec, n_neighbors=3)
results = [{"rank": i+1, "doc": docs[idxs[0][i]], "distance": float(dists[0][i])} for i in range(len(idxs[0]))]
else:
return "❌ 尚未載入任何索引,請先執行 Day24 建立知識庫。"
# ===== 生成清楚的回答 =====
best_doc = results[0]["doc"]
q_text = best_doc.split("\nA:")[0].replace("Q: ", "").strip()
a_text = best_doc.split("\nA:")[1].strip() if "\nA:" in best_doc else best_doc
answer = f"**問題:** {q_text}\n\n**回答:** {a_text}\n\n(相似度距離:{results[0]['distance']:.4f})"
return answer
使用 gr.Interface
快速建立一個基礎的單次問答介面。
# ===== 5️⃣ 建立 Gradio 互動介面 =====
chatbot = gr.ChatInterface(
fn=chatbot_reply,
title="🤖 FAQ 智慧客服 Chatbot",
description="輸入你的問題,我會從知識向量資料庫中找出最合適的答案。",
theme="soft",
textbox=gr.Textbox(placeholder="請輸入問題,例如:如何建立 Google 帳號?"),
examples=[
"如何建立 Google 帳號?",
"我忘記密碼怎麼辦?",
"如何設定兩步驟驗證?"
]
)
chatbot.launch()
本次延伸版成功地將後端數據工程與前端應用介面結合,證明了 RAG 系統可以從持久化數據中快速啟動服務。