Skip to main content
Glama

Two-Tower Recsys MCP — ニューラル検索をMCPで提供

Amazonの2023年レビューコーパスで学習したディープラーニングのツータワー推薦モデルを、MCP(Model Context Protocol)ツールサーバーとして提供し、Geminiエージェントがあなたの代わりにそれらのツールを呼び出せるStreamlitチャットフロントエンドを備えています。

このREADMEでは、パイプライン全体をエンドツーエンドで説明します。モデルが何か、どのように学習されたか、実際の性能(推定ではなく測定値)、MCPサーバーがどのように公開するか、フロントエンドの実行方法やデプロイ方法についてです。


1. これは何か

ツータワーモデルは、大規模な産業用レコメンダーシステムの背後にある標準的なアーキテクチャです(このパターン — ユーザーとアイテムを同じベクトル空間に埋め込む別々の「タワー」を持ち、関連するペアが近くに配置されるように学習する — は、YouTube、Pinterest、Amazon自身の検索システムでも本番で使用されているのと同じ形状です)。このプロジェクトは、それをゼロから実装し、実際のAmazonのインタラクションデータで学習し、典型的なREST APIではなくMCPを介してエージェントが使用できるようにラップしています。

REST APIではなくMCPを使う理由は? MCPは、AnthropicがLLMエージェントをツールやデータに接続するために導入したプロトコルです。学習済みモデルを(たとえばFlaskエンドポイントではなく)MCPツールとしてラップすることで、MCP互換のエージェント — Claude Desktop、このプロジェクト自身のStreamlit+Geminiフロントエンド、その他のMCPクライアント — がrecommend_for_user、similar_itemsなどを直接呼び出せ、LLMが自然言語に基づいてそれらをいつどのように呼び出すかを決定します。

Related MCP server: Modular RAG MCP Server

2. アーキテクチャ

  • ユーザータワー: 学習されたユーザーID埋め込み(64次元)→ 2層MLP → 64次元出力。

  • アイテムタワー: 学習されたアイテムID埋め込み(64次元)に、製品タイトルの凍結されたall-MiniLM-L6-v2文埋め込み(384次元、64次元に射影)を連結したもの → 2層MLP → 64次元出力。凍結されたテキスト埋め込みにより、コールドスタート機能が得られます — インタラクション履歴がゼロでも、タイトルだけでアイテムをベクトル空間に適切に配置できます。

  • 両方のタワーはL2正規化されたベクトルを出力し、類似度はドット積(コサイン類似度と同等)です。

  • 学習損失: バッチ内サンプリングソフトマックス — B個の(ユーザー、アイテム)正のペアのバッチに対して、バッチ内の他のすべてのアイテムが各ユーザーの負例として機能し、結果のB×B類似度行列にクロスエントロピーが適用されます。これは、明示的な負例サンプリングなしで検索タワーを学習するための標準的で計算効率の良い方法です。

  • サービング: アイテム埋め込みは一度事前計算され、FAISS(IndexFlatIP)にインデックス化され、高速な最近傍検索を実現します。生の(未学習の)MiniLMタイトル埋め込み上に構築された2番目のFAISSインデックスにより、学習された協調シグナルとは独立して機能するコールドスタートテキスト検索が可能になります。

        ┌────────────┐                          ┌────────────┐
        │  User ID   │                          │  Item ID   │
        └─────┬──────┘                          └─────┬──────┘
              │ embed(64)                              │ embed(64)
              ▼                                         ▼
        ┌────────────┐                    ┌──────────────────────────┐
        │  MLP (128) │                    │  Item title → MiniLM(384) │
        └─────┬──────┘                    └─────────────┬─────────────┘
              │                                          │ project(64)
              │                                          ▼
              │                                   concat(128) → MLP(128)
              ▼                                          ▼
        user vector (64, L2-norm)          item vector (64, L2-norm)
              └──────────────┬───────────────────────────┘
                              ▼
                    dot product = relevance score

3. データセット

McAuley-Lab/Amazon-Reviews-2023 (UCサンディエゴMcAuley Lab)、Video_Gamesカテゴリ — 生のレビュー+アイテムメタデータ、HuggingFaceから直接ダウンロード。

ステップ

件数

生のレビュー

4,624,615

生のユーザー / アイテム

2,766,656 / 137,249

5コアフィルタリング後(ユーザーとアイテムがそれぞれ≥5インタラクション)

857,505 インタラクション

ユーザー / アイテム(フィルタリング後)

98,906 / 26,354

トレイン / バリデーション / テスト インタラクション

659,693 / 98,906 / 98,906

分割プロトコル — ユーザーごとに最後の2つを除外し、タイムスタンプでソート: 各ユーザーの最新のインタラクション → テスト、2番目に新しい → バリデーション、残り → トレイン。これは時間的分割であり、モデルは学習したものに対して本当に将来の行動を予測するかどうかで評価されます。ランダムに保持されたインタラクション(将来の情報を学習に漏らし、数値を膨らませる)ではありません。

4. 評価(実際の測定値)

評価はフルカタログランキングを使用します — すべての候補が全26,354アイテムに対してスコアリングされ、少数のサンプリングされた負例のサブセットではありません。サンプリングされた負例による評価(古いRecSys論文で一般的、例えば99個のランダムな負例のみに対するランキング)は、オフラインメトリクスを大幅に膨らませることが知られているため、これはより厳しく、より正直なプロトコルです。各ユーザーの既に見たアイテムは、自身の候補ランキングから除外されます。

テストセット — 98,906ユーザー、各ユーザーの保持された最終インタラクション:

メトリック

値

Recall@10

1.40%

NDCG@10

0.70%

HitRate@10

1.40%(leave-one-outではRecall@10と同一: ユーザーごとに正解アイテムがちょうど1つ)

文脈として: 26,354アイテムのカタログでk=10のランダムチャンスは10/26,354 = 0.038%です。学習済みモデルは、フルカタログランキングでランダムより約37倍優れています。

バリデーションのRecall@10はトレーニング中(エポック142/150)に2.43%でピークに達しました。テストの数値が低いのは、テストインタラクションが各ユーザーのトレーニング履歴から見て最も遠い将来のインタラクションであり、本質的に予測が難しいためです。そのギャップは時間的分割の期待される動作であり、バグではありません。テストの数値(1.40%)がどこでも引用されるべきものです — バリデーションはトレーニング中に最良のチェックポイントを選ぶためだけに使用されたため、最終結果として報告することはチェリーピッキングの一種になります。

完全なトレーニング曲線: models/train_history.csv。 生の結果: models/test_results.json。

5. MCPツール(mcp_server.py)

ツール

説明

recommend_for_user(user_id, k)

トップkのパーソナライズされた推薦。ユーザーが既にインタラクションしたアイテムは除外します

similar_items(item_id, k)

学習済みアイテムタワー埋め込みによるアイテム間類似度

search_items(query_text, k)

アイテムタイトルに対するコールドスタート意味検索(MiniLMのみ — 協調モデルが弱いシグナルしか持たないアイテムに対して機能します)

explain_recommendation(user_id, item_id)

類似度スコアと、ターゲットに最も類似したユーザーの過去のアイテムを解釈可能性のために提供します

6. フロントエンド(streamlit_app.py)

weather-mcp-serverと同じスタイルのチャットUIです。MCPサーバーをstdio上のサブプロセスとして起動し、ツールスキーマを取得し、Geminiの関数呼び出し宣言に変換し、エージェントループを実行します — Geminiがメッセージに基づいて4つのツールのどれを呼び出すか(もしあれば)を決定し、ツールは実際の学習済みモデルに対して実行され、結果は最終的な自然言語応答のためにフィードバックされます。サイドバーには各ツールの説明と、学習済みカタログの実際のIDを使用したワンクリックの例、およびモデルの評価統計を含む展開可能なセクションが表示されます。

7. ローカルでの実行

uv venv --python 3.11 .venv
uv pip install -p .venv/bin/python -r requirements.txt

# one-time: reproduce the trained model from scratch
.venv/bin/python src/data_prep.py                  # downloads + filters the dataset
.venv/bin/python src/precompute_text_embeddings.py
.venv/bin/python src/train.py                       # ~150 epochs, ~40s/epoch on an M2 CPU
.venv/bin/python src/evaluate.py                    # writes models/test_results.json
.venv/bin/python src/build_index.py                 # builds FAISS indices for serving

# run the MCP server standalone (stdio transport)
.venv/bin/python mcp_server.py

# or run the chat frontend (spawns the MCP server itself)
cp .streamlit/secrets.toml.example .streamlit/secrets.toml   # then fill in your key
.venv/bin/streamlit run streamlit_app.py

.streamlit/secrets.toml(またはGEMINI_API_KEY環境変数)が設定されていない場合、アプリは実行時にサイドバーでキーを要求するフォールバックを行います。

macOSに関する注意

faissとtorchはmacOSでOpenMPランタイムの初期化に関して競合し、torch/numpyがfaissの前にインポートされ、KMP_DUPLICATE_LIB_OK=TRUEとOMP_NUM_THREADS=1が設定されていない限り、FAISS検索呼び出しがセグメンテーションフォルトを引き起こします。両方ともmcp_server.pyとsrc/build_index.py内で既に処理されています。

8. Streamlit Community Cloudへのデプロイ

  1. このリポジトリをGitHubにプッシュします(公開または非公開 — Community Cloudは個人アカウントのどちらでもデプロイできます)。

  2. share.streamlit.ioに移動し、New appをクリックして、このリポジトリをstreamlit_app.pyをエントリポイントとして指定します。

  3. アプリのSettings → Secretsで、以下を追加します:

    GEMINI_API_KEY = "your_gemini_api_key_here"

    これはローカルで.streamlit/secrets.tomlが使用するのと同じメカニズムです — キーはStreamlitのシークレットストアにのみ存在し、リポジトリやgit履歴には決して含まれず、アプリが自動的に読み取るため、訪問者がキーを入力する必要はありません。

  4. デプロイします。初回起動は(約1〜2分)MiniLMモデルのダウンロードとFAISSインデックスの読み込みのため遅くなります。以降の読み込みは高速です。

リポジトリサイズに関する注意: models/(約110MB: 学習済みチェックポイント+FAISSインデックス)は、デプロイされたアプリがコールドスタートごとに再学習する必要がないようにコミットされています。data/raw/(約2.9GBの生のHuggingFaceダウンロード)はgitignoreされており、ゼロからトレーニングを再現したい場合にのみ必要です。

9. 技術スタック

Python、PyTorch、FAISS、Sentence-Transformers(MiniLM)、FastMCP、MCP Python SDK、Google Gemini API、Streamlit、pandas、HuggingFace datasets/huggingface_hub。

Related MCP Connectors

Related MCP Servers

  • F
    license
    Not graded
    quality
    C
    maintenance
    Provides RAG-based knowledge retrieval and document management as MCP tools, supporting hybrid search, reranking, and retrieval process visualization.
    -
  • A
    license
    Not graded
    quality
    C
    maintenance
    A modular RAG framework exposing knowledge retrieval tools via MCP, enabling AI assistants to perform hybrid search, reranking, and multimodal document queries with full observability and evaluation.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables agents to turn documents into vectors, perform approximate nearest neighbor search with HNSW, and filter by metadata via MCP tools for RAG workflows.
    MIT