Cuba Memroys について
MemoryIndustryは、コーディングエージェント向けの長期記憶MCPサーバーです。PostgreSQLとpgvectorに検索可能なナレッジグラフを保存し、セッションをまたいでコードベースの情報を引き継ぎます。CLIも備え、共有データベースや同期機能で複数マシン間の記憶共有に対応します。
主な機能
- 01長期記憶グラフの保存と検索
- 02PostgreSQLとpgvectorの利用
- 0331個のMCPツールと28個のCLIコマンド
- 04ローカル実行と共有デーモン
- 05データベースまたは同期による記憶共有
活用シーン
- 01エージェントのセッション間でコードベース情報を保持
- 02複数のエディターウィンドウで記憶デーモンを共有
- 03複数マシン間で記憶データを共有
- 04CLIで設定とデータベースの状態を確認
MCP 対応
MCPサーバーとしてクライアントに記憶関連ツールを提供します。独立したstdioプロセスとして起動する方法と、`serve`でローカルHTTPサービスを起動して複数クライアントから接続する方法があります。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
このサーバーのパッケージやリモートエンドポイントはまだ取得できていません。プロジェクトの README の手順に従ってください。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
MemoryIndustry
旧名称は cuba-memorys です。同じデーモン、同じ cuba_* MCP ツールで、製品名が新しくなりました。
AI コーディングエージェントのための長期記憶です。 エージェントが検索し、推論し、修正内容を反映できるナレッジグラフを提供する MCP サーバーです。これにより、セッションをまたいでコードベースの内容を忘れなくなります。
Rust で記述され、PostgreSQL + pgvector を利用しています。MCP ツールは 31 個(CUBA_DOCS=1 の場合は 32 個)、CLI コマンドは 28 個です。以下の数値はすべて、v0.12 以降、実際に公称どおりの測定を行うようになったベンチマークで測定されています。(以前のベンチマークはそうではありませんでした。測定と、嘘をついていたベンチマークをご覧ください。)
インストール
pip install memory-industry # or: npm install -g memory-industry
claude mcp add memory-industry -- memory-industry
# Previous names still install the same binary:
# pip install cuba-memorys
# npm install -g cuba-memorys
セットアップはこれだけです。初回起動時に Docker 経由で PostgreSQL 18 + pgvector コンテナを用意し、スキーマを初期化します。Docker を起動しておく必要があります。 cuba-memorys コマンドもバイナリエイリアスとして引き続き利用できます。
Cursor / Windsurf / VS Code / Zed
{
"mcpServers": {
"memory-industry": {
"command": "memory-industry"
}
}
}
DATABASE_URL は不要です。または cuba-memorys setup(または memory-industry setup)を実行すると、見つかったすべてのクライアントの設定が書き込まれます。続いて cuba-memorys setup check を実行すると、設定間の不一致を監査できます。実際に問題を引き起こすのはこの不一致です(設定が 2 つあり、埋め込み次元も 2 種類で、検索が気付かれないまま壊れます)。
独自の PostgreSQL を使用する
{
"mcpServers": {
"memory-industry": {
"command": "memory-industry",
"env": { "DATABASE_URL": "postgresql://user:pass@localhost:5432/brain" }
}
}
}
vector と pg_trgm 拡張機能が必要です。不足しているものがあれば、cuba-memorys doctor で確認できます。
クライアントごとにプロセスを起動せず、デーモンを共有する
stdio では、クライアントごとに独自のプロセスが起動し、各プロセスがモデル一式を読み込みます。埋め込み、リランカー、NLI を合わせると数 GB に及びます。エディターウィンドウを 3 つ開くと 3 組のモデルが読み込まれ、16 GB のノート PC ではマシンのメモリを使い切ってしまいます。
serve はモデルを一度だけ読み込み、ループバック HTTP 経由ですべてのクライアントに応答します。これは、セッションハンドシェイクを行わず、各リクエストが自己記述的である方式に落ち着いた 2026-07-28 MCP 仕様の構成でもあります。
cuba-memorys serve # 127.0.0.1:8787 by default
cuba-memorys serve 127.0.0.1:9000 # or pick the address
memory-industry serve も同じコマンドです。すべてのクライアントをこのサーバーに接続し、それぞれに固有の Mcp-Client-Id を設定して、セッションを分離してください。設定しないと、あるウィンドウでの jornada start が次のウィンドウのアクティブセッションになってしまいます。
{
"mcpServers": {
"memory-industry": {
"type": "http",
"url": "http://127.0.0.1:8787/mcp",
"headers": { "Mcp-Client-Id": "editor-window-1" }
}
}
}
GET /health は、稼働時間、データベースへの接続状況、確認済みのクライアント数を返します。ステータスは ok、starting、degraded の 3 種類で、HTTP ステータスコードは常に 200 です。503 を返すと、語彙検索を引き続き提供し、問題の説明に使える唯一のエンドポイントも提供しているデーモンが、ローテーションから外されてしまいます。コードではなく status を監視してください。
GET /identity と POST /pair は、クライアントが接続先のデーモンが正しいものだと確認し、ペアリングするためのエンドポイントです。GET /identity は誰に対しても {service, host_id, name} を返します。また、credential id と nonce が指定された場合、その credential のハッシュを持つホストだけが計算できる証明を返します(証明を返せない場合は revoked または unknown を返します)。POST /pair はペアリングコードを引き換え、そのコードを知っているホストだけが計算できる MAC を返します。どちらも、転送ヘッダーまたは外部の Origin を含むリクエストは拒否します。/pair が受け付けるサイズは最大 4 KiB で、すべての失敗に同じ 401 {"error":"pairing failed"} を返します。また、失敗回数は /mcp と同じアドレスごとの制限(1 分あたり 10 回)に加算されます。
Authorization: Bearer ヘッダーに CUBA_HTTP_TOKEN がある場合、応答には runtime も含まれます。内容は mode、resource_tier、embedder、reranker、NLI の {state, device, reason}(device はセッションが実際に開かれた場所を示します。gpu、または GPU プロバイダーの起動が拒否された場合は、その原因を reason に含む cpu です。セッションがまだない、または一度も開かれていないモデルでは null です)、gpu{build, degraded, placement}(placement はプランで指定した内容のみを示し、プロバイダーが起動を拒否した場合も degraded は true になります)、および llm{configured, backend, model, base_url} です。これにより、ソースを読まずに reranker が実際にどこで動作しているかを確認できます。トークンがない場合、これらは一切提供されません。また、内部のホスト名とポートを含む可能性がある graph_db.last_error も提供されません。トークンの有無にかかわらず、キー、DATABASE_URL、モデルのパスが表示されることはありません。
ループバック以外にバインドする場合、トークンは必須です。デーモンはデフォルトで認証なしにグラフ全体を提供するためです。ルーティング可能なアドレスでは、トークンは 32 文字以上でなければなりません。
ポートを先に開き、その後モデルを読み込み、最後にデーモンが自身の起動を通知してサービスを開始します。先にバインドすることで、低コストで原因を診断できます。同じポートで 2 つ目のデーモンを起動すると、2 分間モデルの読み込みに費やしてから失敗するのではなく、すぐに失敗します。読み込み中に届いた接続はカーネルのバックログにキューイングされるため、クライアントは接続拒否ではなく、待機して実際の応答を受け取れます。読み込みに MEMORY_INDUSTRY_WARM_BEFORE_SERVE_SECS(180 s)を超えた場合、デーモンはそのままサービスを開始し、/health は starting を返します。また、reranking を要求する検索はすべて、degraded として返されます。stdio では、stdin を閉じずに 30 s 後に諦めたクライアントが、読み込んだすべてのモデルを保持したまま、放棄された数 GB のプロセスを残してしまうことがありました。現在の stdio は、MEMORY_INDUSTRY_HANDSHAKE_TIMEOUT_SECS(60 s、0 または off で無効化)以内にハンドシェイクが届かない場合、終了します。
このキューが役立つのは、読み込みが終わるまで待てるクライアントだけです。自身のタイムアウトがより短いクライアント(sync peer は CUBA_HANDLER_TIMEOUT_SECS(30 s)後に諦めます)は、引き続き呼び出しを中断します。その際、接続拒否ではなくタイムアウトを受け取ることになります。これはより適切な診断ですが、キューによって得られるはずだった実際の応答ではありません。デーモンがどちらの状態にあるかを示すのが /health であり、これは異なる 2 つの問いに答えます。応答があるということは、ポートでサービスが提供されているということであり、モデルに触れない処理(sync fetch は行を移動します)を呼び出す場合に、呼び出し元が必要とする情報はこれだけです。ready: true が返るということは、モデルの読み込みも完了しているということであり、reranking を要求する検索の前や、最初の検索に読み込み時間を負担させたくない場合に待つべき状態です。最初の待機時間には上限がありますが、2 つ目にはありません。モデルの状態にかかわらず、デーモンは MEMORY_INDUSTRY_WARM_BEFORE_SERVE_SECS 以内に起動します。しかし、ウォームアップがこの上限を超えると、完了までの間、すべてのリクエストを ready: false のまま処理し続けます。そのため、デーモンが正常に応答していても、ready: true が返らないことがあります。独自の上限を設けずに待ち続けると、正常なマシンでハングします。
セマンティック埋め込みとモデル(推奨)
モデルがない場合、埋め込みはハッシュベースです。決定論的ですが、意味的には無意味です。検索は語彙および BM25 の分岐を通じて引き続き機能しますが、*意味*を理解するものはありません。どの OS でも、1 つのコマンドでモデルと ONNX ランタイムをインストールできます。シェルスクリプトも、手動での ORT_DYLIB_PATH 設定も不要です。
cuba-memorys models all # embeddings + NLI + reranker + runtime
cuba-memorys models embed # just the embeddings model (~113 MB)
cuba-memorys models all --gpu # GPU runtime, if you have one
cuba-memorys doctor # confirms what loaded
すべて ~/.cache/memory-industry/(名称変更前にインストールした場合は ~/.cache/cuba-memorys/)に保存され、自動的に検出されます。models は実行したときにのみダウンロードします。知らないうちに何かが取得されることはありません。ダウンロード先はまず <dir>.downloading となり、すべてのファイルのダウンロードと検証が完了してから <dir> になります。そのため、失敗してもローダーが選択する空のディレクトリが残ることはありません。
古いキャッシュを新しい名前に移動する。 このリリースでも ~/.cache/cuba-memorys/ ルートは引き続き読み取られますが、各ファイルは 2 つのルートのどちらを使うかを個別に決定します。新しい名前で作成されたエントリが 1 つでもあれば(起動時の pgpass_app、delete による undo/ など)、models は古いルートにすでに存在するものを再ダウンロードします。古いルートに何かが残っている間は、doctor が警告します。1 つのコマンドで移動できます。
memory-industry cache migrate # the plan: touches nothing
memory-industry cache migrate --apply # moves it
古いルートにしかないエントリは移動されます。両方に同一のものがある場合は、古いコピーが削除されます。内容が異なる場合は、バイナリが現在読み取っているコピーが残されます。pgpass_app なら使用中の pgpass と同じ場所にあるもの、モデルディレクトリなら実際にモデルが含まれているものです。もう一方は新しいルートの <name>.legacy-YYYYMMDD として退避され、削除されることはありません。別のプロセスが使用中のファイル(Windows の場合、実行中のデーモンが読み込んだランタイム)はスキップされ、コマンドはゼロ以外のコードで終了します。デーモンを停止して、もう一度実行してください。すでに移動されたものはそのまま残り、2 回目の実行では移行対象がないことが示されます。
bge-m3(1024-d)はスペイン語では e5-small より優れていますが、性能差の大きさはもはや主張されていません(以前の +21 nDCG という数値は、壊れたベンチマークによるものでした)。次元移行(scripts/migrate-embedding-dim.sh 1024)と CUBA_EMBED_MODEL=bge-m3 CUBA_POOLING=cls が必要です。
モード:ローカル · ネットワーク · 完全
CUBA_MODE は、データベース、モデル、外向きネットワークをまとめて設定するプリセットです。多数の環境変数を個別に設定する代わりに、名前を 1 つ選ぶだけです。
CUBA_MODE | データベース | 機能 | 外向きネットワーク |
|---|---|---|---|
local(デフォルト) | このマシン上の Docker | インストール済みの埋め込み + NLI | なし |
red | 共有のマネージド Postgres(sslmode=require を指定して DATABASE_URL を設定) | + ノードごとの来歴、マシン間のリアルタイム同期 | なし |
completo | DATABASE_URL の指定内容に応じたもの | + リランカー(GPU があれば GPU を使用)+ cuba_docs | cuba_docs |
2 台のマシンで、1 つのメモリを共有します。 両方を同じマネージド Postgres に接続し(Neon と Supabase の無料プランはどちらも pgvector を備え、36 MB のコーパスを何倍も保存できます)、CUBA_NODE_NAME でそれぞれに名前を付けて、CUBA_MODE=red に設定します。一方が書き込んだ内容をもう一方が読み取れ、各メモリにはどのマシンに由来するかが記録されます(origin_node)。共有データベースがない場合は、cuba_sync が Git リポジトリを通じて同じ役割を果たします。マシン間の同期を参照してください。自分の Postgres ポートをインターネットに公開しないでください。マネージドプロバイダーの TLS、または Tailscale のようなプライベートネットワークを使用してください。
共有時にも実際に分離できます。 共有データベースでは、行レベルセキュリティが飾りではなくなります。cuba-memorys secure を(管理者ロールで)1 回実行して、RLS と追記専用監査が実際に適用される非スーパーユーザーの cuba_app を作成し、CUBA_SKIP_MIGRATIONS=1 を指定してランタイムを接続します。cuba-memorys doctor では、ランタイムロールが(すべてを回避できる)スーパーユーザーかどうかが報告されます。
GPU ランタイムの取得は、まだ半分にすぎません。バイナリ自体を --features cuda を指定してビルドする必要があります。そうしないと gpu::configure() がプロバイダーを登録せず、再ランキングは CPU で実行されます。これは仮定の話ではありません。6コアのノート PC で cargo run --release --example rerank_bench を実行して測定した、候補50件の再ランキングにかかる時間です。
| ビルド | 候補50件、長さは混在 | 20 s の予算内か? |
|---|---|---|
CPU、with_intra_threads(2) | 106,9 s | いいえ — スコアは計算されますが、その後破棄されます |
| CPU、物理コア数 | 61,0 s | いいえ |
--features cuda | 4,1 s | はい |
どちらの場合もランキングは同じです。CPU と GPU は候補ごとに一致し、スコアの小数点以下5桁目だけが異なります。任意のマシンで rerank_bench を実行すると、再ランキングが予算内に収まるか、処理がひそかに破棄されているかを確認できます。また、cuba-memorys doctor では、このビルドに GPU プロバイダーがあるかどうかが表示されます。
このセクションには以前、「すべてのモデルがひそかに CPU で実行される」と記載されており、--features cuda を指定してビルドすれば3つすべてが GPU で実行されるかのような印象を与えていました。しかし、GPU で実行されるのは再ランキングだけでした。 埋め込みモデルは INT8 に動的量子化されており、96個の DynamicQuantizeLinear と144個の MatMulInteger を使います。CUDA プロバイダーはどちらにもカーネルを登録しないため、ビルド方法にかかわらず ONNX Runtime はこれらを CPU に割り当てます。このセッションに CUDA を登録しても効果はなく、モデルが一度も計算に使わない VRAM アリーナの分だけコストがかかっていました。全544 MBの重みがホスト RAM に置かれている間、374 MiB が確保されたままでした。 NLI クロスエンコーダーは逆の問題を抱えています。FP32 のまま固定されています。mDeBERTa は FP16 をサポートしないと上流で説明されており、INT8 ビルドでは確信度の高い誤った含意判定が返されるためです。
そこでモデルごとに配置を決定するようになり、GPU を要求するのは再ランキングのみになりました。測定に使った 6 GB の GPU カードでは、デーモンの VRAM 使用量は検索中に 5228 MiB から2950 MiBへ、アイドル時には0へと減りました。以下のメモリ使用量に記載されているオプトインの2つの手順を実行すると、1460 MiB まで減少します。
個別の環境変数(CUBA_DOCS、CUBA_RERANKER_PATH、…)は、常にプリセットより優先されます。
実際の動作
ほとんどのメモリサーバーは、埋め込み機能を後付けしたキー・バリューストアです。このサーバーは、心理学の文献で別々のものとして扱われ、減衰の仕方も異なるとされているため、4種類のメモリをモデル化します。
| 保持する内容 | 強化の仕組み | |
|---|---|---|
| 意味記憶 | エンティティに関する事実 — 「すべてのエンドポイントは非同期です」 | アクセス(Hebbian/BCM、Oja 1982) |
| エピソード記憶 | 登場人物と時刻を伴う出来事 — 「火曜日に v2 をリリースしました」 | べき乗則による減衰(Tulving 1972、Wixted 2004) |
| 手続き記憶 | ここで物事を 行う方法 — 実績を伴うレシピ | アクセスではなく成功(ACT-R) |
| 作業記憶 | 現在のセッションにひも付いたメモ | セッション終了時に消去 |
手続き記憶を、9番目の観察タイプではなく独立したテーブルにしているのには、特別な理由があります。ACT-R では、宣言的記憶(アクセスによって強化)と手続き記憶(成功によって強化)を分けています。観察として扱うと、失敗し続けるために頻繁に参照されるレシピほど重要度が上がってしまいます。評価にはウィルソン下限を用いるため、1/1 の成功は 0.21、47/50 は 0.84 となり、初回の幸運な成功が実績を上回ることはありません。
検索
3つのシグナル(全文検索、BM25(ts_rank_cd)、pgvector HNSW)を対象に、ハイブリッド RRF 融合(k=60、Cormack 2009)を行います。さらに、クエリのシャノンエントロピーが高まるにつれて、キーワード重視からセマンティック重視へと切り替わる、エントロピーに基づく重み付けを適用します。
回答はデフォルトで**compact形式**で返されます。キーを省略形にし、コンテンツは1200文字で切り詰めます。トークン数を30%削減し、nDCG はわずかに向上します — ID がスコア付けされた221件の質問で測定したところ、対応のある95%区間は[+0.0024, +0.0166]、改善幅は+0.0090でした。形式によって、順位付けされるドキュメントが変わることはありません。変わるのは、スコア付けされる前に、レスポンスのトークン予算内にいくつのドキュメントが収まるかです。デフォルトの5000トークン予算では、詳細形式は5286トークンを必要とするため末尾が切り捨てられますが、コンパクト形式は3723トークンで収まり、最後まで保持できます。ブランチごとのスコア内訳をすべて確認するには、"format": "verbose"を渡してください。
実際に検証する検証機能
cuba_faro mode=verify は、主張を保存されている情報と照合します。以前は、取得した証拠との コサイン類似度 で主張をスコア付けしていましたが、これはうまく機能しません。類似度が測るのはテキストの 話題 であって、何を主張しているか ではないからです。「cuba-memorys は Rust で書かれている」と「…Java で書かれている」は、ベクトル空間ではほぼ同じです。実際のコーパスで測定したところ、誤った主張のスコアは 0.61、正しい主張は 0.59 でした。
含意は類似度とは別の問いであり、読み取る ものが必要です。ローカルのクロスエンコーダーが各証拠を supports / contradicts / unrelated と判定するようになり、信頼度は各判定から導出されます。各判定には、その証拠の類似度に応じた重みが付けられます。同じコーパスでの結果は次のとおりです。
| 主張 | 以前(コサイン) | 現在 |
|---|---|---|
| 「Rust で書かれている」(正しい) | 0.59 | 0.995 · 検証済み |
| 「Java で書かれている」(誤り) | 0.61 | 0.00 · 矛盾 |
| 「最高のパエリアにはサフランを使う」(無関係) | 0.45、「証拠」項目が 10 件 | 0.00 · 不明、証拠なし |
話題に沿っていることは、裏付けにはなりません。また、unrelated はどちらの側にも加算されません。
判定器は mDeBERTa-v3-base-xnli で、ONNX 上でローカル実行されます。100 言語に対応し、判定あたり約 50 ms で、API キーもネットワーク接続もコストも不要です。ここではこれが重要です。このコーパスの約 75% はスペイン語であり、最初に選ばれがちな英語専用の NLI モデルでは、4 件中 3 件のメモリを気付かれないまま正しく処理できません。cuba-memorys models nli でインストールしてください。読み込まれたかどうかは cuba-memorys doctor で確認できます。
このモデルがない場合、検証は LLM にフォールバックします(サンプリングを使う MCP クライアント独自のモデル、ローカルの claude CLI、または Anthropic API)。そのいずれも使えない場合は、判定を捏造するのではなく、正直に unknown を返します。
この機能が行わないことが 2 つあります。証拠が弱い場合に主張を確定することはありません。含意には 0.80 を超える必要がありますが、矛盾には 0.60 で十分です。誤った記憶を確定することと、正しい記憶を疑うことは、同じコストの誤りではないためです。また、判定できない場合には、最も大きな数値を返すのではなく、その旨を伝えます。3 クラスのヘッドで argmax を取ると、まったくの誤りである主張に対しても supports を平然と返してしまいます。実際にそうなっていました。
キャリブレーション済みの棄却
分布外ゲートは、コーパスでは回答できないクエリを拒否します。しきい値は根拠のない定数ではありません。Ledoit-Wolf 共分散縮小とコンフォーマル分位点を組み合わせ、cuba-memorys calibrate --dataset <questions.jsonl> --apply で独自のコーパスを使ってキャリブレーションし、結果を保存します(データセットは必須です。指定しないとコマンドは拒否されます)。(理論上の χ² しきい値では、回答可能なクエリが100% 拒否されました。 ここでは分布フリーのキャリブレーションは、あれば望ましいという程度のものではありません。)
git を介したマシン間の同期
CUBA_MODE=red を使うと、2 台のマシンで 1 つのデータベースを共有できます。互いに接続できないマシン向けには、もう 1 つの方法として cuba_sync があります。グラフをコミット可能な JSON として書き出し、もう一方のマシンで読み込みます。
cuba-memorys sync export # write the bundle under .memory-industry/ (or $CUBA_SYNC_DIR)
cuba-memorys sync import # read one back in
cuba-memorys sync diff # entities on disk vs entities in the database
cuba-memorys sync status # which bundles this machine has already imported
cuba-memorys hook install # export after every commit, import after every checkout, merge and rebase
同じ 4 つの操作は cuba_sync action=export|import|diff|status です。バンドルは、各エンティティの観測情報を含む JSON ファイル 1 つずつに加え、episodes/YYYY-MM/、errors/、decisions/、relations.json、projects.json、tombstones.json、manifest.json で構成されます。対象はアクティブなプロジェクトと、プロジェクトに紐付いていないすべての情報です。ただし、--scope all を指定した場合はすべてが対象になります。埋め込みベクトルは指定しない限り含まれません(--with-embeddings)。埋め込みベクトルはバイト数の大部分を占め、再計算できるためです。バンドルは一度だけインポートされます。また、マニフェストのハッシュはバンドル内のすべてのファイルの内容を対象とするため、変更されていないバンドルはスキップされます。一方、手編集されたエンティティファイルは、何もせずに処理を終えるのではなく、新しいバンドルとして扱われます。エクスポートでは、内容が変更されたファイルのみを書き出します。前回のエクスポート以降に誰も書き込んでいないデータベースをエクスポートしても、manifest.json を含め、何も更新されません。そのため、exported_at はバンドルが最後に変更された時刻になります。
hook install はマージ後とリベース後にもインポートします。 4つのフックを書き込みます。post-commit はエクスポート、post-checkout はインポート、post-merge はインポート、post-rewrite はリベース後にインポートします。git は git merge では最初の2つを実行せず、git rebase(独自のコミットがあるブランチでの git pull --rebase が実行するもの)では post-merge を実行しません。そのため、最後の2つがないと、マージまたはリベースされたバンドルがデータベースに反映されず、次のコミットでデータベースがその内容を上書きして、相手側の行が失われていました。githooks(5) によると、post-rewrite は「コミットを書き換えるコマンド(--amend を指定して呼び出された git-commit と git-rebase)によって呼び出され」、最初の引数は amend または rebase です。amend の後は何もしません。修正されたコミットはこのマシン自身のエクスポートだからです。git がマージ中に停止している間は post-merge は実行されません。それを完了するコミットには親が2つあり、そのコミットでは post-commit がエクスポートの前に解決結果をインポートします。これより前にインストールしましたか? もう一度 hook install を実行してください。
hook install は解決したディレクトリを記録します。 各フックは、install が解決したディレクトリを CUBA_SYNC_DIR に設定します。相対パスは、実行したディレクトリではなくリポジトリのルートから解決されます。IDE、GUI クライアント、または cron ジョブから実行されたコミットにはそれぞれ独自の環境があり、固定値がないと、そのコミットはマージドライバーが参照していない場所にエクスポートしてしまうためです。変数を変更したら、もう一度 hook install を実行してください。最初のブロックを残すのではなく、自身のブロックを置き換えます。.gitattributes の行は、git が照合する唯一の方法で書き込まれます。つまり、作業ツリーのトップからの相対パスで、/ を使い、. や .. を含めません(名前に空白がある場合は引用符で囲みます)。また、以前のインストールが残した行は置き換えます。以前は ./dir、絶対パス、Windows の data\sync がそのまま書き込まれていたため照合されず、added と報告されていました。CUBA_SYNC_DIR がリポジトリの外にある場合でも、フックはインストールされ、共有フォルダーへのエクスポートとインポートを行います。ただし、git は作業ツリーの外にあるファイルをマージしないため、マージドライバーも行もありません。install はその旨を表示します。.gitattributes: skipped — <path> is outside this repo; git never merges it
マージドライバーは認識できるものをマージし、それ以外はユーザーに委ねます。 エンティティ、リレーション、プロジェクト、エピソード、エラー、決定は、同期ディレクトリ内での配置(entities/<file>、relations.json、episodes/<YYYY-MM>/<file>、…)によって認識され、ディレクトリ名にかかわらず ID でマージされます。tombstones.json は (table_name, row_id) をキーにした1つのセットとしてマージされます。両ブランチの削除はそれぞれ1回ずつ反映され、同じ行を両方が削除している場合は、後の削除が優先されます。facts.json、procedures.json、artifacts.json、source_trust.json は、インポート時の競合判定に使われるキー(fact_id、id、(path, project_id)、source)でマージされます。両方に行がある場合は、後のものが保持されます。具体的には、オープンなファクトよりクローズ済みのファクト、プロシージャのより新しい updated_at(成功回数と失敗回数は2つのうち大きい方)、アーティファクトのより新しい CRDT クロック、ソースの信頼度の各カウントは最大値が保持されます。manifest.json は、両方の側で project_id、with_embeddings、embedding_dim、embedding_model、schema_version が一致する場合、こちら側の内容でマージされます。これらはインポートがファイルと照合せずに処理する項目です。残りは派生情報で、次の sync export で書き換えられます。いずれかの値が異なる場合は競合となり、メッセージにはフィールド名が示されます。embeddings.bin.zst、その他のファイル、両側のいずれかが全体を解析できないファイル、またはこのビルドが認識しないフィールドを含むファイルがあると、終了コードが0以外になり、git は競合状態のままにします。以前は何も書き込まずに終了コード0を返していたため、git は「マージ済み」と判断し、マーカーもないままこちら側を保持して相手側を破棄していました。
削除はすぐに伝播し、巻き添えが発生する場合はそこで止まります。 行を削除するとトゥームストーンが記録され、受信側では指定された ID だけが正確に削除されます。以前は、削除の到着が遅かったのではなく、取り消されていました。ピアにその行が残っていたため、エクスポートされ、次のラウンドトリップで戻ってきたのです。エンティティのトゥームストーンは、エンティティを削除すると、それに紐づくすべてのものがカスケード削除されるため、危険です。このマシンに、送信側が指定していないそのエンティティ配下の観測やエピソードがない場合に限って適用されます。それ以外の場合は保留され、tombstones_withheld で報告されます。向こう側に子が 3 つあるエンティティのトゥームストーンによって、こちら側で 300 個が削除されてはなりません。
そして、バンドルが気づかれないままデータを消し去ることはありません。 バンドル内のトゥームストーンによって、25 行以上、かつこのマシンの観測の 10% 超が削除される場合、インポートは拒否され、confirm=true を求めます。リモートからの全消去と、大規模な正当な整理は見分けがつきません。違いは、それを意図したかどうかだけです。下限値は比率と同じくらい重要です。観測が 1 つだけのデータベースでは、割合だけを条件にすると、その 1 件を削除するのにも確認が必要になります。通常の整理でも発動するガードは、誰もが confirm=true を通すようになり、結局何も守らなくなります。
conflict=merge はコンテンツをマージせず、そのことを明示するようになりました。 merge と skip は同じポリシーです。こちらにない行は取り込まれ、行がすでに存在していて内容が異なる場合は、先にこちらにあったものが優先され、受信したテキストは破棄されます。変わったのは、これまでのように何も報告されないことです。インポートではそのような行がカウントされ、ID と処理内容の注記とともに diverged として報告されます。conflict=overwrite では受信したバージョンが採用され、置き換えたバージョンは previous_versions に保持されます(新しいものから 20 件を保持)。また、内容が変わった場合は埋め込みをクリアするため、その行は、もはや含まれていない意味によって検索可能な状態ではなくなります。
どちらのポリシーでも、カウンターはマージされます。 エンティティの importance と access_count、そしてリレーションの strength は、どちらか一方からもう一方へコピーされる値ではありません。それぞれのマシンが、独自の強化と独自のたどり方を通じて値を増やします。2 つの値のうち大きい方が採用されるため、冪等です。同じバンドルを 2 回インポートしても値は増えません。(合計すれば「両方のマシンで数えた」という状態をより忠実に表せますが、再インポートで値が二重になるため、数値を壊さないルールの方を優先します。)
どちらのマシンなのか。 各インストールでは、初回のマイグレーション時にそれぞれのデータベース内で UUID が生成されます。1 行に保存され、再起動後も変わらず、仕組み上重複しません。マニフェストにも含まれるため、バンドルから生成元のマシンを特定できます。CUBA_NODE_NAME は従来どおり、人が読めるラベルを意味し、origin_node に保存されます。これは識別子ではなく、識別子にもなり得ません。2 台のマシンが両方とも pop-os と名付けられるのが、最もありがちな結果だからです。
クロックはピアに必要な変更で進み、ローカルのノイズでは進みません。 観測の version が進むのは、内容、タイプ、信頼度、証拠レベル、またはタグが実際に変わったときだけで、それ以外では進みません。減衰処理では importance と last_accessed が変わり、reembed ではベクトルが置き換えられます。どちらかでクロックが進むと、変更されていないグラフがエクスポートのたびに送られ、2 台のマシンは何もないことについて話し続けることになります。同じ内容で行を書き換えてもクロックは進まないため、冪等な再インポートによって、合意している状態から新たな競合が生まれることもありません。
古いバンドルも引き続きインポートできます。 フォーマットは SCHEMA_VERSION 2 です。競合ルールでクロックを比較するにはファイル内にクロックが必要なため、version、updated_at、origin_node、previous_versions、evidence、verification、trust が含まれるようになりました。それ以前に書き出されたバンドルも引き続きインポートできます。新しいフィールドはすべてデフォルト値が設定され、v1 の観測は asserted として取り込まれます。これは、それ以上に強い主張をしていなかったファイルに対する誠実な解釈です。
受信したバンドル内で認証情報のように見えるものは、信頼済みとしてではなく quarantined として保存されます。cuba_eco で昇格させるまで、cuba_faro と cuba_expediente からは除外されます。インポートでは、プッシュ権限を持つ誰もが書き込めるリポジトリから JSON を読み込むためです。
読み取り専用のピア。 CUBA_PEER_TOKEN で利用できるのは、さらに5つの動詞だけです。pull はバンドルをどこにも書き込まず、応答として返します。ファイル単位でページ分割され(limit、offset)、各ページの上限は3 MBです。ページ間で manifest_hash が変わった場合は中断してください。これは、このノードが転送中に書き換えられ、ページが異なる状態を示していることを意味します。ピアトークンで実行できる唯一の書き込み操作が notify です。別のマシンが何かを学習したことを示す、2000文字以内の短い summary に node_id/node_name のタグを付けて記録します。これは次回の cuba_jornada start と status に表示され、対応する manifest_hash を持つバンドルがインポートされると自動的に閉じられます。通知がグラフに加わることはありません。もう一方の機能である conflicts は、2台のマシンで内容が一致しない行を両方のテキストとともに一覧表示します。resolve id=… keep=ours|theirs|both で1件を解決できます。keep=both(デフォルト)では、このマシンのテキストを現行のものとして残し、もう一方を previous_versions に記録するため、何も破棄されません。一方、theirs では、ここにはもう存在しないテキストを説明していた埋め込みもクリアされます。fetch はそのもう一方の機能で、ローカルマシンで実行します。HTTP 経由でピアの pull をページ単位で取得し、ファイルを配置して、あらゆるバンドルに対するものと同じ検証を行ってインポートし、ピアのマニフェストハッシュを記録します。そのため、変更がなければ次回の fetch はトランザクションを開始する前に終了します。エクスポートではデフォルトで埋め込みを省略し、pull ではデフォルトで埋め込みを含めます。ベクトルを含まないテキストを受け取ったピアは、再埋め込みが完了するまで受信した内容を検索できません。GPU のないマシンでは再埋め込みに時間がかかり、逐次処理になります。また、モデルや次元がこのマシンと一致しないバンドルは、異なるベクトル空間のベクトルでインデックスがひそかに埋め尽くされることがないよう、拒否されます。
壊れたときにも知らせてくれます
$ cuba-memorys doctor
[ ok ] migrations 49 aplicadas, ninguna dirty
[ ok ] embedding_dim runtime 1024-d == columna vector(1024)
[ ok ] runtime_role 'cuba_app' sin superuser — RLS y audit efectivos
[ warn ] binary_freshness 4 proceso(s) MCP corren un binario más viejo que el de disco
これは、ハイブリッド検索エンジンの障害がクラッシュではないために存在します。ベクトル検索の分岐が停止し、何の症状もないまま検索がひそかに語彙検索へ切り替わるのが、実際の障害モードです。サーバーは埋め込みの次元が一致しない場合、起動を拒否するようになりました。また、検索では分岐に障害が発生すると、応答に degraded: true が設定されます。
CLI:LLM を介さずに使える自分の記憶
コマンドは23個です。memory-industry --help ですべて確認できます。
serve | エディターウィンドウごとに1つのプロセス(とモデルのコピー)を起動する代わりに、すべてのクライアントで共有する1つの HTTP デーモン |
search <query> · save · delete · export | シェルから脳の内容を読み書きします |
dashboard | 保存内容を表示する、自己完結型の HTML ビュー |
doctor | ヘルスチェック:スキーマ、次元、設定の整合性、古いプロセス |
recall | セッション開始時のコンテキスト注入 — setup hook で接続します |
reembed | 必要なものを再エンコードします(デフォルトではすべてではなく、古い行のみ) |
calibrate | コーパスから棄却しきい値を再計算します |
link | NPMI 共起によってエンティティを自動リンクします |
dedupe | 異なる名前で登録された、同一のエンティティ — 詳細は後述 |
sync · hook | グラフをコミット可能な JSON として書き出し、別のマシンで読み戻します — マシン間の同期を参照してください。hook install はこれを git に接続します |
skills <dir> | 手順を Claude Code Skills としてエクスポートします |
eval | 検索ベンチマーク — 信頼区間付き nDCG@10、MRR、再現率、トークンコスト |
setup | MCP クライアントに接続します。setup check でクライアントを監査できます |
dedupe — 文字列が異なれば、別のエンティティです
cuba_alma create は ON CONFLICT (name) を使って挿入します。そのため、1つのプロジェクトが Mapupita-Web、Mapupitta-Web(誤字)、Mapupita Web、mapupita…に分散し、いずれか1つを検索しても、ほかの名前は見つかりません。実際の266エンティティのグラフでは、158個(59%)にリレーションが1つもありませんでした。PageRank や複数ホップ検索では、存在しないも同然でした。
マージを決める基準は、埋め込みの重心ではありません。一見よさそうな案ですが、これは誤りです。M-Codes Reference Guide と G-Codes Reference Guide の重心間のコサイン類似度は 0.811 です。1つのドメインを扱うコーパスでは、重心の類似度が示すのは ドメイン であって、エンティティ ではありません。しきい値を0.80にすると、異なる2つのCNCガイドが取り返しのつかない形でマージされていたでしょう。
そのため、--apply がマージするのは確実に同一と証明できるもののみです(大文字小文字と区切り文字を正規化した後に同一になるもの)。誤字や類似した名前は表示され、--judge で1つずつ判定します。古い名前は brain_entity_aliases に記録されるため、情報は失われません。古い名前で検索しても、引き続き解決できます。
31個のツール
キューバ文化にちなんだ名前です。cuba-memorys はすべてのツールを公開します。または、CUBA_TOOL_PROFILE=lean を設定すると、日常的に使う13個のコアツールに cuba_tools と cuba_call を加えたツールを公開します。つまり、31個中15個を公開し、カタログを49%縮小しながら、機能を一切失うことなく、残りのツールも必要に応じて利用できます。
ナレッジグラフ — cuba_alma(エンティティ) · cuba_cronica(観察事項、エピソード、タイムライン) · cuba_puente(型付きリレーション、探索、リンク予測) · cuba_ingesta(一括インポート)
検索 — cuba_faro(ハイブリッドRRF、検証、MMRによる多様化、OOD時の棄権)
エラー記憶 — cuba_alarma(報告) · cuba_remedio(解決) · cuba_expediente(過去のエラーを検索し、以前に失敗したアプローチの場合は警告)
セッションと意思決定 — cuba_jornada(セッションのライフサイクル、差分) · cuba_decreto(アーキテクチャ上の決定) · cuba_proyecto(プロジェクトごとの分離) · cuba_pre_compact(/compact 後も維持)
手順 — cuba_receta(Wilson下限で順位付けされたレシピ)
認知 — cuba_reflexion(ギャップ検出) · cuba_hipotesis(アブダクション推論) · cuba_contradiccion(意味上の競合) · cuba_juez(LLM判定) · cuba_centinela(先行トリガー) · cuba_calibrar(ベイズ校正、情報源の信頼性)
メンテナンス — cuba_zafra(減衰、枝刈り、マージ、PageRank、Leidenコミュニティ) · cuba_eco(RLHFフィードバック) · cuba_vigia(健全性、ドリフト、中心性) · cuba_forget(GDPRによる消去) · cuba_archivo(CFR-21ハッシュチェーン監査ログ) · cuba_pizarra(作業メモリー) · cuba_sync(マシン間のGit対応エクスポート/インポート。削除の伝播とリモート消去ガードに対応)
メタ — cuba_tools(検出) · cuba_call(呼び出し)
設定
| 変数 | デフォルト | 機能 |
|---|---|---|
CUBA_MODE | local | local / red(共有クラウド DB) / completo(すべて+GPU)。残りの設定のプリセットです。 |
CUBA_NODE_NAME | $HOSTNAME / $COMPUTERNAME | このマシンを示す人が読みやすいラベルです。origin_node に書き込まれ、ペアリング中のクライアントに対して GET /identity から表示されます。フォールバックは $HOSTNAME ですが、シェルはこれを子プロセスにエクスポートしないため、Linux ではこれを設定しない限り origin_node は空のままです。これはこのインストールの ID ではありません。ID は独自のデータベースで生成される UUID です。同じ名前を選ぶマシンが複数あっても問題ありません |
DATABASE_URL | auto (Docker) | PostgreSQL 接続先です。red モードでは外部接続+TLS の値を設定してください。 |
ONNX_MODEL_PATH + ORT_DYLIB_PATH | auto (~/.cache) | セマンティック埋め込みです。cuba-memorys models を実行すると設定されます。 |
RUST_LOG | cuba_memorys=info | tracing の EnvFilter が読み取るログレベルです。ログは stderr に出力されます。stdio トランスポートでは stdout が JSON-RPC チャネルであり、そこにほかの内容を出力するとクライアントが動作しなくなります。ハンドラーごとの詳細を確認するには cuba_memorys=debug、すべてのクエリを確認するには sqlx=debug を指定します。 |
CUBA_EMBED_MODEL · CUBA_EMBEDDING_DIM · CUBA_POOLING | multilingual-e5-small · 384 · mean | より強力なスペイン語モデルを使うには、bge-m3 · 1024 · cls に設定します |
CUBA_QUERY_PREFIX · CUBA_PASSAGE_PREFIX | query: · passage: | トークン化の前に付加する指示プレフィックスです。E5 はこれらを使って学習されていますが、bge-m3 は使っていません。切り替える際は両方を空文字列に設定してください。そうしないと、モデルがその形式で見たことのないテキストからすべてのベクトルが計算されます |
CUBA_CHUNK_THRESHOLD_CHARS · CUBA_CHUNK_CHARS | 1800 · 1400 | しきい値より長いコンテンツは、この文字数のチャンクに分割されます(200 文字重複)。CUBA_CHUNK_CHARS の下限は 200 です。正の整数でない値を指定すると、デフォルト値が使われます |
CUBA_EMBED_CONCURRENCY | 1 | ONNX 埋め込みセッションを囲むセマフォの許可数です。初回使用時に一度だけ設定されます |
CUBA_TOOL_PROFILE | full | lean → 31 個中 15 個のツールになり、カタログが 49%小さくなりますが、機能は失われません。13 個は日常的に使う中核ツール(whoami/artefacto/contexto を含む)です。残りも cuba_call から引き続き利用できます |
CUBA_JUDGE · MEMORY_INDUSTRY_JUDGE | auto | nli / mcp_sampling / claude_cli / heuristic / 名前付き OpenAI 互換プロバイダーです。推奨名は MEMORY_INDUSTRY_* です。CUBA_* も引き続き使えます |
CUBA_JUEZ_CLI · MEMORY_INDUSTRY_LLM_CLI · CUBA_JUEZ_MODEL · MEMORY_INDUSTRY_LLM_MODEL | claude · claude-haiku-4-5 | オフライン判定でシェルから起動する CLI と、指定するモデルです。CUBA_JUEZ_CLI / MEMORY_INDUSTRY_LLM_CLI は自動選択の経路も決定します。この名前が PATH 上にない場合、CLI 判定は利用できず、選択は次の候補に進みます |
CUBA_JUEZ_TIMEOUT_SECS · MEMORY_INDUSTRY_LLM_TIMEOUT_SECS | 30 | CLI と API のどちらにも適用される、1 回の判定に使える時間です。整数として解析できない値を指定すると、デフォルト値が使われます |
CUBA_JUEZ_MAX_PAIRS · MEMORY_INDUSTRY_LLM_MAX_PAIRS | 5 | cuba_juez が呼び出しごとに送信する候補ペア数です |
MEMORY_INDUSTRY_LLM_PROVIDER · CUBA_LLM_PROVIDER | unset | 名前付きのクラウド/ローカルプリセット(deepseek、qwen、ollama など)、または openai_compat です |
MEMORY_INDUSTRY_LLM_BASE_URL · CUBA_LLM_BASE_URL | unset | OpenAI 互換の /v1 ベース URL(Ollama、vLLM、ベンダーゲートウェイ)です |
MEMORY_INDUSTRY_LLM_API_KEY · CUBA_LLM_API_KEY | unset | そのベース URL 用の Bearer トークンです。ベンダーへのフォールバックには OPENAI_API_KEY、DEEPSEEK_API_KEY、DASHSCOPE_API_KEY(およびその他のプリセット用キー)が使われます |
CUBA_NLI_PATH | ~/.cache/cuba-memorys/models-nli | ローカルの含意モデル(cuba-memorys models nli)です |
CUBA_NLI_ESCALATE · MEMORY_INDUSTRY_NLI_ESCALATE | off | NLI が判定できなかった主張を LLM に送信します。再現率は向上しますが、1 件あたり約 12 秒かかります |
MEMORY_INDUSTRY_GRAPH_DB · CUBA_GRAPH_DB | off | オプションのグラフプロジェクションです。falkor / neo4j / off を指定します。信頼できる唯一の情報源は引き続き Postgres です |
MEMORY_INDUSTRY_GRAPH_URL · CUBA_GRAPH_URL | unset | グラフのエンドポイントです(FalkorDB の場合は redis://…) |
MEMORY_INDUSTRY_GRAPH_NAME · CUBA_GRAPH_NAME | memory_industry | Falkor/RedisGraph のグラフキーです。ゲートでは未設定/off のままにして、使い捨ての書き込みがライブグラフに反映されないようにします |
MEMORY_INDUSTRY_ENTITY_FACTOID | on | エンティティに言及するファクトイドクエリ用の追加 RRF 経路です。ハイブリッドランキングを変更しない場合は off / 0 / false に設定します |
CUBA_RERANKER_PATH · CUBA_RERANK_TIMEOUT_SECS | ~/.cache/…/reranker · 20 | クロスエンコーダーの再ランキング器(nDCG が 93%向上)です。CPU では、予算を超えると RRF にフォールバックします |
CUBA_RERANK_INTRA_THREADS | 物理コア数(GPU では 2) | 再ランキング推論ごとの ONNX スレッド数です。物理コア数を超えると遅くなります。増やす前に rerank_bench で測定してください |
CUBA_RERANK_LENGTH_BUCKETING | on(固定シェイプでは off) | パディングが計算量にならないよう、長さが近い候補をまとめてバッチ処理します。スコアは変わりません |
CUBA_RERANK_CHUNK | 16 | フォワードパスあたりの候補数です。固定シェイプではすべてのバッチが 512 トークンにパディングされるため、GPU アリーナに対する主な調整項目です。16 → 2938 MiB、4 → 2364 MiB です。スコアは変わりません。16 と 4 で詳細検索を行った結果、バイト単位で完全に一致しました |
CUBA_RERANK_CONCURRENCY | 1 | 再ランキング器セッションを囲むセマフォの許可数です。セッションはミューテックスで保護されるため、値を増やすと並列処理されるのではなく、呼び出しがキューに入ります |
CUBA_RERANK_BUCKET | 512 | パディング後のシーケンス長を、この値の倍数に切り上げます。受け付けられるのは 0 または 512 以下の 2 のべき乗のみです。それ以外を指定すると、デフォルト値が使われます。0 の場合は最長の候補に合わせてパディングします |
CUBA_RERANK_FIXED_SHAPE | 再ランキング器が GPU で実行される場合は on | すべてのバッチを同じ 512 トークンのシェイプにパディングします。0 / off / false で無効になり、それ以外の値で有効になります。CUBA_RERANK_LENGTH_BUCKETING のデフォルト値も切り替わります。すべてのバッチが同じサイズになると、この設定は不要になるためです。また、VRAM に対する主な調整項目が CUBA_RERANK_CHUNK になるのもこの設定によるものです |
CUBA_EMBED_DEVICE · MEMORY_INDUSTRY_RERANK_DEVICE · CUBA_RERANK_DEVICE · CUBA_NLI_DEVICE | cpu · gpu · cpu | モデルごとの配置先です。GPU の効果があるのは再ランキング器だけです。INT8 埋め込み器は GPU を使えず、FP32 NLI は VRAM を使う価値がありません。リソース計画では再ランキング器だけを考慮するため、GPU に固定された埋め込み器または NLI は、こちらでアリーナ上限を設定せずに CUDA セッションを実行します(SameAsRequested は要求された分だけ増加します)。これは 0.30 で決定された仕様であり、見落としではありません。再ビルドせずに配置を A/B テストするには、gpu / cpu を設定します。オペレーターが設定する再ランキング器には MEMORY_INDUSTRY_* という名前も用意されていますが、ほかの 2 つは従来の名前のみです |
MEMORY_INDUSTRY_GPU_MEM_LIMIT_MB · CUBA_GPU_MEM_LIMIT_MB | 空き VRAM から 512 MiB を差し引いた値(実測) | CUDA アリーナに上限を設け、arena_extend_strategy を SameAsRequested に固定します。デフォルトの NextPowerOfTwo は拡張のたびに予約量を 2 倍にします。その結果、1,65 GB の重みが 5 GB 以上の VRAM を使うことになりました。上限は セッションごとです。通常、この値を設定する必要はありません。リソース計画でカードを測定し、計画を適用するのは main だけです。変数も計画もない場合(テスト、サンプル、ライブラリ呼び出し)、上限はまったく渡されず、ONNX Runtime はカードを使用します。以前ここにあった値(2048 MB)では 4,23 GiB の再ランキング器を動かせなかったため、組み込みの値はありません。モデルの必要最低値より低い値を設定すると、両方の値を示す ERROR が出て、上限が引き上げられます。最低値より低くしても VRAM は節約できず、ロード途中で失敗するだけだからです。使用量を減らすには、再ランキング器のデバイスを cpu に設定してください |
CUBA_EMBED_INTRA_THREADS | 論理コア数の半分、最大 4 | 埋め込みあたりの ONNX スレッド数です。12 スレッドでの測定値:1 → 94,8 ms、2 → 52,3 ms、4 → 35,8 ms、6 → 68,1 ms、12 → 155,4 ms(クエリあたり) |
CUBA_IDLE_SHUTDOWN_SECS | 0(無効) | いずれのクライアントからもこの時間リクエストがないと終了します。systemd の .socket ユニットと組み合わせると、次の呼び出しでデーモンが再起動します。詳しくはフットプリントを参照してください |
MEMORY_INDUSTRY_WARM_RERANKER · CUBA_WARM_RERANKER | on | 最初の検索時ではなく起動時にクロスエンコーダーをロードします。現在はオプトアウトのみです(0 / off / false / no)。後回しにしてもコストは減らず、1.1 GB の読み込みを 20 秒の予算内で行うリクエストに移すだけです |
CUBA_HTTP_ALLOWED_ORIGINS | unset | /mcp と /panel への呼び出しを許可する追加のブラウザーオリジンを、カンマ区切りで指定します。Origin のないリクエストは常に許可されます。これはすべての MCP クライアントからのリクエストであり、ブラウザーからのものはありません。Origin があるリクエストはページから来たもので、このデーモン自体のページだけが対象です。トークンなしでループバックにバインドするのがドキュメントに記載されたデフォルト設定ですが、そうしないと DNS リバインディングによって、オペレーターが訪問した任意のサイトからデーモンを操作できてしまいます。意図的にどこにも CORS ヘッダーを出力しません。ブラウザーがすでに拒否しているプリフライトが、この制御を担います |
MEMORY_INDUSTRY_WARM_BEFORE_SERVE_SECS · CUBA_WARM_BEFORE_SERVE_SECS | 180 | バインド後、モデルの準備を待ってから起動を通知し、応答を開始するまでの serve の待機時間です。予算を超えると、そのままサービスを開始します。/health は ready:false を返し、再ランキングを求める検索は劣化状態のマーク付きで返されます。ポートを開かないデーモンには、何をしているのか尋ねることすらできません |
MEMORY_INDUSTRY_BIND_WAIT_SECS · CUBA_BIND_WAIT_SECS | 300 | このマシンにまだ割り当てられていないアドレスへのバインドに失敗した場合に、serve が再試行する時間です。ネットワークカードに固定 IP が設定される前にデーモンが起動する、Windows の起動直後の状況を想定しています。5 秒ごとに再試行し、その後 is not assigned to this machine とともに終了するため、スーパーバイザーが再起動します。0 は 1 回だけ試行します。秒数として解釈できない値を指定すると 300 が使われます。ポートがすでに使用中の場合は、再試行せず即座に失敗します |
CUBA_HTTP_ADDR · CUBA_HTTP_TOKEN | 127.0.0.1:8787 · unset | serve のアドレスと、要求される Bearer トークンです。ループバック以外にバインドするにはトークンが必須で、ルーティング可能なアドレスでは 32 文字以上でなければなりません。アドレスがすでに使用されている場合、doctor が警告します |
MEMORY_INDUSTRY_CLIENT_ID · CUBA_CLIENT_ID | unset | クライアントから Mcp-Client-Id が送信されない場合の、stdio/whoami 用ワークスペースラベルです。HTTP ではワークスペースごとに引き続きヘッダーが必要です。デーモンは 1 つのプロセスであるため、この環境変数を使ってチャットを分離することはできません |
CUBA_PANEL | unset | 1 に設定すると、serve は GET /panel にも応答します。これはバイナリにコンパイルされたコントロールページで、デーモンの状態、接続中のクライアント、最近の呼び出し、未解決の問題を読み取ります。独自のデータは保持しません。表示する情報はすべて、ほかの MCP クライアントと同じ Bearer トークンを使って POST /mcp 経由で取得するため、保護すべき 2 つ目のエンドポイントはありません。デフォルトでは無効です |
CUBA_PANEL_PUBLIC | unset | これを設定しない場合、/panel は転送ヘッダー(Forwarded、X-Forwarded-For、CF-Connecting-IP など、ほかに 6 種類)を含むリクエストを拒否します。これらは HTTP プロキシの存在を示すものです。Cloudflare トンネルは 127.0.0.1 に接続するため、どちらの場合もクライアントアドレスはループバックとなり、2 つを区別できるのはヘッダーだけです。検出できないもの:生の TCP 転送(ssh -L、socat、ngrok tcp)ではヘッダーが追加されず、ローカルリクエストと区別できません。そのため、これは HTTP プロキシを停止させるものであり、リクエストがローカルからのものだと証明するものではありません。パネルを意図的に公開するには 1 に設定します |
CUBA_PEER_URL | unset | cuba_sync action=fetch で使う、もう一方のデーモンのデフォルトアドレスです。例:https://brain.example.net。これはフォールバックにすぎず、最初のフェッチ成功後はピア名ごとにアドレスが記憶されます |
CUBA_PEER_TOKEN | unset | このマシンと同期する別のマシン用の 2 つ目の Bearer トークンです。使用できるのは同期動詞のみで、cuba_forget、cuba_zafra prune、cuba_sync import は使えません。そのため、ピアはこのノードが知っている内容を読み取れますが、行を書き込んだり削除したりすることはできません。トンネルにも使われる CUBA_HTTP_TOKEN とは異なる値にする必要があります。一致すると serve は起動を拒否します |
MEMORY_INDUSTRY_REQUIRE_ACCOUNTS | unset | 1(true、yes、on も可)に設定すると、ほかのマシンからアクセスできるアドレスにバインドされたデーモンは、/mcp、/events、/events/ticket での CUBA_HTTP_TOKEN の受け付けを停止します。「共有トークンは受け付けられなくなった」ことと、デバイスのペアリング方法を説明する文を添えて 401 を返します。アカウント認証情報(memory-industry account pair と memory-industry bridge pair から取得する mi1.…)および CUBA_PEER_TOKEN は引き続き使えます。また、モニターが遮断されないよう、/health は共有トークンに対して引き続きすべての情報を返します。このスイッチによって拒否された共有トークンは、ログイン失敗として数えられません。ループバックへのバインドでは共有トークンが唯一のアクセス手段のため、このスイッチは無視され、デーモンは警告をログに記録します。有効になっていて認証情報が何も有効でない場合、doctor は失敗します |
MEMORY_INDUSTRY_DOCTOR_DEEP_SECS · CUBA_DOCTOR_DEEP_SECS | 300 | doctor --deep が、モデルのロードを失敗と判断するまで待つ時間です。ロードはブロッキングスレッドで実行されるため、応答しないモデルがツールをハングさせることはありません。予算を超えた場合は理由付きの fail となり、スキップにはなりません |
MEMORY_INDUSTRY_HANDSHAKE_TIMEOUT_SECS · CUBA_HANDSHAKE_TIMEOUT_SECS | 60 | MCP ハンドシェイクが届かない場合、諦めたクライアントのためにモデルを保持し続けず、stdio を終了します。単位は秒で、前後の空白は無視されます。0(00、+0 も可)または off で無効になります。それ以外(60s、1.5、-5 など)では 60 秒のデフォルトが維持され、値を示す警告がログに記録されます。以前は何も通知せずにウォッチドッグが無効になっていました |
CUBA_HANDLER_TIMEOUT_SECS | 30 | 1 回のツール呼び出しに対する上限時間です。cuba_ingesta 内の LLM 抽出にも、この値の 60%が予算として適用されます。値を増やすと、抽出にかけられる時間も長くなります |
CUBA_DOCS | off | 1 にすると、マシンの外部にデータを送信する唯一のツールである cuba_docs が有効になります。未設定の場合、このツールは表示すらされません。 |
CUBA_COMPACT_CHARS | 1200 | コンパクト化時の切り詰め文字数(実測による変曲点) |
CUBA_OOD_THRESHOLD | calibrated | 棄却しきい値を上書きします |
CUBA_BITEMPORAL | on | 観測結果を brain_facts に複製します |
CUBA_AUDIT_KEY | unset → ~/.cache/cuba-memorys/audit_key | cuba_archivo ハッシュチェーン用の HMAC キーです。キーがない場合、チェーンは単純な SHA-256 になります。テーブルへの書き込み権限がある人なら誰でも再計算できるため、エントリーの整合性は保たれ、偽造も検出されません |
CUBA_APP_ROLE | on | マイグレーション後、プールは権限のない cuba_app ロールとして再接続します。0 / off / false にすると管理者接続が維持され、セッション全体を通じてスーパーユーザー接続が有効なままになります |
CUBA_PROJECT_FILTER | unset(フィルター有効) | 大文字小文字を問わず off を指定すると、プロジェクトごとのスコープ設定が無効になり、RLS スコープが * となってすべてのプロジェクトのメモリが一度に表示されます。それ以外の値ではフィルターが有効のままです |
CUBA_QUARANTINE_INFERENCE | off | 1 / on / true を指定すると、呼び出し元が信頼レベルを明示的に設定していない限り、source=inference のものはすべて trusted ではなく quarantined として保存されます |
CUBA_PG_BIND | 127.0.0.1 | 管理対象の Postgres コンテナがポートを公開するホストアドレスです。ループバック以外を指定すると、データベースがネットワークに公開されます |
CUBA_RANDOM_PAGE_COST · CUBA_IO_CONCURRENCY | 1.1 · 200 | プールの接続ごとのプランナー設定です。受け付けられる範囲は 0.1~10.0 と ≤ 1000 です。範囲外ではデフォルト値が使われます |
CUBA_REM_AUTOLINK | on | 0 / off / false にすると、REM サイクルによるエンティティ間の NPMI 共起エッジの作成が停止します |
CUBA_GATE_MIN_FREE_GB | 8 | ゲートがコンパイル前に要求するディスク空き容量です。これを下回ると、理由を示して起動を拒否します。パーティションの 98%が使用済みの状態で実行した際、collect2: fatal error: ld terminated with signal 7 [Bus error] で終了し、3 つのテストバイナリが «could not compile» と報告されました。この出力にはディスクに関する記述がないため、コードのエラーのように見えます |
CUBA_GATE_SWEEP_BELOW_GB | 20 | ゲートが実行前にビルド成果物を整理する空きディスク容量のしきい値です。cargo は以前のコンパイルのバイナリを削除しません。編集するたびに新しいハッシュが作られ、古いものが残るため、target/debug/deps は際限なく増え続けます。ここでは 64 GB に達しました |
CUBA_GATE_SWEEP_DAYS | 7 | 整理対象となる成果物の経過日数です。意図的にサイズではなく経過日数で判定します。この実行に必要なものはすべて当日に作成されているためです |
CUBA_REM_FIRST_DELAY_SECS | 300 | 起動後、最初の REM 統合が実行されるまでの時間です。以前は REM_INTERVAL(4 時間)でした。ループが間隔の最初のティックを消費し、瞬時に解決していたためです。stdio ではプロセスがそこまで長く動作することはほとんどないため、その環境ではサイクルがまったく実行されず、マシンを再起動するたびにカウンターがゼロに戻っていました |
CUBA_REM_RELATION_BATCH | 5 | REM サイクルが 1 パスで関係スキャンを行うエンティティ数です。0 にするとスキャンをスキップします。未設定の場合は自動調整され、未処理のエンティティが 50 件以上ある間は 20 件、キューが解消すると 5 件に戻ります。4 時間ごとに 5 件ずつ処理すると、226 件の未処理分が完了するまで 1 週間かかります |
CUBA_REM_SCAN_TIMEOUT_SECS | 90 | 1 件のエンティティの関係スキャンに使える時間です |
CUBA_REM_EXTRACTION_BATCH | 5 | 関係スキャンの直後に REM サイクルが 1 パスで cuba_ingesta auto_extract を実行する観測結果の数です。0 にするとスキップします。検出されたものは常に trust=quarantined として書き込まれます。これはグラフに対する唯一の完全自動の書き込み処理であるため、cuba_eco action=promote で手動承認するまで、cuba_faro からは何も見えません |
CUBA_REM_BACKFILL_LIMIT | 100 | REM サイクルが 1 パスで埋め戻す、埋め込みのない観測結果の数です。0 にすると埋め戻しが無効になり、負の値を指定するとデフォルト値が使われます |
CUBA_SYNC_DIR | unset → 作業ディレクトリ内の .memory-industry。ただし、.cuba-memorys がすでに存在し、優先されるディレクトリが存在しない場合はそちらを使用 | cuba_sync のエクスポート/インポート用ルートです。これがアクセス制限の境界でもあります。このルート外を指定する --dir は拒否されるため、../ で範囲外に出るのではなく、別の場所に同期するにはこの値を設定します。hook install は一度だけ値を解決し(相対パスの場合はリポジトリルートを基準にします)、結果をフックに書き込みます。変数を変更した場合は、再実行してください |
CUBA_UNDO_DIR | ~/.cache/memory-industry/undo。ただし、~/.cache/cuba-memorys/undo がすでに存在する場合はそちらを使用 | 破壊的な CLI コマンドが取り消し用スナップショットを書き込む場所です。明示的に設定すると、ホームディレクトリの解決前に値が読み取られるため、HOME も USERPROFILE も定義されていないマシンでも機能します |
フットプリント
メモリサーバーはインフラストラクチャです。使用していないときも動作しています。これが計測された 6 GB のノート PC 用 GPU では、以前は起動時から 5228 MiB の VRAM、つまりカードの 93% を占有しており、他の GPU プログラムが起動できなくなっていました。NVIDIA ドライバーはチャネル作成時に NV_ERR_NO_MEMORY を返しており、これはゲームや GPU アクセラレーション対応ターミナルが失敗する原因です。
以下の数値のうち 2 つはデフォルトとして出荷され、2 つは 1 行の設定が必要です。この表は、あたかも無償で得られたかのように最良の数値を引用するのではなく、それらを区別して示しています。
| 以前 | 0.20.0 のデフォルト | CUBA_RERANK_CHUNK=4 | + フューズドアーティファクト | |
|---|---|---|---|---|
| 検索中の VRAM | 5228 MiB | 2950 MiB | 2364 MiB | 1460 MiB |
| アイドル時の VRAM | 5228 MiB | 0 — プロセスは終了しています | 0 | 0 |
| コールドスタートから応答まで | 11.1 秒 | 0.027 秒 | 0.027 秒 | 0.027 秒 |
| 検索、ウォーム時 | 5.90 秒 | 5.25 秒 | 3.73 秒 | 1.70 秒 |
| 1 クエリの埋め込み | 52.3 ms | 35.8 ms | 35.8 ms | 35.8 ms |
デフォルト列のすべては出荷されるコードです。CUBA_RERANK_CHUNK=4 は 1 つの環境変数です。最後の列はさらに、以下で説明する再ビルドされたリランカーが必要です。そのいずれも機能を削除していません。
これを実現したのは 4 つのことです。
プロセス単位ではなくモデル単位の配置。 GPU で高速化されるのはリランカーのみです — INT8 のエンベッダーは高速化できず、FP32 の NLI は、時折実行され 150~400 ms を許容するジャッジのために 1 ギガバイトの VRAM を費やす価値はありません。アリーナの上限はセッション単位であるため、6 GB のカードで 3 つのセッションが CUDA を要求するのは、失敗を待つだけの 3 倍のオーバーコミットです。
倍々で増えるのを止める CUDA アリーナ。 ArenaExtendStrategy::NextPowerOfTwo は ONNX Runtime のデフォルトであり、セッションが要求した量ではなく 2 のべき乗で予約します。
リランカーは最初のバッチでロードされます。 ソケットアクティベーションでは、デーモンはリランクするよりもはるかに頻繁に起動し、それらの起動の多くは save に応答するだけです。
誰も要求していないときには動作していないデーモン。 CUBA_IDLE_SHUTDOWN_SECS に加えて systemd の .socket ユニット: ソケットがポートを所有し、デーモンは最初の実際の接続で起動し、アイドルウィンドウの後に終了します。終了は通常の経路を通ります — serve が戻り、バックグラウンドのドレインが処理中の埋め込み書き込みをフラッシュし、sqlx がプールを閉じます — なぜならプロセスを直接終了するとそれらの書き込みが黙って失われるからです。
systemd のペア
# ~/.config/systemd/user/memory-industry.socket
[Socket]
ListenStream=127.0.0.1:8787
Accept=no
[Install]
WantedBy=default.target
# ~/.config/systemd/user/memory-industry.service (abridged)
[Service]
Type=exec
ExecStart=%h/.local/bin/memory-industry serve 127.0.0.1:8787
# Every knob — CUBA_IDLE_SHUTDOWN_SECS, the per-model devices — lives in the env file, never here.
EnvironmentFile=%h/.config/memory-industry/memory-industry.env
# An idle shutdown exits 0 — Restart=always would bounce it straight back up.
Restart=on-failure
両方のユニット、およびすべてのキーを提供する env ファイルは、バイナリによって生成され、packaging/ でバージョン管理されています: memory-industry setup service は計画を出力し、--apply はそれらをインストールします。ExecStart は実際にインストールしたバイナリを指定する必要があり — command -v memory-industry — 間違ったパスは status=203/EXEC として失敗します。以前のリリースが出荷していた cuba-memorys.service / cuba-memorys.socket のペアは、0.28.0 でリポジトリから削除されました。まだインストールされている場合は、CHANGELOG の 0.28.0 の項目を参照してください。
serve は systemd が fd 3 (LISTEN_FDS) として渡すソケットを採用するため、デーモンが動作していない間もポートが保持され、どのクライアントも接続拒否を見ることはありません。
ユニットはループバックにもバインドする必要があります。ソケットアクティベーションでは .socket ユニットの ListenStream がアドレスを決定し、CUBA_HTTP_ADDR は無視されるため、serve は渡されたソケットのアドレスを確認し、CUBA_HTTP_TOKEN が設定されていない限り、ルーティング可能なアドレスを拒否します。
VRAM はまだ半分にすぎませんでした。重みはホストメモリにも存在し、その消費量は以前はマシンのスペックに関係なく固定でした。cargo run --release --features cuda --example mem_bench で計測、デーモン停止状態、6 GB のノート PC GPU 上での結果は次のとおりです。
| ステージ | 追加 RSS | VRAM | ロード |
|---|---|---|---|
| プロセス起動 | 5,5 MiB | 0 | — |
| + PostgreSQL プール | +1,3 MiB | 0 | — |
| + エンベッダー (bge-m3, CPU) | +862,0 MiB | 0 | 1,72 秒 |
| + リランカー (fused FP16, GPU) | +1034,7 MiB | 1460 MiB | 3,73 秒 |
| + OOD フィット (n=1811, d=1024) | +37,4 MiB | 0 | 11,45 秒 |
| ピーク | 2677 MiB | 1460 MiB |
常駐メモリは 1941 MiB 付近で落ち着きますが、ピークは 2677 です。これは 1,1 GB の ONNX ファイルをロードすると、その後に残る重みに加えて一時的なメモリが必要になるためです。収まらなければならないのは定常状態ではなく、このピーク値です。
計測を行ったマシンでは問題ありません。しかし 4 GB のノート PC ではそうはいかず、MemoryHigh=4500M で制限された systemd ユニット下では 2,56 GiB のスワップ が発生することが確認されています。MemoryHigh はプロセスを kill せず、メモリを回収します。そして回収とはスワップへのページングです。
これを再実行する場合に知っておく価値のある落とし穴が 2 つあります。mem_bench は nvidia-smi --query-compute-apps を使って VRAM を自身の PID に帰属させます。memory.used を読むとカード上の他のすべてのプロセスの分も計上されてしまうためです。これにより、最初の試行では「プロセス起動時」に 3590 MiB が表示されましたが、それはゲームとデスクトップシェルのものでした。また、デーモン自身の環境で実行してください。CUBA_RERANKER_PATH を未設定にすると fused でない アーティファクトを黙ってロードし、ウォームアップが CPU 上で 3,7 秒から 131 秒になります。
そこでデーモンは起動時にマシンを読み取り、レベルを選択するようになりました。このために新しく作られたものは何もありません。3 つのデグラデーションはすべてすでに存在し、テスト済みです。
| レベル | ロードされるモデル | ホスト RAM | 失うもの |
|---|---|---|---|
| minimal | なし | 約 220 MiB | セマンティック検索。BM25 + 全文検索 + トライグラムは引き続き応答します |
| lean | エンベッダー | 約 1,1 GiB | リランキングとローカル含意判定 |
| standard | エンベッダー + リランカー | 約 2,2 GiB | NLI ジャッジ。独自のフォールバックラダーに切り替わります |
| full | 3 つすべて | 約 3,3 GiB | なし |
レベルの選び方。 予算は min(cgroup 制限, システムの利用可能量) − 768 MiB のヘッドルームで、cgroup の値が優先されます。このマシンでは /proc/meminfo が 7,16 GB の利用可能量を報告しますが、デーモンの cgroup は 4,39 GiB に制限しています。/proc を信じると、カーネルがすでにページングを始めている制限に対して 2,6 GiB の重みをロードすることになります。リーダーは cgroup のルートからリーフまでたどり、見つけた中で最も厳しい memory.max または memory.high を採用します。制限は通常、祖先に設定されているためです。
次にモデルは計測された価値の順に適合されます。まずエンベッダー、次にリランカー (+93% nDCG なのでジャッジより優先)、そして NLI です。
計画は減らすことしかできません。 すべての設定値はデーモンがすでに使用していた値で上限が決まるため、余裕のあるマシンではレベルは full となり何も変わりません。デグラデーションは下方向にしか進みません。
あなたは常に有利です。 これらを手動で設定した場合、その値はそのまま維持されます。レギュレーターは隙間を埋めるものであり、決定を上書きするものではありません。
CUBA_EMBED_INTRA_THREADS CUBA_RERANK_INTRA_THREADS CUBA_NLI_INTRA_THREADS
CUBA_RERANK_CHUNK CUBA_GPU_MEM_LIMIT_MB CUBA_OOD_FIT_LIMIT
CUBA_DB_MAX_CONNECTIONS
予算に関係なくモデルを強制的にオフにするには、存在しないパスを指定します — CUBA_RERANKER_PATH=/nonexistent または CUBA_NLI_PATH=/nonexistent。これはレギュレーター自身が使用しているのと同じ仕組みです。
何を決定したかを確認するには cuba-memorys doctor を実行してください。読み取り値とその結果の計画を報告し、レベルが minimal に落ちたときには警告を出します。計画は起動時にも、その背後にあるマシンの完全な読み取り値とともにログに記録されます。
リランカーの成果物
公開されている bge-reranker-v2-m3 ONNX は、グラフ融合の前に FP16 へ変換されているため、785 個の Cast ノードが内部に残っています。ONNX Runtime はロード時にその一部を取り戻します(2023 → 897 ノード、49 個の SkipLayerNormalization)が、Gelu を融合することはできず、コールドスタートのたびに同じ処理を繰り返します。FP32 エクスポートから再ビルドし、先に融合すると:
python -m onnxruntime.transformers.optimizer \
--input model.onnx --output model.onnx \
--model_type bert --num_heads 16 --hidden_size 1024 \
--opt_level 1 --use_gpu --float16
| VRAM | 検索 p50 | ロード + ウォーム | |
|---|---|---|---|
| 提供されている FP16 | 2364 MiB | 3,73 秒 | 22,8 秒 |
| 融合してから FP16 | 1460 MiB | 1,70 秒 | 10,2 秒 |
実際の検索でのトップ 10 の順序は同一で、fused_score の差は最大 0,0029 です。実際のバッチ形状での合成ロジットでは、Pearson ≥ 0,9997 で、すべてのバッチで順位も同じです。
Attention は融合されませんが、これはここでは修正できません。 is_fully_optimized: Attention (or MultiHeadAttention) not fused が、opt_level 0、1、2、99 のいずれでも、FP16 の成果物とクリーンな FP32 の成果物の両方で発生します。エクスポートは動的形状のサブグラフ(Shape → Gather → Unsqueeze → Concat → Reshape)から Q/K/V のリシェイプを構築し、AttentionFusion は num_heads と head_size を読み取るために定数形状の Reshape を必要とします。そのため、静的形状を使って再エクスポートしない限り、flash/efficient attention は利用できないままです。誰かがこれに午後を費やす前に知っておく価値があります。
測定結果 — そして嘘をついていたベンチマーク
v0.12 まで、このセクションには 「ここにあるすべての数値は仮定ではなく測定されたものです」 という一文があり、そこにあるすべての数値は間違っていました。ベンチマークには 3 つの欠陥があり、それを突き止めるのに 2 つの公開された結論が犠牲になりました。
クエリが 10 件しかありませんでした。 95% 区間はおよそ ±0.12 で、検出できる最小の効果は約 0.25 nDCG でした。それより小さい差についての主張は、小数点をまとったノイズでした。
関連性は部分文字列一致で判定されていました。 結果のテキストがマーカー単語を単に含んでいれば正解とみなされたため、「postgres」に言及したあらゆる観察が、postgres に関するあらゆる質問への正解としてスコアされました。それが何かに答えているかどうかは関係ありません。これはキーワードの存在を測っているのであって検索を測っているのではなく、ベンチマーク全体を字句ベースの枝に傾け、ベクトルの枝に不利にしています。
nDCG は存在するものではなく、検索されたものに対して正規化されていました。 コーパスに 5 件の関連文書があり、2 件が見つかった場合、「理想の」順位はその 2 件とされました。そのため、答えの 60% を取り逃したシステムが満点の 1.0 を獲得しました。(しかも R@10 = 3.125 がこのファイルに記載されていました。再現率は割合です。)
本当の数値は 0.894 ではありません。221 件の id スコア付きクエリでは nDCG@10 = 0.50 [95% CI 0.44–0.56] です。システムが悪くなったのではありません。0.894 だったことなど一度もないのです。
それがもたらした代償
-
「クロスエンコーダー・リランカーは何ももたらさない」— それは一度も実行されていませんでした。 連鎖した 3 つのバグ:faroが呼び出しをif let Ok(..)で包んでエラーを握りつぶしていた;XLM-RoBERTa であるモデルにtoken_type_idsを渡していた(そんなものはない);f16ロジットをf32として読んでいた。出力がリランキングなしと「ビット単位で同一」だったのは、リランキングが何も変えなかったからではなく、そもそも実行されていなかったからです。修正済みで、現在は適切に測定されています。 -
連想検索は実際に劣化します — ただし、古い証拠(n=10 で −0.03)ではそれを示せませんでした。ペアードブートストラップ(正しい検定:両方のアームで同じクエリ)を用いた新しいデータセットでは、区間は [−0.051, −0.018] で、ゼロに触れることはありません。0 件のクエリを改善し、23 件を悪化させます。判断は正しかったが、その根拠は正しくなかったのです。検出力はより多くのデータにあったのではなく、正しい検定を使うことにあったのです。
何が生き残るか、正直に再測定
デフォルトで compact | トークン −30%、nDCG +0.0090(ペアの 95% CI [+0.0024, +0.0166]、n=191)。以前の「ちょうど 0.0000」は、5000 トークンの応答予算がランキングを採点前に切り詰めるハーネスで測定されていました。冗長なものは末尾を失い、compact は失いませんでした。古い「−40%」は壊れたベンチマークから来ていました。 |
| Conformal による棄権 | 分布外クエリの 100% を検出、誤った棄権は 0%。 |
lean ツールプロファイル | 31 個中 15 個のツール、カタログ −49%、失われた機能はゼロ。 |
| bge-m3 対 e5-small | 方向性はほぼ確実に正しいですが、+21.2 nDCG という数値は撤回します — それは壊れたベンチマークから来ており、再確立するにはコーパスを 2 回再埋め込みする必要があります。 |
| ベンチマーク自体 | 221 クエリ(以前は 10)、関連性はドキュメント id による、ブートストラップ信頼区間、そしてすべての結果の横に印刷される 最小検出可能効果 — これで誰も 3 ポイントの差を発見事項として読むことはもうありません。 |
基盤
| アルゴリズム | 参考文献 |
|---|---|
| RRF 融合 (k=60) | Cormack et al. (2009) |
| ヘブ則 + BCM メタ可塑性 | Oja (1982); Bienenstock, Cooper & Munro (1982) |
| Conformal 予測 | Vovk (2005); Angelopoulos & Bates (2023) |
| Ledoit-Wolf 共分散縮小 | Ledoit & Wolf (2004) |
| マハラノビス OOD 検出 | Lee et al. (NeurIPS 2018) |
| Wilson スコア区間 | Wilson (1927) |
| 宣言的記憶と手続き的記憶 | Anderson & Lebiere (ACT-R) |
| テスト効果 | Karpicke & Roediger (Science 2008) |
| べき乗則忘却 | Wixted (2004) |
| エピソード記憶と意味記憶 | Tulving (1972) |
| PageRank · Leiden · Brandes | Brin & Page (1998); Traag et al. (2019); Brandes (2001) |
| NPMI 共起 | Bouma (2009) |
| MMR 多様化 | Carbonell & Goldstein (1998) |
| コンテキスト検索 | Anthropic (2024) |
| プロンプトインジェクションのスポットライティング | Hines et al. (2024) |
開発
git clone https://github.com/LeandroPG19/Memorys.git
cd Memorys/rust && cargo build --release
# On an NVIDIA machine, build this way instead — without it the reranker spends
# its whole budget for a ranking that gets discarded. It accelerates the
# reranker only; see Footprint for why the other two models stay on the CPU.
cargo build --release --features docs,cuda
./scripts/demo.sh # runs on a throwaway Postgres it removes on exit
./scripts/merge-gate.sh # local CI 100% — sole merge judge (see docs/gate.md)
./scripts/como-el-ci.sh todo # same SIL
./scripts/quality-gate.sh # second judge: CRAP + mutants of rust/src in the diff
cargo run --release --example rerank_bench # does the reranker fit its budget here?
公開はタグ駆動です。LeandroPG19/Memorys の v* が GitHub Release バイナリ(5 プラットフォーム)、PyPI ホイール(memory-industry と cuba-memorys エイリアス)、npm(memory-industry と cuba-memorys エイリアス)、および MCP レジストリ(io.github.LeandroPG19/memory-industry)をトリガーします。テストが Cargo.toml、package.json、pyproject.toml、server.json を固定しているため、これらのバージョンはずれることがありません。
ライセンス
Apache-2.0 — 使用、変更、配布、販売、クローズド製品への組み込みが可能です。コピーレフト義務はありません。このライセンスは特許権も明示的に付与しており、これが法務部門が気にする部分です。
著者
Leandro Perez G. — @LeandroPG19
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。
