Blog

寫下來,然後呢?

  • #知識管理
  • #開發工具
  • #Claude Code

想存筆記的時候,發現早就存過了

踩了一個坑。修完之後想「這個值得記下來」,打開筆記庫準備新增。

搜尋標題,跳出一篇一模一樣的。自己寫的,兩週前。

這件事付了兩次錢:第一次是踩坑,第二次是把它寫下來。而第二次完全沒有省下第一次。

筆記庫裡有 325 篇。那 325 篇裡,有多少是已經忘了自己寫過的?


筆記是被動的

一篇筆記要幫到你,需要三件事同時成立:

  1. 寫過
  2. 在需要的當下,你想起來你可能寫過
  3. 去查了

第 1 項已經做得很好。問題全部出在第 2 項。

而第 2 項有一個殘酷的性質:你想不起來的時候,正是最需要它的時候。

記得「我寫過這個坑」的人,多半也記得怎麼避開,根本不需要查。真正需要筆記的場合, 是你完全沒意識到自己正走進一個舊坑——那個狀態下,不會有人想到要搜尋。

筆記能解決「查得到」的問題,解決不了「不知道要查」的問題。


以為已經處理過了

這個問題三個月前就想過,也寫了一支程式來解決。

它掛在 AI 助手的「使用者送出訊息」這個時機:每次送出,抓出句子裡的關鍵詞, 去筆記庫的索引比對,把最相關的幾篇塞進 AI 的上下文。

就算沒想到要查,相關的舊筆記也會自己浮上來。

設計是對的。它也真的在跑。

實測一句話:「確認一下 merge 有沒有成功」

精準命中那篇兩週前的筆記,排第一。


但它在工作的地方是關掉的

同一句話,換個位置再測一次。

工作目錄在家目錄底下——命中。 工作目錄在 ~/Projects/某個專案 底下——完全沒有反應

原因在程式的第 23 行:

SKIP_DIRS = [os.path.expanduser("~/Projects")]

工作目錄在 ~/Projects 底下時,整個召回機制直接跳過。

而所有專案都在 ~/Projects 底下。

筆記召回機制

  家目錄、筆記庫    →  ✅ 會召回
  ~/Projects/*     →  ❌ 跳過
                        └── 99% 的工作時間都在這裡

一個解決「想不起來」的機制,被關在唯一會想不起來的地方之外。


當初關掉它是有道理的

發現的當下很想罵人,直到去看了成本。

那支程式塞進上下文的,是筆記索引裡的整段摘要。量一下索引檔:

字元數
每筆索引平均長度489
最長的一筆2,238
一次注入 5 筆約 2,445

每一次送出訊息都要付 2,445 字元。一場三小時的開發對話送出五十次訊息, 就是十二萬字元,塞的還多半跟當下無關。

所以那個決定完全合理:專注寫程式的時候,不需要每句話都被灌一堆舊筆記。

問題出在關掉的範圍。 為了省資源,最省事的做法是整片關掉,而那片剛好是全部。


修法:把兩件事拆開

卡住的地方在於,「用來比對的文字」和「塞進上下文的文字」被當成同一份東西。

它們的需求剛好相反:

  • 比對要越多字越好——字越多,越容易命中
  • 顯示要越少字越好——每次都要付錢

拆開之後就沒有取捨了:用完整的索引行去比對,只把標題、一小段摘要和檔名塞進上下文。

索引行(489 字元)

   ├──→ 拿去比對關鍵詞  ← 用完整版,命中率不變

   └──→ 縮成「標題|80 字摘要 → 檔名.md」(約 150 字元)
                                    └──→ 這個才塞進上下文

改完的數字:

每個訊息注入
改之前約 2,445 字元
改之後約 430 字元

省了八成,命中的是同一批筆記。省下的空間讓 SKIP_DIRS 可以清空, 召回在所有地方都打開了。

改完不宣稱成功,拿真的會講的話再測一次:

  • 「確認一下 merge 有沒有成功」→ 428 字元,正確那篇排第一 ✅
  • 「用 git log 看一下最近的 commit」→ 550 字元,三篇相關的 ✅

剩下的缺口

同一批測試裡,有一句話完全沒有召回

「幫我 push 上去」 —— 太短,沒有足夠特別的詞可以比對。

這句話暴露了這個機制的天花板:它綁在使用者說的話上面。而危險發生的時刻, 常常在說完話之後很多步——當工具真的要執行那個指令的時候。

說「幫我 push 上去」,AI 接下來會跑十個指令,坑在第七個。而召回只在第 0 步發生過一次。

所以再加一層:在指令執行前攔一下。這一層跟使用者說了什麼無關,它看的是即將發生的動作。

以那個 git log 的坑為例(那台機器上的 git log 會被代理工具改寫,預設濾掉 merge commit,所以拿它判斷「合併成功了沒」會得到錯的答案):

#!/usr/bin/env bash
input=$(cat)
cmd=$(printf '%s' "$input" | jq -r '.tool_input.command // empty')

# 比對前先把引號內容挖掉,否則 `grep "git log"` 會被誤判
bare=$(printf '%s' "$cmd" | sed -e 's/"[^"]*"//g' -e "s/'[^']*'//g")

# 同一段裡出現 git … log 才算([^;&|]* 擋住跨管線的誤判)
printf '%s' "$bare" | grep -qE '\bgit\b[^;&|]*\blog\b' || exit 0

# 已經明確處理過的就別囉唆
printf '%s' "$bare" | grep -qE '(--(no-)?merges|rev-parse|ls-remote)' && exit 0

# 一個對話只提醒一次
sid=$(printf '%s' "$input" | jq -r '.session_id // empty')
marker="${TMPDIR:-/tmp}/.gitlog-reminder-${sid}"
[ -e "$marker" ] && exit 0
: > "$marker"

jq -nc '{systemMessage: "提醒:git log 預設濾掉 merge commit——判斷「有沒有發生」請改用 rev-parse / ls-remote"}'

三個設計決定,每一個都是為了讓它活得久:

不擋,只提醒。 git log 絕大多數用途是正當的。會擋住正常工作的提醒,最後一定被關掉。

一個對話只講一次。 同樣的理由。提醒一次足以建立警覺,第五次只會讓人麻木。

先挖掉引號再比對。 第一版沒做這件事,cat notes.md | grep "git log" 也會觸發。 假陽性是這類提醒最大的敵人——它讓人覺得這東西不準,然後關掉它。


三層,以及什麼時候該往下搬

寫下來的知識可以放三個地方,差別在誰負責在對的時刻把它拿出來

誰執行什麼時候失效
筆記未來的你得先想到要查
自動召回程式,在你說話的時候你的話裡沒有可比對的詞
指令攔截工具,在動作發生前只有寫錯規則才失效

由上往下,可靠度遞增,寫起來也遞增麻煩。所以不必每一課都做到最底層。

判準只有一句:同一個坑踩第二次,就往下搬一層。

第一次踩,寫筆記就好,它可能再也不會發生。 第二次踩,代表這件事超出記得住的範圍,該交給機器了。

這個判準有一個漏洞

「踩第二次」聽起來很清楚。但要知道這是第二次,你得先記得第一次—— 而這篇文章的前提正是你不會記得。

老實講,這次之所以知道是第二次,是因為想存筆記時發現早就存過了。那是撞上的, 不是判準在運作。

所以它比較像一個事後的分類原則,不是事前的觸發條件。

真正有機會自動告訴你「這是第二次」的,只有中間那層——它比對的是你現在說的話 和你以前寫過的東西。所以召回層不是備援,它是三層裡唯一會主動指出「你踩過這個」的一層。

這一切假設你動得了工具鏈

上面說的 hook、常駐規則、lint 設定,前提是你有權改它們。

在公司裡,多數人不能動 CI、不能裝 hook、不能改共用的設定檔。那種情況下能做的比較有限: 把規則寫進團隊的 code review 檢查表、寫進 PR 模板、或是在自己的編輯器裡加一條 lint。

這些都比 hook 弱,因為它們仍然要靠某個人在對的時刻看一眼——而那正是第一層失效的原因。

這是個真實的限制,這篇文章沒有解法。


最諷刺的部分

診斷這整件事的時候,翻到筆記庫裡的一篇。三個月前寫的,標題是 〈guardrail 要檢查「有沒有做過決定」〉。

裡面有這麼一段:

指令層與保證層是兩種東西:前者由模型自己權衡,會被長對話稀釋、被當下任務壓過、 單純漏看;後者由工具執行,只有寫錯才失效,不會「忘記」。

還有一句:

規則寫在上下文裡了,仍然直接做出了錯的事。

這篇文章的結論,三個月前就寫完了。

那篇解釋「為什麼筆記救不了你」的筆記,本身就是一篇沒有救到我的筆記。

需要一個證明來說明為什麼知識要往下層搬的話,這就是了。


寫下來的那一刻,你確實卸下了什麼

寫筆記讓人有一種踏實感——這件事處理過了,記下來了。

那份踏實感是真的,而且量得出來。

有一組睡眠實驗室的研究,讓受試者在睡前寫一份清單,再用儀器量他們多久睡著。 寫「接下來幾天要做的事」那組,平均 15.8 分鐘入睡;寫「前幾天已經做完的事」 那組,25.1 分鐘

兩組都在寫清單。差別只有一個:一組寫的是還沒處理的事,另一組寫的是已經處理完的。 腦袋只對前者鬆手。

還有一個細節:待辦清單上的項目寫得越多,入睡越快。而已完成清單剛好相反, 寫得越多反而睡得越慢。

所以讓人放鬆的是「還沒處理的事被安排出去了」這件事。

但那份放鬆是預支來的

腦袋願意鬆手,條件是它相信這件事會回來找你

寫進行事曆有用,因為行事曆附帶一個東西:時間到了它會響。 你之所以能安心 忘記下週二的門診,是因為手機會在對的時候把它端到你面前。

筆記沒有這個部分。它只有前半段(把東西從腦袋搬到硬碟),缺的是後半段 (在對的時刻自己出現)。

於是你付出了信任,卻沒有拿到對應的東西。 腦袋以為這件事已經被安排好, 所以停止提醒你——而實際上沒有任何機制會在你需要的那一刻把它拿出來。

風險一點都沒有降低。坑還在原地,下次照樣會踩。改變的只有那份不安, 而那份不安本來是唯一會讓你警覺的東西。

所以真正的問題在於:寫完之後,誰負責在對的時刻讓它響。

兩條真的有用的路

一篇筆記要改變什麼,只有兩條路走得通。

一、內化成你的預設動作。 下次遇到類似情境,手會自動先做那個檢查—— 這比「記得有這回事」深一層。它需要刻意回頭讀、刻意練習。 而多數人(包括我)寫完就不會再翻,所以這條路的失敗率很高。

二、搬進工具裡。 hook、CLAUDE.md、型別系統、測試、lint 規則—— 任何「不靠你記得也會發生」的東西。

第二條路的門檻其實比第一條低。寫一支二十行的 hook,比養成一個新習慣容易得多, 而且它不會有心情不好的一天。


最後

現在寫完一篇踩坑筆記,會多問一句:

這一課,我打算靠誰在對的時刻把它拿出來?

答案如果是「靠我自己記得」,那它高機率會再發生一次。

來源

  1. https://pmc.ncbi.nlm.nih.gov/articles/PMC5758411/