コンテンツにスキップ

recall プレビュー階層の設計 (v2.5.0)

翻訳について: 正本は英語版です。日本語版が古い場合は英語版を参照してください。

ステータス: 2026-07-15 承認 (設計レビュー)、2.5.0 pre-release ラインで出荷済み。 対象範囲: recall / recall_with_context の MCP 応答、および新規ツール get_contents

問題

recall 系ツールはヒットした全行の本文を丸ごと返します。Claude Code 環境での 実測 (トークン棚卸し、2026-07-11): 記憶 1 件あたり ~815 トークン、エピソード 1 件 あたり ~1,040 トークン、limit=10 の 1 呼び出しあたり ≈9–10k トークン、 ×5.2 呼び出し/セッション ≈ 23k トークン/セッション — recall 側では単独で最大の コンテキストコストです。そのテキストの大半は読まれません: 呼び出し側がふつう必要と するのは、リスト全体に対する関連度の判断と、そのうち 1〜2 行の全文だけです。

設計原則 (v2.5.0 の limit 再配置と共通)

整形と上限は境界層の仕事であり、ライブラリは完全なデータを返す。 先行する 変更が、エージェントから見える limit の上限を MCP の JSON Schema へ移し、 ライブラリ側はスキャン窓へのクランプだけを行うようにしました。ライブラリを直接 呼ぶ側が、コンテキスト窓を守るために存在する上限に縛られないようにするためです。プレビュー階層は、 同じ移動をペイロードの形に対して行うものです:

  • do_recall / do_recall_with_context (ライブラリ) — 本文は全文のまま、変更なし。
  • MCP ツールのラッパー (server.py) — 呼び出し側がオプトアウトしない限り、本文を プレビュー階層まで切り詰める。

この配置により、ベンチハーネス (ライブラリの直接呼び出し) と、将来の in-process な 利用側はいずれも、この減量に対して構造的に免疫を持ちます: プレビューを見るのは MCP 経路の利用側だけです。

応答の契約

recall が返す各メッセージには、全文取得のための安定したハンドルが付きます:

{
  "ref": "mem:123",            // or "ep:45" — always present on DB-backed rows
  "content": "<pure prefix, at most CPERSONA_RECALL_PREVIEW_CHARS chars>",
  "content_truncated": true,    // only present when trimmed
  "content_len": 1893,          // full length, only present when trimmed
  "source": {...}, "timestamp": "...", "id": "<msg_id>", "confidence": {...}
}

契約の詳細。いずれも意味を担っています:

  • ref は新規で、常に存在します (記憶・エピソードとも)。2.5.0 より前は応答に msg_id しか出ておらず (エピソードには存在しません) — 追って全文を取りにいく ために行を指す手段がありませんでした。種別を接頭辞にしている (mem:/ep:) のは、 両テーブルが 1 つの AUTOINCREMENT id 空間を共有しているためです (bug-040/041 の 衝突クラス)。
  • プレビューは純粋な前方部分であり、省略記号のマーカーは付きません。 exclude_contents の重複排除契約 (_content_excluded) は、正規化した上での 双方向の前方一致です。したがって、プレビューを後続の呼び出しに渡し返しても、 保存されている全文と一致します。マーカーを埋め込むと、これが無言で壊れます。
  • マーカーは切り詰めた時だけ付きます。 短い行は content_truncatedcontent_len も持ちません (ペイロード減量の一貫性)。
  • recall_with_context がエコーバックする会話エントリは一律に切り詰められます (呼び出し側はすでにその全文を持っているため)。これらに ref は付きません。

全文へのアクセス (2 つの経路)

  1. full_content: true — 両方の recall 系ツールに追加された真偽値パラメータ (既定は false): 信頼できる利用側のためのオプトアウトで、bug-211 以降は 1 応答あたり 200,000 文字の予算で上限が付いています — それを超えた行はプレビュー 階層に戻り (関連度の高い行から全文で残します、bug-214)、応答には full_content_budget_chars が付きます。2.5.0 より前のサーバーは未知の パラメータを無視するので、利用側は自分のコネクタが更新されるより前にこれを 採用できます (前方互換な移行)。
  2. get_contents(agent_id, refs) — 新規ツール (27 → 28): 最大 20 件の ref を まとめて全文の行に解決します。読み取りは id をキーとし (出どころは recall)、 agent_id の所有述語を伴います。他のエージェントの ref は missing に入り、 漏洩になることはありません。不正な形式の ref も missing に入ります (fail-soft — 1 件の不正な ref がバッチ全体を中断させてはなりません)。20 件と いう上限があるのは、全文の行 1 件が ~800 トークンに相当するからです: これより 大きなバッチは、プレビューが塞いだコンテキスト爆発の穴を開け直すことになります。

設定

CPERSONA_RECALL_PREVIEW_CHARS — 既定は 500 (設計レビューで決定。長い行は トークンの ~60%+ を落とせる一方、プレビューは関連度の判断には十分なままです)。 0 は切り詰めを完全に無効化します。

利用側への影響と移行

利用側 影響 移行
ベンチハーネス (LMEB) なし — ライブラリの直接呼び出しであり、検索の指標は id ベース なし
ClotoCore カーネル (build_chat_messages、Discord ブリッジ) recall の本文をそのまま LLM プロンプトへ注入するため、プレビューでは品質が落ちる フェーズ 1: full_content: true を渡す (応答が bug-211 の 200,000 文字予算に収まっている限り挙動は同一 — 超えると行はプレビューに降格し、カーネルは残りを get_contents で取得する必要があります。コネクタの bump より先に入れても安全)。フェーズ 2 (任意): カーネル側のコンテキスト減量のためにプレビュー + get_contents を採用する
Claude Code セッション 最大の受益者 (~15k トークン/セッションの見込み) なし — 既定でプレビュー、必要に応じて get_contents

バージョニング

MCP プロトコルとしては追加のみ (新しいフィールド、新しい任意パラメータ、新しい ツール) ですが、既定の本文の形が変わります — これは意図的な破壊的挙動変更であり、 2.5.0 の「破壊的だが内部にとどまる」安定化の軸に分類されます。利用側は 2.5.0 の alpha ラインの間に、stable なコネクタが出るより前に移行します。DB 層では何も 置き換えません: スキーマ変更なし (SCHEMA_VERSION は 13 のまま)。