two-tower-recsys-mcp
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 score3. Набор данных
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)
Инструмент | Описание |
| Топ-k персонализированных рекомендаций, исключает товары, с которыми пользователь уже взаимодействовал |
| Сходство товар-товар через обученные встраивания башни товаров |
| Семантический поиск с холодным стартом по названиям товаров (только MiniLM — работает для товаров, по которым у коллаборативной модели слабый сигнал) |
| Оценка сходства плюс прошлые товары пользователя, наиболее похожие на целевой, для интерпретируемости |
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
Запушьте этот репозиторий на GitHub (публичный или приватный — Community Cloud может развернуть оба для личного аккаунта).
Перейдите на share.streamlit.io, нажмите New app и укажите этот репозиторий с
streamlit_app.pyв качестве точки входа.В Settings → Secrets приложения добавьте:
GEMINI_API_KEY = "your_gemini_api_key_here"Это тот же механизм, что и
.streamlit/secrets.tomlлокально — ключ хранится только в хранилище секретов Streamlit, никогда в репозитории или истории git, и приложение читает его автоматически, так что посетителям никогда не нужно вводить ключ.Разверните. Первый запуск будет медленным (~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.
This server cannot be installed
Maintenance
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
- AlicenseNot gradedqualityCmaintenanceA 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
- AlicenseNot gradedqualityBmaintenanceExposes RAG and document intelligence pipelines as 8 composable tools for MCP-compatible clients, enabling querying, indexing, classifying, extracting, and assessing documents.1MIT
- FlicenseNot gradedqualityCmaintenanceProvides RAG-based knowledge retrieval and document management as MCP tools, supporting hybrid search, reranking, and retrieval process visualization.
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants to query documents in a Bedrock Knowledge Base through the MCP protocol, with tools for semantic search and agentic retrieval.MIT
Related MCP Connectors
Real-time Amazon, WIPO & PACER data for AI agents — 19 tools via the MCP protocol.
100+ MCP tools for AI agents: content metadata, trade intelligence, business-expertise analysis.
Reddit & X data for AI agents over MCP. Semantic search, hosted, no Reddit API.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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