技術決策 — 選型、協作方式、與中途轉向
先講限制 — 選型都是從這裡長出來的
- 時程:hackathon 等級 — 以「週」為單位,不是「季」
- 團隊:4 人 — 1 PM、2 編輯、1 工程;工程實作是一人 + AI 協作
- 硬體:demo 機是 2 vCPU、無 GPU 的小 VPS;預算 $0
- 內容:中文片庫 — 所有模型選擇都要先問「中文行不行」
底層原則:簡化選擇 — 把複雜度預算全部花在「服務如何整合 AI」上,基礎設施一律選最無聊、最不會半夜出事的(boring technology)。重點從來不是堆技術,而是 AI 進來之後,搜尋、標籤、營運的工作流程怎麼變。
初始選型 — 選了什麼、為什麼
| 選擇 | 為什麼選它 |
|---|---|
| Python + FastAPI | 自動生 API 文件(Swagger)— 跟 PM / 編輯溝通介面零成本;全專案統一 Python,AI 生態同語言 |
| NiceGUI(純 Python UI) | 框架包掉前端細節,不用分心管前端 — 同樣 Python-based,一個人顧全棧 |
| SQLite | 簡單,小專案剛好 — 零維運、單一檔案可備份可搬遷 |
| Qdrant | 之前用過、社群活躍,踩坑容易查到解法 |
| bge-m3(embedding) | 主流開源 embedding model,中文表現好、可完全本地跑 |
| LLM:免費雲端優先、可切回本地(LLM) | 重活(embedding、rerank、評測 judge)全留本地;只有需要大模型推理的 LLM(讀片選標籤、讀懂查詢)走雲端。路線繞了一圈:雲端免費 → 太不穩(429、模型下架)一度改全本地 Ollama → 再回到 OpenRouter 免費(會自動跳可用模型),並保留「一鍵切回全本地 local LLM」當後路 — 零成本,又不被單一雲端綁死 |
| Docker Compose + Traefik @ VPS | 自有 VPS 零邊際成本、公開 URL、無冷啟動;四個 service 一份 compose 檔講完 |
怎麼協作 — 不是 vibe coding
工程實作是「一人 + AI」,但每一步都有可驗證的紀律,AI 負責產出、人負責方向與把關:
| 機制 | 做法 | 擋掉什麼 |
|---|---|---|
| ADR 決策紀錄 | 每個重要技術轉向寫一篇:背景、選項、理由、影響(共 8 篇) | 「為什麼當初這樣做」失憶;AI 重構時亂動既有決策 |
| 評測閉環 | 45 條真實查詢自動評分,每輪改動跑分 — 贏的留、輸的回滾(見「評測迭代」頁) | 憑感覺調參;「看起來變好了」的錯覺 |
| pre-commit 關卡 | lint、格式、secret 掃描、測試,連「前端不准寫死中文文案」都有自動檢查 | AI 大量產碼時夾帶的低級錯誤與壞習慣 |
| git-based 部署 | 一律 commit → push → 伺服器 pull → rebuild;禁止直接上機改檔 | demo 機與程式庫漂移;出事無法回滾定位 |
| 人工審核迴圈 | AI 建議標籤 → 編輯逐一核可/退回 → 回饋進 wiki 累積成知識庫 | AI 的品味問題被靜默放行 |
| AI-native 工作流 | 匯入新片、抓獎項、片庫健檢等營運流程包成自然語言指令(agent skills),AI 跑流程、人下指令看結果 | 重複性營運工作吃掉工程時間 |
中途的重要轉向(ADR 摘要)
① 純向量搜尋 → Hybrid(BM25 + 向量 + RRF + 精排)
**痛點:**只用向量 cosine 時分數全擠在 0.55–0.65 窄帶、片名等專有名詞抓不準。
**轉向:**參考開源專案 qmd 的做法 — 字面(BM25)與語意(向量)兩路各自召回,用 RRF 融合排名,再交給精排模型。
**理由:**兩種檢索的失敗模式互補 — 字面救專有名詞,語意救意圖;融合用「排名」而非「分數」,迴避兩邊分數尺度不可比的問題。
② 手刻關鍵字解析 → LLM 查詢理解(雙保險)
**痛點:**手刻關鍵字表只覆蓋 3 個維度,「被仙人跳分手該看什麼療傷」這種句子完全無解。
**轉向:**用一次 LLM 呼叫把查詢展開成:14 維標籤訊號 + 假想劇情(HyDE)+ 檢索關鍵字。
**理由:**LLM 懂語意但會失敗(限流/壞輸出)— 所以設計成「LLM 失敗就退回手刻 parser」,搜尋永遠不會因為 AI 掛掉而開天窗。快取讓同查詢只花一次。
③ 旋鈕寫死在程式裡 → 熱載設定 + 可解釋輸出
**痛點:**調一個權重要改 code 重建;搜尋是黑盒,怪片排高無從 debug。
**轉向:**所有權重/門檻集中到一個 JSON,存檔即生效;每個結果帶「為什麼上榜」(符合哪些條件、哪條路找到的)。
**理由:**調參迭代速度 = 實驗次數;可解釋性同時服務 debug 與產品信任,一魚兩吃。
④ 手設權重 → 自我矯正評測閉環(LLM-as-judge)
**痛點:**設定值全是拍腦袋的先驗;demo 沒有真實流量,沒有點擊數據可學。
**轉向:**讓 LLM 當裁判對搜尋結果打分,建自動評測 — 系統不靠流量也能自己量、自己調。
**理由:**沒有訊號就製造訊號。這個閉環後來支撐了 v1–v8 全部的調參敘事(見「評測迭代」頁)。
⑤ 戰壕系列:把本地 LLM 裁判跑穩(四篇 ADR 的連續劇)
裁判搬到本地後連環踩坑,每個坑一篇 ADR:
- 取消傳不到伺服器 → LLM 呼叫改 streaming,client 斷線才擋得住失控生成
- 模型還在熱身就被打 → 先繞過(跑前探活),後來拆解成熟開源 agent 怎麼打同一個服務,改成「SDK + 重試吃掉熱身期」— 借鑑比硬造好
- 回應神祕變空字串 → 不是壞掉,是思考型模型把輸出額度全花在「想」上;加大輸出預算解決
**理由(整個系列的共同教訓)😗*基礎設施問題要追到「別人怎麼解的」為止,不滿足於第一個能跑的 workaround — 每次誤判也照實記錄在 ADR 裡。
INFO
完整 ADR(含被推翻的誤判過程)保存在私有程式庫;此頁為對外摘要。