今天是國慶日,台灣生日快樂,我今天買了一杯星巴克、星巴克店員幫我畫了插畫超可愛的,然後今天都沒做什麼事情,唯一重要的就是晚上的三個會議(ai社、數位遊牧、steering),還有把幾票給買了,26800開飛。
昨天說下一步是把 Spark Operator 裝起來。但是今天沒有照做,先繞去做了更前面的事:幫專案取名、寫 PRD 和 TDD、把規模鎖住,再架出一個接好線但還沒有內容的骨架。
取名:Rekindle
專案叫 Rekindle,重新點燃。Spark 是火花,job 掛掉就是火熄了,這個平台做的事是把它再點起來。這個系列同時也是把荒廢的部落格重新點起來,所以一個名字用在兩個地方。這是ai幫我想的,我很滿意,然後spark和kindle又有互文性質。
先寫 PRD 和 TDD
動手之前我習慣先寫兩份文件(這也是我跟吳恩達學的,讚嘆andrew ng):
- PRD(產品需求文件):他回答「要做什麼、給誰用」。
- TDD(技術設計文件):他回答「打算怎麼做」。
PRD 裡最有用的一段是使用者。我列了三種:面試官、跑 Spark 的資料工程師、我自己。把面試官寫進去之後,有些設計自己就決定了。面試官的注意力大概只有兩分鐘,但 spot 節點從零擴起來就要好幾分鐘,所以故障實驗室預設播放錄製好的真實執行,即時執行另外給配額。
TDD 沿用我之前專案的寫法:我想要build出來的技術組合、設計選擇、資料流、評測、故障模式。「故障模式」這一節最值得寫,因為它逼我在寫程式前先想系統會怎麼壞。例如案例庫最大的風險是false positive,也就是把 A 故障的修法套到 B 故障上。所以誤命中率被列成正式指標,套用後驗證失敗的案例也會被降級,不再自動套用。
怎麼鎖定規模
- 非目標寫得比目標具體。在這個階段,我們不改寫使用者的程式碼(只進行痛點診斷)、不做通用平台、自然語言入口不放第一版。
- 只有一個硬目標。階段 3(故障實驗室公開上線)要在明年 2 月完成,其他階段都可以延後。
- 每個階段有完成定義。例如階段 0 是「一行指令能重建環境並跑完一個查詢,而且我能在 History Server 上說明一個 stage 的時間花在哪」。
- 把 agent 能做的事列成清單。修法只能從白名單挑,目前是 11 個 Spark 設定。歸因只能從 7 個固定類別選,其中一類是「設定修不了,需要人處理」。
- 不知道的先不定。成功指標我只定義了要量什麼,目標值等跑出 baseline 再說。現在填一個 80% 進去,也只是猜的。
- 先寫好要砍什麼。時間不夠的時候,先砍 agno 的對照組,再砍自然語言入口。
骨架:接線是真的,內容是空的
文件寫完就接著架骨架。原則是結構先到位,內容先留空。這個部分請AI幫我代勞,但我還是需要肉眼看一次:
- 資料夾、資料表、API 路由、分診循環的節點和分支都建好了。
- 每個節點和服務只有函式簽章和一句說明,呼叫了會直接丟
NotImplementedError,這是我在國泰學會的方法,先把想要implement的function先弄好。 - 用不到的套件先不裝。Gemini 的 SDK 和資料庫連線都還沒加,等真的要接再說。
- 暫時填的數字(embedding 維度、重試上限)都記在 TDD 的未決事項裡。
這樣做的好處是討論可以對著實際的東西進行。分診循環畫成圖只是一張圖;寫成能編譯的 LangGraph 之後,「驗證失敗要退回哪一步」這種問題就不能含糊帶過。骨架也便宜,現在整個改掉不會心疼。
迭代的方式是我和 Claude Code 來回:我給方向和限制,它起草文件和骨架,我做決定。今天定下來的決定有三個:LLM 用 Gemini、embedding 也用 Gemini、成功指標的目標值先不定。
有三個資料夾我刻意只放了說明檔:infra/、spark-jobs/、eval/。Spark 和 K8s 是這個專案要補的東西,這部分我要親手做。
測試現在測什麼
節點都還是空的,但已經有 9 個測試。它們測的是設計,不是功能:
- 路由:
/healthz正常,三個還沒實作的路由回 501。 - 資料表:7 張表的名稱和 TDD 一致。
- 循環接線:圖能編譯,10 個節點都在。
- 分支規則:案例庫命中就跳過調查;歸因是「需要人處理」就不進修法;修法不合格會重試,到上限就放棄;驗證失敗會重新調查,直到重送次數用完。
分支規則是純函式,不需要 Spark、K8s 或 LLM 就能測。這些規則原本是 TDD 裡的句子,寫成測試之後,文件和程式就不會各說各話。之後填節點內容時,只要這些測試還會過,我就知道流程沒被改壞。
cd backend && uv run pytest
之後會加的測試有兩種。一種用固定的樣本檔測確定性的部分:指紋正規化、白名單檢查、event log 解析、遮蔽規則。另一種是評測集:每個故障都有標準答案,它就是整個系統的測試。
現在的框架
| 層 | 選擇 | 狀態 |
|---|---|---|
| 資料處理 | Spark(PySpark) | 還沒開始 |
| 叢集 | 本機 kind,線上 GKE;Kubeflow Spark Operator | 還沒開始 |
| 後端 | Python、FastAPI、Pydantic、SQLAlchemy | 骨架已建 |
| 資料庫 | Postgres + pgvector | 資料表已定義,還沒連線 |
| Agent | LangGraph;agno 當對照組 | LangGraph 的接線已建 |
| LLM | gemini-3.6-flash 做調查,gemini-3.1-flash-lite 做分類 | 還沒接 |
| Embedding | gemini-embedding-2 | 還沒接 |
| 前端 | React + Vite | 只有一頁 |
| 工具 | uv、pytest、ruff | 在用 |
目錄長這樣:
backend/ FastAPI、資料模型、服務、LangGraph 分診循環
frontend/ React + Vite
faults/ 故障定義與標準答案
spark-jobs/ TPC-DS 與 Binance 的 PySpark job
infra/ kind、GKE、Spark Operator 的設定
eval/ 評測腳本與結果
docs/ PRD、TDD
接下來
下一步真的是階段 0 了:在本機用 kind 裝 Spark Operator,跑出第一個 job。最先要決定的是 Spark 用哪個版本,以及 TPC-DS 的資料怎麼產生。