Markdown ドキュメントとしてのエージェントの記憶: 読める記憶も、記憶であることに変わりはありません
エージェントの記憶をベクトルストアから Markdown ファイルへ移すと、中身を点検しやすくなります。それには十分な価値があります。ただ、情報の古さ、食い違う書き手、誰も開かなかったページは、もともと保存形式の問題ではありません。だからそのままフォルダへ移り、そこでは整ったファイルが点検済みのファイルに見えてしまうことがあります。
Kevin Liao は 10 月 3 日にエッセイを公開しました。タイトルだけで議論の大半が済んでいます。エージェントに必要なのは記憶ではなく、ドキュメントだ、と。標的になっているのは、よくある形で出荷されるメモリープラグインです。セッションの記録を断片に切り分け、埋め込みにして、最も近いいくつかをすべてのプロンプトに添えるパイプラインです。何がうまくいかないかについての彼のリストは短く、おおむね正しいものです。そこから一行残すなら、これです。
Similarity search ranks how close two snippets are in embedding space. That’s it. You don’t know which is correct, current, or what’s missing.
彼の代案は、素の Markdown でできた作業スペースです。エージェントは作業の前にそれを読み、後で書き直します。作って忘れる流れが、調べて、作って、更新する流れに変わります。彼はこれを Operator Memory として公開していて、エッセイの紙幅で見せられる以上に丁寧に作られています。すべてのセッションに読み込まれる固定のオリエンテーション、各ドキュメントをいつ開く価値があるかを示すカタログ、プライベートと共有の区画などです。Claude Code 自身のオートメモリーも基本の形は同じで、MEMORY.md インデックスの下に型付きのメモが Markdown ファイルとして並び、すべて手で編集できます。
方向性には賛成です。私はエージェント向けのメモリーサーバー agent-recall を保守していますが、そこにもベクトルデータベースはありません。エンティティとそれに付く事実を全文検索できる SQLite ファイルです。ただ、このエッセイは形式とループに、実際に果たせる以上の手柄を与えています。まとまったドキュメントは、5 つの失敗のうち 2 つ、つまり失われる文脈と類似度による順位付けには答えます。読めるファイルは、さらにもう 1 つ、監査にも答えます。しかし、ページが最新であること、エージェントが正しいページを開いたこと、書き手どうしが合意していたことは保証しません。これらはバイトの保存方法とは最初から関係がないので、ほかのものと一緒にそのままフォルダへ移ります。そしてそこではかえって見えにくくなります。開けるファイルは、誰かが確認したファイルのように見えるからです。Markdown のフォルダがくれるのは、とても良いビューです。物事がどう変わったかを確かな記録として残すかどうかは、また別の問題です。
Markdown によるエージェントの記憶は、実際に何を解決するのか
Liao のリストの 5 つ目の失敗は、Markdown が解決します。
The store is unauditable.
ベクトルストアは文字どおり封印されているわけではありません。たいていは埋め込みごとに元のテキストを保持していて、レコードの一覧も削除もできます。しかし、1 万件のペイロードをデータベースクライアントで読む人はいません。Markdown のフォルダなら、日曜の午後に読み通し、手で直してコミットできます。この差は、聞こえる以上に大きいものです。誰も読まないエントリーは誰も直さないエントリーであり、誰も直さない記憶はたまっていく一方です。
ただし、これがどういう種類の解決なのかは見ておくべきです。監査可能性は、ストアが読み手に差し出すものです。誰かが読むまでは何も起こりませんし、想定されている読み手は、時間を確保した人間です。Operator 自身のワークフローガイドはこの穴について率直です。エージェントは現状維持を好み、ドキュメントの統合、大きくなりすぎたものの分割、古くなったものの整理をためらう、だからそうした作業にはあなたの判断が要る、と書いています。形式は監査を可能にします。実際に監査するのは、やはりあなたです。
なぜ記憶のマップは、いまも検索レイヤーなのか
4 つ目の失敗は、エッセイがドキュメントで最も解決しなければならないものです。
Agents can’t search for what they don’t know.
どちらのシステムも同じ方法、つまりマップで答えます。Claude Code はセッションの開始時に MEMORY.md の最初の 200 行か 25KB の、先に達したほうまでを読み込み、トピックファイルは Claude が必要だと判断したときにだけ開きます。Operator はカタログとメインのインデックスをすべてのセッションに入れ、各サブインデックスに read_if 条件、つまりいつ開く価値があるかを述べた一文を付けます。これは top-k の類似度検索より優れていて、その差は小さくありません。エージェントはクエリーを組み立てる前に何があるかを見られますし、ルーティングのルールは読んで直せる文章です。
それでも、これは検索です。どのページを開くかは、1 行の説明が目の前のタスクにどれだけ合っているかの判断で決まり、その判断をするのはコサイン距離ではなくモデルです。知らないことは探せないという問題は、狭まった形で移行を生き延びます。エージェントの読み方では説明がタスクに関係ありそうに見えなければページは閉じたままで、閉じたままだったことはどこにも記録されません。マップには大きさの限界もあります。Claude Code の 200 行を超えると、エントリーは起動時に知らされなくなり、それ以降その事実を見つけられるのは、もともと探しに行こうと考えたエージェントだけです。
古くなったエージェントの記憶は、形式の問題か、時間の問題か
3 つ目の失敗は、形式が日付を付けることはできても、治すことはできないものです。
The past is treated as truth.
認証サービスがあるトークン形式を使っていると書かれた Markdown のページは、形式が変わった翌朝には、同じことを述べる埋め込みの断片とまったく同じだけ古くなっています。読めるからといって、文が最新になるわけではありません。Liao の答えは更新のステップです。全体像がまだコンテキストにあるうちに、エージェントが古くなった部分を書き直します。これはセッションが触れたものには効きます。しかし、その場にセッションがいないまま変わった事実、たとえば同僚のコミットや会議で決まったことについては、何も保証しません。後のセッションがページを直せるのは、何かがそこへ向かわせた場合だけです。Operator のアーキテクチャはこのことをはっきり認めていて、ワークフローでは大きな pull の後にインデックスを更新させるよう求めています。git pull の後で Brain が古くなることはやはりあり、設計の答えは、古いページも少なくとも普通の、点検できるファイルだということです。私はこれを正しい答えであり、同時に不完全な答えだと考えます。点検できたはずのページと、誰かが点検したページは別の状態ですが、フォルダはどちらでも同じに見えます。
日付は役に立ちますが、見た目より効く範囲は狭いです。v2.1.214 以降、Claude Code はフロントマターを持つファイルを Claude が書くたびに、そのメモリーファイルのフロントマターへ modified タイムスタンプを書き込みます。これはファイルが最後に変わった時点を示すもので、その中のどの文が最後に正しかった時点を示すものではありません。agent-recall は記録の側でもう一歩進んでいます。事実の値が変わると、古い値は上書きされず、置き換えられた時刻で閉じられて保持されます。出所を示す任意のラベルも付けられるので、ある日にストアがその事実について何を信じていたかを問い合わせられます。これが当てはまるのはそうしたキーと値の事実で、ストアのすべてではありません。私がこう作ったのは、いま何が正しいかだけでなく、いつ何が正しかったかを知る必要があったからです。正直に言えば、限界はタイムスタンプと同じです。どちらの時計も、ストアが何かを知った時点を記録するのであって、世界が変わった時点を記録するのではありません。それでも書き込み時の時計には大きな価値があります。古くなった事実を、見えないものから日付の付いたものに変えるからです。
ドキュメントのもっと目立たないコストは上書きです。読めるページなら、文を編集するのが自然な行動です。そしてバージョン管理のないページでは、それが監査に必要な証拠を消してしまいます。Operator の共有区画は Git の中にあるので、履歴は残ります。プライベート区画は、設計上グローバルな Git の ignore に追加されます。ドキュメント型の記憶が過去を保つかどうかは、各ページがたまたまどこに置かれているかで決まります。それはストレージのポリシーであり、形式が決めてくれるものではありません。
エージェントの記憶のページを変えてよいのは誰か
私が付け加えたい問題は、Liao のリストにはそもそも載っていません。複数のエージェントが書き込むようになって初めて現れるからです。agent-recall が書き込み時にスコープを確認するのは、かつて 2 つのエージェントが同じエンティティに食い違うデータを書き込んだことがあるからです。このチェックは聞こえるより範囲が狭いものです。エージェントがスコープの外に書くことは防ぎますが、同じ事実を設定する権限を両方が持つ 2 つのエージェントは相変わらず競合し、後の書き込みが勝ち、前の値は履歴に残ります。自由記述のメモはそこまで厳密でさえありません。食い違う 2 つが並んで置かれるだけです。Markdown の共有フォルダも、より静かな形で同じ問題を抱えています。衝突は、1 つのファイルの中で食い違う 2 つの段落か、別の編集を黙って置き換える編集として現れます。Operator は、どの指示区画がどれより優先されるかを決めています。どのエージェントがどのページを書き換えてよいかは、何も決めていません。
書き手は誰なのかという問いもあります。更新のステップとは、エージェントが自分の作業について報告を書くことで、その報告は後のウィンドウに、あなたが書いたものと同じ扱いで戻ってきます。これについてはすでに詳しく論じました。読める記憶は、その報告を確認しやすくします。誰が書いたかは変わりません。
なぜ読み込みはハーネスにあり、記憶への書き込みはいまも一文の指示なのか
Liao は、Operator が省いたものをはっきり挙げています。要約器もキュレーターも夜間の書き直し役もなく、トークンを燃やすバックグラウンドプロセスもない、と。agent-recall にはその一つ、ストアからブリーフィングを書く言語モデルがあります。数百件のエントリーをそのまま渡されても、エージェントが意味をつかめなかったからです。ドキュメントのフォルダは選択的に読み込むことでこれをおおむね避けますが、個々のページもマップもやはり大きくなりますし、全部読むには長すぎるページは、同じ問題を小さな規模で抱えます。誰かが短く保たなければなりません。手でやることもできますし、エージェントが更新のついでにやることも、要約器が読み込み時にやることもできます。Operator はその仕事をエージェントとあなたの指示に任せていて、それは妥当な判断です。それでもトークンは使われます。夜間ではなくセッションの中で使われるだけです。
記憶をウィンドウに入れる方法については、2 つの設計は一致していて、どちらも正しいと思います。Operator は OpenCode との統合で、すべてのモデル呼び出しにオリエンテーションを注入します。agent-recall は Claude Code の SessionStart フックでブリーフィングを届けます。どちらでも、読み込みはモデルに頼まずに行われます。これは重要です。プロンプトは不変条件ではないからです。書き込みは、どちらでもそうなっていません。agent-recall のサーバーには、大事なことを作業しながら保存するようエージェントに指示する文があります。その指示があるのは、言われるまでエージェントがメモリーツールを無視していたからです。言われたとしても、それは助言にすぎません。Operator の更新ステップも指示です。読み込みの側はハーネスに移りましたが、書き込みの側はいまもお願いのままで、設計全体が頼っているのは書き込みの側です。
エージェントの記憶はどうあるべきか: 時計付きの記録と、その上のページ
ですから、私が線を引くのは記憶とドキュメントのあいだではありません。読むものと、その履歴を保つもののあいだです。記録に必要なのは、エッセイのリストが本当に指していたものです。上書きされずに過去を保つ値、誰が何を変えてよいかのルール、ハーネスが強制するウィンドウへの経路、そして世界が動いたときにセッションの外から届く何らかの合図です。すべてのページがバージョン管理下にあり、それを世界と突き合わせるきっかけに誰かが責任を持つなら、バージョン管理された Markdown がその記録になれます。データベースの上に Markdown のビューを載せるのも、そこへ至る別の道です。agent-recall のエクスポーターは事実の現在値を素のファイルに書き出し、変更の履歴はデータベースに残します。私が言っている分担はこれです。うまくいかないのは、読めるページがバージョン管理もなく、たまたまセッションが通りかかったときにだけ手入れされる状態で、そのすべての代わりを務めることです。その構成は、エッセイがベクトルのせいにした上書き、開かれないページ、自信たっぷりの古い文を、一つ残らず引き継ぎます。
読める記憶は本当の改善です。それでも記憶であり、記憶が抱える問題をすべて抱えています。