後端做得再精巧,使用者感受到的只有兩件事: 「我按下去之後,看得到它在動嗎?」以及「這些任務能不能串起來自動接力?」 前者是即時進度串流,後者是 pipeline 編排。 這一篇——系列最終回——談的就是這條「從按鈕到體驗」的最後一哩路。
四篇走下來:架構(Part 1)、可靠性引擎(Part 2)、實戰任務(Part 3)、生產化(Part 4)。這些都是使用者看不見的底層。最後這一篇,我們談使用者唯一真正碰得到的東西:前端體驗,以及把任務串起來的 Pipeline。
一、即時進度串流:AI 任務為什麼非有它不可
先講「為什麼」。一個 AI 任務可能跑 30 秒到好幾分鐘。如果使用者按下「執行」後盯著一個沒有任何反應的畫面……
沒有即時進度
─────────────────────────────
[執行] → (轉圈圈…30 秒…) → 使用者以為當機 → 重新整理 → 更慘
有即時進度
─────────────────────────────
[執行] → 「登入中…」→「掃描案件…」→「撰寫第 3 篇提案…」
→ 「送出中…」→「完成 ✅」
使用者全程知道它活著、在做什麼、還要多久
對 AI 任務來說,即時進度不是體驗加分,是信任的基礎。因為任務久、又是黑箱,不給回饋,使用者就會焦慮、就會亂按。
這個系統用 SSE(Server-Sent Events) 解決,Part 1 提過機制,這裡完整拆開。
二、SSE 串流的完整運作:四個零件
┌───────────────────────────────────────────────────────────┐
│ ① 背景任務寫 log │
│ Flow 執行中,每完成一小步就 append 一條 log 到 DB │
│ progress.py 用「原子的 SQL json_insert」寫入 │
│ (不是 read-modify-write,避免併發競態) │
└───────────────────────┬───────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────┐
│ ② DB 是「單一事實來源」 │
│ Run 紀錄裡有一個 JSON 陣列的 log + 當前 status │
└───────────────────────┬───────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────┐
│ ③ SSE handler 每 0.5 秒 poll 一次 DB │
│ GET /api/runs/{id}/stream │
│ 讀出「比上次多的新 log」+ 最新 status,推給前端 │
│ 直到 status 進入終態(success/failed)才關閉串流 │
└───────────────────────┬───────────────────────────────────┘
▼
┌───────────────────────────────────────────────────────────┐
│ ④ 前端用原生 EventSource 接收 │
│ const es = new EventSource(`/api/runs/${id}/stream`) │
│ es.onmessage = e => appendLogLine(JSON.parse(e.data)) │
│ 零函式庫、瀏覽器原生支援 │
└───────────────────────────────────────────────────────────┘
三個設計決策值得深究。
決策 1:為什麼是「poll DB」而不是 WebSocket + pub/sub?
選擇 poll DB 的理由 WebSocket+Redis 的問題
────────────────────────────────────────────────────────────────
SSE + poll DB log 本來就寫在 DB(要持久化); 要多架一個 Redis;
(這個專案) poll 只是多讀一次; WebSocket 是雙向的,
天然抗重整——重連就從 DB 但進度只需單向推送
接著讀;實作極簡 (殺雞用牛刀)
WebSocket+pub/sub 即時性更高(毫秒級) 架構複雜、多一個故障點
關鍵洞察:進度串流是「伺服器單向推給瀏覽器」——這正是 SSE 的主場,不需要 WebSocket 的雙向能力。而且 log 反正要寫進 DB(才能事後查、才能重整不丟),那 SSE handler「順便每 0.5 秒讀一下」幾乎是免費的。用剛好夠的工具,別為了「聽起來高級」而上 WebSocket + Redis。
決策 2:為什麼 0.5 秒,不是 0.1 秒或 2 秒?
這是一個延遲 vs 負載的平衡:
0.1 秒:太頻繁,DB 被 poll 爆(每個在跑的 run 每秒 10 次查詢)
2.0 秒:太慢,使用者感覺「卡卡的」、不夠即時
0.5 秒:人眼幾乎感覺不到延遲,DB 負載也可接受 ← 甜蜜點
翻轉條件:當同時在跑的任務數暴增(例如上千個並發 run),每個都 0.5 秒 poll 一次 DB 會變成負擔——那時才值得升級到 pub/sub 或 LISTEN/NOTIFY。在那之前,poll 的簡單性完勝。
決策 3:為什麼用原子 json_insert 寫 log?
因為背景任務在併發地產生 log。如果用「讀出整個陣列 → append 一筆 → 寫回」(read-modify-write),兩個併發寫入會互相覆蓋、掉 log。
危險做法(read-modify-write):
任務A 讀 [1,2] ──┐
任務B 讀 [1,2] ──┤ 兩個都讀到 [1,2]
任務A 寫 [1,2,3] ┘
任務B 寫 [1,2,4] → 蓋掉 A!log 3 消失 💥
安全做法(原子 json_insert):
直接叫 DB「在陣列尾巴插一筆」,由 DB 保證原子性
→ 不管多少併發寫入,一筆都不掉 ✅
這是一個很小但很專業的細節:把併發安全性下推給 DB,而不是在應用層自己用鎖去搞——後者又複雜又容易錯。
三、前端:為什麼一個 AI 平台選擇「不用框架」
這個專案的前端是 Vanilla JS + HTML/CSS,沒有 React、沒有 Vue、沒有 build step。在 2026 年這聽起來像逆流,但對這個專案是對的選擇。
選擇 Vanilla JS 的理由 React/Vue 的代價
────────────────────────────────────────────────────────────────
Vanilla JS 無 build pipeline(改完存檔重整 要 node/npm/webpack/vite;
(這個專案) 即可看);EventSource 原生支援; build 產物要打進 image;
頁面少、互動不複雜,框架的 對「幾個表單 + 一條 log
好處用不太上 串流」是殺雞用牛刀
React/Vue 複雜 SPA、大量共享狀態、 對這個規模是純負擔
團隊協作大型前端時才划算
判斷準則很簡單:這個前端有多少「複雜的共享狀態」? 這個平台的前端主要是「選任務 → 填表單 → 看一條即時 log」。狀態很淺、頁面很少。框架最大的價值(複雜狀態管理、組件複用)在這裡幾乎用不到,卻要付出整套 build 工具鏈的代價。
翻轉條件:當前端長出複雜的互動(拖拉式的 pipeline 編輯器、大量即時協作、深層狀態樹)時,框架才開始回本。這也提醒我們:技術選型要看「這個專案現在是什麼」,不是「業界流行什麼」。
四、兩個 UI 的 PR:landing page 與 Waymo 主題
對應 PR #6:
feat(ui): add landing page and refine typography/header對應 PR #15:redesign(ui): Waymo cinematic daylight theme
PR #6:landing page 與排版
一個工具型平台,第一印象很重要。這個 PR 加了 landing page,並打磨了 typography 與 header。這件事常被工程師輕視,但排版與第一眼觀感,直接影響使用者對「這東西專不專業」的判斷——尤其當你想給非工程師(例如業務、行銷)使用時。
PR #15:Waymo 電影感日光主題
這是一次完整的 UI 重新設計,採用一套「Waymo Cinematic」規格:
Waymo Cinematic Daylight 主題
─────────────────────────────
• 明亮的 light theme(日光感)
• Waymo 藍的色票(clean、科技、可信)
• 電影感的 hero 漸層 + 環境感知光暈(ambient perception glow)
為什麼一個個人專案要花力氣做這麼完整的視覺語言?因為一致的設計系統會讓產品「感覺是真的」。零散、東拼西湊的樣式,會讓使用者潛意識覺得「這是個玩具、不太可靠」。一套有명確色票、字體、光影規則的主題,傳遞的訊息是「這是認真做的東西」——而這份信任,會延伸到他們對「AI 跑出來的結果可不可信」的判斷。
UI 不只是好看:對一個要處理金錢(成本)、操作外部帳號(投標、寄信)的平台,視覺上的「專業感」是建立信任的一環。
五、Pipeline 編排:把任務串成自動化工作流
到目前為止,每個任務都是「單獨跑一次」。但真正的自動化威力,來自把任務串起來——一個任務的輸出,變成下一個任務的輸入。這就是 pipeline 任務類型。
{{steps.N.result}} 模板語法
pipeline.py 用一個簡單的模板語法,讓後面的步驟引用前面步驟的結果:
Pipeline 定義(概念)
─────────────────────────────
步驟 0:shopee_seller_scraper → 爬出一批賣家
步驟 1:web_scraper → 輸入 = {{steps.0.result}}
(拿步驟 0 的賣家清單去逐一抓資料)
步驟 2:email_sender → 輸入 = {{steps.1.result}}
(把整理好的資料寄出)
┌──────────┐ result ┌──────────┐ result ┌──────────┐
│ 步驟 0 │──────────▶│ 步驟 1 │──────────▶│ 步驟 2 │
│ 爬賣家 │ {{steps │ 抓資料 │ {{steps │ 寄信 │
│ │ .0.result}}│ │ .1.result}}│ │
└──────────┘ └──────────┘ └──────────┘
每一步都是「一個完整的任務」,享有 Part 2 的全套 Harness
(重試、驗證、評分、記帳)——pipeline 只負責把它們接起來
為什麼是「字串模板」而不是視覺化拖拉編輯器?
選擇 字串模板的理由 視覺化編輯器的代價
────────────────────────────────────────────────────────────────
{{steps.N.result}} 實作極簡;pipeline 可存成 要做一整套前端 DAG
(這個專案) 純 JSON;好版本控管、好複製 編輯器,工程量巨大
視覺化 DAG 對非工程師友善、直觀 殺雞用牛刀(現階段)
這又是一次「複雜度剛好」的取捨。字串模板雖然不如拖拉介面漂亮,但它把 pipeline 變成一份可序列化的 JSON——可以存、可以版本控管、可以複製貼上分享。對現階段的使用者(多半懂技術)這完全夠用。翻轉條件:當目標使用者變成完全不懂技術的人、且 pipeline 變得很長很複雜時,視覺化編輯器的投資才開始值得。
Pipeline 的美妙之處:組合的威力
pipeline 讓 11 個任務不再是 11 個孤島,而是可以自由組合的積木:
潛在客戶開發 pipeline:
Google Maps 名單 → email 驗證分級 → 個人化寄信
競品研究 pipeline:
Shopee 爬賣家 → web_scraper 抓各賣家詳情 → 彙整報告
內容監控 pipeline:
HN digest → X 爬文 → 摘要整理 → 寄給自己
而且——每一個步驟都自動繼承了整套 Harness(Part 2):步驟裡的任何一步 LLM 掛了會自動 fallback、結果不合格會自我修正、每步都被評分記帳。pipeline 本身只做一件事:把結果從一步傳到下一步。可靠性?底層早就包辦了。
這正是整個系列的主題在最後收束:因為地基(Harness)打得好,上層的組合(pipeline)才能又簡單又可靠。
六、五篇回顧:這個專案到底教了我們什麼
走完五篇,我們把 agent_auto_system 從裡到外看了一遍。如果要濃縮成幾條可帶走的原則:
| 主題 | 核心原則 |
|---|---|
| Part 1 架構 | 把「跟 LLM 打交道的髒活」抽成 Harness,業務邏輯才乾淨 |
| Part 2 可靠性 | LLM 預設不可靠;分清「值得重試」與「重試也沒用」;品質要有客觀分數 |
| Part 3 任務 | 沒有唯一的 AI 寫法;讓 LLM 少做、把對的步驟交給對的工具 |
| Part 4 生產化 | 為「短命基礎設施 + 多人使用」而設計;可觀測性掛在唯一漏斗 |
| Part 5 前端 | 用剛好夠的工具(SSE 不 WebSocket、Vanilla 不框架);組合的威力來自扎實的地基 |
貫穿全部的一條線是:「複雜度要花在刀口上」。這個專案在該講究的地方(Harness 可靠性、可觀測性、權限)毫不妥協;在不必要的地方(前端框架、pub/sub、視覺化編輯器)果斷選擇簡單。這種判斷力——知道什麼時候該複雜、什麼時候該簡單——才是把一個 AI Demo 做成一個 AI 產品,真正的分水嶺。
系列導覽
- Part 1:系統總覽、架構、資料流
- Part 2:Harness 引擎——多模型容錯、自我修正、LLM 評審、成本追蹤
- Part 3:自動化任務實戰——Shopee、Google Maps、Tasker、利潤健檢
- Part 4:生產化之路——Langfuse、Docker、AWS、權限
- Part 5(本篇):前端體驗與 Pipeline 編排
對應的 PR:#6 landing page、#15 Waymo 主題 專案原始碼:github.com/yennanliu/agent_auto_system
感謝一路讀到這裡。如果這個系列對你有幫助,不妨去 clone 這個 repo,自己跑一次——最好的學習,永遠是把它跑起來、然後試著加一個你自己的任務。
