この記事が良かったら
「チップをリクエスト」で著者にチップの受け取り設定をお願いできます

「毎回ゼロから説明」をやめた話
この記事が良かったら
「チップをリクエスト」で著者にチップの受け取り設定をお願いできます
Claude Codeはセッションを跨ぐと忘れます。新しいセッションを開くたびに、プロジェクトの背景・過去に決めたこと・自分の好みを説明し直す。これを数ヶ月続けた結果、「記憶を1つのメモに全部書く」のではなく、鮮度と読み手で4層に分ける設計に落ち着きました。
この記事では、その4層アーキテクチャの設計・実装・運用して踏んだ落とし穴を書きます。最後に「今日30分で真似できる最小構成」を置いておくので、全部読まなくてもそこだけ持ち帰ってもらえれば十分です。
「Claude Codeに記憶を持たせたい」と一口に言っても、中身は性質の違う4種類が混ざっています。
これを1ファイルに混ぜると、生ログが好みを埋もれさせ、古い作業メモが今の判断を汚染します。そこで層を分け、層ごとに「書いてよい主体」を1つに固定しました。
流れは一方向です。
4層アーキテクチャの一方向フロー図
原則は1つだけ。下流は上流を上書きしない。 権威は常に層(1)で、(2)(3)(4)はそこからの派生です。食い違いが起きたら(1)に照らして直します。
Claude Codeはセッションの全文を ~/.claude/projects/ 配下にJSONLで残しています。これをStop hook(セッション終了時に発火するhook)でMarkdownに変換し、追記専用ディレクトリに落とします。
settings.json(抜粋):
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{ "type": "command",
"command": "python3 ~/knowledge-base/extract_conversations.py" }
]
}
]
}
}変換スクリプトの骨子(extract_conversations.py の要点だけ抜粋):
#!/usr/bin/env python3
"""Claude Code会話ログをknowledge base用のMarkdownに変換する"""
import json
from pathlib import Path
PROJECTS_DIR = Path.home() / ".claude" / "projects"
RAW_DIR = Path.home() / "knowledge-base" / "raw" / "conversations"
def parse_session(jsonl_path):
messages = []
with open(jsonl_path, encoding="utf-8") as f:
for line in f:
obj = json.loads(line)
if obj.get("type") not in ("user", "assistant"):
continue
# user/assistantのテキストだけ抽出(tool_resultは捨てる)
...
return messagesポイントは2つ。tool_result(ツール実行結果)は捨てること(量が爆発する割に後から読まない)。そして出力ファイル名を <プロジェクト名>_<セッションID>.md にして、後からgrepで「あの作業いつやったっけ」を引けるようにすることです。
層(2)は公式の remember プラグインに任せています。直近の作業が now.md に入り、夜間のconsolidateで recent.md → archive.md へ熟成されつつ重複排除され、SessionStartで自動的に文脈へロードされます。
⚠️ 層(2)のファイルは手編集禁止です。手で触るとconsolidateが壊れます。
層(3)は「確定した事実だけ」を置く場所です。MEMORY.md を索引にして、1トピック1ファイルで事実を書く。「このプロジェクトのDBはTurso」「危険な操作は実行前に確認を取る」のような、腐らない情報だけをここに入れます。作業ログを混ぜないのがコツです。
Obsidian Vault側の hot.md には「直近10セッションの表」が自動で入ります。仕組みは単純で、層(1)のファイル名と日時行を正規表現でパースし、マーカーで囲った領域に表を注入するだけ。LLMを呼ばないのでコストはゼロです。
hot.md(注入領域のイメージ):
<!-- recent:start auto-updated by update_hot_recent.py -->
| 日時 | プロジェクト | 最初の指示 |
|---|---|---|
| 2026-06-10 09:12 | my-app | レスポンシブ崩れを直して |
<!-- recent:end -->💡 マーカーで「自動領域」を明示するのは地味に重要です。これがないと人間(自分)がうっかり手編集して、次回の注入が壊れます。
これが最大のハマりでした。夜間の自動ジョブをlaunchdで回したところ、**~/Documents 配下のVaultへの書き込みだけが静かに失敗**。エラーも出ません。
切り分けると、launchd配下でも bash の書き込みは通るのに、claude バイナリ・git・python だけが弾かれる。原因はmacOSのTCC(プライバシー保護)で、~/Documents ~/Desktop ~/Downloads はバイナリ単位のFull Disk Accessがないと書けない領域でした。
対策はFDAを付与して回るより、書き込み先を保護領域の外に出す方が堅牢です(バイナリが更新されてもパスが変わっても効く)。Vaultの実体を ~/Documents の外へ移設し、元の場所にはsymlinkを張りました。
層(2)の recent.md(LLM用)と層(4)の hot.md(人間用)は内容が被ります。一見冗長で統合したくなりますが、読み手が違うだけで両方とも層(1)由来。どちらかを正にして他方を従わせると、フォーマット要件が衝突して両方使いにくくなります。冗長なまま放置が正解でした。
自動パイプラインは静かに止まります。早期警告のシグナルを決めておくと拾えます。
うちではこれを1本のヘルスチェックスクリプトにまとめ、全層の死活を1コマンドで確認できるようにしています。
4層全部はいりません。層(1)だけで効果があります。
これだけで「全セッションがgrep可能なテキスト」になります。「先週どう直したっけ」が grep -r "レスポンシブ" ~/knowledge-base/raw/ で引けるのは、想像以上に効きます。層(2)〜(4)は、その生ログが溜まって不便を感じてから足せば間に合います。
この環境には他にも、Claudeが自分でスキルを書いて成長するauto-skills運用や、毎朝GitHubを自動巡回して有用OSSを拾うパイプラインが乗っています。続編で書く予定なので、興味があればフォローしてもらえると嬉しいです。
Lily(@bokuwalily)― 個人開発者。Claude Code で自動化基盤を組みながら、iOSアプリやWebサービスを量産しています
◼︎作ったアプリは **ポートフォリオ** にまとめています📱
◼︎新着・開発の裏側は X **@bokuwalily** で発信しています🐦
◼︎OSS: **github.com/bokuwalily**🐙
皆さんの ❤️ やシェアが励みになります!