系列說明 >> 本系列大部分 PR 來自私人或公司專案,部分程式碼、環境設定與實作細節不便公開。
我只能保證文中提到的 PR 與問題皆為真實案例,但會經過必要的匿名化與內容調整。
本系列主要分享問題如何被發現、背後的思考方式,以及解法如何形成,不會深入討論完整實作與部署細節,敬請見諒。
Base Repo: paulsha-hippo
Change Ref: Issue #136-knowledge 層衛生 → PR #137-feat(hygiene): provenance、show --agent、supersedes、episodic、follow-ups
Issue: Hippo 已經能 Capture、整理、Search、Recall,也能知道 Agent 是否真的 Read;但 Knowledge 長期累積後,舊答案、新答案、Session 當下狀態與尚未完成的 Follow-up 全部混在同一層。真實 MCU Knowledge 盤點甚至抓到舊 Flash Layout Note 仍被 Recall,而更新後的 Note 已經存在。
Root Cause: Memory 只解決「不要忘記」還不夠。Knowledge 缺少可回指 Code State 的完整 Provenance、跨 Session 的 Supersedes Lifecycle、Episodic/Durable Knowledge 分層,以及把「還欠著的事」拆成獨立可追蹤待辦的能力;結果是「被記住」的內容很容易全部長得像現在仍有效的答案。
Solution: SessionEnd 補齊 commit/branch/dirty 並貫通 Provenance;同主題 Knowledge 建立明確 Supersedes 關係;Session-state Memory 降到 memory_layer: episodic、保留但不進 Index;可行動缺口抽成 Follow-up Ledger——也就是把可行動缺口拆成獨立可重查、可驗證、可關閉的待辦帳本。Agent Read Path 另補 hippo show --agent,避免每次把大塊 Frontmatter 一起塞進 Context。
Evidence: 2026-08-25 盤點全域 4,178 筆 Knowledge:3,218 筆 provenance.commit=_unknown;舊/新 Flash Layout Note 無 Supersedes 且舊 Note 仍會被 Hook 撈到;104 筆 Body 含 Session-state 訊號,收緊分類後 Live Dry-run 實得 32 筆 Episodic;235 筆含可行動語句、337 個 path:line 引用散在 159 筆。PR #137 全套 1949 passed / 4 skipped / 193 subtests,OpenSpec 21/21,Whole-branch Review 的 2 Critical+4 Important 全部修完再審通過。這些證據支持 Knowledge Lifecycle 與 Hygiene 能被機械化管理,不證明系統已能自動判定哪一則工程經驗永遠正確。
D23 一路做到 D28,Hippo 終於不再只是存檔。
Session 會進來。
Knowledge 會長出來。
Task 來了也真的會 Recall。
用了一段時間後,我在兩個 MCU Project 做了一次 Knowledge 資產盤點。
第一個把我叫醒的不是 Missing。
是兩份都在。
一份 8/17 的 Flash Layout Note,內容還停在:
NVS 沒有保留。
另一份 8/21 的新版,已經有 Linker Assertion 把雙目標 Flash Layout 保留下來。
新的在。
舊的也在。
而且 Hook 還真的會把舊的撈回來。
……
這次不是 Memory 忘記。
是它記得太完整。
我要先搞清楚,這兩份到底各自是在什麼 Code State 下成立。
結果把全庫攤開:
knowledge: 4,178
provenance.commit = _unknown: 3,218
三千多筆 Knowledge,我知道它哪天被收進來。
但不知道它對應哪個 Commit。
captured_at 可以告訴我筆記哪天進 Memory。
不能告訴我那天 Repo 到底長什麼樣。
這對工程 Knowledge 很致命。
同一句:
deadline_seconds = 600
在不同 Commit、不同 Branch,甚至 Dirty Worktree 裡,都可能有完全不同的意思。
所以 PR #137 先把來源往前補。
SessionEnd Hook 開始留下:
commit
git_branch
git_dirty
一路經過 Importer、Atomizer,再進 Knowledge:
repo
commit
path
commit_source
branch
dirty
舊資料也能 Backfill。
但 Backfill 沒裝神仙。
Live Dry-run 裡,只有 183 筆 Archive Payload 有 ended_at,可以比較像樣地往 Git History 找。
其他只能退回 captured_at,而且明確標:
commit_source: backfill-approx
不知道就寫不知道。
大概知道,也不要裝成精確知道。
來源補齊,只解決「這張 Note 從哪裡來」。
沒有解決:
它現在還該不該跟新版一起出現在 Shortlist?
這裡才需要 Supersedes。
意思很簡單:
舊 Knowledge 保留歷史,但有另一則已被接受的新 Knowledge 明確取代它。
我不想把這件事做成:
日期新
→ 舊的自動消失
因為日期不是語意。
連前面那兩份真正需要連起來的 Flash Layout Note,標題字詞重疊的 Jaccard 也只有 0.33。
如果只靠標題相似度門檻 0.6,反而抓不到。
最後分兩層。
很明確的同 Project/同 Title 或 Alias,可以走 Auto。
像這種跨 Project、標題又不像,但共享 cc2674、flash-layout Tags 的,先透過 projects.yaml families 知道它們屬於同一產品家族,再丟進 Review Tier。
Review Tier 不准 --apply 偷過。
一定要 --accept。
因為「哪一份經驗已經取代另一份」,最後還是在改 Knowledge Lifecycle。
這種權力我不想交給一個相似度數字。
盤點繼續往下翻,又看到另一批很眼熟的句子:
尚未 commit
session 結束時
handoff
下一步要……
診斷 Grep 一開始抓到 104 筆。
分類條件收緊後,Live mark-episodic --dry-run 實際落到 32 筆。
數字少很多,反而讓我放心一點。
因為這裡寧可漏,不要亂殺。
這些內容不是假的。
「這次 Session 還沒 Commit」在當下完全可能是真的。
但半年後另一隻 Agent 進來,它不該把:
還沒 Commit。
當成工程經驗。
所以沒有刪。
把它降成 Episodic。
也就是:
這是某次 Session 當下的狀態,可以保留歷史,但不進長期 Knowledge Index。
這個差別很重要。
真實,不代表值得跨 Session 傳承。
同一輪盤點還看到:
235 筆含可行動語句
337 個 path:line
散在 159 筆 Knowledge
其中一個 README 漂移,已經被 Note 記了八天。
記得非常清楚。
也八天沒人處理。
……
所以:
「這裡有個問題要修。」
到底是 Knowledge,還是待辦?
以前它們混在 Body 裡。
下一隻 Agent Search 到,也許會看到。
也許不會。
就算看到,也不知道這件事到底還欠不欠。
PR #137 把這類內容另外抽成 Follow-up Ledger。
它不是另一份 Knowledge,而是把可行動缺口拆成可重查、可關閉的待辦帳本。
actionable statement
source path:line
expected old value
status
verify 再回原始位置唯讀檢查。
舊值已經消失,就可以 Close。
現在查不到,標 unverifiable,之後再查。
Memory 負責記住經驗。
Follow-up 負責承認:
這件事還沒做完。
兩個不能寫在同一格,然後期待未來的 Agent 自己猜。
做到這裡還有一個很現實的副作用。
Knowledge 的 Provenance 越補越完整,Frontmatter 也越來越肥。
盤點時,Frontmatter 已經占整體 Knowledge Corpus 約 79%。
其中 distiller: 又吃掉 Frontmatter 約 54%。
這些對 Audit、Lifecycle、Debug 很重要。
但 Agent 每次只是要讀「這則 Knowledge 在說什麼」,根本不需要全部吞進 Context。
所以另外補了:
hippo show <ref> --agent
只給精簡 Header+Body。
大約少掉 70% Token。
Read Event 還是照記。
不是 Metadata 不重要。
是它重要到應該由系統保管,而不是每次都拿去塞模型。
D23 的時候,我最怕的是:
下一隻 Agent 又忘了。
做到 D29,問題反過來了。
這是在哪個 Commit 學到的?
→ Provenance
這份已經被新版取代了嗎?
→ Supersedes
這只是當時 Session 的狀態嗎?
→ Episodic
這其實是一件還欠著的事嗎?
→ Follow-up
Hippo 沒有因為這些分類就變成 Truth Machine。
它只是終於不再把:
「我曾經記過。」
直接等同:
「這就是現在的答案。」
這條技術線到這裡,我覺得差不多了。
最後一天不加 Feature。
回頭看這三十天。
從 UART、PASS、Audit、Persona、Cortex,一路到 Memory。
我到底是在讓 Agent 變得更聰明,還是在一路替它限制:
哪些話不能只因為它自己說了,就算數?
明天收尾。
Have a nice day.