Claude Code に長期記憶を持たせたら、壁打ちの質が変わった
Claude Code に長期記憶を持たせたら、壁打ちの質が変わった | Zenn
目次

2026 年 3 月、AI エンジニアの noprogllama 氏が、Claude Code に長期記憶を持たせるための自作ツール「sui-memory」を公開した。現在1,942 セッション分の会話から7,059 件のメモリが蓄積され、壁打ちの質が明らかに変わったという。

本稿はこの仕組みの概要、CLAUDE.md との棲み分け、そして技術的な実装詳細を解説する。

CLAUDE.md には書けないもの

CLAUDE.md という仕組みがある。プロジェクトのルートに置いておくと、Claude Code がセッション開始時に読み込んで、プロジェクトの方針や技術スタックを把握してくれる。開発に必要な情報はこれで十分伝わる。

だが、CLAUDE.md には書けないものがある。

  • 「前にこの方針で議論して、こういう理由で却下したよね」
  • 「あの時ハマったの、覚えてる?」

過去の会話の文脈だ。

noprogllama 氏は Claude Code を開発だけでなく壁打ち相手としても使っている。戦略の相談、記事の構成、設計判断の議論。こういう用途では、過去に何を話したかが重要だ。

新しいセッションを開くたびに前提の共有からやり直すのは、率直に言ってしんどい。毎朝出社したら同僚が記憶喪失になっている、あの感覚だ。

この問題を解決するために、自作の記憶エンジンを作った。結果として 1,942 セッション分の会話が蓄積され、壁打ちの質が明らかに変わった。

以前、一度失敗している

実はこれが 2 回目の挑戦だ。

1 回目は、claude-memという OSS プラグインをフォークして使おうとした。コミット 1,493 件(2026-03-22 現在)のそれなりに成熟したプロジェクトだ。セキュリティ上の懸念があったのでサニタイズして、Dropbox でマシン間同期も設計したのだが…3 日後に全面無効化した。

プロジェクトごとに記憶が断絶していたこと、バックグラウンドで毎メッセージ結構なトークンを消費していたことが主な理由だった。

今回の設計思想:sui-memory

2 回目は一から作った。名前はsui-memory

前回の反省を踏まえて、設計方針を 3 つに絞っている。

  1. 外部サービスに依存しない - SQLite の 1 ファイルに全データを格納する
  2. バックグラウンドでトークンを消費しない - 記憶の保存に LLM を使わない
  3. セッション終了時に自動で保存される - 手動操作ゼロ

依存パッケージも実質 2 つだ(sentence-transformers と sqlite-vec)。削ぎ落として、ようやく動くものになった。

仕組みの全体像

やっていることは素朴だ。セッションが終わると、会話の全文が sui-memory に送られる。sui-memory はそれをQ&A 形式のチャンクに分割し、日本語特化の埋め込みモデル(Ruri v3)でベクトル化して、SQLite に保存する。

ユーザー側で必要な設定は、Claude Code の settings.json に Hook を数行足すだけだ。セッション終了時に自動で発火するので、普段の使い方は何も変わらない。

CLAUDE.md との棲み分け

この仕組みは、CLAUDE.md と競合するものではない。役割が違う。

CLAUDE.md sui-memory
役割 プロジェクトのルールブック 過去の会話の蓄積
内容 技術スタック、コーディング規約、ディレクトリ構成 設計の理由、却下した経緯、失敗した試み
性質 静的な情報 動的な情報(議論の文脈)
変化 セッションが変わっても同じ セッションごとに蓄積

CLAUDE.md が取扱説明書なら、sui-memory は共有した経験に近い。どちらか一方では足りない。両方あって初めて、文脈を持った壁打ち相手になる。

2 つの検索を組み合わせる

記憶は保存するだけでは意味がない。必要な時に、必要なものを取り出せなければ価値がない。

sui-memory では、2 つの検索を組み合わせている。

1. キーワード検索

SQLite に組み込みの全文検索(FTS5)を使い、日本語を形態素解析なしに検索する。一般的には MeCab などの外部辞書を使うが、ここではtrigram トークナイザという方式を採用した。

文字列を 3 文字ずつの断片に分割するだけの素朴な方法だが、追加の依存パッケージが不要だ。「Tailscale」「LaunchAgent」のような固有名詞にはこちらが強い。

2. ベクトル検索

テキストの意味的な近さで検索する。エンベディングモデルにはRuri v3-310mという日本語特化モデルを選んだ。310M パラメータと小型ながら CPU でも十分な速度で動く。

OpenAI の Embeddings API を使わない選択をしたのは、コストとプライバシーの両面からだ。全ての会話ログを外部に送信するのは避けたかった。

RRF(Reciprocal Rank Fusion)で統合

この 2 つの結果をRRF(Reciprocal Rank Fusion)という方法で統合する。それぞれの検索結果の順位を使ってスコアを合算する仕組みで、検索方式ごとのスコアの単位が違っても公平に扱える。

キーワード検索だけだと意味的な類似を見逃し、ベクトル検索だけだと固有名詞に弱い。両方を組み合わせることで、お互いの弱点を補う。

時間減衰

さらに、時間減衰を入れている。古い記憶ほどスコアが下がる設計で、半減期は 30 日だ。

  • 30 日前の記憶:スコアが半分に
  • 60 日前の記憶:スコアが 4 分の 1 に

人間の記憶も直近のことほど鮮明で、古いことは薄れていくので、この仕組みを導入した。

実際に使ってみて

現在、1,942 セッション分の会話から7,059 件のメモリが蓄積されている。検索のレスポンスは100ms 前後だ。

体感として最も変わったのは、壁打ちの精度だ。

たとえば記事のテーマを相談するとき、以前なら毎回ゼロからブレストしていた。今は過去に検討したテーマや、その時の判断理由が文脈として残っているので、同じ議論を繰り返さずに済む。前回の結論を踏まえて、その先の議論ができる。

これは開発の効率化というよりも、思考の相手としての質の変化だ。壁打ちが壁打ちとして機能するようになった、という感覚がある。

もちろん万能ではない。7,000 件のメモリの中から本当に必要な数件を引き当てられるかは、クエリの質とチャンクの粒度に依存する。検索精度の調整は今も続けている。

前回の失敗との違い

前回(claude-mem)と今回(sui-memory)の違いを整理する。

claude-mem sui-memory
コード量 大規模 1,759 行
言語 TypeScript Python
記憶の保存 LLM で要約・圧縮 生の transcript をチャンク化
トークン消費 毎メッセージ数千 ゼロ(LLM 不使用)
検索 Chroma(外部 DB) SQLite 内蔵 (FTS5+sqlite-vec)
外部依存 ChromaDB 等 sentence-transformers, sqlite-vec
セットアップ 複数プロセス起動 uv sync のみ

根本的な差は、記憶の保存に LLM を使うかどうかだ。

claude-mem はバックグラウンドで LLM を起動して会話を要約・圧縮していた。sui-memory は生の transcript をルールベースでチャンクに分割するだけだ。LLM を使わないから、トークンも消費しないし、応答速度にも影響しない。

要約しないぶん情報は多少冗長になるが、大事な文脈が要約の過程で消えてしまうリスクを避けられる。何を残して何を捨てるかの判断は、保存時ではなく検索時にやればよい。

大規模なシステムを捨てて、1,759 行で作り直した。やっていることの本質は同じだ。会話を保存して、必要な時に取り出す。余計なものを全部削ぎ落としたら、ようやく納得のいくものになった。

結論:2 層構造が最適解

noprogllama 氏の Claude Code は今、過去の会話を覚えている相手になっている。

CLAUDE.md でルールを伝え、sui-memory で経験を共有する。

この 2 層構造が、今の彼にとっての最適解だ。


参考:

引用元・参考リンク

免責事項 — 掲載情報は執筆時点のものです。料金・機能は変更される場合があります。最新情報は各公式サイトをご確認ください。