跳轉到

ADR 0009: Collector-Driven Streaming Contract 與 Typing 極簡化

Context (背景脈絡)

目前串流渲染邏輯同時分散於多處(collector、typewriter 狀態機、UI helper),導致以下問題: 1. 職責混雜:欄位切換、等待動畫、字元節奏、結束判斷分散在不同層,維護時容易漂移。 2. 雙軌邏輯:存在多套近似的串流處理流程,調整動畫或時序時容易出現「一處改了、另一處沒改」的回歸。 3. 重構目標不清:當前主要痛點是「打字機體驗穩定性」,但若同時擴充 structured streaming(additional outputs 增量更新)會引入不必要複雜度。

Decision Drivers (決策準則)

本次對話中,實際用以下準則收斂方案: 1. 先修好打字機:優先解決 typing 體驗與穩定性,不擴充功能面。 2. 單一真相來源:欄位切換、等待中、結束時機不應分散決策。 3. 避免重構擴散additional_outputs 先維持現況 final-only。 4. 可觀測且可除錯:終止語義不能完全依賴「沉默」推測。

Options Considered (討論過的方案)

Option A: 完全無終止訊號(沉默即結束)

  • 內容:不送 DONE/ERROR,typing 靠 grace/idle timeout 判斷結束。
  • 優點:事件最少,typing 心智模型簡單。
  • 缺點:無法精準區分 done/error;監控與除錯訊號弱;網路抖動可能誤判完成。
  • 結論:Rejected(不採用)。可作 fallback,不應是主終止機制。

Option B: CHUNK-only(用 field 變化推斷欄位切換)

  • 內容:不送 FIELD_START,typing 看到 CHUNK.field 改變就自動切段。
  • 優點:協議極簡。
  • 缺點:renderer 承擔推斷責任,corner case 增加,debug 可讀性變差。
  • 結論:Rejected(不採用)。不符合「collector 決策、typing 執行」的邊界。

Option C: FIELD_START + CHUNK + DONE/ERROR(Collector 主導)

  • 內容:欄位切換由 collector 宣告,typing 只依事件渲染。
  • 優點:語義清楚、責任邊界明確、容易測試與追蹤。
  • 缺點:事件數比極簡方案多。
  • 結論:Accepted(採用)

Option D: Collector 直接輸出最終格式化字串(不保留欄位語義)

  • 內容:typing 只 append 字元,collector 負責標籤與 Markdown 組字串。
  • 優點:typing 最純、最簡單。
  • 缺點:collector 與 UI 表現強耦合;改版面會牽動 collector;不利未來多視圖。
  • 結論:Deferred(延後)。目前保留 presenter 層,避免過早綁死表現格式。

Discussion Outcome (對話結論)

  1. Typing 要極簡:主職責為「打字節奏 + grace timeout 防卡死」。
  2. 欄位切換決策放 collector:typing 不再自行推斷欄位。
  3. 需要終止訊號:採 DONE/ERROR 作主結束,idle timeout 作保險。
  4. additional outputs 不擴 scope:先維持 final prediction only,不做 incremental structured channel。

Decision (技術決策)

採用 Collector-Driven 的事件契約,將 typing 收斂為極簡渲染器;additional outputs 維持 final-only。

1. 事件邊界

Collector 作為唯一事件來源,負責欄位切換與生命週期訊號。最小事件集合為: - WAIT - FIELD_START - CHUNK - DONE - ERROR

2. 職責分層

  1. Collector
  2. 發送等待中事件(WAIT)
  3. 決定欄位切換並發送 FIELD_START
  4. 發送 CHUNK/DONE/ERROR
  5. Presenter/Formatter
  6. 將事件轉為 UI markdown 顯示(可持續演進,不綁 collector)
  7. Typing Renderer
  8. 僅處理字元節奏與 grace timeout
  9. 不自行推斷欄位切換決策

3. Additional Outputs 策略

additional_outputs(例如任務 DataFrame)維持 final prediction only: - 不做增量 structured streaming - 不從 chat 字串反解析 - 由最終 prediction 提取 metadata 更新

Consequences (決策結果)

Positive (優點)

  • 正本清源:collector 與 typing 的責任邊界清楚,重構與除錯更穩定。
  • 降低回歸風險:等待動畫、欄位切換與結束時序由單一來源掌控。
  • 聚焦當前痛點:先修好打字機體驗,不引入 additional outputs 增量化的額外複雜度。
  • 保留擴充彈性:未來若要增量 structured channel,可在現有分層上新增,不需推倒重來。

Negative (缺點)

  • 短期內仍有相容負擔:切換到單一路徑前,舊流程與新契約可能暫時共存。
  • 事件模型變正式:初期需要補測試與文件,導入成本上升。
  • 仍需 timeout 參數治理:idle/grace 設太短會誤收尾,設太長會體感卡頓。

Follow-up Notes (後續追蹤)

  1. 若未來需要 live structured UI,再開獨立 change 討論 incremental channel。
  2. 若要讓 collector 直接組 UI 字串,需先有明確多視圖需求再評估是否接受耦合成本。