Skip to main content
Glama

Two-Tower Recsys MCP — нейросетевой поиск, доступный через MCP

Глубокая модель рекомендаций с двумя башнями, обученная на собственном корпусе отзывов Amazon 2023 года, развернутая как сервер инструментов MCP (Model Context Protocol), с чат-интерфейсом на Streamlit, который позволяет агенту Gemini вызывать эти инструменты от вашего имени.

Этот README описывает весь конвейер от начала до конца: что представляет собой модель, как она обучалась, насколько хорошо она реально работает (измерено, а не оценено), как MCP-сервер ее предоставляет и как запустить или развернуть фронтенд.


1. Что это такое

Модели с двумя башнями — стандартная архитектура для крупномасштабных промышленных рекомендательных систем (этот паттерн — отдельные «башни», которые встраивают пользователя и товар в одно и то же векторное пространство, обучаясь так, чтобы релевантные пары оказывались близко друг к другу, — используется в продакшене YouTube, Pinterest и собственных системах поиска Amazon). Этот проект реализует такую модель с нуля, обучает ее на реальных данных взаимодействий Amazon и оборачивает для использования агентами через MCP вместо типичного REST API.

Почему MCP вместо REST API? MCP — это протокол, который Anthropic представила для подключения LLM-агентов к инструментам и данным. Обертывание обученной модели в виде MCP-инструментов (а не, скажем, конечной точки Flask) означает, что любой MCP-совместимый агент — Claude Desktop, собственный фронтенд этого проекта на Streamlit+Gemini или любой другой MCP-клиент — может напрямую вызывать recommend_for_user, similar_items и т.д., при этом LLM сама решает, когда и как их вызывать, на основе естественного языка.

Related MCP server: consulting-mcp-server

2. Архитектура

  • Башня пользователя: изученное встраивание ID пользователя (64-мерное) → 2-слойный MLP → 64-мерный выход.

  • Башня товара: изученное встраивание ID товара (64-мерное), объединенное с замороженным встраиванием предложения all-MiniLM-L6-v2 из названия товара (384-мерное, проецируемое в 64-мерное) → 2-слойный MLP → 64-мерный выход. Замороженное текстовое встраивание дает модели возможность холодного старта — она может разумно разместить товар в векторном пространстве даже без истории взаимодействий, только на основе его названия.

  • Обе башни выдают L2-нормализованные векторы; сходство — это скалярное произведение (эквивалентно косинусному сходству).

  • Функция потерь при обучении: внутрипакетный семплированный softmax — для пакета из B положительных пар (пользователь, товар) каждый другой товар в пакете выступает отрицательным для каждого пользователя, и применяется кросс-энтропия к полученной матрице сходства B×B. Это стандартный, вычислительно эффективный способ обучения башен поиска без явного отрицательного сэмплирования.

  • Сервинг: встраивания товаров предвычисляются один раз и индексируются в FAISS (IndexFlatIP) для быстрого поиска ближайших соседей. Второй индекс FAISS, построенный на сырых (необученных) встраиваниях MiniLM из названий, обеспечивает текстовый поиск с холодным стартом, который работает независимо от обученного коллаборативного сигнала.

        ┌────────────┐                          ┌────────────┐
        │  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 (лаборатория McAuley, UCSD), категория Video_Games — сырые отзывы + метаданные товаров, загруженные напрямую с HuggingFace.

Шаг

Количество

Сырые отзывы

4 624 615

Сырые пользователи / товары

2 766 656 / 137 249

После 5-core фильтрации (пользователи и товары с ≥5 взаимодействиями)

857 505 взаимодействий

Пользователи / товары (после фильтрации)

98 906 / 26 354

Взаимодействия Train / Valid / Test

659 693 / 98 906 / 98 906

Протокол разбиения — leave-last-two-out для каждого пользователя, отсортировано по времени: самое последнее взаимодействие каждого пользователя → тест, второе с конца → валидация, остальные → обучение. Это временное разбиение, поэтому модель оценивается на предсказании действительно будущего поведения относительно того, на чем она обучалась, а не на случайно удержанных взаимодействиях (что привело бы к утечке будущей информации в обучение и завышению показателей).

4. Оценка (реальные, измеренные числа)

Оценка использует ранжирование по полному каталогу — каждый кандидат оценивается по всем 26 354 товарам, а не по небольшой выборке отрицательных примеров. Оценка с семплированными отрицательными примерами (распространенная в старых статьях по RecSys, например, ранжирование только по 99 случайным отрицательным) известна тем, что существенно завышает офлайн-метрики, поэтому здесь используется более сложный и честный протокол. Уже просмотренные пользователем товары исключаются из его собственного ранжирования кандидатов.

Тестовый набор — 98 906 пользователей, у каждого удержанное финальное взаимодействие:

Метрика

Значение

Recall@10

1.40%

NDCG@10

0.70%

HitRate@10

1.40% (идентично Recall@10 при leave-one-out: ровно один релевантный товар на пользователя)

Для контекста: случайный шанс в каталоге из 26 354 товаров при k=10 составляет 10/26 354 = 0.038%. Обученная модель примерно в 37 раз лучше случайной при ранжировании по полному каталогу.

Валидационный Recall@10 достиг пика 2.43% (эпоха 142/150) во время обучения — тестовое число ниже, потому что тестовое взаимодействие — это самое дальнее взаимодействие каждого пользователя в будущее относительно его обучающей истории, что по своей сути является более сложным предсказанием. Этот разрыв — ожидаемое поведение для временного разбиения, а не ошибка. Тестовое число (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: он запускает 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 конфликтуют из-за инициализации OpenMP runtime на macOS, что вызывает segfault при вызовах поиска FAISS, если torch/numpy не импортированы до faiss, с установленными KMP_DUPLICATE_LIB_OK=TRUE и OMP_NUM_THREADS=1. Оба уже обработаны внутри 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/ (~110 МБ: обученная контрольная точка + индексы FAISS) включен в коммит, чтобы развернутому приложению не нужно было переобучаться при каждом холодном старте. data/raw/ (~2.9 ГБ сырых загрузок с HuggingFace) находится в .gitignore и нужен только если вы хотите воспроизвести обучение с нуля.

9. Технологический стек

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

F
license - not found
Not graded
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    A pluggable, observable modular RAG service framework that exposes tool interfaces via the MCP protocol, enabling AI assistants like Copilot and Claude to directly invoke knowledge retrieval and reasoning capabilities.
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Exposes RAG and document intelligence pipelines as 8 composable tools for MCP-compatible clients, enabling querying, indexing, classifying, extracting, and assessing documents.
    1
    MIT
  • 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
    Enables AI assistants to query documents in a Bedrock Knowledge Base through the MCP protocol, with tools for semantic search and agentic retrieval.
    MIT

View all related MCP servers

Related MCP Connectors

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/shreyaschhabra/two-tower-recsys-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server