讓 Agent 教 Agent:我的 QA Pipeline 怎麼做跨 Session 自我進化
這是 QA Pipeline 系列的第 3 篇。前兩篇講了 整條 pipeline 的結構 跟 為什麼 SSOT 是 spec 不是 test。這一篇講整個系統最有趣的一層 — 讓 agent 跨 session 累積知識。
講真的,大多數 multi-agent 系統的最大弱點就在這:每次 session 從零開始。修好的 bug、踩過的坑、debug 兩小時才通的 edge case — 下次 session 完全不記得。結果系統的「集體 IQ」= 單一 agent 的 IQ。
這篇講我怎麼解。
一個真實事故
2026-04-10 的 night shift,qa-fixer 接到一個 issue:設定頁的「修改密碼」按下去,密碼不夠強時後端會回 HTTP 422。前端原本把 error.response.data.detail 當 string 顯示在 toast — 但 FastAPI 在 validation 失敗時回的 detail 是 array of { loc, msg, type, ... }。結果 toast 顯示了 [object Object]。
Fixer 改了 useChangePassword.ts、加上 typeof detail === 'string' ? detail : Array.isArray(detail) && ... ? detail[0].msg : fallback 的處理、發 PR、merge。
故事結束?以「傳統 agent demo」來說是。但這裡有個問題:一樣的錯誤格式在 useLogin、useUpdateProfile、還有其他十幾個 hook 裡都會發生。除非剛好踩到 test、讓 E2E 失敗、讓 analyzer 報進 Linear,否則它們會繼續埋著,在 production 出現「顯示 [object Object]」這種丟臉訊息。
人類 engineer 碰到這個案例,第一個反應通常是「啊這個要全域搜一下」。但 agent 沒這個本能 — 除非你把它寫進系統設計。
跨 Session 的資料結構:.learned-patterns.json
我在 reports/.learned-patterns.json 留一個跨 session memory 檔。以 FastAPI 422 那條 pattern 為例,實際內容:
{
"name": "FastAPI 422 array detail — propagation to all hooks",
"trigger": "any hook that calls err.response.data?.detail without handling array format from FastAPI",
"fix": "apply useChangePassword.ts pattern: typeof detail === 'string' ? detail : Array.isArray(detail) && detail.length > 0 && typeof detail[0]?.msg === 'string' ? detail[0].msg : fallback",
"learned_from": "code-bug::useLogin::fastapi-422-array-detail",
"date": "2026-04-11",
"files_affected": [
"src/features/auth/hooks/useLogin.ts",
"src/features/settings/hooks/useUpdateProfile.ts"
],
"applied_count": 3,
"graduated": true,
"graduated_date": "2026-04-16",
"graduated_to": ".claude/skills/api-patterns.md"
}
五個欄位是整個系統的靈魂:
trigger— 什麼條件下 planner 要去掃fix— 發現時要怎麼處理(給 fixer 的 playbook)applied_count— 被自動套用了幾次graduated— 是不是已經從「熱區記憶」畢業成永久 skillgraduated_to— 畢業後的落腳處(例:.claude/skills/api-patterns.md)
注意 graduated 這個欄位。它是我後來才想到的,但現在整個系統最重要的設計之一。下面會講。
Feedback loop 怎麼串
以 FastAPI 422 那個 case 為例,實際跑了這幾步:
Day 1(2026-04-10)— 初次發現
qa-fixer 修完 useChangePassword.ts 後多做了一件事:問自己「這個 bug 的 root cause 有沒有通用性?」FastAPI validation response 格式不是一次性 bug,是一個 class of bug。於是寫了 Pattern #1 進 .learned-patterns.json,applied_count: 1。
Day 2(2026-04-11)— Planner 主動擴散
隔天早上 AM shift,qa-planner 開始跑。除了讀 spec,它也讀 .learned-patterns.json(對 planner 來說是 optional input,有才讀)。看到 Pattern #1 的 trigger 是「任何 hook 處理 FastAPI 422」、files_affected 只列了一個檔,於是 planner 把這條規則加進 test plan:
Requirement: 所有呼叫 FastAPI endpoint 的 hook 都必須正確處理
detail為 array 的情況。
Analyzer 拿到這個 requirement 去掃所有 hook、發現 useLogin.ts 跟 useUpdateProfile.ts 都有相同問題、開了新 Linear issue。Fixer 把兩個檔案一起修、發了 PR,commit message:fix(auth,settings): extract FastAPI array detail in 422 handlers。
applied_count 從 1 升到 3。同一天 fixer 把 Pattern #1 升級成 Pattern #2(加上 propagation trigger、更精確的 fix playbook) — 寫 learned-patterns 的是 fixer,不是 reporter,職責切得很乾淨。
Day 6(2026-04-16)— 畢業
applied_count 累積到 3、且這條 pattern 的 fix PR 沒被任何 revert 碰過 — 觸發 graduation 條件。週末跑的 qa-weekly agent 把這條 pattern graduated = true、寫進 .claude/skills/api-patterns.md、從熱區記憶裡拿掉。
之後 planner 每次 session 不用再掃這條 rule。但這個知識沒有消失 — 它現在是每個 agent 開啟時預載的 skill 之一,像內建 lint rule 一樣。
為什麼「畢業」很重要
我 skeleton 裡原本把這一層叫「auto-pruning」,但跑過兩週後發現「畢業(graduation)」才是正確的語彙。差別在:
- Pruning 暗示「丟掉」— 但這些 knowledge 我不想丟
- Graduation 暗示「升級成更穩定的 context」— planner 不用每次重掃,但 fixer 寫新 code 時還是會讀到
實際上 .claude/skills/ 裡現在有兩個畢業去處:
api-patterns.md— 跟後端溝通的通用規則(422 處理、error shape、auth header…)e2e-testing.md— Playwright 專屬陷阱(Worktree.env.development、browser cache、route mocking…)
這就是為什麼我的熱區 pattern 不會無限成長 — applied_count ≥ 3 且 fix 沒被 revert 就觸發 graduation、從熱區搬到永久 skill。兩週下來 9 條 pattern,4 條已經畢業,佔 44%。
兩週 9 條 pattern 的完整清單
為了不讓讀者覺得只有 FastAPI 這一個 case,以下是 .learned-patterns.json 目前的全部 9 條(applied_count 排序):
| Pattern | Trigger 本質 | Applied | Graduated |
|---|---|---|---|
Worktree 缺 .env.development 導致 module-level throw | git worktree 設定漏檔 | 4 | ✅ → e2e-testing.md |
| Playwright browser cache invalidation | 版本升級後 browser 遺失 | 4 | ✅ → e2e-testing.md |
| FastAPI 422 array detail propagation | 後端錯誤格式 | 3 | ✅ → api-patterns.md |
測試 helper goToStepN() 自己該 mock API | 多步流程 flaky 根因 | 3 | ✅ → e2e-testing.md |
| StoreTable dual-table strict mode | Playwright locator 歧義 | 2 | 還差 1 次套用 |
Playwright clock + useEffect([countdown]) | fake timer 語意陷阱 | 2 | 還差 1 次套用 |
process.env vs import.meta.env in Playwright mocks | test runtime 環境差異 | 2 | 還差 1 次套用 |
| FastAPI 422 array detail(初版) | (已被 propagation 版取代) | 1 | — |
| Playwright clock fastForward loading-state check | N-1ms 同步問題 | 1 | 還差 2 次套用 |
22 個總 application 次數 — 等於這 9 條 pattern 裡每一條、平均替人類省下 2.4 次 find-and-replace 級別的手動作業。4 條畢業成 skill — 代表這些知識已經從「熱區試誤」變成「系統預設」。
為什麼這不是 RAG
有人看到「跨 session memory」會直覺聯想到 RAG(retrieval-augmented generation)。不是。
差別在:
- RAG 是「塞 context 進 prompt 讓 LLM 用」— 仰賴 LLM 自己判斷相關性、容易漏
- Learned-patterns 是「deterministic 的掃描規則,planner 主動 grep / AST 掃描」— 結果可預期、可複現
舉個極端對比。如果是 RAG:
planner 拿到 80 條 pattern embedding,對每個檔案做 cosine similarity,top-K 拿進 prompt,LLM 自行判斷要不要 flag。
這條路有兩個漏洞:embedding 的相似度跟「這個 pattern 適用於這個檔」是兩回事;LLM 拿到一堆 context 還是有機會判錯。
我的作法:
planner 讀 pattern #2,看到 trigger 是「任何呼叫 FastAPI err.response.data?.detail 的 hook」。planner 直接 bash grep + AST 掃描所有
src/**/hooks/**/*.ts,找出符合 trigger 的檔案,加進 spec requirement 交給 analyzer。
沒有 similarity、沒有「LLM 自行判斷」。規則是明確的、掃描是 deterministic 的、結果可重現。LLM 只在「發現時要怎麼修」這一步才出場。
這個取捨在多數 agent 系統裡被搞反。
這個模式能抽出來嗎
可以。這其實是一個通用的「三段式自進化」結構:
- Discover(發現)— agent 修完一個問題後主動抽象出 pattern(由 fixer 寫入
.learned-patterns.json) - Propagate(擴散)— 下一 session 由 planner 把 pattern 當 requirement 擴散到整個 repo
- Graduate(畢業)—
applied_count ≥ 3且 fix 沒被 revert 過,weekly agent 把 pattern 升級成永久 skill
對應還有一個 Demote(降級)機制:如果一條已畢業的 pattern 後來被發現 regression、週 agent 會把 graduated 改回 false、從 skill 檔移除 — 避免壞知識永久污染系統。
我打算把這套抽成 claude-qa-pipeline 的 OSS 版(前置作業已經在 ~/Project/claude-qa-pipeline)。Core 部分跟 QA 解耦 — 你可以用它做 security review pattern、performance review pattern、accessibility review pattern。
對 multi-agent 系統設計的一個 takeaway
把這篇整理成一句話:
跨 session memory 是把「單 agent 的 IQ」乘法成「系統級智慧」的關鍵。沒有這一層,你的 multi-agent 系統不是系統,是一堆獨立 agent。
這也是為什麼我對「agent 框架之爭」沒什麼興趣。LangGraph vs Claude SDK vs CrewAI 是 node / edge / tool 層的選擇。真正決定系統好不好用的不是框架,是你有沒有設計跨 session 的 memory 跟 feedback loop。
下一篇 #4 會講完全相反的另一面 — 這個系統怎麼跑在一台 MacBook 上、不用 cloud、兩個班次、launchd 排程、lock 管理、Slack 通知。Infra 要簡單,agent 才能複雜。
如果你也在做 multi-agent 系統,我特別想聽到:你是怎麼處理跨 session memory 的? RAG?JSON 檔?SQLite?human-curated skill library?Twitter @waiting7777 聊聊。
Waiting7777
WoW Arena 冠軍轉前端,用電競 meta 思維拆解技術趨勢。
相關文章
AI Agents開發者工具+1Orca 七個月拿下 10 萬顆星:我拆了它的 GitHub 數據,看到 agent 開發真正的瓶頸
一個由 agent 寫出來、拿來管 agent 的開源 IDE。19,000 個 commit、979 次 release 背後,最值得看的數字是 2,525 個放了一個月還沒人處理的 PR。
2026年10月20日 · Waiting7777 · 7 分鐘閱讀
繼續閱讀 →
AI Agents自動化測試+1跑了兩週 Agent QA Pipeline — 真實數字、踩到的坑、重來我會怎麼做
這是 post-mortem,不是 case study。做對的三件事、做錯的三件事、以及為什麼這套東西接下來要抽成 `claude-qa-pipeline` OSS。
2026年10月20日 · Waiting7777 · 6 分鐘閱讀
繼續閱讀 →
自動化測試開發者工具你的 100% Coverage 是假的 — 用 test-audit 抓出沒被真正測到的分支
AI 寫的 code 跑出漂亮的覆蓋率數字,但 error path 根本沒人測過,這工具專門抓這個。
2026年10月20日 · Waiting7777 · 3 分鐘閱讀
繼續閱讀 →這篇文章對你有幫助嗎?
每週一篇 — 技術趨勢背後的商業邏輯
AI 產業在變什麼、工程師該注意什麼——拆清楚寄到你的信箱。