我把 repo 做成 OKF bundle,讓 AI agent 不再失憶,卻差點讓我漏掉一個 bug
這個網站是我和 Claude Code、Codex 一點一點做出來的。想到什麼就請它們加什麼,速度很快;直到有一天我發現,它們每次進來都不知道前面做過什麼。
不是它們笨,是它們沒有記憶。每開一個新 session,都是冷啟動——不可能每寫一篇文章,就回頭重讀整個 repo、爬一遍 git。久了,它們會重新猜架構,甚至跟前一版的自己打架。
我最近正在研究 Google 的 OKF(Open Knowledge Format)——一種給 AI agent 使用的結構化 Markdown 知識格式。於是我把這個 repo 的「腦」做成一個 OKF bundle。做完後,我沒有先假設它有用,而是直接做了個實驗。結果它救了我一次,也差點讓我漏掉一次。
這個網站沒有一份會持續維護的記憶
問題在於,知識沒有放在 agent 會讀、也會更新的地方。架構為什麼這樣設計、有哪些踩過的雷、哪些是刻意的決策——這些散落在 commit 訊息、我的腦袋,還有某次過去的對話裡。下一個 session 一律看不到。
於是每個 session 都要重新爬 code,重建自己的心智模型:慢,也貴。碰到 code 裡看不出來的決策(例如某個坑當初是用平台設定解的,不是改 code),agent 根本無從得知,很可能往錯的方向修。
我替 repo 加上一個 OKF bundle
OKF 的核心其實很單純:一個目錄的 Markdown 檔,每個檔案放一個概念,開頭用 YAML frontmatter 標示 type(concept/howto/reference/decision),概念之間再用普通 Markdown link 連成一張知識圖。agent 從 index 出發,沿著連結走訪,而不是漫無目的地搜尋。
我在 repo 建了一個 /knowledge 目錄,把這幾週真的做過的東西各寫成一個節點:SSR 雙渲染器、文章資料模型、系列文章系統、i18n、資料快照管線、部署方式,還有踩過的雷。這些內容都有實際依據,不是憑空生成的。
但 bundle 不是放進 repo 就會自己運作。agent 不會自動讀、也不會自動更新。我把「動工前先讀相關節點、改完後更新它」的紀律,寫進 agent 預設會載入的入口檔(AGENTS.md 給 Codex、CLAUDE.md 給 Claude Code)。有人讀、有人更新,這份記憶才有機會活下來。
我沒有靠感覺,直接做 A/B 實驗
我不想只憑感覺說「有用」。所以我開了兩個冷啟動 agent,丟給它們同一個刁鑽、只有這個 repo 才答得出的問題:一組被要求先讀 /knowledge,另一組被禁止讀知識庫、只能爬 code。接著比較兩件事:答得對不對,以及花了多少功夫。
第一題:兩組都答對,但讀記憶快一倍
第一題問的是:「如果我在文章頁加一個 React 元件,Google 爬蟲看得到它嗎?要改哪個檔?」正解不直覺——文章頁的爬蟲 HTML 是另一支腳本手寫產生的,client 端又用 createRoot 清空重繪,所以只加到 React 元件是不夠的。
兩組都答對了。這題對知識庫有點不公平,因為那支腳本剛好有一段很有幫助的註解,純爬 code 也查得到。但讀知識庫的那組,用了一半的時間、三分之一的 token 就到位。這一題比的是效率。
第二題:知識庫補上了 code 裡沒有的答案
第二題我給的是真實症狀,不給答案:「線上網站 console 一直噴 React #418(hydration mismatch),手機 LCP 慢到 8 秒,該怎麼修?是改 code 還是別的?」
讀知識庫的那組在幾秒內就答對真因:是 Cloudflare 的 Email Obfuscation 在邊緣把頁面上的 email 改寫掉,讓伺服器 HTML 跟 React 對不上。它也給了正確的決策——去 Cloudflare 後台關掉那個開關,不要寫 code 繞過它。
純爬 code 的 agent 無從知道這件事,因為它根本不在程式碼裡。這正是 OKF 記憶能補上的地方:它記得「為什麼」和「當初的決策」,而這些不是 code 本身能表達的。
但記憶也差點把我帶偏
只爬 code 的那組反而挖出一個知識庫裡沒有的真 bug:首頁有段程式在畫面渲染當下呼叫 new Date() 算「今天是第幾天」,build 當天的數字會被烤進預渲染的 HTML,隔天訪客算出不同數字——一樣會觸發 #418。
讀知識庫的那組則停在知識庫記錄的原因(Cloudflare),沒有繼續往下查,因此漏掉了這個 bug。記憶讓它不再懷疑,也不再繼續找。這就是錨定偏誤。
| 只爬 code | 讀知識庫 | |
|---|---|---|
| 成本 | 約 6.7 分、23 次工具呼叫 | 約 49 秒、4 次 |
| 答出「code 裡沒有的決策」 | 不可能 | 是(關 dashboard、別改 code) |
| 找到另一個真 bug | 找到 | 沒有(錨定在已記錄的原因) |
真正的教訓:記憶用來召回,不用來當真相
兩組其實都答對了,只是對的是不同的 #418。這個網站真的有兩個 hydration 地雷:一個是邊緣層改寫 HTML(code 裡看不到,靠記憶才知道當初怎麼解),一個是 render 期的日期值(記憶裡沒有,靠當下分析才挖到)。
我的結論是,記憶可以用來召回:它速度快,也能帶出 code 沒寫的決策;但現況仍要重新驗證,不能讓記憶變成停止追查的理由。驗證後找到的新資訊,也要回寫進記憶。
這次我把那個雷區節點從「只講 Cloudflare」改成「#418 的兩類成因」,加上一條明確規則:「別對到第一類就停手」,也順手把那個真 bug 修掉。實驗本身讓這份記憶變得更好——這大概才是這套做法該有的樣子。
我還不確定這是不是管理 agent 記憶最好的方法。但至少現在,Claude 跟 Codex 來這個 repo 不再是完全的冷啟動,而我也記住了:不能把記憶當成不用再查的藉口。這份 knowledge bundle 就在 repo 裡,和程式碼一起被版本控制。
延伸閱讀:Google OKF 會取代 RAG 與向量資料庫嗎?