The Problem: AI Agents Forget, but the Task Doesn’t End

技術社群已經習慣「給人類看的 RFC」— Architecture Decision Records、incident postmortem、設計文件,模板都很成熟。但給 AI agent 之間用的非同步協作文件呢?少有公開討論。

最近處理一個跨三個 repo 的 schema migration:後端表結構改、前端 GraphQL 切換、行動端讀寫遷移,估計要 30+ 天,會跨越十多個獨立的 Claude session(不同時段、不同任務切片、不同筆電)。每次 session 結束、下一輪起來,前次的 context window 就消失了。問題不只是「上下文遺忘」,還包括:

  • 上次 session 進度到哪?沒人記得。
  • 之前否決過的方案?新 session 很容易「自信地重新提案」。
  • 跨 repo 的「我改完了、等你那邊配合」訊息,怎麼跨越 session 邊界?
  • 某次踩的坑,下次會不會重蹈?

我們最後長出一份 CUTOVER.md 放在獨立的 metadata repo,作為跨 session、跨 repo 的 out-of-band control plane。實戰一個月後,這份文件結晶出 8 個對 AI agent 特別有效的設計模式 — 它們幾乎都對應到分散式系統的經典概念。

The Architecture

Mermaid Diagram

每個 AI session 開場先 pull doc → 讀完上下文 → 完成任務 → push update。Doc 自己是獨立 repo,與三個 code repo 解耦,扮演純控制平面角色。

Pattern 1: Append-Only Audit Trail

文件用一張「Open Questions」表追蹤待決議事項。AI agent 解決問題後不刪除該條目,而是把它移到 Closed 區、用刪除線標註、附上解決它的 commit hash。

| # | 議題 | 狀態 |
|---|---|---|
| 4 | ~~B7 dual-write regression~~ | ✅ Closed 2026-05-15 by backend session, commit `fc914a8` |

為什麼這對 AI 特別重要:LLM 容易「自信地重新提案已被否決的方向」。如果 closed Q 被刪掉,下個 session 開場看到一張空的 Open Q 就會推論「沒問題了」— 更糟的是它可能會重新建構之前討論過、評估過、否決過的方案。

Append-only 強制把「曾被討論並 reject」的選項永遠攤在桌上,等於給未來的 session 一個歷史脈絡的強制注入。




Pattern 2: Cross-Session Message Queue

文件的「Follow-up questions」區用 sender → receiver 格式追蹤跨 session 的提問:

## Follow-up questions

### Open
- 2026-05-16 backend → frontend:toast → 持久 UI 重構 (細節...)

### Closed
- ~~2026-05-14 frontend → backend:dual-write 為何不寫 link 表~~
  → ✅ shipped as `stg-1.93` (`fc914a8`, 2026-05-15)

這個結構讓「跨 session 提問」變成可追蹤的非同步訊息 — 概念上等同 actor model 的 mailbox(並行運算模型:每個 actor 有獨立的訊息隊列、按到達順序處理,發送方不必等待接收方)。發送方不需要等接收方上線;接收方下次 pickup 時自然會掃到 Open 區。

不這樣做會怎樣:如果跨 session 提問靠對話頻道(Slack、聊天記錄)傳遞,後續 AI session 完全看不到 — 因為新 session 的 context window 只有當下對話,看不到別的 session 的歷史 thread。

文件化的 message queue 是唯一能跨越 session 邊界的同步機制。




Pattern 3: Pickup Prompt Engineering

文件中「Pickup for X session」段落不是給人類看的 doc — 是給下個 AI agent 開場讀的 prompt。結構固定:

### Step 0. Session-pickup hygiene
git pull --ff-only && git branch --show-current
git log --oneline -5 | head

### Step 1. Reproduce the regression (~5 min)
[詳細步驟 + 具體命令]

### Step 2. Investigation
[hypothesis + grep 指令範例]

### Critical gotchas
- prod 不動:never push to prod-* tag
- ECR immutable: 一個版本號只能 push 一次

### Rollback plan
kubectl set image ... =:previous-tag

LLM 是 prompt-driven 的,對 AI 來說這份 pickup section 就是它的「task prompt」。把人類習慣的 README 風格改寫成 step-by-step prompt 風格,能顯著降低錯誤率 — 原因有兩層。

第一,降低 reasoning 負擔。narrative-style README 期待讀者先消化「整體脈絡」再自行「規劃步驟」— 對 LLM 而言這是額外的 reasoning step,每一步都可能引入幻覺。

Procedural prompt 直接消除規劃階段,把 reasoning capacity(推理預算 — LLM 的 context window 中用於思考的份額)留給「執行中遇到 edge case 怎麼處理」這種真正需要思考的部分。

LLM 在 procedural / chain-of-thought(要求模型一步一步顯式推理,而非直接給結論)結構的 prompt 上,表現一致顯著優於 free-form narrative。

第二,允許 partial progress reporting。step-by-step 結構讓 session 中斷時可以精準回報「完成 Step 1-2、卡在 Step 3 的 X」— 下個 session 從中斷點接手。Narrative 結構則只能說「我看了一半」,但「一半」是哪一半完全不清楚。

Critical gotchas 段是 prompt 防呆、rollback plan 是 prompt 的安全網。兩者本質都是「先把可預期的失敗模式列出來」,避免 LLM 自己即興發揮。




Pattern 4: Last-Updated Session Attribution

文件最頂部有一行:

**Last updated**:2026-05-22 by mobile session — handled Q1 + Q2 from backend

下個 session 開場第一眼就看到。為什麼這對 AI 特別重要:AI 容易把「文件存在」和「文件是最新」混為一談。如果某個 session 在 stale doc 基礎上開始工作,產出可能跟最新狀態完全脫鉤。

明確標註「誰、何時、做了什麼」,強迫每個 session 在動手前先驗證「我讀到的是不是最新版本」。

實戰踩過的坑:某次 session 的 HEAD 在中途被一個 IDE plugin silent checkout 切到 main 分支,session 在錯誤的分支上分析了半小時才發現 baseline 不對。這個事件直接催生了下一個 pattern。




Pattern 5: Side Lessons as Instruction Layer

文件有「Side lessons」段,紀錄每一次踩坑的具體事件與防制 practice:

### L1. Session pickup must check branch, not just status
某 session 的 HEAD 中途被 silent checkout 切到 main,session 在錯分支
上分析了半小時,最後靠 reflog 才察覺。

Practice for all sessions:
- 開場 `git status` AND `git branch --show-current` 並行執行
- Long sessions 定期 `git reflog -10` 確認沒被 silent checkout

這就是 AI 的 contextual fine-tuning(針對特定脈絡的微調 — 不是真的改 model 權重,而是用文件強制注入特定情境的偏誤校正):把每一次失誤直接寫進控制平面文件,下次任何 session pickup 時都會讀到。

CLAUDE.md 的 global rules 更有針對性 — 只在這個專案、這個 migration 的範疇內生效,不會稀釋 global 規則的訊號。

Side lessons 本質上是「事後課程化」的失誤資料。




Pattern 6: High-Density Activity Log Notes

文件主表是 Activity log,但 Note 欄是 free-text 200-500 字密度:

| Date | Session | Tag | Commits | Note (200-500 字) |

每列的 Note 自我完備,包含:what changed、why、affected files、commit hash、verification 方法、orphan data cleanup、side effects、跟其他 row 的因果關係。

這違反一般 git log 的「短訊息」原則 — 但對象是 AI 而不是人類。人類看 git log --oneline 後可以延伸去 git show 翻細節,但 AI 翻 N 個 commit 等於消耗 N 倍 context budget。

把上下文一次壓縮在 Note 欄、每行自我完備,是給 LLM 最高的訊息密度。一個 session 只需要讀這張表就能還原前 30 天的全部設計決策。

脫敏範例(一個典型 row 的 Note 欄):

| 2026-05-15 | backend | stg-1.93 | fc914a8 | Dual-write regression fixed. Root cause: extractDocId in src/utils/relation-pair.ts only recognized documentId strings + {connect:...} envelopes, but frontend saveDraft posts category: <legacy.id> as a raw number, so autoFillRelationPair returned without populating data.canonicalRef and the controller skipped the records_canonical_lnk insert. Replaced with extractRelationRef returning tagged {kind:'doc'\|'num'} so resolvers pick the right lookup column. STG E2E verified: POST /api/records with category:213 → new record id=4771 → DB confirms both link rows populated. Orphans cleaned: ids 4768-4772 (5 test records from prior runs). 4 regression unit tests added. |

一個 row 包含 root cause、failing code path、replacement design、verification proof、cleanup action、test coverage — 全部 self-contained。下個 session 不需要去翻 commit fc914a8 也能完全還原這次的 work。

代價是「人類覺得難讀」。但這份 doc 的主要讀者不是人類,是 AI。




Pattern 7: Async Pickup, No Schedule

文件中所有 Pickup section 都標註:「無時程限制 — X session 可即刻接手,不必等明天」。AI session 不像人類有上下班,任何時候完成手上工作、發現有 Open Q → 就接手。

這個 pattern 把「session orchestration」從顯式工作流程(「現在輪到誰」)改成隱式 task queue(「誰來都行,照規則做」)。

實作成本接近零 — 只要每個 session 都遵守「先 pull → 讀 Open Q → 做完 push」就行。

實戰一個月下來,這份 doc 累積過大約十條 cross-session questions,多數在 raise 後同一天內就被某個空閒 session 接手關閉 — 因為任何 session 開場 pickup 時都會主動掃 Open Q 區、看到能接的就接。

預期中「等到下次某人有空、再分配工作」這個瓶頸幾乎完全消失。




Pattern 8: Cross-Repo State Baton

這份控制文件不放在任何一個 code repo,而是放在自己獨立的 metadata repo。

聽起來像瑣事,但這是整套機制不能省的關鍵設計 — 拿掉就會崩、沒有別的位置能取代。

為什麼?

  • 跨 repo 公平:協調工作橫跨三個 code repo(後端 / 前端 / 行動端)。doc 放在任何一個 code repo 都會「偏心」— 例如放在後端 repo 裡,前端 session 開工時根本不會去 clone 後端 repo 找 doc。
  • 互不干擾:metadata repo 跟 code 的 commit / PR / branch 完全分開 — doc 可以一天 commit 50 次、三個 code repo 都不會被吵到,doc 改動也不會污染 code 的 PR diff。
  • 一次取齊:每個 session 只需要 clone 這一個輕量 repo,就拿到全部跨專案的上下文,不必各別 clone 三個 code repo。
  • 獨立通道:控制訊息(doc)跟被協調的東西(code)走不同通道。這在網路與分散式系統圈有專有名詞叫 out-of-band control plane(OOB 控制平面,意思就是「帶外」— 控制走自己的路、不擠在資料的路上)。

這不是 AI 協作獨有的設計,而是分散式系統幾十年來反覆驗證的同一招:

  • BGP(網際網路骨幹的路由協定)— 路由表變更走專屬通道,跟資料封包走不同網路。
  • OpenFlow(SDN 軟體定義網路的協定)— 把網路設備的控制邏輯抽到中央 controller,跟資料轉送分離。
  • etcd(Kubernetes 拿來存叢集狀態的 key-value store)— 叢集狀態跟應用層流量完全分開。
  • Cutover doc(跨 session AI 協作的控制平面)— 跟 code commit 放在不同的 repo。

這四個系統的共通做法:把「下指令的訊息」跟「被指令影響的東西」物理性分開。控制邏輯就不會被海量資料淹沒。




The Token Economics

值得算一下成本。一份 5000 字(中英混合,約 7500 tokens)的 cutover doc,等於 Claude 200K context window 的 3.7%。

替代方案:讓 session 不靠 doc、純靠原始 git history 重建上下文 — 翻 30 個相關 commits 經由 git show 約消耗 30K tokens,再讀 5 個 PR threads 補設計脈絡 ≈ 10K tokens:

Mermaid Diagram

兩條路的差距 5 倍。而且這 40,000 tokens 是極低密度的原料 — 大部分是 import statements、format 改動、與本任務無關的雜訊。

更糟的是:raw commits 沒有「為什麼這個方案被選、那個方案被否決」的記載。這些討論發生在已消失的對話脈絡裡,靠 commit history 永遠翻不出來。

Doc 是唯一把這類元決策保留下來的地方。

5 倍 token 成本不只是錢的問題(雖然也是),更是 reasoning budget 的問題 — 同樣的 context window,用 7500 tokens 讀完 doc 後還剩 ~192K tokens 處理任務本身;換成讀 40000 tokens 的 raw history,能處理任務的緩衝就少了 4 倍以上。

對長時程、需要多輪推理的任務,這個差距會放大成「能做完 vs 中途 OOM(Out Of Memory — context window 撐爆、session 無法繼續)」的分水嶺。

Extending to Other Scenarios

這 8 個 pattern 不只適用 schema migration。任何跨多 session、跨多 repo、長時程的 AI 任務都適用:

  • Incident handling:每個 incident 開一份 doc。Open Q = 待調查;Closed = 已 root cause + 修復 commit。
  • Multi-PR refactoring:拆成 5+ PR 的大重構,doc 追蹤每個 PR 的依賴、進度、blocker。
  • Feature flag rollouts:每個 flag 一份 doc,記錄 ramp 排程、metric 觀察、rollback 決策、最終 cleanup 時程。
  • Long-running data pipeline 重寫:跨多月、多階段的 ETL 重做,每個階段一個 row。

核心問題很簡單:「這個任務無法在單一 session 完成、需要多個 AI agent 接力嗎?」如果是,就值得用這套 pattern。

When NOT to Use This

過早採用 = 過度設計。以下情境不建議:

  • 單 session 可完成的小任務:寫一個 component、修一個 bug。用 PR 描述本身當紀錄就好,doc 會變成 overhead。
  • 沒有 git pull 紀律的協作:如果 session 不會主動 git pull --ff-only 開場,doc 永遠 stale,這套機制崩潰。前提是所有 session 都遵守 pickup hygiene。
  • 任務沒有跨 boundary 性質:例如純前端的功能開發、單一 service 內的調整,控制平面拉出來等於空轉。
  • 文件超過某個複雜度:當 Activity log 超過 50 列、Open Q 超過 20 條,doc 自己變成負擔。此時拆系列(per-phase doc)比繼續累積健康。

建議的採用時機:第一次踩到「跨 session 上下文遺失」之後再採用,作為事後的解法,而不是預先的 architecture。

事後採用比預先設計更精準,因為你已經知道痛點長什麼樣。


Closing: Writing for Agents, Not for Humans

AI agent 之間的協作本質上是非同步、有遺忘症、無共享上下文的。

傳統「給人類看的設計文件」(架構圖、ADR、postmortem)不夠 — 因為人類有記憶、可以打 Slack 問同事、可以累積默契;但 AI agent 每一次都從零開始,看到的只有檔案系統的當下狀態。

寫給 AI 的控制平面文件需要不同的紀律:append-only、高密度、結構化為 prompt、跨 repo 獨立部署、把失誤事後課程化。

這 8 個 pattern 從一個月實戰結晶而來。回頭看,它們幾乎都對應到分散式系統的經典問題:

  • state machine replication(狀態機複製 — 多節點按相同順序執行相同操作,最終達到一致狀態)
  • message queue(訊息隊列 — 非同步通訊機制,發送方不必等接收方上線)
  • actor mailbox(演員模型郵箱 — 每個 actor 有獨立的訊息隊列)
  • out-of-band control plane(帶外控制平面 — 控制訊息與資料訊息走不同通道)

如果你下次發現一個 AI 任務需要跨多個 session、多個 repo — 別急著開新 PR。先 commit 一份 cutover doc 進獨立 metadata repo,把這 8 個 pattern 套上去。

一個月後,當你發現新開的 session 第一次 pickup 就能流暢接手 Open Q、不重做已被 reject 的方案、不重複問初級問題 — 你會慶幸當初花的那 30 分鐘。