mcpField
K
keymem

keymem

ローカル
github.com/donggyun112メモリ・ナレッジ31 週間前に更新

keymem について

keymemは、概念と記憶を結ぶグラフを基盤とする、LLMエージェント向けのローカルな連想メモリMCPサーバーです。クエリと語句が似ていない情報も共有キーの経路から探し、コンテキストに入れる記憶を制御できます。

主な機能

  1. 01キーと記憶の多対多グラフ
  2. 02エージェント主導の多段階検索
  3. 03鮮度と確認状態の追跡
  4. 04訂正履歴とバージョン管理
  5. 05ローカルJSON保存と埋め込み

活用シーン

  1. 01概念のつながりから事実を想起
  2. 02保存した記憶を段階的に探索
  3. 03記憶が現在も有効か確認
  4. 04情報を訂正し旧版を保持

MCP 対応

ローカルでMCPサーバーとして動作し、対応するエージェントに記憶の想起、探索、保存、確認機能を提供します。MCPのフローでは、エージェントがキーと記憶の間を段階的に移動します。

設定

ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。

mcp.json · 共通設定
{
"mcpServers": {
"donggyun112-keymem": {
"command": "npx",
"args": [
"-y",
"keymem"
]
}
}
}
起動コマンドnpx -y keymem

ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。

パッケージ
npmkeymem0.17.0

ツール

11

掲載時のツール一覧 · 2 週間前に更新

パラメーター型必須説明
querystringはい
top_knumberいいえ
injectbooleanいいえ
namespacestringいいえ
inject_top_knumberいいえ
inject_prefer_depthbooleanいいえ
inject_explore_shallowbooleanいいえ

詳細

keymem

npm version Node.js License: MIT

LLM エージェントのための連想記憶レイヤー — 類似性だけでなく、連想によって思い出します。

keymem chain recall — one recall() per hop walks Key → Memory → Key; skipped branches stay one call away

エージェントのメモリの多くはベクトルストアです。クエリに「似ている」ものを提示しますが、クエリに「つながっている」ものはすべて見落とします。

keymem は、メモリを代わりにキーワードグラフに保存します。**「Newton」で検索しても、Newton → apple → fruit → strawberry とたどって、「strawberries」**に到達できます。この経路は埋め込み空間ではなく、グラフ内に存在します。MCP サーバーとしてローカルで動作するため、MCP 互換のエージェントならどれでも、外部データベースなしで人間のような連想による想起を利用できます。

対応: Claude Desktop · Claude Code · MCP 互換のあらゆる LLM エージェント


なぜ連想記憶なのか?

ベクトルストアのメモリは埋め込みの類似性に基づいて情報を取得します。必要なものが、入力した言葉と似ていない場合は、うまくいきません。

Query: "Newton"
Similarity search finds: "Newton discovered gravity"  ✅
Similarity search misses: "user likes strawberries"   ❌

人はそれでも連想をたどります。Newton から apple を思い出し、apple は fruit で、自分は strawberries が好きだと思い出します。keymem も、キーワードグラフ内に経路が存在するため、同じ連想をたどります。Newton → apple memory → fruit key → strawberry memory。「Newton」と「strawberry」をつなぐ埋め込み距離はありません。あるのは、共有キーの連鎖です。

これが中心となる考え方です。メモリは距離で順位付けされた孤立した島ではありません。エージェントがたどることのできるN:M のキー/値グラフのノードです。


仕組み

Key Space (concepts)         Value Space (memories)
[apple]   ────────┬─────────→      ↑ same memory
[gravity] ────────┘
                  │
[apple]   ────────┼─────────→ "apples are red fruit"
[fruit]   ──────┬─┘
[red]     ──────┤
                │
[fruit]   ──────┼─────────→ "user likes strawberries"
[strawberry]────┘

メモリは値空間に保存され、そこへは独立したキー空間を経由してアクセスします。1 つのメモリには複数のキーからアクセスでき、1 つのキーから複数のメモリにたどれます。

recall("Newton") は、[Newton] や [apple] のような一致するキーのクラスターと、最も強いキーに関連付けられた受動的な Top-1 メモリを返します。このメモリには matched_key、validity、connected_keys が含まれるため、エージェントはすぐに回答することも、read_key(fruit) を通じて先へ進むこともできます。

デフォルトの MCP フローは引き続きキー → メモリ → キーですが、最初のキー → メモリへの移動は 1 回の呼び出しで完了します。自動的にコンテキストへ追加されるメモリは 1 つだけで、それ以降の移動は意図的に行います。

上部のアニメーションでは、実際のストアでこの動作を確認できます。3 回の recall() 呼び出しで、プロフィールキーから検証の考え方までたどり、3 つのメモリ(約 2.4k トークン)を読み込みます。エージェントがたどらなかった分岐も、1 回の呼び出しでアクセスできます。同じ 12 メモリのハブを自動展開した場合は、約 8k トークンが必要になります。グラフが道筋を把握し、エージェントが進む手順を決めます。ソース:docs/demo/chain-walk.html。


クイックスタート

keymem は MCP サーバー(CLI)であり、ライブラリではありません。 npx -y keymem(推奨 — 常に最新のバージョン)で実行するか、npm i -g keymem でコマンドをグローバルにインストールしてください。依存関係として npm i keymem を使ってアプリに追加しないでください。openai、zod、MCP SDK が同梱されているため、既存のプロジェクト内ではそれらが重複するだけでなく(アプリの zod/ openai のバージョンと競合する可能性もあります)。npm のパッケージページに表示される npm i keymem はライブラリ向けのもので、ここでは 該当しません。

# Optional global install (npx needs none). This puts a `keymem` command on PATH that
# MCP clients can spawn. Run bare, it starts a stdio MCP server and waits for a client —
# so point your MCP config at `keymem` (or just use `npx -y keymem` as shown below).
npm i -g keymem
Claude Desktop

claude_desktop_config.json に追加します。

OpenAI の埋め込み:

{
  "mcpServers": {
    "keymem": {
      "command": "npx",
      "args": ["-y", "keymem"],
      "env": {
        "OPENAI_API_KEY": "your-openai-api-key"
      }
    }
  }
}

ローカル埋め込み(API キー不要)— bge-m3 を推奨:

{
  "mcpServers": {
    "keymem": {
      "command": "npx",
      "args": ["-y", "keymem"],
      "env": {
        "EMBEDDING_BACKEND": "local",
        "LOCAL_EMBEDDING_MODEL": "bge-m3"
      }
    }
  }
}

bge-m3(多言語対応、推奨)は初回実行時に約 570 MB を自動ダウンロードし、その後キャッシュします。より軽量なデフォルト(fast-multilingual-e5-large)を使う場合は、LOCAL_EMBEDDING_MODEL を省略してください。クロスエンコーダーによる再ランキングは、コアの想起処理に含まれており、初回使用時に 2 つ目のモデルをダウンロードします。無効にする場合に限り、"KEYMEM_RERANK": "false" を設定してください。

プラグイン(推奨 — Claude Code と Codex)

このリポジトリはプラグインマーケットプレイスでもあるため、インストールするとすべてが設定されます。MCP サーバー(デーモンを利用するシム)、各プロンプトで関連する記憶を受動的に提示する UserPromptSubmit フック、想起/記憶プロトコルを備えた keymem スキルが含まれます。

Claude Code:

/plugin marketplace add donggyun112/keymem
/plugin install keymem@keymem

Codex CLI:

codex plugin marketplace add donggyun112/keymem
codex plugin add keymem@keymem

Codex ではフックを信頼するかどうかを一度だけ確認されます。承認しない場合、プッシュ経路は動作しません。プラグインはデフォルトでローカルの bge-m3 埋め込みを使用します(初回実行時に約570MBを自動ダウンロードし、API キーは不要です)。OpenAI の埋め込みを使う場合は、代わりに以下の手動セットアップを使用してください。プラグインの MCP サーバーから参照できるのは、宣言された環境変数のみです。同期フックは /inject の応答を最大9秒待ちます(KEYMEM_HOOK_TIMEOUT_MS で調整できます)。想起が発生するプロンプトでは、ローカルの埋め込みと再ランク付けによって数秒追加される場合があります。

Claude Code(手動)
# OpenAI embeddings
claude mcp add keymem -e OPENAI_API_KEY=your-key -- npx -y keymem

# Local embeddings (no API key required) — bge-m3 recommended (auto-downloads ~570MB on first run)
claude mcp add keymem -e EMBEDDING_BACKEND=local -e LOCAL_EMBEDDING_MODEL=bge-m3 -- npx -y keymem
Codex CLI(手動)
# OpenAI embeddings
codex mcp add keymem --env OPENAI_API_KEY=your-key -- npx -y -p keymem@latest keymem-shim

# Local embeddings (no API key required)
codex mcp add keymem --env EMBEDDING_BACKEND=local --env LOCAL_EMBEDDING_MODEL=bge-m3 -- npx -y -p keymem@latest keymem-shim

keymem ではなく、keymem-shim のエントリーポイントを使用してください。プッシュ経路のフックが通信する共有デーモンを実行します。フックを使用するには、~/.codex/config.toml に以下を追加してください。

[[hooks.UserPromptSubmit]]
[[hooks.UserPromptSubmit.hooks]]
type = "command"
command = "node /absolute/path/to/keymem/hooks/keymem-hook.mjs"
timeout = 10

Codex は MCP エントリーで宣言された環境変数のみを転送するため、すべての KEYMEM_* / SUPER_MEMORY_* オーバーライドを --env で渡してください。

これで完了です。想起と記憶がすぐに使えます。エージェントは最初の応答前に recall を呼び出し、read_key/read_memory で移動し、remember で保存します。

Claude Code で確実に先回りして保存するには、以下を ~/.claude/CLAUDE.md に追加してください(MCP プロンプトは、永続的な Claude Code の指示として自動的には適用されません)。

## keymem

- Before the first reply and whenever the topic changes, call `recall` silently with short noun-keyword queries.
- Before ending every reply, check whether this turn revealed a durable fact: a name, preference, decision, correction, project fact, or goal.
- If it did, call `remember` or `remember_batch` silently in the same turn with 3-6 diverse keys. A durable fact left unsaved is a bug.
- Use `correct` when existing information changes. Save nothing only when the turn revealed nothing durable.
- Treat `read_memory` as retrieval, not confirmation. Use its `validity.status`: qualify `aging`, and never assert `stale` as current without checking an external source or asking the user.
- Call `confirm_memory` only after an explicit current user assertion, an authoritative current source, or direct observation — never merely because a read succeeded.
- Never mention memory lookup or saving to the user.

Codex では、同じブロックを ~/.codex/AGENTS.md に記述してください。(プラグインをインストールすると、これは keymem スキルとして含まれるため、そちらでは省略できます。)

その他の MCP クライアントでは、memory_system_prompt MCP プロンプトをエージェントの永続的なシステム指示に含めてください。最初の応答前に recall() を3回、手がかりなしで呼び出すよう指示します。受動的に情報を提示するフックがない場合に適しています。独自に UserPromptSubmit 相当のプッシュ経路を設定する場合は、KEYMEM_HOOK_INSTALLED=true を設定してください。そうすると、プロンプトは手がかりなしで推測を重ねるのではなく、提示された情報に応じて動作します(プラグインの .mcp.json では自動的に設定されます)。

手動/開発
git clone https://github.com/donggyun112/keymem
cd keymem
pnpm install

.env を作成します:

OPENAI_API_KEY=your-openai-api-key
OPENAI_EMBEDDING_MODEL=text-embedding-3-small

または、ローカルの埋め込みを使用します(API キーは不要です):

EMBEDDING_BACKEND=local
LOCAL_EMBEDDING_MODEL=fast-multilingual-e5-large  # default; best fit for Korean/multilingual keys
pnpm dev
# or:
pnpm build
pnpm start

要件:

  • Node.js 20+
  • ローカル開発には pnpm
  • OpenAI の埋め込みには OpenAI API キー、ローカルの埋め込みには fastembed

特長

  • N:M キー/値グラフ — メモリと、それらを索引付けする概念は別々の空間にあり、多対多で関連付けられています。1つのメモリには複数のキーからアクセスでき、1つのキーから複数のメモリにたどり着けます。
  • エージェント主導のキー → メモリ → キーのナビゲーション — エージェントは、情報を不透明な単一の類似検索にまとめるのではなく、意図的にグラフをたどります。
  • 連想的なマルチホップ想起 — 共有キーの連鎖をたどることで、埋め込み距離では結び付かないメモリにも到達できます。
  • 確認に応じた鮮度管理 — 読み取りによって有用な経路は学習されますが、古い内容が正しいと認定されることはありません。明示的な根拠によって、4種類の減衰プロファイルに沿って事実が更新されます。
  • 1段階のバージョン管理 — 修正時には直前の内容が保持され、置き換えた対象が記録されます。
  • キーの種類 — concept キーは類似度で一致し、name/proper_noun キーは完全一致します。そのため、「동건」が短いというだけで「뉴턴」と一致することはありません。
  • 言語をまたいだキーの統合(IDF) — 파이썬 と Python は分断されたキー空間にならず、1つの正規クラスターにまとめられます。
  • ヘッブ型リンク学習 — エージェントが実際にたどった経路が強化され(「共に発火するものは共に結び付く」)、有用な連想に到達しやすくなります。
  • ハイブリッド検索(オプションのダイレクトモード)— キーとコンテンツに対する Kiwi BM25 + キー埋め込み + Reciprocal Rank Fusion を使用し、深度/確認鮮度による調整と、設定可能なマルチホップ展開を行います。
  • クロスエンコーダーによる再ランキング(コアのデフォルト)— bge-reranker-v2-m3 がパッシブ Top-1 プールと、互換性のあるダイレクトモード候補を再スコアリングします。
  • ローカルファースト — すべてのデータはローカルの JSON グラフに保存され、外部データベースは不要です。OpenAI または完全ローカルの埋め込みを使用します(自動ダウンロード)。
鮮度と深度

すべてのメモリには深度スコア 0.0 → 1.0 がありますが、検索と確認は別々に扱われます。

段階深度挙動
浅い< 0.3明示的な確認が少なく、ランキングのブーストは最小限です。
中程度0.3–0.7繰り返し確認され、ランキングが適度にブーストされます。
深い> 0.7強く確認されていますが、引き続き修正可能で、経過時間の影響も受けます。ランキングは最大限にブーストされます。

read_memory() はアクセスメタデータを増やし、たどったキーのエッジを強化する場合がありますが、 深度、last_confirmed_at、confirmation_count、鮮度は変更しません。有効性を更新し、 深度を +0.05 増やすのは confirm_memory(memory_id, evidence) だけです。受け入れられる 根拠は、ユーザーによる現在の状況についての明示的な表明、権威ある最新の情報源、または直接の 観察です。読み取りに成功しただけでは、確認とはみなされません。

鮮度は、メモリの decay_profile に従って last_confirmed_at から減衰します。

プロファイルデフォルトの半減期想定用途
transient7日一時的な計画や対応可否など、急速に変化する状態
standard90日一般的な事実。デフォルト
stable365日変化が緩やかな事実
permanent減衰なし意図的に不変または不朽とする事実

すべてのメモリビューには、freshness、status(fresh、aging、stale のいずれか)、 age_days、last_confirmed_at、confirmation_count、decay_profile、 verification_recommended、verification_required を含む validity ペイロードがあります。 fresh は鮮度が 0.5 以上、aging は 0.125 以上 0.5 未満、stale は 0.125 未満を 意味し、検証しない限り現在の事実として断言してはいけません。減衰はソフトに適用され、 0.2 + 0.8 × freshness によってランキングを下げますが、メモリを削除することはありません。 自動的な有効期限の仕組みは TTL のみです。

たとえば、remember(..., decay_profile:"transient") を使って一時的な計画を保存し、ユーザーが 現在も有効だと明示した後に、confirm_memory(memory_id, evidence:"user") を呼び出します。 read_memory がその計画を返したというだけで確認してはいけません。

キーの種類

すべてのキーを同じように扱うべきではありません。名前は意味的に似ていても一致させるべきではありません。「동건」と「뉴턴」は、どちらも短い韓国語の単語だからといって一致してはなりません。

種類マッチング用途
concept(デフォルト)埋め込みの類似度 ≥ 閾値(OpenAI は 0.28/ローカルは 0.60)トピック、カテゴリ、属性
name完全一致のみ人名
proper_noun完全一致のみブランド、場所

name/proper_noun キーが多数のメモリにつながるハブキーになると、IDF ペナルティ(×0.5)も適用され、無関係な検索に影響するのを防ぎます。

バージョン管理(直前のバージョンのみ)
"user lives in Seoul"   (depth: 0.4 → weakened to 0.12, preserved)
        ↑ superseded by
"user moved to Busan"   (depth: 0.0, new)

keymem は、上書きするのではなく直前のバージョンを保持します。置き換えられたレコードは、どれだけ深い位置にあるかにかかわらず、アクティブな検索対象から除外され、新しいバージョンが現行になります。後から修正が行われると、その前の世代は削除されるため、来歴は全履歴のアーカイブではなく、1 段階のみ保持されます。

キーの統合
Add key "파이썬"  → finds existing "Python" (similarity 0.87 > threshold 0.85)
                 → reuses existing key instead of creating duplicate

キー空間の分断を防ぎます。言語や言い回しが異なっていても、同じ概念は統一された状態で保持されます。

エージェント主導の検索(デフォルト)

デフォルトの MCP API は、明示的なグラフナビゲーションを維持しながら、キー空間から 1 つの値へと進みます。

  1. recall(query, context) は、スコア順に並べたキーのクラスターと、最も強いキーに属する Top-1 メモリを返します。生の context は、そのキー内のメモリをランキングします。
  2. メモリは、validity、matched_key、およびすべての connected_keys[].key_id を含む受動的なプレビューです。アクセス/深度を増やしたり、リンクを強化したり、エイリアスを学習したり、新しさを確認したりすることはありません。
  3. エージェントは、質問にどの事実が必要かを確認します。それらの事実が含まれていればプレビューを使用し、そうでなければ、不足している事実について read_key(key_id, missing_fact_query) で接続先のキーをたどるか、その事実を直接再検索します。証拠が揃うか、有望な経路がなくなると処理を終了します。後続のメモリが自動的に追加されることはありません。
  4. read_memory(memory_id, via_key_id) は、明示的な全文読み取りの手段として引き続き使用できます。アクセスメタデータを更新し、たどったエッジのみを強化します。メモリの確認や深度の増加は行いません。

意味的に統合されたキーは、1 つの正規キーのクラスター上でエイリアスとして保持されます(例:Python + 파이썬)。推奨される bge-m3 プロファイルでは、短いキーの保守的な統合がデフォルトで有効です。KEYMEM_SHORT_KEY_MERGE で上書きまたは無効化できます。アクティブなメモリが 3 件以上リンクされたキーは、IDF によって隠されるのではなく、is_hub、memory_count、specificity メタデータを持つハブとして表示されます。KEYMEM_KEY_HUB_MIN_LINKS でハブのしきい値を上書きできます。

連想検索エンジン(ライブラリ API、内部で recallInject が使用)

MemoryGraph.recall() は、recallInject() が使用する単一呼び出しのランキング検索手段で、ライブラリの呼び出し元も利用できます。相互順位融合(RRF_K = 60)を使って、2 つの候補検索経路を組み合わせます。

  • Kiwi BM25: Node で公式の kiwi-nlp WASM トークナイザーを使って、メモリのキーとコンテンツを対象に語彙検索を行います。韓国語の形態素解析はローカルでトークン化されるため、Python プロセスは不要です。
  • キーのマッチング: クエリの埋め込みが概念キーに一致し、完全一致する名前とエイリアスが直接の入口になります。キーリンクから候補メモリにたどり着きます。

メモリ本文には埋め込みがありません。クロスエンコーダーによる再ランキングは引き続き有効で、クエリとコンテンツのペアを直接スコアリングします。グラフの展開、深度、確認時の新しさは、引き続きランキングに影響します。デフォルトの MCP recall ツールは明示的なキーのナビゲーションを使用するため、エージェントは後続のホップを意図的に進められます。

ヘブ的リンク学習

メモリ全体を読むことは、単なる読み取りではなく書き込みです。デフォルトのフローでは、recall() と read_key() は読み取り専用です。read_memory(memory_id, via_key_id) は、選択された経路を変化させます。

  • たどった via_key_id → memory_id のリンクが強化されます(+0.1、上限は 3.0)。
  • 全文を読むとアクセスメタデータは更新されますが、深度も last_confirmed_at も変化しないため、鮮度は更新されません。経過時間が増えるにつれて、鮮度は引き続き低下します。根拠に基づく confirm_memory() は深度を高め、last_confirmed_at を進めることで鮮度を更新します。

強化の対象は、メモリに関連付けられたすべてのキーではなく、エージェントが実際にたどったキーに限定されます。これは文字どおりのヘッブ則(「ともに発火するものは、ともに結びつく」)であり、別の概念を経由してメモリに到達したときに、無関係な関連性が強まるのを防ぎます。重みは [0.1, 3.0] の範囲に制限されます。

リンクの重みは read_key() のランキングに反映されるため、繰り返し選択された経路ほど到達しやすくなります。recall()/recallInject() では、従来のマッチしたリンクの強化と、探索済みリンクの減衰の挙動が維持されます。


アーキテクチャ

│                      Key Space                          │
│   [name] [동건] [programming] [python] [fruit] [red]   │
│      ↓      ↓         ↓           ↓       ↓      ↓     │
│   [vec]  [exact]    [vec]       [vec]   [vec]  [vec]   │
                         │ N:M links
                         ↓
│                     Value Space                         │
│   "user's name is Donggeon"     depth: 0.85  (deep)    │
│   "user likes Python"           depth: 0.30  (medium)  │
│   "user likes strawberries"     depth: 0.05  (shallow) │

デフォルトの MCP ナビゲーション:

  1. クエリを埋め込み、正規化されたキー概念と完全一致するエイリアスを照合します。
  2. memory_count、is_hub、specificity を含むキーのクラスタを返します。メモリの内容は返しません。
  3. 選択したキーのメモリハンドルを、リンクの重み、深度、確認の鮮度に基づいて read_key() でランク付けします。
  4. read_memory() で全文と隣接するキーのクラスタを返します。via_key_id が指定された場合にのみ、たどったエッジを強化します。
  5. read_key(next_key_id) を繰り返し、意図的にグラフをたどります。

recall() アルゴリズム(ハイブリッド方式、1~5 ホップを設定可能、デフォルトは 2 ホップ、内部で recallInject() が使用):

  1. Kiwi でクエリとメモリのキー/内容をトークン化し、BM25 の候補を取得します。
  2. クエリの埋め込みおよび名前/エイリアスのリテラル一致で概念キーを照合し、それらのメモリリンクをたどります。
  3. RRF(RRF_K = 60)を使って、語彙的な順位とキー経路の順位を統合します。
  4. 深度と確認の鮮度を適用し、設定されたホップ数の範囲で、共有キーと明示的な related_to リンクをたどって展開します。
  5. クロスエンコーダーで候補を再ランク付けし、ランク順の結果を返します。マッチしたリンクの強化と、探索済みリンクの減衰は従来の挙動を維持します。
ストレージと重複の処理

埋め込みベクトルを保持するのはキーのみです。既存のメモリ本文ベクトルは読み込み時に無視され、次回の保存が成功した後は省略されます。キーのベクトル、メモリテキスト、リンク、履歴は保持されます。以前のリリースでストアを再度開く必要がある場合は、アップグレード前にバックアップしてください。

重複の検出では、既存の名前空間とアクティブメモリのルールの範囲内で、正規化されたメモリテキストを完全一致で比較します。言い換えは別のレコードとして扱われます。自動確認には、発話テキストと本文テキストが正規化後に完全一致する必要があります。本文ベクトルの類似性を理由に、新しいメモリにリンクや矛盾フラグが追加されることはありません。キーと明示的な関連性を指定し、事実が変わった場合は correct を使用してください。既存の明示的なリンクと記録済みの矛盾は引き続き読み取れます。

キーの類似度しきい値

埋め込みのしきい値は、メモリの本文ではなくキーに適用されます。src/embedding.ts の getThresholdProfile() がモデル固有のデフォルト値を提供します。キーのルーティングと正規化を調整するには、KEYMEM_KEY_RECALL、KEYMEM_KEY_MERGE、KEYMEM_KEY_AUTOLINK(キーの自動エイリアス昇格)、KEYMEM_SHORT_KEY_MERGE を使用してください。未調整のモデルでは、警告とともに BGE プロファイルがフォールバックとして使用されます。

従来の本文コサイン設定(KEYMEM_CONTENT_RECALL、KEYMEM_CONTENT_RECALL_SHORT、KEYMEM_MEMORY_DEDUP、KEYMEM_CONTRADICTION、KEYMEM_MIN_SCORE、KEYMEM_GATE_Z、KEYMEM_KEY_GATE)は、メモリの検索や書き込みを制御しなくなりました。BM25 スコア、キーのコサインスコア、RRF ランク、クロスエンコーダースコアはそれぞれ異なる尺度です。古い本文コサインしきい値を、語彙的な関連度のしきい値として再利用しないでください。

環境変数デフォルト値説明
KEYMEM_AUTOKEYtrue自動キーによる自己修復:実際の使用状況から不足している検索語を学習します。無効にするには false を設定します。
KEYMEM_AUTOKEY_PROMOTE_N3(key, query) のペアをルーティング確認済みの選択として扱い、クエリをキー空間に取り込むまでに必要な回数です。
KEYMEM_AUTOKEY_CONFIRM_FLOOR0.45ルーティング確認による学習の対象となる、query↔key のコサイン類似度の下限です。同じキーを介した選択が繰り返されると、しきい値を下回るクエリエイリアスも学習できます。確認されるのはルーティングのみで、コンテンツの鮮度は確認されません。境界付近の言い換えをより多く検出するには低く設定します(例:0.40)。無効にするには、リコールしきい値以上に設定します(≥)。
KEYMEM_AUTOKEY_MAX_ALIASES8キーごとに昇格できる、学習済みエイリアスの最大数です。
KEYMEM_AUTOKEY_PRUNE_AGE2592000一度もヒットしていない学習済みエイリアスが、デーモンの自動 TTL スイープによって削除されるまでの秒数です(30 日)。
KEYMEM_CLEANUP_INTERVAL_MS3600000 (1h)デーモンが期限切れのメモリやエイリアスを自動スイープする間隔です。stdio 経由では、スケジュール対象となる長時間実行ループがないため、起動時に一度だけスイープします。
KEYMEM_DECAY_TRANSIENT_DAYS7transient メモリの半減期(日数)です。有限の値かつゼロより大きい値を指定する必要があります。
KEYMEM_DECAY_STANDARD_DAYS90デフォルトの standard プロファイルの半減期(日数)です。有限の値かつゼロより大きい値を指定する必要があります。
KEYMEM_DECAY_STABLE_DAYS365stable メモリの半減期(日数)です。有限の値かつゼロより大きい値を指定する必要があります。

言語をまたいで検索するには、複数言語のキーとエイリアスを保持してください(例:딸기 と strawberry)。本文の語彙検索だけでは、クエリは翻訳されません。


MCP ツール

ツールセットには10個のツールがあります。

ツール説明
recall(query, top_k?, namespace?, context?, explain?)ランク付けされたキーと、validity、matched_key、connected_keysを含む受動的なTop-1メモリを返します。explain:trueを指定すると、ネームスペースごとの件数も返します。
browse_keys(namespace, hubs_only?, limit?, offset?)recallで該当するエントリーが見つからなかった場合に、アクティブなキーの語彙をネームスペース内で参照します。ハブが先に表示されます。
read_key(key_id, query?, namespace?, limit?, offset?)1つのキーに関連付けられたメモリIDとメタデータを、ランク順で一覧表示します。関連性順に並べるには元のクエリを渡します。ハブのページネーションに対応しています。
read_memory(memory_id, via_key_id?, namespace?)メモリの全文、関連付けられたキー、validityを読み取ります。アクセス状況と、via_key_idが指定されている場合は、経由したエッジを更新します。内容の確認や深掘りは行いません。
confirm_memory(memory_id, evidence, namespace?, source?)ユーザーからの明示的な根拠、信頼できる情報源、または直接観察に基づいて、現在のメモリの鮮度を更新し、情報を深めます。読み取っただけでは根拠になりません。
remember(content, keys, key_types?, namespace?, ttl_seconds?, decay_profile?, related_to?)キー概念とともにメモリを保存します。TTLと、transient、standard、stable、permanentの減衰プロファイルは任意で指定できます。
correct(memory_id, content, keys?, key_types?, ttl_seconds?, decay_profile?, related_to?)バージョン管理された更新を行います。直前のバージョンは保持されますが、非アクティブになります。省略されたTTLやプロファイルは、直前のバージョンから引き継がれます。
dismiss(memory_id, key_id, namespace?)否定的なフィードバックです。事実は正しいものの、このキーから表示されるべきではなかったことを伝えます。そのエッジのみを弱め(最低値は維持し、切断はしません)、保留中の別名学習を取り消します。
forget(memory_id)永続的に削除します
remember_batch(items)複数のメモリを保存します。各項目ではttl_secondsとdecay_profileを指定できます。

TTLの期限が切れたメモリと、キー/メモリ/リンクの件数は、エージェント向けツールではなくなりました。デーモンが期限切れのメモリを自動的に削除します(起動時に1回、その後はKEYMEM_CLEANUP_INTERVAL_MSごとに実行します。stdio経由の場合は起動時に1回実行します)。キー/メモリ/リンクの統計情報は毎ターンSERVER_INSTRUCTIONSにすでに受動的に挿入されるため、別途memory_stats()を呼び出すのは完全な重複でした。

スコアにはscore_kindが含まれます。キーの検索では、キーのコサイン類似度または上限付きの語彙的関連度に基づくkey_relevanceが公開されます。content_relevanceは上限付きBM25(s / (1 + s))であり、コサイン類似度ではありません。read_keyはwithin_key_rankを使用し、クロスエンコーダーによる関連度が利用できる場合はそれを、利用できない場合は上限付きの語彙的関連度を使用します。直接検索ではrrf_fused_rankを使用します。RRFのrank_scoreは順位を融合した値であり、類似度の確率ではありません。

memory_system_prompt MCPプロンプトからシステムプロンプトのテンプレートも利用できます。これを含めると、エージェントに、ユーザーに知らせずにメモリを検索すること、多様なキーを使うこと、メモリシステムについてユーザーに決して言及しないことを指示できます。


ローカル埋め込みモデル

BGE-M3(推奨、多言語対応) — 自動ダウンロード: LOCAL_EMBEDDING_MODEL=bge-m3を設定します(別名:bgem3、baai/bge-m3、fast-bge-m3)。初回使用時にモデルが見つからない場合は自動的に取得されます。量子化ONNX(onnx-community/bge-m3-ONNXから約570MB)と、トークナイザー/設定(BAAI/bge-m3から)がダウンロードされ、~/.keymem/models/bge-m3にキャッシュされます。手動でダウンロードする必要はありません。

EMBEDDING_BACKEND=local
LOCAL_EMBEDDING_MODEL=bge-m3
# optional — point at an existing model dir to skip the download (backward compatible):
# LOCAL_EMBEDDING_MODEL_PATH=/absolute/path/to/model-dir   # dir with model.onnx + tokenizer files
# LOCAL_EMBEDDING_MODEL_FILE=model.onnx                    # optional; default is model.onnx
# optional: KEYMEM_EMBED_THREADS=4                         # ONNX intra-op threads (default: a quarter of the machine, max 6)

初回実行時に約570MBが一度だけダウンロードされ、その後はキャッシュが再利用されます。LOCAL_EMBEDDING_MODEL_PATHにモデルがすでにある場合は、そのまま使用され、ダウンロードは行われません(不完全なディレクトリは自動修復され、不足しているファイルのみ取得されます)。オンラインAPIバックエンド(OpenAI)とfastembedの組み込みモデルには影響しません。

**bge-m3はfastembed経由ではなく、独自のONNXセッションで動作します。**入力には実際のトークン長が使用されます。そのベクトル空間のフィンガープリントはlocal:bge-m3+nopadです。フィンガープリントが変わるとキーのベクトルが移行されます。BENCHMARKS.mdに記載されている以前のパディング/レイテンシと検索の測定値は、旧ボディ埋め込みエンジンに関するものです。 クロスエンコーダーによる再ランキング(コア機能): デフォルトの recall MCP ツールでは、Top-1 の経路で最も強いキーに基づき、bge-reranker-v2-m3 を使って候補プールを再スコアリングします。recall()/recallInject() の結果も再ランキングされます。このモデル(量子化済み、約570MB)は初回使用時に自動ダウンロードされ、~/.keymem/models/reranker にキャッシュされます。

# optional: KEYMEM_RERANK=false              # disable the core reranker
# optional: KEYMEM_RERANK_MODEL_PATH=/dir    # use an existing model directory
# optional: KEYMEM_RERANK_POOL=30            # candidates re-scored (default 30)
# optional: KEYMEM_RERANK_THREADS=4          # ONNX intra-op threads (default: a quarter of the machine, max 6)

デフォルトで有効です。モデルを読み込めない場合、recall は統合ランキングにフォールバックします。クエリの分解は引き続き呼び出し元の責任です。

タスク条件付きのエビデンス選択: 拡張 recall は引き続き連想検索を行いますが、比較形式のクエリは最終的なエビデンス選択の段階で投影され、指定された両方のエンティティがタスクに関連する情報を得られるようになります。KeyMem はエンティティ名を取り除いてから比較の観点(たとえば nationality や founded)を導き出し、グラフの候補プールと関連度スコアをそのまま維持するとともに、強化処理のため、元の連想検索で選ばれた結果も保持します。デフォルトで有効です。KEYMEM_TASK_EVIDENCE_SELECTION=false で無効にできます。

再ランキングの未検出ゲート(KEYMEM_RERANK_MIN_SCORE): recall()/recallInject() で、クロスエンコーダーの最上位ロジットがこの下限を下回った場合、結果全体を拒否します。

KEYMEM_RERANK_MIN_SCORE=0   # reject when top rerank logit < 0 (bge-reranker-v2-m3 scale)

⚠️ 注意点。 (1)デフォルトでは未設定のため、ゲートは適用されません。(2)ロジットのスケールはモデルによって異なります。bge-reranker-v2-m3 には 0(約 sigmoid 0.5)が適しています(測定値:同一言語で見つかった場合は約 +3.9、見つからない場合は約 −5~−6)。ほかの再ランキングモデルでは再調整してください。(3)同一言語の場合のみ信頼できます。 関連性がある場合でも、言語間の関連性ロジットは低くなります(KR のクエリ ↔ EN のメモリでは約 −5.4)。そのため、言語間のヒットを誤って拒否しないよう、スクリプトが一致しない場合(KR↔Latin)はゲートが自動的にバイパスされます。つまり、言語間のコンテンツにはバイリンガルキー(["Jiwoo","지우"])からアクセスできる必要があり、言語間での「見つからない」ケースの精度には既知の制限があります。バイリンガルキーを付けられない場合は、この機能を無効にしてください。

プレフィックスの動作: BGE-M3 では passage:/query: プレフィックスを使用しません。埋め込みはそのまま渡されます。その他すべてのローカルモデル(e5、BGE-en、MiniLM)では、従来どおりプレフィックスが使用されます。

多言語キーの埋め込み: bge-m3 は引き続きサポートされています。過去のモデル比較では、現在の Kiwi BM25 アーキテクチャの品質や未検出時の動作は確認できていません。

OPENAI_API_KEY が設定されておらず、EMBEDDING_BACKEND も未設定の場合、サーバーはローカルの fastembed バックエンドを自動的に使用します。 英語のみでの使用やローカルリソースの使用量を抑えたい場合は、LOCAL_EMBEDDING_MODEL=fast-bge-base-en-v1.5 または LOCAL_EMBEDDING_MODEL=fast-bge-small-en-v1.5 を設定してください。

バックエンドの切り替えは安全です。 グラフには、埋め込みが属するベクトル空間を識別する埋め込みのフィンガープリント(バックエンド+モデル ID)が記録されます。起動時に、現在のバックエンドのフィンガープリントまたは次元数が保存済みの値と異なる場合、グラフは自動移行されます。すべてのキーが新しいバックエンドで再埋め込みされ、内容、リンク、深さ、アクセス履歴は保持されます(最初に graph.json.bak.* のバックアップが作成されます)。フィンガープリントが重要なのは、次元数が同じでもモデルによって互換性のないベクトルが生成されることがあるためです(例:fast-multilingual-e5-large と bge-m3 はどちらも1024次元です)。次元数だけを確認していると、この切り替えを検出できず、すべての類似度が気付かないうちに壊れてしまいます。KEYMEM_AUTO_MIGRATE=false で無効にできます。OpenAI を使った再埋め込みには、キー数に応じた一度限りの API 費用が発生します。

フィンガープリント導入前の(レガシー)グラフを、同じ次元数のモデル間で移行する場合。 フィンガープリント導入前に作成されたグラフにはベクトル空間の記録がないため、同じ次元数のモデルへの切り替えは自動検出できません。再埋め込みを無条件で実行し、フィンガープリントを記録するには、起動時に一度だけ KEYMEM_FORCE_REEMBED=true を設定してください。その後は設定を削除してください(設定したままにすると、起動のたびに再埋め込みされます)。既存の e5 グラフを bge-m3 に移行する場合に必要なのは、まさにこの一度限りの処理です。


データストレージ

すべてのデータはローカルに保存されます。外部データベースは不要です。

~/.keymem/
└── graph.json              # canonical keys, aliases, memories, weighted links

別の保存先ディレクトリを使用するには、KEYMEM_DATA_DIR を設定してください。

メモリを元の会話にリンクする。 メモリを保存すると、keymem は出所を記録するため、アクティブなホストセッションを source(host_session / host_agent / host_turn)に記録します。keymem は会話の記録を読み取るツールを公開していません。アクティブなセッションを特定するために、内部で読み取るだけです。保存場所は OS ごとに自動検出され、エージェントの環境変数による上書きが適用されます。

  • Claude Code — ~/.claude/projects/**/{session_id}.jsonl($CLAUDE_CONFIG_DIR)
  • Codex — ~/.codex/sessions/**/rollout-*-{session_id}.jsonl($CODEX_HOME)

アクセスには制限があります。 会話の記録はローカルに保存された、機密情報を含む可能性のある履歴であるため、keymem は、所有者個人のローカルエージェントとして信頼できる場合に限り、出所情報を記録するために読み取ります。つまり、認識されたホストがセッション環境変数(CLAUDE_CODE_SESSION_ID / CODEX_THREAD_ID)を注入した場合、または KEYMEM_TRANSCRIPT_ACCESS=true を明示的に設定した場合です。それ以外の場合(通常のサーバー、リモートデプロイ、所有者以外のエージェントやカスタムエージェント)には、メモリはホストへのリンクなしで保存されます。ホストエージェントの実行時も含めてアクセスを強制的に無効にするには、KEYMEM_TRANSCRIPT_ACCESS=false を設定してください。

アクティブなセッションは、次の 2 つの方法で特定されます。

  1. 確定的な方法 — ホストは起動するすべての MCP サーバーにセッション ID を注入し、keymem はそれを直接読み取ります。Claude Code では CLAUDE_CODE_SESSION_ID、Codex では CODEX_THREAD_ID(ロールアウトファイルのセッション ID と同じ)を使用します。推測なしで正確にリンクされます。
  2. ヒューリスティックなフォールバック — セッション ID を公開しないホスト(例:Claude Desktop)では、keymem は最終更新日時が最も新しい会話記録を使用します(古さを判定するガード付き)。

ベンチマーク

以前の本文埋め込みエンジンで測定した過去の結果:HotpotQA のブリッジ質問(実際の外部マルチホップデータ、正解ラベル付き、LLM による判定なし。さらに、質問や回答を一切見ていない独立したサブエージェントが、ブラインドでキーを生成)では、キーグラフは正解の根拠段落両方を 63% の割合で取得します。フラットなセマンティック検索では 53%、字句検索では 35% です(それぞれ +10 ポイント / +28 ポイント)。これは、キーグラフが対応するよう設計された、つながりはあるものの類似性の低いケースです。(正直に言えば、私自身が手作業で導出したキーを使うと 78% / 60% に上がりました。「比較」のようなマルチホップでない質問では、グラフはわずかに悪化します。これは検索再現率の指標であり、回答精度ではありません。)読み取りパスも O(1) です(read_memory の p50 は約 45 ms から、メモリ 500 件で約 0.01 ms に短縮)。詳しい手法と注意事項については、BENCHMARKS.md を参照してください。これらのスコアは、現在の Kiwi BM25 + キー埋め込みエンジンの性能を検証するものではありません。このエンジンについては、本番環境での品質スコアは確立されていません。


制限事項

  • 線形スキャン — 個人利用(メモリ約 10,000 件)に適しています。より大規模な利用に向けて、FAISS/ChromaDB との統合を予定しています。
  • 後続ホップの往復 — recall は最初の Key → Memory のホップを完了しますが、つながりのあるキーをたどって別のメモリに進むには、引き続き read_key → read_memory が必要です。
  • ハブの広がり — 幅広いキーは多くのメモリにつながることがあります。read_key() はハブをページ分割して返します。エージェントは、ページをさらに取得するか、隣接するより具体的なキーをたどるかを選ぶ必要があります。
  • エージェントの品質が重要 — remember でのキー選択は検索品質に影響します。システムプロンプトの調整が重要です。
  • 字句検索のカバレッジ — Kiwi BM25 は、それ自体では翻訳も言い換えの検出も行いません。多言語キー、キー埋め込み、再ランキングは候補の振り分けや順位付けに役立ちますが、見落とされた候補の再発見を保証するものではありません。
  • しきい値の調整 — しきい値は埋め込みモデルごとに調整されています。新しいモデルや未調整のモデルは、BGE プロファイルにフォールバックします(警告が表示されます)。KEYMEM_* 環境変数による上書きで再調整してください。

テスト

pnpm test                        # unit tests (fast, no model download)
tsx test/scenarios.ts            # 21 end-to-end behavioral checks (local e5)
tsx test/robustness.ts           # threshold overrides + Hebbian pollution bounds
tsx test/migration.ts            # backend/dimension switch auto-migration (no brick)
tsx test/nhop.ts                 # N-hop chained traversal (recall hops parameter)
tsx test/depth-noise.ts          # deep-hop noise bounds + relative score floor
tsx test/live-multilingual.ts    # interactive multilingual recall demo

# Manual retriever-quality check (NOT part of pnpm test):
EMBEDDING_BACKEND=local npx tsx test/retriever-quality.live.ts
# For bge-m3: also set LOCAL_EMBEDDING_MODEL=bge-m3 LOCAL_EMBEDDING_MODEL_PATH=/abs/dir

scenarios.ts と robustness.ts は、実際のローカル埋め込みバックエンドをテストします(直接的・連想的・多言語間の想起、バージョン管理、深さの増加、重複排除、TTL、ヘブ学習、名前空間の分離)。キー閾値の再調整と検索品質の検証には、エンジンまたはモデルの変更後に新たな実行が必要です。過去にテストが成功した結果は、このアーキテクチャを検証するものではありません。


ロードマップ

  • スケール対応のための FAISS/ChromaDB
  • コーディングエージェントのプロファイル(コードコンテキスト向けの異なるキー戦略)
  • メモリのエクスポート/インポート
  • マルチユーザー対応

作者

donggyun112 — github.com/donggyun112

リポジトリ:donggyun112/keymem · Issue や PR を歓迎します。

ライセンス

MIT © donggyun112

コメント

MCP は初めてですか?

プロトコルの概要から最初のサーバーの導入まで、よくある疑問にまとめて答えます。

AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。