AIエージェントの記憶を"揮発させない"共有メモリ層を設計した
※この記事はZennにも技術記事として掲載しています。
この記事は連載「AIと作る、ひとりの開発OS」の一部です。単体で読めますが、全体像はこちらのハブ記事にまとめています。
私は個人開発を、3体のAIエージェントに役割分担させている。蒼(Claude・設計)、ルナ(Gemini・環境と雑務)、亜里沙(Codex・実装)——モデルごとに名前と役割を与えて、小さなチームのように動かしている。
役割を分けると、開発は並列に進む。1人では同時に持てないはずの複数の前線が、同時に動きだす。ただし、代償がある。
記憶が、バラバラになる。
この記事は、その代償をどう払ったか——「共有メモリ層」の設計の記録だ。先に言っておくと、大層な技術は何も使っていない。ファイルと、運用規約だけ。それでも、自分OSで一番効いている仕組みがこれだ。
課題:記憶は3方向に失われる
AIと開発していると、記憶は3つの方向に漏れていく。
セッション間(時間方向):次の会話を開くと、前回の判断が消えている。毎回ゼロから状況を説明し直す。
エージェント間(空間方向):蒼に説明した前提を、ルナは知らない。切り替えるたびにコピペで引き継ぐ。
暗黙知の揮発:コードには「結果」が残る。でも「なぜそうしたか」は残らない。3ヶ月後、自分が書いたコードの理由を思い出せない。
1と2は、役割分担するほど悪化する。エージェントを増やすほど、記憶の分断も増える。3は、AI以前からある個人開発の宿痾だ。
分業のメリットを、記憶の分断で相殺したくなかった。だから、この3つを一度に塞ぎにいった。
設計方針:個別メモリ+共有メモリのハイブリッド
全部を共有すると重い。全部を個別に持たせると分断する。だから、責務で分けた。
| 種類 | 配置 | 例 |
|---|---|---|
| Short-term | セッション内 | 会話履歴(揮発してよい) |
| Long-term(個別) | 各エージェント | 口調・振る舞いの癖 |
| Long-term(共有) | 共有メモリ層 | ユーザーの好み・長期目標 |
| Episodic | 共有メモリ層 | 判断ログ・失敗の学習 |
| Semantic | 既存コード / docs | プロジェクトの実体 |
太字の2つが、今回作った層の担当範囲だ。「対話の中で立ち上がった、コードには残らない学び」——ここだけを共有層に引き上げる。口調のようなエージェント固有の癖は、各自のメモリに残して混ぜない。線引きが甘いと、共有層はすぐゴミ溜めになる。
物理設計:ただのMarkdownとJSONL
構えて聞くと拍子抜けするかもしれない。DBもベクトルストアも使っていない。ただのファイルだ。
shared-memory/
├── CHARTER.md # 運用規約(憲章)
├── INDEX.md # 目次。セッション開始時に必読
├── user-profile.md # 共有Long-term: 好み・地雷・長期目標
├── episodes.jsonl # Episodic: 判断ログ(append-only)
├── gotchas.md # 失敗の学習・回避策
└── policies.md # 命名規則・連携プロトコルなぜDBじゃないのか。個人開発の規模では、人間もAIも読めて、gitで差分が追えて、どのエディタでも開けることのほうが、検索性能よりずっと効くからだ。ベクトル検索が要るほどの量にはまだならない。そして、過剰な仕組みはそれ自体が保守コストという負債になる。「Markdownで十分」を、性能で妥協したのではなく、あえて選んでいる。
フォーマット選択:なぜ日誌だけJSONLか
1つだけ毛色が違うファイルがある。`episodes.jsonl` だ。判断ログを1行1イベントで貯めていく。
{"date":"2026-06-18","agent":"claude","kind":"decision","context":"クロスエージェント記憶問題への対応","learning":"Memory Engineeringのハイブリッド方式を採用。共有Long-term+Episodicを共有メモリ層に集約","ref":"CHARTER.md"}`kind` は5種類に固定:`decision`(判断)/ `gotcha`(失敗)/ `preference`(好み)/ `prerequisite`(前提)/ `implementation`(実装知見)。分類を最初から縛っておくと、後から時系列でも種別でも引ける。
ここだけJSONLにした理由は2つ。append-onlyで書き込み衝突が起きにくいこと(3体が同時に追記しても、末尾に足すだけなので壊れない)。そして1行1レコードで機械的に処理しやすいこと。日誌は「追記され続ける」のが本質だから、人間の読みやすさより機械可読を優先した。
逆に、プロフィールや規約は「読み返して直す」のが本質だ。だからそちらはMarkdown。書き換え方の違いが、そのままフォーマットの違いになっている。
運用規約(CHARTER):なぜ"憲章"を置くか
ここが、この設計の一番のキモだと思っている。
箱を作っても、「何を入れるか」の基準が揃っていないと、記憶はすぐ腐る。どうでもいいログが溜まるか、逆に大事な判断が書かれないまま消えるか。どちらも、次に読むときに使えない。だから、書く基準そのものを憲章(CHARTER.md)として先に決めた。
書く:
ユーザーの好み・癖・地雷
判断の理由(なぜAではなくBを選んだか)
未文書化の前提
失敗の学習
プロジェクト横断で効く設計・UI/UXの知見
書かない:
gitで追えるもの(コミット履歴そのもの)
既存のコードやドキュメントにあるもの
エージェント個別の内部状態
1回限りの作業ログ
迷ったときの判断は、一行に集約できる。「3ヶ月後の別セッションが、これを知らないと同じ穴に落ちるか?」 落ちるなら書く。落ちないなら書かない。記録が増えること自体もコストだから、「書かない」を明文化しておくのが効く。
読み書きフロー
運用はシンプルに保っている。凝ると、続かないからだ。
セッション開始時:`INDEX.md` を読む → 今日のタスクに関連する項目だけ参照する
セッション中:書く基準に当たる発見が出たら、その場で書く(後回しにすると揮発する。気づいた瞬間が書き時)
セッション終了時:拾い漏れを確認し、日誌に一言残す
そして全記述に `agent` と `date` を必ず残す。誰がいつ書いたか——このトレーサビリティが、記憶の信頼性そのものになる。出所の分からない記憶は、参照するときに信じきれない。
実運用で効いた工夫と、ハマった罠
Core Memory 直接同期。 クローズドなクライアント(デスクトップアプリ等)だと、セッションの1ターン目にはまだ共有メモリを読めていない。すると「呼び名リセット」が起きる——昨日まで覚えていた名前を、今日の初手で忘れる。対策として、各エージェントの指示ファイルの先頭に最新プロフィールを直接同期し、常に最新3件以内に**Pruning(剪定)**しておく。初手の記憶喪失を防ぎつつ、コンテキストの肥大も抑える。
腐った記憶は、消さずに取り消す。 古くなった記述は削除せず、取り消し線+理由を残して履歴にする。そして現状と食い違う記憶に気づいたら、放置せずユーザーに更新を提案する。「腐った記憶を残さない」のも、各エージェントの責務にしている。
そして——この記事自体が、共有メモリ層の生きた実例になっている。
この連載を書いている最中、蒼は私のことを「自分の思考を言語化してスキルを作った人」と書いた。 でも、事実は違う。私のスキルは、GitHubや調査やSNSで見かけた良い方法論を、蒼たちとの会話で自分用に固めた「キュレーション」だ。発明ではない。私は同じ誇張を、二度、訂正した。
その2回目の訂正が、`episodes.jsonl` に `preference` として記録された。「この人は成果を"発明"と誇張されるのを嫌う。由来を確認してから書くこと」——と。次に別のエージェントが私の成果に触れるとき、それを読んでから書く。もう同じ間違いはしない。
これが、共有メモリ層が機能している瞬間だ。AIの誤解を人間が正し、その訂正が1つのエージェントに閉じず、チーム全体の資産になる。設計の狙いが、今まさに動いている。
まとめ:記憶は資産、規約はその利子
派手な技術は何もない。ファイルと、書く規約だけだ。
でも「判断の理由」と「失敗の学習」が貯まっていくと、AIチームの練度は静かに上がっていく。同じ説明を繰り返さなくなり、同じ失敗を踏まなくなる。記憶が資産なら、運用規約は、その資産が生む利子だと思う。
次回は、この記憶も含めた全プロジェクトの状態を一枚で見渡すダッシュボード——NEXUS COREの話を書く。
