今天,我們要讓推薦系統不只「提供推薦」,
而是開始能蒐集使用者互動資料,為後續的 A/B Test 分析 做準備。
當使用者在 Streamlit 介面中點擊推薦動畫時,
我們會將事件(包含暱稱、模型名稱、點擊動畫、時間)
傳送到 FastAPI 的 /log-ab-event
端點,並記錄成 log。
[使用者點擊推薦動畫]
↓
Streamlit → FastAPI /log-ab-event
↓
紀錄 CSV (ab_events.csv)
你的 FastAPI 已內建 /log-ab-event
:
@app.post("/log-ab-event")
def log_ab_event(event: ABEvent):
LOG_DIR = "/usr/mlflow/workspace/logs"
os.makedirs(LOG_DIR, exist_ok=True)
log_path = os.path.join(LOG_DIR, "ab_events.csv")
file_exists = os.path.isfile(log_path)
with open(log_path, "a", newline="", encoding="utf-8") as f:
writer = csv.writer(f)
if not file_exists:
writer.writerow(["timestamp", "user_id", "model_name", "model_version", "recommended_title", "clicked"])
writer.writerow([
event.timestamp.isoformat(),
event.user_id,
event.model_name,
event.model_version,
event.recommended_title,
event.clicked
])
return {"message": "Event logged successfully ✅", "event": event.dict()}
✅ 每次呼叫
/log-ab-event
,都會把事件寫入/usr/mlflow/workspace/logs/ab_events.csv
。
今天我們要優化 Streamlit 程式,
將「取得推薦結果」與「點擊事件紀錄」分開成兩段邏輯。
/src/api/main_streamlit.py
import os
import pandas as pd
import streamlit as st
import requests
from datetime import datetime
FASTAPI_URL = os.getenv("FASTAPI_URL", "http://localhost:8000")
ANIME_CSV_PATH = "/src/api/notebooks/data/anime_clean.csv"
st.set_page_config(page_title="🎬 Anime Recommender", layout="wide")
st.title("🎬 Anime Recommendation System")
st.markdown("""
這是一個基於 **MLflow + FastAPI** 的推薦系統展示 🎯
請輸入暱稱並選擇你喜歡的動畫,我們會為你推薦相似作品!
""")
# --- Step 1. 使用者暱稱 ---
nickname = st.text_input("請輸入你的暱稱 👤", placeholder="例如:Josh、小智、NekoFan")
if not nickname:
st.info("請輸入暱稱後再繼續。")
# --- Step 2. 載入動畫清單 ---
@st.cache_data
def load_anime_list():
if not os.path.exists(ANIME_CSV_PATH):
st.error(f"❌ 找不到資料檔案:{ANIME_CSV_PATH}")
return []
df = pd.read_csv(ANIME_CSV_PATH)
return df["name"].dropna().unique().tolist()
anime_list = load_anime_list()
# --- Step 3. 使用者選擇動畫 ---
selected_anime = st.multiselect(
"選擇你喜歡的動畫(最多5部) 🎥",
anime_list,
max_selections=5,
placeholder="例如:Naruto、Bleach、Attack on Titan..."
)
# --- Step 4. 定義輔助函式 ---
def get_recommendations(user_id: str, anime_titles: list[str]):
"""呼叫 FastAPI /recommend API"""
payload = {"user_id": user_id, "anime_titles": anime_titles}
params = {"model_name": "AnimeRecsysModel"}
res = requests.post(f"{FASTAPI_URL}/recommend", json=payload, params=params)
if res.status_code == 200:
return res.json()
else:
st.error(f"❌ 無法取得推薦結果:{res.text}")
return None
def log_click_event(user_id: str, model_name: str, model_version: int, title: str):
"""呼叫 /log-ab-event 紀錄點擊事件"""
event = {
"user_id": user_id,
"model_name": model_name,
"model_version": model_version,
"recommended_title": title,
"clicked": True,
"timestamp": datetime.utcnow().isoformat()
}
try:
r = requests.post(f"{FASTAPI_URL}/log-ab-event", json=event)
if r.status_code == 200:
st.toast(f"✅ 已紀錄點擊:{title}")
else:
st.warning(f"⚠️ 紀錄失敗:{r.text}")
except requests.exceptions.RequestException:
st.warning("⚠️ 無法連線至 FastAPI。")
# --- Step 5. 取得推薦結果 ---
if st.button("🚀 取得推薦結果"):
if not nickname:
st.warning("請先輸入暱稱。")
elif not selected_anime:
st.warning("請至少選擇一部動畫。")
else:
data = get_recommendations(nickname, selected_anime)
if data:
st.session_state["recommendations"] = data.get("recommendations", [])
st.session_state["model_name"] = data.get("model_name", "AnimeRecsysModel")
st.session_state["model_version"] = 1
st.success("✅ 推薦結果已更新!")
# --- Step 6. 顯示推薦結果並可記錄點擊 ---
if "recommendations" in st.session_state:
recs = st.session_state["recommendations"]
model_name = st.session_state.get("model_name", "AnimeRecsysModel")
model_version = st.session_state.get("model_version", 1)
if recs:
st.markdown("---")
st.subheader(f"✨ 為 **{nickname}** 推薦的動畫:")
for i, title in enumerate(recs[:10], 1):
if st.button(f"{i}. {title}", key=f"rec_{i}"):
log_click_event(nickname, model_name, model_version, title)
else:
st.warning("⚠️ 沒有可顯示的推薦結果。")
每當使用者點擊動畫時,FastAPI 都會在 /usr/mlflow/workspace/logs/ab_events.csv
中追加記錄:
1️⃣ 啟動整個系統
docker-compose up --build
2️⃣ 開啟 Streamlit
👉 http://localhost:8501
3️⃣ 操作流程:
4️⃣ 驗證結果
進入 FastAPI 容器檢查紀錄:
docker exec -it fastapi bash
cat /usr/mlflow/workspace/logs/ab_events.csv
你會看到點擊事件已被成功寫入 🎉
項目 | 說明 |
---|---|
🧠 新增 /log-ab-event API |
FastAPI 紀錄使用者互動行為 |
🧩 Streamlit 整合 | 點擊推薦動畫即上報事件 |
📂 Log 儲存 | ab_events.csv 可做後續分析 |
🔗 下一步 | Day 28 會讀取這份紀錄並分析點擊率 (CTR)、模型效果 |
🎉 Day 27 完成!
我們正式讓推薦系統具備「使用者行為回饋」功能。
明天(Day 28),你將學會如何將這些紀錄轉換為可視化報表,
分析不同模型在真實互動下的成效差異。