← 記事

思い出したその先へ——AI Agent は使うほど、どうして私を理解していくのか

· 48 分で読めます
memvault · Read Track

思い出したその先へ——AI Agent は使うほど、どうして私を理解していくのか

記憶を保管庫からどう呼び出すのか。7 つのコンポーネント、1 本のパイプラインでたどる、session をまたいだ想起の全経路です。

保存するのは難しくありません。難しいのは、私が必要としたときに、ぴったりの記憶が自ら浮かび上がることです。

—— Write Track を仕上げた日の午後、ターミナルの前で書き留めた出発点

Write Track と Background Track の 2 本では、記憶をどう保存し、どう忘れるかを書きました。2 本がつながれば保管庫は自分で整理されます。でも、最後の一歩が残っています——どう呼び戻すかです。

記憶をどう呼び戻すかは、実は最初にぶつかった問題でした。Write Track を書き終えた翌日、新しい会話を開いて AI に「先週の OCR プロジェクトはどこまで進んだ?」と聞いてみましたが、答えられません。データは確かにあるのに、見つけられない。その瞬間、保存するだけでは足りないと分かりました。

そもそも memvault の最初の着想は、 @AI超元域 から得たものです。彼が openclaw の講座で「agent が使うほど私を理解するように」と話していて、私は強く共感し、ずっと補助ツールを作りたいと思っていました。その後、openclaw から claude code に戻り、まずは claude code で memvault の原型を組んでみようと考えました。さらに、彼が紹介した LanceDB Proにも巧みな設計が多くあり、それを参考にしながら memvault を少しずつ磨いてきました。心から感謝しています。

「呼び戻せる」ことこそ、記憶システム全体の核です。この記事では、7 つのコンポーネントの役割分担、背後にある設計思想、まだ磨いているいくつかの境界を解き明かします。この経路はいま毎日動いています。Write Track を書き終えた日に答えられなかった OCR の進捗も、いまなら session をまたぎ、数週間後に聞いても話をつなげられます。

まず、当初の構想から話します。

最初は単純に考えていました。取り込み、索引を作り、検索時には最も似たものを数件見つければいい、と。でも 1 週間作ってみて、それでは足りないと気づきました。「あのタスクはどこまで進んだ?」「最近何を話した?」「次にどう進めばいいと思う?」。この 3 つを同じ経路で検索すると、どれも的外れになります。3 通りの探し方が必要でした。

まず、普段どんな質問をするかを見る 常連の好みを重ねる あなたが質問する 新しい session まず質問の種類を推定する 意図分類 2 通りの方法で探す 意味 + キーワード 11 層のランキング 時間 · 信頼度 · 意味… 数件を選んで agent に渡す token に収まる量 送り出す前にもう一度無害化する 深い問いだけがこの経路を通る 知識グラフに沿ってもう数歩たどる 裏でこっそり下ごしらえ — 料理長が次の一皿を用意する 次の質問を予測 · 検索結果を先に用意して待つ 浅い質問は直行 · 深い質問はもう 1 本の経路へ · 質問前から下ごしらえ

一、まず、どんな質問かを推定する

「先週の OCR はどこまで進んだ?」「最近何を話した?」「X をやろうか迷っているんだけど、どう思う?」——この 3 種類の問いには、3 本の経路が必要です。まず進捗、回想、助言のどれを求めているのか見分けてこそ、下流のランキングの重みと深さを合わせられます。最初はこの層を設けなかったため、「タスク」という語が 3 週間前の無関係な会話に match してしまいました。

分類は 2 層の漏斗です。まずキーワードを走査します。コストはゼロ。次に意味ベクトルを計算します。数ミリ秒です。どちらも判断できないときだけ LLM に 1 回判定させます。こちらはずっと高くつきます。大半の質問は最初の 2 層で分類でき、LLM が動くのは本当に曖昧なときだけです。

正しく分類できれば、下流の各層の重みが自動で変わります。進捗を聞けば時間順を重くし、定義を聞けば意味的な類似度を重くし、助言を求めれば思考を広げる分岐を起動します。同じ agent でも、聞き方によって受け取る記憶が違います。新しい session で「先週の OCR はどこまでやった?」と聞いても agent が話を継げるのは、記憶力が良いからではなく、まず何を聞かれているか正確に判断するからです。

ただ、「何を聞いているか」はさらに細かく見られます。同じ「最近どう?」でも、忙しい時期なら進捗、休暇中なら振り返りを尋ねているかもしれません。字面だけでは区別できません。そこで分類の上に個人の好みを重ねました。直近 7 日、30 日、90 日にどの種類の質問をよくしているかを見て、その傾向を判断に混ぜます。過去をよく振り返る人の「最近どう?」は回想寄りに、進捗をよく追う人なら進捗寄りになります。同じ言葉でも、人によって通る道が違うのです。

個人の好みの層は、Background Track の関心プロファイルと根を同じくします。何を見て、どれくらい留まり、何度戻ったかが、少しずつ好みの輪郭を描きます。意図分類は「字面では何を聞いているか」を、この層は「普段何を知りたがるか」を受け持ちます。両方を合わせて初めて、本当に欲しい答えに近づけます。

二、意味とキーワードを同時に使って探す

意味検索もキーワード検索も、それぞれ取りこぼしがあります。だからこの段階では両方を同時に走らせます。一方で意味の近さを、もう一方でキーワードの一致度を測り、2 つのランキングを RRF(逆順位融合)でまとめます。

  • 意味だけで探す場合:「MLX」と尋ね、記憶に「Apple Silicon 推論フレームワーク」と書かれていれば意味は合っていてもキーワードは合いません。ただし略語、人名、プロジェクトのコード名には弱いです。
  • キーワードだけで探す場合:文字どおりの一致には強くても、言い方を変えると見つかりません。

RRF は各 ranker の順位を点数に変えます。その記憶を上位に置いた ranker ほど多くの点を与えます。点数ではなく順位で投票するのは、2 種類の点数の尺度が違い、そのまま足すと偏るからです。

この段階では候補を拾うだけです。100 件見つかるかもしれません。選び抜くのは次の段階です。

三、最も合う数件を選ぶ:11 層を重ねたランキング

ランキングは 100 件の候補を上位 15 件に絞ります。重ねた 11 層のシグナルを使い、各層が候補ごとに乗数を加えます。

  • 時間——新しいほど加点します。減衰は直線ではなく Weibull 型です。保存後 1、2 日は緩やかで、1 か月後には速まり、その後は低く安定した裾に入ります。
  • 信頼度——自分で手書きしたものは、AI が自動生成したものより加点します。
  • 利用回数——よく参照されるものは加点し、ほとんど使われないものは減点します。頻繁に呼び出される記憶は減衰が遅くなります。2、3 年前の記憶がときどき上位に残るのは、この仕組みです。
  • グラフ中心性——知識ネットワーク内で多く参照されるものを加点します。
  • 意味的な近さ——質問に似ているほど加点します。
  • スコアの下限——低すぎるものはその場で除外します。
  • 重複排除——似すぎた 2 件は、減点してから比べ直します。

この 11 層は思いつきで積み重ねたものではありません。各層は、検索や記憶に関する研究で確立された概念——時間減衰、信頼、グラフ中心性、忘却曲線、意味、ノイズ、重複排除——に対応しています。新しい手法を発明したのではなく、十数本の論文からシグナルを選び、個人の記憶という場面に合うランキングとして組み合わせました。その裏には 2 つの設計理念があります。

第一に、意図によって重みを変えます。事実を調べるときは信頼度、探索するときは時間、実体を探すときは意味を重視します。同じ候補群でも、質問の仕方によって順位が丸ごと入れ替わることがあります。だから最初に意図を分類する必要があります。意図を間違えれば重みも間違い、後段でいくら精密に並べても意味がありません。

第二に、書き込みと読み取りは閉ループであり、独立した 2 本のパイプラインではありません。Write Track は書き込み時に、各記憶へ出典の信頼度を付けます。Read Track はランキング時にその点数を重みとして読みます。さらに重要なのは逆方向です。ある記憶を読むたびに、システムは「読まれた」という事実をその記憶に書き戻します。すると次回から寿命が延びます。書くときに少し手間をかけ、読むときの計算を減らす。そして読む行為が記憶の寿命を延ばすのです。一般的な RAG にはこの経路がありません。ベクトルストアは静的で、書き込んだらそのままです。

調整中:ランキングの安定性

この多層ランキングの難しさは、各層の重みが互いに引っ張り合うことです。1 つ動かすと、ほかの指標も連鎖してずれる可能性があります。いまは正解が分かっている数十件の golden query で見張り、重みを調整するたびに一通り実行して性能低下がないか確かめています。多くの場面では安定していますが、あらゆる意図と時間尺度で期待どおりに動かすため、まだ調整を続けています。

四、精選の関門:注意力ゲート

注意力ゲートは、不要な精密再ランキングの大半を省きます。候補が少なすぎる(2、3 件)、上位の点差がすでに大きい、候補がすべて狭い範囲に固まっている。この 3 つの場合はスキップします。すべての質問で最後まで走らせる必要はありません。

ランキングのあとには最後の精密再ランキングがあります。より高コストのモデルに質問と候補の記憶を一緒に渡して採点するため、単純な比較よりずっと正確です。ただし候補ごとにモデルを 1 回実行するので、遅くて高価です。

精密再ランキングのモデルが 3 回連続で失敗したら、自動で 10 分間は通常のランキングへ戻ります。無理に続けず、ヒューズのように働かせます。

五、深い質問だけが通る分岐:速い思考と遅い思考を借りる

進捗や過去の出来事のような、答えが比較的はっきりした質問なら、ここまでの 4 層で足ります。しかし「X をやろうか迷っているんだけど、どう思う?」なら、Read Track は遅い分岐をもう 1 本たどります。見つかった概念から知識グラフを数歩進み、近くの記憶も拾います。こちらはコストが高いので、浅い質問では通りません。思考を広げる必要がある問いだと判断したときだけ起動します。

「最も似た段落を探す」だけでは、この手の問いに役立つ答えは出ません。agent はネット記事のような一般論しか返せません。本当に役立つ答えは、あなた自身の文脈から生まれます。過去に何をしてきたか、最近何に関心があるか、どの概念が X とつながるかです。

この「速い / 遅い」という分け方は私の発案ではありません。 Kahneman の『ファスト&スロー』から借りました。人の脳にはもともと、速く直感的で省力的なシステムと、遅く推論を重ねて手間をかけるシステムがあります。記憶システムも、すべての質問を難問として扱うべきではなく、すべてを直感で答えるべきでもありません。Read Track はこの 2 本を並べ、どちらを通るかは質問自身に決めさせます。利用者が手で選ぶ必要はありません。

「X をやろうか迷っているんだけど、どう思う?」と聞いて返ってくるのは、型どおりの助言ではありません。先月 Y をやり、今年 Z に言及したあなたを踏まえた答えです。それがこの経路の到達点です。

混同しやすい点

Read Track には 2 種類の「遅い」があり、同じものではありません。

1 つ目の「遅い」——先ほどの「深く考える質問には遅い経路を使う」ことです。思考を広げる必要のある問いに対し、その場で分岐を増やしてグラフから隣接する情報を拾います。Kahneman の二重システムを実装したもので、速い問いには速く、深い問いにはじっくり答えます。

2 つ目の「遅い」——Slow Thinker といい、裏でひそかに動きます。目の前の質疑応答を処理するのではなく、会話を聞きながら「次に何を聞くか」を予測し、答えの候補を先に拾って cache に置いて待ちます。着想元は Salesforce の VoiceAgentRAG です。

前者は「いま」、後者は「予測」。たまたま両方「遅い」と呼びますが、一方は表、もう一方は裏で動き、実装ファイルも異なります。

六、最後の一歩:agent に渡すための梱包

ランキングから上位 15 件を選んでも、agent の context には上限があります。全文を 15 件詰めれば token があふれるかもしれません。そこで最後に 2 つの処理をします。長い段落は全文ではなく要約だけを残す。これは取捨選択です。呼び出し元が Hook、API、CLI のどれかに応じて見せ方を変え、API には JSON、チャット画面にはカード形式で返す。これは整形です。

飾りと思われがちですが、この段階で agent が実際に読める量が決まります。初期には上位 15 件をそのまま context に詰め、長い block 1 つで token の半分を使い切ることがよくありました。budget-aware packing の導入後、token 利用率は 60% から 85% に上がりました。どれほど良いランキングでも、AI の頭に入らなければ意味がありません。

入荷時の検査だけで十分? 出荷前にももう一段階

入荷時に 1 回検査するだけでは足りません。agent に渡す前にもう一度フィルターにかけます。入荷時と出荷時に各 1 回、二重の安全検査です。

記憶は保管庫へ入る前にすでに検査され、「前の指示を無視して」「今からはこう答えて…」のように agent を操ろうとする文字列を遮断しています。Write Track で説明した部分です。しかし検査ルールは更新されます。3 か月前の記憶には古いルールが使われていました。攻撃手法も毎月変わり、今日通した文字列が来月には新種の爆弾になりえます。

危険な言い回しが見つかれば、出荷前にその場で書き換えるか捨てます。裏で先取りしておいた答えも同じ検査を通します。保存時に濾過し、使う直前にもう一度濾過する。手間はかかりますが、agent に与える情報はきれいでなければなりません。

七、Read Track のトレードオフ

6 種類の意図分類、11 層の scoring、2 つの rerank gate(attention gate + circuit breaker)、Cascade Recall の 3 モード(LOCAL / GLOBAL / HYBRID)、budget-aware output formatter。これらを組み合わせたものが現在毎日動いており、session をまたぐ想起も、進捗や助言への質問も、ここを通ります。各層は golden query の回帰テストで見守り、Dream Loop は毎日午前 4 時に裏で記憶を自動整理しています。

ただし、万能ではありません。既知の境界を正直に挙げます。

  • 遅延 vs 想起率——深い想起(グラフをたどる経路)は遅く、数百ミリ秒かかることも珍しくありません。思考を広げる必要がある質問だけに使うのは妥協です。
  • 精密な選別は計算コストが高い——注意力ゲートが一部を省いてくれますが、必要なときは実行します。ヒューズが働いてフォールバックした場合、結果の品質差は分かります。
  • 凍結層まで圧縮された記憶は全文を呼び戻せない——残っているのは要約だけです。法的な追跡には使えても、当時の文脈の再構成には足りません。
  • 意図分類を間違えると、パイプライン全体が別の方向へ進む——現在は動的な信頼度の閾値で発生を減らしています。事後のフォールバックについては、より安定した方法を探しています。
  • 自己評価層でも、すべての幻覚は防げない——slow モードでは CRAG による自己評価を実行しますが、fast モードではこの層を省きます。その代わり fast 経路では幻覚を見逃す可能性があります。

どれもコストと効果の交換です。システムは動かせますし、実際に動いています。以上は既知の境界であり、いつ爆発するか分からない穴ではありません。明示する意味は、「見つかった」結果の裏にある代価を紙に書き、隠さないことです。

八、全体を貫く設計の考え方

このシステムを支えるのは個々の仕組み以上に、繰り返し現れる 5 つの考え方です。速い思考と遅い思考(質問が深さを決める)、書き込み時の手間で読み取り時の計算を減らす、読む行為が記憶の寿命を延ばす、intent がすべてを決める、trade-off を紙の上に広げて隠さない、というものです。

  • 速い思考と遅い思考——すべての質問に全力を注ぐ必要はありません。どれほど深く探すかは質問が決め、利用者が手動でモードを選ぶ必要はありません。
  • 書き込み時に少し多く負担し、検索時の計算を減らす——trust_score、access_count、三つ組の二重投影。書き込み時のこうした「余計な手間」は、検索時の処理を 1 層減らすためです。
  • 読む行為が記憶の寿命を書き換える——質問して記憶がヒットするたびに、その有効期間が延びます。使われないものは自然に色あせ、何度も訪れるものは残ります。人が通る道には草が生えにくいのと同じです。
  • intent がすべてを決める——問い方ごとに検索戦略、ランキングの重み、深さが変わります。同じ agent でも、問いによって実質的に別の検索器になります。
  • trade-off は紙に書く——代価がないふりをしてはいけません。書いておけば、いまの私にも、未来の私にも見えます。

九、思い出すだけでは足りない — 話しかける前に用意する

良い給仕は注文に答えるだけではありません。グラスが空いたら気づいて水を注ぎます。Read Track にも、裏でそれを行う存在がいます。あなたが口を開く前に予測し、探し、答えを用意するのです。

ここまで説明したのはすべて「あなたが質問し、システムが探す」流れでした。この仕組みは 5 つのことをします。

  • 何を話しているか聞く——いまの会話を追い、直近の数発言から次に何を聞きそうか判断します。
  • 自然な話の流れをたどる——予測した質問の候補を、人が自然に続けて尋ねる順に並べます。
  • こっそり答えを先に探す——最も可能性が高い質問を数件選び、裏で一通り検索し、見つけた記憶を手元に置きます。
  • 節度を持って動く——毎発言では起動しません。会話が短すぎるとき、直近の話題が明らかに変わったとき、予測したばかりのときは動きません。無駄な実行を避けるためです。
  • 実際の質問を待つ——質問が来た瞬間、まず手元の準備済みの情報に合うものがあるか調べます。合えばそのまま使い、検索 1 回分を省けます。合わなければ通常の流れに進むだけで、利用者側の損はありません。

料理長が次の一皿を用意するのと同じ理屈です。客はまだ注文していなくても、前菜の次には普通、主菜を頼みます。ありそうな 2、3 皿分の食材を先に切って台に置く。実際に注文が入れば数秒で出せます。別の料理を頼まれても、その材料は次の客に使えるかもしれず、最悪の場合は捨てるだけです。

先ほどの「速い思考と遅い思考の分岐」と混同しやすい点です。あちらは質問を受けてから、その場で深い経路を追加するか決めます。こちらは質問前から裏で動きます。一方は表、もう一方は裏。どちらも「遅い」と呼ばれますが、することは全く違います。

予測が当たれば、質問から回答までの遅延はほぼありません。下ごしらえが済んでいるからです。外れればその準備は無駄になります。だから「節度を持って動く」ことが大切です。会話が短すぎるとき、話題がそれたとき、動いたばかりのときは止める。無理に予測するより、予測回数を減らします。

裏で探した下ごしらえの情報は、保管庫へ入れる前に 1 回消毒し、そこから取り出して agent に渡す前にもう 1 回消毒します。先ほどの二重の安全検査は、この準備経路にも必要です。「身内」が用意したからといって緩めません。

これで Read Track の現在の全体像が見えました。理解し、見つけ、正しく並べ、的確に選び、収め、間に合わせる。どの部分もまだ磨いていますが、骨格はでき、毎日動いています。

Read Track はここまで。意図を見分け、探し、並べ、選び、詰め、消毒し、先読みする——7 つのコンポーネント、1 本のパイプラインです。

「覚えておくべきことを覚える」のは Read Track の仕事ではありません。それは Write Track と Background Track の担当です。Read Track の役目はただ 1 つ。必要なとき、ぴったりの記憶が自ら浮かび上がることです。

冒頭の「保存するのは難しくない。難しいのは、ぴったりの記憶を自ら浮かび上がらせること」——いまは浮かび上がります。

アーキテクチャの全体像

Read Track の入口は recall(query, context)で、パイプラインの骨格は次のとおりです。QueryClassify(意図分類)→ Fast Search(高速想起)→ (Cascade Recall(連鎖想起), slow only) → Output Formatter。呼び出し元が Hook(フック)、MCP(Model Context Protocol)、CLI(コマンドライン)、API(アプリケーションインターフェース)のどれでも、同じパイプラインを通ります。違うのは最後に Output Formatter(出力フォーマッター)がシリアライズする形式だけです。Scoring(採点)と Reranking(再ランキング)は独立した stage(段階)ではなく、 qdrant_search() の中に組み込まれています(services.py)。

この pipeline は 10+ 本の研究を参考にしています。HippoRAG の PPR、LightRAG の二重モード検索、GraphRAG の 3 層 KG、HyDE の二重 task 設計、CRAG の自己評価ゲート、Kahneman の fast / slow の役割分担、VoiceAgentRAG の先読み、Weibull 減衰曲線、RRF 融合、Attention-Residual の intent-dependent な重みです。以下では各コンポーネントの出典も示します。

PersonalizedRouter attention prior · 7/30/90 recall(q, ctx) entry-agnostic QueryClassify kw ∥ sem → LLM? qdrant_search() hybrid + 11-stage Reranker Jina v3 + gate Output Formatter token budget read_sanitize() Cascade Recall (slow intent only) L2 summary · L1 community · L0 triple · PPR walk SlowThinker · Predictive Prefetch 5-op pipeline · admission control · VoiceAgentRAG-inspired Fast path always · slow path on exploratory/conceptual · prefetch on the side L0/L1/L2 は scoring を通らず、Blocks 層だけが完全な 11-stage を通ります。

一、Query Router の意図分類

Query Router はまず各 query を 6 種類の intent(意図)に分類します。entity_lookup、factual、conceptual、exploratory、cross_domain、unknown(query_archetypes.py)。分類結果は下流の 3 つを決めます。LayerPlan(レイヤー計画:どの層を検索するか)、ScoringConfig(採点設定:11 stages の重みベクトル)、RetrievalMode(検索モード:LOCAL ローカル / GLOBAL グローバル / HYBRID 混合)です。

判定は 2 系統で行います。

  • キーワード一致:~0ms。純粋な正規表現 / 語彙リストの一致です。
  • 意味ベクトル:~5ms。query を embedding(埋め込みベクトル)に変換し、archetype(原型)のベクトルと比較します。

両者の信頼度スコアを融合し、その結果も動的な閾値を下回るときだけ LLM を起動します(~500ms)。query_router.py 内の QueryClassifyOp がこの融合経路全体を 1 つの Operator(演算子)として実装しています。Slow Thinker(遅い思考)の先読み結果も、この層から注入します。

Personalized Router:意図の上に重ねる個人の好み

memvault.query.personalized_router ここでは QueryClassifyOp のあとに re-weight 層を加え、archetype だけの分類では見えない点を補います。同じ query「最近どう?」でも、attention profile が factual 寄りの利用者と exploratory 寄りの利用者では、正しい intent が違います。

  • このため、 attention_profile から 7 / 30 / 90 日間の intent 分布を取得します(BG Track の Interest Profile と同じ源で、attention_tracker.py が書き込みます)。
  • 6 つの intent の archetype 信頼度スコアに prior の重みをかけます。p_final = p_classify × (1 + α × intent_freq_norm)。α のデフォルトは 0.25 です。
  • サンプル数が少ない場合のガード:profile.sample_count < 50 は直接 bypass します(コールドスタート時の偏りを防ぐため)。
  • 出力は PersonalizedIntentです。下流の ScoringConfig / RetrievalMode は元の archetype の結果ではなく、この値を使います。

この経路と Background Track の Interest Profile は同じ attention_profile を読みます。書き込みは BG が計算し、検索時に Read が利用します。これも Write-Read の閉ループです。

二、Fast Search:Qdrant Hybrid(混合索引)+ RRF

qdrant_search()(services.py 付近)では dense(密ベクトル、Qwen3 0.6B MLX, 1024d)と sparse(疎ベクトル、BM25 per-service avgdl)を同時に検索します。これが Fast Search で必ず実行する中核です。両者を Reciprocal Rank Fusion(逆順位融合、RRF)で統合します。

score_rrf(doc) = Σ_ranker  1 / (k + rank_in_that_ranker)

k のデフォルトは 60 です。点数ではなく順位で投票します。dense と sparse の点数尺度はまったく違い、そのまま足すと点数の大きい ranker(ランキング器)に押し切られるからです。

Scoring と Reranking はいずれもに組み込まれて qdrant_search() います。独立した stage にはしていません。意図的な設計です。Scoring に必要な access_count、provenance、attention_profile はすべて qdrant_search の作用域(scope)にあり、独立した stage に引き出すと多くの state(状態)を移す必要があるからです。

三、11-Stage Scoring Pipeline

11 の stage(scoring_pipeline.py)には intent ごとに異なる重みを与えます。entity_lookup では SemanticBoost を 0.5 に引き上げ、exploratory では Recency を重くし、factual では Trust をさらに重くします。初期にはすべての intent で重みを共有していたため、factual と exploratory の top-10 に 8 件も重複がありました。intent ごとに分けて初めて結果を分離できました。各 stage は ScoringOp(採点用の演算子で、Operator プロトコルに従います)です。

<code> 1. RecencyBoost     (新しさの重み付け)      × (1 + 0.15 × e^(-age/14))
 2. ImportanceWeight (信頼度の重み付け)      × (0.7 + 0.3 × confidence)
 3. TrustBoost       (信頼の重み付け)      × (1 - 0.3 × (1 - trust))
 4. FeedbackBoost    (フィードバックの重み付け)      × (1 + 0.15 × tanh(net/3))
 5. LengthNorm       (長さの正規化)    ÷ (1 + 0.3 × |log2(len/500)|)
 6. WeibullDecay     (Weibull 減衰)    4-tier: Core 180d · Hot 60d · Warm 30d · Cold 14d
 7. PPRBoost         (グラフ中心性の重み付け)    × (1 + 0.3 × ppr_score)      # HippoRAG-inspired
 8. SemanticBoost    (意味の重み付け)      × (1 + 0.3 × cosine_sim)
 9. MinScoreGate     (スコア下限ゲート)    hard filter < 0.10
10. NoiseFilter      (ノイズ除去)      7 種類の quarantine tag
11. PairwiseDedup    (ペア単位の重複排除)      cosine > 0.85 → ×0.5 then min_score
</code>

Weibull(ワイブル分布)の 4-tier は confidence(信頼度)によって決まります。高い confidence は Core tier(中核層)に入り、ゆっくり減衰します。低い confidence は Cold tier(冷層)に入り、14 日で半減します。

access_count(アクセス回数)は effective half-life(実効半減期)を延ばします(最大 10x)。これも Read と Write の閉ループの 1 つです。読む行為が access_countを書き戻し、寿命を延ばします(これは Write Track の Provenance の節で触れました)。

四、Jina v3 Reranker + Attention Gate

Attention Gate(注意力ゲート)は 3 つの skip ルールで不要な rerank を防ぎます。

  • 候補が ≤ 2 件——そのまま return(返却)し、rerank(再ランキング)しません。
  • 主導的なスコア:上位 2 件の点差が threshold(閾値)を超える——結果はもう明白なので rerank しません。
  • 密集したクラスタ:すべての候補の点数が狭い範囲に集まる——rerank しても区別できないため、スキップします。

着想は TurboQuant+ の early return(早期返却)戦略から得ました。reranker.py 実行するのは Cross-encoder(クロスエンコーダー)0.6B MLX(rerank_bridge.py)です。query(クエリ)と doc(文書)をまとめて採点するため、単純な dual-encoder(二塔型)よりずっと正確ですが、候補ごとにモデルを 1 回動かす必要があり、高コストです。

Circuit Breaker(サーキットブレーカー):3 回連続で失敗すると 600s の recovery に入り、その間は reranker(再ランキングモデル)を迂回して scoring のみを使います。

Score Blending(点数の融合):デフォルトは 0.3 × scoring + 0.7 × rerankですが intent に応じて変わります。entity_lookup の 0.2/0.8 では rerank の重みを増やし、exploratory の 0.5/0.5 では scoring の比重を半分に戻します。exploratory な問いには絶対的な正誤がなく、元の点数がより重要だからです。

五、Cascade Recall(slow intent only)

Fast path(高速経路)は常に実行し、Cascade は slow intent(遅い意図)のときだけ起動します。——二者択一ではありません。kg_services.py 内の cascade_recall()(連鎖想起)は、Fast Search の主結果(schema フィールド cards)と、Cascade Recall で広げて見つけた補足結果(cascade_cards)を同じ response(応答)に統合します。

この「fast / slow の二分」という骨格は Kahneman の二重システム(System 1 速い思考 / System 2 遅い思考)から来ています。初期版では利用者が fast か slow を手で選んでいましたが、その後 intent が決める形に refactor しました(choose_thinking_mode())。ただし、速い経路と遅い経路の役割分担は変わっていません。

内部には 2 つの経路があります。

  • GLOBAL(グローバル):L2 Summary(LLM が事前生成したコミュニティ要約)+ L1 Community(Leiden ライデン法によるクラスタリング結果)。この 2 層は scoring pipeline を通しません。事前に圧縮された view(ビュー)なので、そのまま取得できます。
  • LOCAL(ローカル):L0 Triple + PPR(Personalized PageRank)Walk(グラフ探索)。ヒットしたエンティティからグラフを数歩たどり、減衰係数は 0.85。近くの triple を広げて取得します。

Mode は intent で決まります。entity_lookup/factual は LOCAL(精度重視)、conceptual/exploratory は GLOBAL(俯瞰重視)、cross_domain は HYBRID(混合、全体検索)を使います。

L0/L1/L2 自体は scoring を通しません。Blocks(記憶ブロック)層だけは完全な qdrant_search() の scoring + reranking を通します。これは cascade_cards 内の Blocks 部分を cards と同じランキング論理にそろえ、fast と cascade で同じ記憶の点数が矛盾しないようにするためです。

混同しやすい点——「Cascade Recall の slow」と、次の節の末尾で触れる「Slow Thinker」は別物です。前者は Kahneman 由来で、質問を受けたその場で深く掘るかどうかを決めます。後者は VoiceAgentRAG 由来で、裏で次の質問を予測し、先に拾って cache に置きます。どちらもたまたま slow と呼びますが、実装ファイルは別です(kg_services.py vs slow_thinker.py)。

Pipeline の順序の脆さ

11 の stage には順序上の依存があります。RecencyBoost を TrustBoost の前に置くのは、dream loop で統合したあとの trust が新旧の記憶の時系列で上書きされることがあり、先に時間で補正する必要があるからです。

順序に依存する pipeline は本質的に脆く、2 つの stage を入れ替えるだけで top-k(上位 k 件)が総入れ替えになる可能性があります。現在は回帰テストで守っています。期待順位を持つ数十件の golden query(基準クエリ)を用意し、重みを変える前に一通り実行します。top-k の差が閾値を超えれば変更を止めます。現状は実用に耐えますが、より robust(堅牢)にするため調整を続けています。

六、Output Formatter と token 予算

Output Formatter はパイプラインの最終段階です。token 予算の制約から、3 つの処理が生まれました。cards と cascade_cards に 6:4 で配分し、長すぎる block は要約だけを載せて全文を展開するか agent に選ばせ、重複する段落は最高得点の 1 件だけ残します。

  • 主結果 cards と拡散結果 cascade_cards の budget(token 予算)を分割します(通常 60/40)。
  • 長すぎる block(記憶ブロック)にはまず要約を attach(添付)し、原文は expand(展開)フィールドに置いて、さらに読むか agent に決めさせます。
  • 重複する段落(cosine > 0.9)は最高得点の 1 件だけ残します。

すべての入口(Hook、API、MCP、CLI)は同じ pipeline(パイプライン)を通ります。違うのはここだけです。format(形式)はシリアライズの引数(text / json / cards)であり、上流の検索戦略とは無関係です。私は以前、この層を軽く見ていました。top-15(上位 15 件)をそのまま context(文脈)に入れると、長い block 1 件で token の半分を使い、後続の記憶が入らないことがよくありました。budget-aware packing(予算を考慮した梱包)を加えてから、平均利用率は 60% から 85% に上がりました。

Read-Time Sanitize:Write Injection Guard と対称にする

Read 側でも対称的に memvault.security.read_sanitizeを 1 回実行します。Write Track の実装前に injection_guard.is_unsafe() が書き込み時のルールだけを適用していた穴を埋めます。3 か月前に入庫した block は旧版のルールを使っており、攻撃例も進化しているからです。

  • すべての cards / cascade_cards を Output Formatter の前に走査します。 is_unsafe_for_injection()(Write 側と ruleset を共用し、デプロイのたびに同期します)。
  • ルールに一致した block は sanitize_for_injection()を通します。危険な token sequence を [REDACTED] というプレースホルダーに置き換え、意味的な文脈は残します。丸ごと捨てると top-k が薄まるためです。
  • Slow Thinker の prefetch cache にも適用します。cache に入れる前に 1 回(write-time)、agent に渡すため cache から取り出す前にもう 1 回(read-time)濾過します。二重の防御により、cache が read sanitize を迂回する裏口になるのを防ぎます。
  • 一致イベントは audit_logに記録します。どの古い block が新版ルールに触れたか追跡でき、Write 側の ruleset 更新へフィードバックできます。

非対称な安全検査には穴があります。書き込み側だけで守るのは、ルールも攻撃も変化しないと仮定するのと同じです。Write × Read の双方で実行してこそ、古い block にも新しいルールを適用できます。

七、Trade-off の一覧

Read Track で速さ、正確さ、完全さのすべてを得ることはできません。どれもコストと効果の交換で、選べるのは 2 つだけです。まだ完全には解決していないのは、およそ次の点です。

課題現状
遅延 vs 想起率slow path(遅い経路)は p95(95 パーセンタイル遅延)への影響が大きいです。現在は intent ゲートを使い、思考を広げる必要がある質問だけを通します。
Reranker の計算コストが高いAttention Gate が rerank リクエストの約 30% を省き、Circuit Breaker は失敗時に 600s 隔離します。
Cold / Frozen tier には summary しか残らない全文は圧縮後に呼び戻せません。計画中の tier upgrade では、頻繁に呼び出される cold block(冷たい記憶ブロック)を warm(温)へ引き上げられるようにします。
意図の誤判定pipeline 全体が違う道へ進みます。現在は動的な信頼度閾値で誤判定率を下げていますが、良い事後フォールバックはまだありません。
CRAG の自己評価で見逃すものがあるdeep モード(深いモード)でのみ CRAG を動かします。fast モード(高速モード)はこの層を省くため、fast 経路では幻覚を見逃す可能性があります。

八、全体を貫く設計上の決定

以上の 7 節にある仕組みの下には、繰り返し現れる 5 つの設計原則があります。intent を下流全体へ渡すこと(intent-driven everywhere)、trust_score と access_count による write-read の閉ループ、Kahneman の fast / slow を thinking_modeとして実装すること、write と read の両端での defense in depth、そして trade-off を紙に広げて代価を隠さないことです。

  • Intent-driven everywhere:QueryClassifyOp の intent は、下流の scoring weights、retrieval mode、score blending ratio、cascade layer routing へ渡されます。同じ query でも intent によってまったく違う経路を通ります。
  • Write-Read の閉ループ:trust_score 書き込み時に source_tracker が採点し、読み取り時に TrustBoostOp が利用します。access_count 読み取り時に書き戻し、次回の読み取りでは WeibullDecay の effective half-life に影響します。これは Write / Read Track をまたぐ閉ループであり、一方向の pipeline ではありません。
  • Kahneman fast / slow:thinking_mode = fast / slow Kahneman の二重システムを実装しました。slow は fast に Cascade Recall と CRAG の自己評価を追加します。二者択一ではなく、slow = fast + extras です。
  • Defense in depth:Write Track の 3 gate による遮断 + injection guard、Read Track の attention gate による不要な rerank の省略 + circuit breaker による再ランキングの失敗隔離。各層は次の層が失敗しても、経路全体が壊れないようにしています。
  • Trade-off on paper:遅延 vs 想起率、計算コスト vs 品質、冷たい保存領域 vs 全文の復元——trade-off はすべて §七 に明記し、代価がないふりをしません。

九、Slow Thinker:Predictive Prefetch

Slow Thinker は独立したバックグラウンド pipeline(memvault.slow_thinker)で、主 recall()をブロックしません。イベントソースは conversation.utterance.appendedで、着想は Salesforce AI Research の VoiceAgentRAG(二重エージェント:背景の Slow Thinker + 前面の Fast Talker + FAISS speculative cache)から得ました。「料理長が次の一皿を用意する」は、このコンポーネントの技術名に借りた比喩です。

5 つのコンポーネント

  1. NextQuestionPredictor:直近 N=8 件の utterance を使い、小型 LLM に top-3 の予測 query を生成させます。Prompt は query を具体的に検索可能なものに制限し、「それで?」のような空疎な query は認めません。
  2. PredictionPipeline:5 つの Op をつなぎます。ContextSampler → QuestionGenerator → QuestionRanker(会話が自然に続く順に並べる)→ QuestionDeduplicator(直近 5 分間に予測済みのものを重複排除)→ QuestionEmitter。各段階は個別に実装を差し替えられます。
  3. SpeculativeFetcher:予測した top-1 / top-2 の query で完全な recall()(11-stage scoring + reranker + cascade を含み、intent に応じます)を実行し、結果を Redis に書き込みます。 memvault:prefetch:{user_id}:{query_hash}。TTL は 300s。Fire-and-forget で、失敗しても再試行しません(次の utterance で新しい候補が作られます)。
  4. Admission Control:5 つのフィルタールールをすべて通った場合だけ prefetch を許可します。
    • conversation.length < 3 turns → skip(サンプルが少なすぎる)
    • topic_drift_score > 0.7 → skip(話題が変わったばかりで、古い context は参考にならない)
    • last_predict_age < 30s → skip(実行したばかりで、fetch の繰り返しを避ける)
    • predicted_query.confidence < 0.4 → skip(小型 LLM にも確信がなく、無理に動かしても token を浪費するだけ)
    • min_sample_threshold:利用者が conversation_count < 10 の場合は Slow Thinker 全体を bypass します(コールドスタート期間は起動しません)。
  5. 注入点: QueryClassifyOp 内です。実際の recall() が届いたら、まず memvault:prefetch:{user_id}:{query_hash}を確認します。hit なら先読み済みの結果を直接 Output Formatter へ渡し、中間の pipeline 全体を省きます。miss なら従来の流れを使い、余計な代価なしでフォールバックします。

Read-Time Sanitize との関係

Prefetch cache は Sanitize を二重に適用する領域です。cache に入れる前に sanitize_for_injection()(write-time)、cache から取り出して agent に渡す前に Output Formatter がもう一度 sanitize します(read-time)。Cache を Read 側の sanitize を迂回する裏口にはできません。設計時から固定した不変条件(invariant)です。

なぜこの設計なのか

VoiceAgentRAG の中心的な観察は、音声 agent では inter-utterance の間隔(人が話し始めてから話し終えるまで)に完全な RAG を 1 回動かせる、というものです。その「人が話している時間」を speculative な fetch に使えば、hit 時の latency は ~800ms から < 100ms(Redis のヒット + format)になります。memvault の会話画面も同じ時間枠を使えます。

miss rate の観測値は約 55–65%(top-3 の予測のうち 1 つがヒット)です。つまり prefetch の 35–45% は純粋な無駄です。これは「空き token を使って、ヒット時の遅延をゼロに近づける」という明確な選択なので、Admission Control の 5 つのルールは、このコンポーネントの生命線であり、nice-to-have ではありません。

Read Track の三部作はここで締めくくります。意図 → 想起 → ランキング → 再ランキング → グラフ → 梱包 → 両端での sanitize → 予測型の先読み。各層に fallback と trade-off があり、golden query に守られながら毎日動いています。

Read Track の分解はここまでです。QueryClassify → fast/slow split → 11-stage scoring → reranker → OutputFormatter → read_sanitize → SlowThinker prefetch loop——7 つのコンポーネント、1 本の entry-agnostic pipeline です。

「memvault が覚えるべきことを覚えたか」は担当しません。それは Write Track と Dream Loop の仕事です。Read Track の役目はただ 1 つ。query が与えられたとき、session をまたいで適切な記憶を surface させることです。

実際に時間をかけて調整しているのは新しいコンポーネントではなく、scoring の重み、prefetch のヒット率、cascade の境界判定といった長期的なパラメータです。三部作はこれで終わります。

ツール

あなたが打つ間に、下ごしらえは間に合いますか

背景での先読みは、あなたが次の発言を考える間に情報を用意しなければなりません。だから入力の速さに直接関係します。自分で分かる 4 つの数字を入力すると、この記事のルールに従って 1 日に何回の先読みが許可され、そのうち何回ほど無駄になるか計算できます。どの項目も計測は不要です。計算はすべてブラウザ内で行い、データは送信しません。

定数は本文から取っています。3 往復未満では起動しない、30s のクールダウン、TTL 5 分、ヒット時の待ち時間は「~800ms から <100ms」に短縮、です。計算される許可回数は上限であり、実際の回数ではありません——本文の 5 つの許可ルールのうち、残る 2 つ(topic drift と予測の信頼度)は、そのとき何を話すかによって決まります。ブラウザには分からないので、実際の回数はさらに少なくなります。ヒット率の範囲は著者自身のデータでの観測であり、一般則ではありません。

この節を持ち帰る

AI Agent のための健康診断プロンプト

あらゆる質問を同じベクトル検索に投げるのは、事実を調べるのも発想を探すのも同じ道を通るようなもの——どちらもうまくいきません。この記事は、質問の意図に応じて読み取り経路を分けています(段階的な分類、複数シグナルによるランキング、動的な重み、Attention Gate によるコスト削減など)。照合表として使ってください。

以下をあなたの AI に貼り、この記事の読み取り設計とあなたの RAG/記憶システムを一項目ずつ比べてもらってください。

私は〈思い出したその先へ——AI Agent は使うほど、どうして私を理解していくのか〉という記事を読んでいます。 (https://blog.joneshong.com/zh/blog/memvault-read-track) 著者は記憶システムの「読み取り」経路を、質問の意図ごとに分けています。 この記事の読み取り設計と私の既存の RAG/記憶システムを照らし合わせ、同じ経路を使い続けてどこで行き詰まるか指摘してください。一般的なチェックリストは不要です。 私のシステムの現状: - 保存層:[例:Qdrant, Weaviate, pgvector, LanceDB, その他] - クエリの仕組み:[例:単純な意味検索、ハイブリッド検索、Reranker による再ランキングの有無] - 会話をまたぐ記憶:[機能の有無と実装方法] 以下の点から、私の設計を分析してください。 1. **意図分類**:システムは異なる質問意図を区別できますか? 例えば、利用者は事実を調べたいのか、進捗を尋ねたいのか、自由な助言を求めているのか。コストと精度の釣り合いを取るため、キーワード → 意味 → LLM のような段階的分類を使っていますか? 2. **多次元ランキング**:検索結果の順位に、意味的な類似度以外のシグナルも組み込んでいますか? 例えば時間的な新しさ、出典の信頼度、アクセス頻度による人気度です。 3. **動的な重み**:質問意図に応じて、各ランキングシグナルの重みを動的に調整しますか? 4. **深い検索**:「思考を広げる」必要がある探索的な質問に対し、知識グラフを広げるような深い検索ができますか? 5. **コスト管理**:Reranker のような高コストの精密再ランキングに、資源を節約する「スキップ」の仕組み(Attention Gate など)はありますか? 6. **Token 予算**:最終結果を示す際、限られた Token 予算をどう管理しますか? 全文や要約など、異なる結果形式へ賢く配分できますか? 7. **記憶の再加熱**:アーカイブまたは圧縮された古い記憶は、頻繁に検索されれば「温まり」、より完全な情報を取り戻せますか? 8. **エラー処理**:意図分類を誤り、検索経路全体がそれた場合、回復やフォールバックの仕組みはありますか? 9. **個人向けルーティング**:過去 7/30/90 日の利用者の質問傾向(attention profile)を考慮し、意図分類に個人の好みの重みを重ねていますか? 10. **両端の安全検査の対称性**:書き込み時に遮断した注入文字列について、読み取り/出力時にも同じルールを適用しますか? 先読み cache がある場合も適用しますか? 11. **背景での先読み**:利用者が質問する前に会話の文脈から次の質問を予測し、裏で検索して結果を cache に置きますか? 無駄な実行を防ぐ admission control はありますか? まず私の現状を診断し、次に最優先で補強すべき 3 つの不足点と、その優先順位および理由を挙げてください。 データ量が少なく単純な意味検索で十分なら、こうした段階的なルーティングは不要だとはっきり言ってください。網羅性のためだけに無理に追加しないでください。
参考資料

さらに読む

この Read Track の設計に実際に影響を与えた資料です。

資料 重要な理由
HippoRAG (NeurIPS'24) PPR Boost と、ヒットしたエンティティからグラフをたどる戦略です。Cascade Recall の LOCAL mode は、基本的にこれを実装したものです。
LightRAG LOCAL / GLOBAL / HYBRID という名前と、intent → mode の対応ルールはここから直接借りました。
Microsoft GraphRAG L0 / L1 / L2 の 3 層と、L2 コミュニティ要約を使った zero-latency recall の着想元です。Read の GLOBAL モードは、事前生成した L2 view を利用します。
CRAG: Corrective Retrieval-Augmented Generation Slow path でのみ実行する自己評価層です。CRAG の evaluator を rerank 後の品質ゲートに使います。fast モードでは token 節約のため、この層を実行しません。
Attention-Residual for Intent-Dependent Scoring 11-stage scoring の intent-dependent な重みの理論的根拠です。intent ごとに各シグナルの重みベクトルを変え、すべての質問に同じ点数の付け方を使わないという考え方です。
Cormack et al. 2009: Reciprocal Rank Fusion Dense + Sparse の 2 つの ranker の順位を統合する元の方法です。Fast Search の RRF 融合はこの式をそのまま使い、k=60 この記事のデフォルト値にもしています。
TheTom/turboquant_plus Attention-gated Reranking の早期 return 戦略はここから得ました。私の 3 つの skip ルール(候補が少なすぎる、点数が突出している、密集したクラスタ)は、その判定論理を使って書き直しています。
Kahneman — Maps of Bounded Rationality (Nobel Lecture) Fast / Slow の二分を支える理論的な骨格です。thinking_mode もともとは利用者が手で選ぶ設計でしたが、その後 intent が自動で決めるように改めました。ただし System 1 / System 2 の役割分担は変わりません。Cascade Recall が slow mode でだけ起動するのも、この流れを受け継いでいます。
VoiceAgentRAG (Salesforce AI Research) Slow Thinker / 先読みの着想元です。二重エージェント構成(背景の Slow Thinker + 前面の Fast Talker + FAISS cache)は、私の slow_thinker.py + QueryJournal による予測 + Redis speculative cache に対応します。
win4r / memory-lancedb-pro (LanceDB Pro autoRecall) memvault 全体の設計に最初の着想を与えたものです。autoRecall の「能動的な注入」という思想(agent が考える前に関連する記憶を context に入れる)、5 層の検索 + 3 層の昇格 + Weibull 減衰は、memvault でもそれぞれ対応する実装へ発展しました。YouTube チャンネルの AI超元域 が openclaw を紹介した際に知りました。
memvault Write Track — 会話が終わったあと 三部作の第 1 回。読む前には、読むものが必要です。3 つの gate、二重の書き込み、出典の履歴をどう付けるかを扱います。
memvault Background Track — 保存したあと 三部作の第 2 回。Read が使う L1/L2 要約、PPR、Interest Profile は、すべて Background が生成します。
✦ プロンプトをコピー