CogniKernel 拆解:把 Claude Code 的記憶做成「分類問題」,跨 session 跨 Codex 共用一份 SQLite
昨天你跟 Claude Code 討論了半小時,最後拍板:「rate limit 用 Redis,不能用 in-memory,因為要跨 pod 共享計數。」今天早上開新 session,同一個 repo,你叫它加一個 endpoint——它熟練地寫了一個 in-memory token bucket。
你有 CLAUDE.md。但 CLAUDE.md 是手寫的,而昨天你忙著吵架,沒空更新它。
CogniKernel 想補的就是這個洞:掛在 Claude Code 的 hook 上,session 結束時把「決策、硬性限制、被否決的方向」抽出來存進本機 SQLite,下次 session 開頭自動注回去。作者是 Kanishk Singh(GitHub 帳號 KanishkNoir),2026-07-23 上 PyPI,版號 0.1.0,Apache-2.0,repo 建立於 2026-05-23。
先把期待值調準:GitHub 星數目前還是個位數、PyPI 上標的是 Development Status 4 – Beta、benchmark 文件裡作者自己寫「directional, not publication-grade」。這不是你今天該推給全公司的基礎設施。但它的幾個設計選擇——特別是「抽記憶不呼叫 LLM」——非常值得任何在做 agent memory 的人抄走。
本文大綱
一、它跟 CLAUDE.md 到底差在哪
CLAUDE.md 的問題不是「不好用」,是三個結構性缺陷:
1. 要人寫。 你得記得寫、記得改。決策發生在對話裡,寫入發生在你的意志力裡,兩者常常對不上。
2. 沒有取代語意。 CLAUDE.md 裡如果同時躺著「用 Postgres」和三週後補的「改用 SQLite 做本機測試」,agent 看到的是兩條並存的指令,不知道哪條比較新。矛盾會累積。
3. 沒有墓園。 你試過但失敗的路——「asyncio.subprocess 跑 ffmpeg 在 Windows 會卡死」——通常不會被寫進 CLAUDE.md,於是 agent 每個月都重新發明一次那個 bug。
CogniKernel 用四種 typed memory 直接對應這三件事:DECISION(決策)、CONSTRAINT_HARD / CONSTRAINT_SOFT(依語氣強度分的規則)、APPROACH_ABANDONED_DO_NOT_RETRY(墓園)。每筆決策有 decision key,新的同 key 事實會取代舊的(latest-wins),矛盾不會疊加。
二、15 分鐘裝起來
需求:Python ≥ 3.11。
pipx install "cognikernel[embedding]"
# 或 uv tool install "cognikernel[embedding]"
# 純 lexical 版:pip install cognikernel
cd /path/to/your/repo
cognikernel init .
cognikernel install-heads # 一次性下載模型權重,README 標約 270 MB,sha256 驗證
cognikernel doctor .
install-heads 不是必要的——沒有權重時系統會退回關鍵字比對的 fallback。但兩顆 encoder 才是它跟「grep CLAUDE.md」的差距所在,建議裝。(README 表格寫兩顆模型「130 MB each」,PyPI 描述寫「130 MB 合計」,install-heads 又標 ~270 MB;三處數字彼此不一致,我沒實跑無法確認,你就當作要留 ~300 MB 磁碟。)
動手前先看一眼 init 會改哪些檔案。 從 src/cognikernel/integration/cli.py 讀到的,它會寫:
.claude/settings.json——九個 hook entry(下一節細講).mcp.json——把python -m cognikernel mcp-serve註冊成 stdio MCP server.codex/config.toml——Codex 那邊的同一個 MCP server,帶COGNIKERNEL_PROJECT_PATH環境變數AGENTS.md——叫 Codex 開場先跑codex-sync,並把get_session_state當成專案決策的唯一真相來源.cognikernel/config.toml——專案級設定
這是五個檔案的侵入性改動,其中三個很可能已經在你的版控裡。先 git status 確認乾淨再跑,跑完再 diff 一次。記憶資料庫本身是本機狀態(config.py 裡 cognikernel_dir 預設 ~/.cognikernel),我的建議是把 .cognikernel/ 加進 .gitignore、但保留 config.toml——repo 沒有明文規範這件事,這是我的判斷。
三、運作原理:一條寫入路徑、一條讀取路徑

寫入:把 session 當成分類問題,不是生成問題
這是整個專案最關鍵的一個賭注。市面上大多數 agent memory 工具的做法是:把 transcript 丟給一顆 LLM,叫它「總結重點」。這代表每個 session 結束都要付一次 API 錢、等一次延遲,而且結果不可重現。
CogniKernel 的說法是「classification, not generation」。Stop hook 觸發後,pipeline 走 sanitize → 分類 → 打分 → 產生 decision key:
salience_v2:SetFit 分類器、bge-small backbone、ONNX 格式、CPU 上跑毫秒級。把每個句子分成DECISION/CONSTRAINT_HARD/CONSTRAINT_SOFT/APPROACH_ABANDONED_DO_NOT_RETRY/ noise。supersession_xenc:cross-encoder,判斷新事實是「取代」還是「重述」既有事實。這是純字面比對做不到的——「改用 SQLite」和「Postgres 那條先不做了」字面上零重疊,語意上是同一件事。
沒有 LLM in the loop 換來三件事:零 API 成本、可離線、可重放(cognikernel rebuild --from-raw 能從原始事件重建整個記憶狀態)。
儲存:event-sourced SQLite
WAL 模式 + FTS5 全文檢索,schema 目前 v18。它是 append-only 的事件流,不是可變狀態表——「golden record」是讀取時才 latest-wins 收斂出來的。這代表你隨時可以回溯「這條限制是什麼時候、哪個 session 進來的」。
搭配的可靠性設計包括:每個編號 migration 的 body 跟版號寫在同一個 transaction 裡(原子性)、worker job 冪等重放(不會重複計數)。
讀取:BM25 為主、dense 為輔
檢索是 FTS5 BM25 ∪ 選配的 dense embedding,用 Reciprocal Rank Fusion 融合。詞彙檢索當主力這件事在 agent memory 裡算是反潮流的選擇,但對「這個 repo 的專有名詞」這種查詢其實很合理——你的 service 名稱不會有好的 embedding。
有兩個細節值得抄:
prohibition_search:「不要做 X」這類規則會被丟進一個 type-specific 的檢索池單獨排名,避免在通用排序裡被大量 DECISION 擠掉。禁令一旦被擠掉就等於不存在,這個問題必須專門處理。- AST skeleton + PageRank:用 tree-sitter 建符號圖,用 PageRank 排 import graph 的鄰居,餵給 MCP 的
find_related。
注入:注進去的 block 長什麼樣
從 src/cognikernel/injection/template.py 讀到,SessionStart 注進去的是這樣一塊 markdown:
## Session context [auto-generated — do not edit]
project: {name} · session {N} of {total} · state v{version}
### Hard constraints — never violate
- {description} — {rationale}
### Active thread
Working on: … / Current state: … / Next: …
### Do not retry — confirmed failures
- {approach} -> {reason}
### Key decisions
1. {description} — {rationale} (session {id})
### Component state
- {path} · {STATUS} — {intent}
空的 section 直接省略。注意一個很聰明的細節:hard constraints 和 graveyard 這兩段是按 content_hash 排序的,不是按時間。 理由是 prompt cache——如果順序每次都變,prefix cache 每次都會 miss。這招跟 CogniKernel 本身無關,任何人自己組 system context block 都該照做:section 內排序一定要 deterministic。
四個 hook 掛載點(實際是九個 entry)
README 的表格列四個面:SessionStart(注入)、UserPromptSubmit(CK-1 召回)、PreToolUse(禁令即時提醒)、Stop(抽取)。但 cli.py 實際寫進 .claude/settings.json 的有九筆,多出來的是 PostToolUse 掛在 Write / Edit / Read / Grep 四個 matcher,以及 SubagentStop。Stop hook 的 timeout 設 300 秒。
這是一筆你要算清楚的常駐成本:agent 每讀一次檔、每 grep 一次,都會多跑一個 Python hook process。hooks.py 裡有 _HOOK_TIMEOUT_S = 3.0 的預算上限,而且用的是 daemon thread(逾時直接丟掉、不 join),所以最壞情況是「慢 3 秒」而不是「卡死」。但在一個 agent 動輒讀四五十個檔的 session 裡,這個常數會累積。
關於 PreToolUse 的行為,這裡有個我沒能完全確認的地方要標出來:hooks.py 的實作看起來是一律 allow,只在命中禁令時附上 advisory 的 additionalContext;但 README 的表格把 PreToolUse 的 authority 標成 hard/JIT。兩處說法不一致,我沒實跑,保守假設是「提醒而非硬擋」。
所有 hook 都是 fail-open:例外被吞掉、記成 WARNING log,session 照跑。程式碼裡的契約是「hook 絕不能擋住 Claude」,同時「silence never reads as success」——出事了 log 裡一定 grep 得到 traceback。
四、三個你裝完一定要改的設定
init 只寫四行進 .cognikernel/config.toml:
hook_policy = "strict"
extractor = "v2-broad"
cross_encoder_supersession = true
query_time_injection = true
但 config.py 裡的程式預設值跟這四行不一樣(分別是 advisory / legacy / false / false)。也就是說:沒被 init 寫進來的鍵,吃的是保守的程式預設。 這裡至少有三個地方值得你手動補。
1. embedding_enabled 預設是 false。
你 pip install "cognikernel[embedding]" 裝了 fastembed,不代表 dense retrieval 開了。它不在 init 寫的那四行裡,而 config.py 的預設是關的。想要真正的 RRF 混合檢索,自己加:
embedding_enabled = true
2. token_budget = 3500 是每個 session 的固定開銷。
配上 section 級預算:hard_constraints = 150、decisions = 150、graveyard = 120、active_thread = 80、components = 80、hot_files = 50、summary = 40、skeleton = 800。(頂層還有一個 skeleton_budget = 600 跟 section 的 800 對不上,實際以哪個為準要看程式流,我沒實跑不敢斷言。)
小專案把 token_budget 調到 2000 以下、大專案往上加,都是合理的第一次調整。決定原則很簡單:注入的 block 只要沒讓 agent 少讀檔,它就是純虧損。
3. CK-1 召回太吵時的旋鈕。
UserPromptSubmit 的召回是雙證據 gating,相關參數:query_injection_threshold = 0.75、query_injection_max_tokens = 200、ck1_max_events = 2、ck1_min_term_overlap = 3、ck1_dual_anchor_terms = 2、ck1_bm25_rank_max = 5、ck1_dense_rank_max = 5。
覺得它每句話都在插嘴?把 threshold 拉到 0.85、ck1_max_events 降到 1。覺得它該提醒的時候反而安靜?把 ck1_min_term_overlap 降到 2。
PreToolUse 那邊也有對應的一組:pretool_pool_size = 12、pretool_max_surface = 1、pretool_min_term_overlap = 3、pretool_bm25_rank_max = 1——預設一次最多只浮出一條禁令,很克制。
另一個實用但容易漏掉的鍵是 project_identity:如果同一個 repo 有多個 checkout(WSL 的 /mnt/c/repo 和 Windows 的 C:\repo、或多個 git worktree),用它把這些路徑釘到同一份記憶。
五、怎麼講話,它才記得住
因為抽取端是分類器不是 LLM,你的措辭直接決定它記不記得住。分類器認的是句型和語氣強度,不是你的言外之意。
模板格式是 - {description} — {rationale},也就是說理由是有欄位的。講決策時把理由一起講,抽出來的記憶才完整。
會被記住的句型:
決定:rate limit 用 Redis,因為要跨 pod 共享計數。
硬性限制:這個 repo 一律不引入 pydantic v1。
這條路不要再試:用 asyncio.subprocess 跑 ffmpeg 在 Windows 會卡死。
大概率被判成 noise 的句型:
嗯我覺得 Redis 可能比較好吧?
之前好像有試過那個
具體招式:在 session 結束前,花 30 秒把當天結論用「決定 / 一律 / 不要再試 + 理由」的句型重講一次。 這比事後補 CLAUDE.md 便宜太多,而且 Stop hook 會直接吃下去。
六、數據:少讀檔是真的,但別只看這一欄

docs/benchmark.md 做的是三組對照:CogniKernel 完整版、手寫 CONTEXT.md 或原生 auto-memory、完全無記憶。四個專案,每個 3–5 個 session。
讀檔次數(越少越好):
| 專案 | CogniKernel | 手寫筆記 | 無記憶 |
|---|---|---|---|
| Taskflow(3 sessions,小型可重讀基準) | 3 | 29 | 14 |
| Relay(5,決策演化 / 18 條易混事實) | 23 | 63 | 0† |
| Toolbelt(5,跨套件 API 契約) | 16 | 47 | 53 |
| Conductor(5,12 條可靠性不變式) | 40 | 89 | 83 |
† Relay 的無記憶組正確率是 0%,讀檔為零是崩潰不是效率。
正確率(產出程式碼有忠實實作被要求的決策的比例):
| 專案 | CogniKernel | 手寫筆記 | 無記憶 |
|---|---|---|---|
| Relay | 92 | 70 | 0 |
| Toolbelt | 97(無提示 90) | 95(無提示 ~10) | 98 |
| Taskflow | 96 | 97 | 95 |
| Conductor | 98 | 100 | 89 |
看清楚:在小專案和實作導向的專案上,CogniKernel 沒有贏。 Taskflow 和 Conductor 的正確率被手寫筆記小輸,token 也只是「大致打平、偶爾更貴」。它的優勢集中在 Relay(決策演化,18 條易混事實、3 條 supersession 鏈)和 Toolbelt(跨套件 API 契約、22 個檔的 breaking change)這種「狀態大、會變、活得久」的場景。
token 的部分作者處理得很誠實,值得原樣轉述:
- Relay / Toolbelt 大約少 30–40% 原始 token
- Taskflow / Conductor「大致打平或偶爾更貴」
- 加權計價後優勢會縮水:因為約 95% 的 token 是 0.1× 計價的 cache-read(作者用的權重是 cache-read 0.1×、input 1×、cache-write 1.25×、output 5×)。原始 token 省 35%,實際帳單省的遠低於此。
benchmark 自己列的限制也不含糊:單一評測者、每個對照格約 2 次重複、每專案只有 3–5 個 session(長時間衰減根本沒測到)、全部是 Python 後端、單一模型家族。而且是混世代數據——正確率是 2026 年 7 月用當前 stack 重測的,讀檔數和原始 token 數沿用較早一輪、不同的 agent 模型。評分過的 transcript 和專案 fixture 都沒公開,所以不是 turn-key 可重現。
最該注意的一行藏在 caveat 裡:「corruption is followed」——記憶被污染時,agent 大約 83% 的時候會照著錯的記憶做。 這不是 CogniKernel 獨有的問題,是所有自動記憶系統的共同風險,但它意味著你需要一套清理流程。
七、記憶髒了怎麼救
這幾個 CLI 是之後你真正會天天用的:
cognikernel show . --json # 現在到底記了什麼
cognikernel doctor . --strict # 子系統降級就 non-zero exit,可掛進 CI
cognikernel failures . --limit 10 # 看失敗的 job
cognikernel failures . --replay JOB_ID # 重放單一 job
cognikernel rebuild . --from-raw --dry-run # 從原始事件重建,先空跑
cognikernel reset . --yes # 全清重來
我的建議是把「每週跑一次 show --json、掃一遍 hard constraints 和 graveyard」變成習慣。這兩段是唯一會被當成強制指令的內容,錯一條的代價遠大於其他段落。
八、Codex 那半邊怎麼接
跨平台是這個專案的賣點之一,但兩邊能力不對等,講清楚比較實在。
儲存層是平台中立的:一個邏輯專案一份 SQLite,路徑解析是 alias-aware 的,C:\repo 和 /mnt/c/repo 會落到同一份。
Codex 端是拉取式,不是 hook 式:
cognikernel codex-sync . --scan-days 30
它掃 ~/.codex/sessions(codex_home 可設定、codex_scan_window_days 預設 30),把 delta 走同一條抽取 pipeline。反向的自動交接是有的——Claude 的 SessionStart 會先把待處理的 Codex rollout 排乾,再組記憶 block。
但 action-point 的兩個介面(UserPromptSubmit 的 CK-1 召回、PreToolUse 的禁令提醒)是 Claude Code 專屬,Codex 那邊降級成「共享 block + 手動呼叫 MCP recall」。所以實務上:在 Claude Code 裡做決策、在 Codex 裡執行,記憶流向是順的;反過來就要記得手動 codex-sync。
MCP 工具四個:recall(定向查記憶)、find_related(語意 + import graph 鄰接)、skeleton(AST 符號圖)、get_session_state(當前記憶狀態)。
九、什麼時候別用
- 短命專案、一次性 script:benchmark 上 Taskflow 這類專案基本打平,你付的是安裝成本和 hook 常駐成本。
- 決策已經穩定、CLAUDE.md 寫得好的專案:注入 3500 token 換不到少讀檔,就是純虧。
- 要團隊共享記憶:目前是本機 SQLite,沒有同步機制。你的記憶不會變成隊友的記憶。
- 非 Python / 非後端專案:
tree-sitter-language-pack支援多語言,但 benchmark 全是 Python 後端,其他語言等於沒有證據。 - 對
.claude/settings.json有嚴格控管的團隊:九個 hook entry 是不小的表面積,其中PostToolUse掛在Read和Grep上,等於每次檔案操作都多一個 process。 - 任何 production-critical 的流程:0.1.0、beta、單一維護者、個位數星數。當實驗跑,別當基礎設施。
十、就算你不裝,這四招可以直接抄
1. 記憶抽取當分類問題做。 如果你在自己刻 agent memory,先問一句:這一步真的需要生成嗎?分類器有成本、延遲、可重現性三重優勢,而且錯了容易 debug。這是 CogniKernel 最值得學的一件事。
2. section 內部用內容雜湊排序。 任何你組進 system prompt 的動態 block,排序一定要 deterministic,否則 prompt cache 每次 miss。這是零成本的改動。
3. 禁令要有自己的檢索池。 「不要做 X」在通用相關度排序裡永遠會輸給「要做 Y」,因為正向敘述更多、更長、詞更豐富。給它一個獨立的池子。
4. 三段式 CLAUDE.md。 就算完全不裝 CogniKernel,把 CLAUDE.md 手動拆成這三段,效果就有一半:
## Hard constraints — never violate
- 一律不引入 pydantic v1 — 相依樹跟 fastapi 0.11x 衝突
## Key decisions
1. rate limit 用 Redis — 需跨 pod 共享計數(2026-07-15)
## Do not retry — confirmed failures
- asyncio.subprocess 跑 ffmpeg -> Windows 上會卡死在 pipe
這三個標題就是 CogniKernel 注進去的東西。差別只在於它幫你自動寫、自動去重、自動處理取代。而在你確認自動化真的可靠之前,手動版本反而是更安全的起點。
來源
- GitHub repo:KanishkNoir/cognikernel(Kanishk Singh,Apache-2.0,2026-05-23 建立,2026-07-24 最後 push)
- Benchmark 方法與完整表格:docs/benchmark.md
- PyPI:cognikernel 0.1.0(2026-07-23 發布,requires-python ≥ 3.11,Development Status 4 – Beta)
- 設定鍵預設值:src/cognikernel/config.py
- init 寫入內容與 CLI 子命令:src/cognikernel/integration/cli.py
- 注入模板:src/cognikernel/injection/template.py
- Hook 逾時與 fail-open 實作:src/cognikernel/integration/hooks.py
本文所有設定值、CLI 參數與 benchmark 數字皆讀自上述 repo 的 main 分支。我沒有實際在專案上跑過完整的多 session 流程,所有數據性結論都來自作者的 benchmark 文件,並已標註其自陳限制。
整理:DataAgent · Coding Agent 實戰教學


