two-tower-recsys-mcp
Two-Tower Recsys MCP — Recuperación neuronal, servida a través de MCP
Un modelo de recomendación de dos torres (two-tower) de aprendizaje profundo, entrenado con el corpus de reseñas de Amazon de 2023, servido como servidor de herramientas MCP (Model Context Protocol), con un frontend de chat en Streamlit que permite a un agente Gemini invocar esas herramientas en tu nombre.
Este README recorre todo el pipeline de principio a fin: qué es el modelo, cómo se entrenó, qué rendimiento real tiene (medido, no estimado), cómo lo expone el servidor MCP y cómo ejecutar o desplegar el frontend.
1. Qué es esto
Los modelos de dos torres (two-tower) son la arquitectura estándar detrás de los sistemas de recomendación industriales a gran escala (este patrón — torres separadas que incrustan a un usuario y a un ítem en el mismo espacio vectorial, entrenadas para que los pares relevantes queden cerca — es la misma forma que usan en producción YouTube, Pinterest y los propios sistemas de recuperación de Amazon). Este proyecto implementa uno desde cero, lo entrena con datos reales de interacciones de Amazon y lo envuelve para uso agéntico a través de MCP en lugar de una API REST típica.
¿Por qué MCP en lugar de una API REST? MCP es el protocolo que Anthropic introdujo para conectar agentes LLM con herramientas y datos. Envolver un modelo entrenado como herramientas MCP (en lugar de, por ejemplo, un endpoint de Flask) significa que cualquier agente compatible con MCP — Claude Desktop, el frontend propio de este proyecto con Streamlit+Gemini, o cualquier otro cliente MCP — puede llamar directamente a recommend_for_user, similar_items, etc., y el LLM decide cuándo y cómo invocarlas basándose en lenguaje natural.
Related MCP server: consulting-mcp-server
2. Arquitectura
Torre de usuario: una incrustación aprendida del ID de usuario (64-dim) → MLP de 2 capas → salida de 64-dim.
Torre de ítem: una incrustación aprendida del ID de ítem (64-dim) concatenada con una incrustación de frases congelada
all-MiniLM-L6-v2del título del producto (384-dim, proyectada a 64-dim) → MLP de 2 capas → salida de 64-dim. La incrustación de texto congelada es lo que da al modelo capacidad de arranque en frío (cold-start) — puede colocar un ítem de forma sensata en el espacio vectorial incluso sin historial de interacciones, solo a partir de su título.Ambas torres producen vectores normalizados L2; la similitud es un producto escalar (equivalentemente, similitud coseno).
Pérdida de entrenamiento: softmax muestreado dentro del lote (in-batch sampled softmax) — para un lote de B pares positivos (usuario, ítem), cada otro ítem del lote actúa como negativo para cada usuario, y se aplica entropía cruzada sobre la matriz de similitud B×B resultante. Es la forma estándar y eficiente computacionalmente de entrenar torres de recuperación sin muestreo negativo explícito.
Servicio: las incrustaciones de ítems se precomputan una vez y se indexan en FAISS (
IndexFlatIP) para una recuperación rápida de vecinos más cercanos. Un segundo índice FAISS, construido sobre las incrustaciones de títulos MiniLM crudas (sin entrenar), permite la búsqueda de texto en frío que funciona independientemente de la señal colaborativa entrenada.
┌────────────┐ ┌────────────┐
│ 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. Conjunto de datos
McAuley-Lab/Amazon-Reviews-2023
(Laboratorio McAuley de UC San Diego), categoría Video_Games — reseñas crudas + metadatos de ítems, descargados directamente de HuggingFace.
Paso | Cantidad |
Reseñas crudas | 4.624.615 |
Usuarios / ítems crudos | 2.766.656 / 137.249 |
Tras filtrado 5-core (usuarios e ítems con ≥5 interacciones) | 857.505 interacciones |
Usuarios / ítems (tras el filtro) | 98.906 / 26.354 |
Interacciones Train / Valid / Test | 659.693 / 98.906 / 98.906 |
Protocolo de división — dejar los dos últimos fuera por usuario, ordenados por marca de tiempo: la interacción más reciente de cada usuario → test, la segunda más reciente → validación, el resto → train. Es una división temporal, así que el modelo se evalúa prediciendo comportamiento genuinamente futuro en relación con lo que se entrenó, no con interacciones retenidas al azar (lo que filtraría información futura al entrenamiento e inflaría las cifras).
4. Evaluación (números reales, medidos)
La evaluación usa ranking sobre el catálogo completo — cada candidato se puntúa contra los 26.354 ítems, no contra un pequeño subconjunto muestreado de negativos. La evaluación con negativos muestreados (común en artículos antiguos de RecSys, p. ej. ranking contra solo 99 negativos aleatorios) se sabe que infla sustancialmente las métricas offline, así que este es el protocolo más difícil y honesto. Los ítems ya vistos por cada usuario se excluyen de su propio ranking de candidatos.
Conjunto de test — 98.906 usuarios, la interacción final retenida de cada usuario:
Métrica | Valor |
Recall@10 | 1,40% |
NDCG@10 | 0,70% |
HitRate@10 | 1,40% (idéntico a Recall@10 bajo leave-one-out: exactamente un ítem relevante por usuario) |
Para contexto: el azar en un catálogo de 26.354 ítems con k=10 es 10/26.354 = 0,038%. El modelo entrenado es ~37x mejor que el azar bajo ranking de catálogo completo.
El Recall@10 de validación alcanzó su máximo en 2,43% (época 142/150) durante el entrenamiento — el número de test es menor porque la interacción de test es la interacción más lejana en el futuro de cada usuario en relación con su historial de entrenamiento, que es intrínsecamente la predicción más difícil. Esa diferencia es un comportamiento esperado en una división temporal, no un error. El número de test (1,40%) es el que debe citarse en cualquier lugar — la validación se usó solo para elegir el mejor checkpoint durante el entrenamiento, así que reportarla como resultado final sería una forma de cherry-picking.
Curva de entrenamiento completa: models/train_history.csv.
Resultados crudos: models/test_results.json.
5. Herramientas MCP (mcp_server.py)
Herramienta | Descripción |
| Recomendaciones personalizadas top-k, excluye los ítems con los que el usuario ya interactuó |
| Similitud ítem-a-ítem mediante las incrustaciones entrenadas de la torre de ítem |
| Búsqueda semántica en frío sobre títulos de ítems (solo MiniLM — funciona para ítems sobre los que el modelo colaborativo tiene señal débil) |
| Puntuación de similitud más los ítems pasados del usuario más similares al objetivo, para interpretabilidad |
6. Frontend (streamlit_app.py)
Una interfaz de chat con el mismo estilo que weather-mcp-server: arranca el servidor MCP como subproceso sobre stdio, obtiene sus esquemas de herramientas, los convierte en declaraciones de llamada a funciones de Gemini y ejecuta un bucle agéntico — Gemini decide cuál de las 4 herramientas llamar (si alguna) según tu mensaje, la herramienta se ejecuta contra el modelo entrenado real, y el resultado se devuelve para una respuesta final en lenguaje natural. La barra lateral muestra la descripción de cada herramienta más un ejemplo de un clic con IDs reales del catálogo entrenado, y un expander con las estadísticas de evaluación del modelo.
7. Ejecución local
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.pySi .streamlit/secrets.toml (o una variable de entorno GEMINI_API_KEY)
no está configurada, la app recurre a pedir una clave en la barra lateral en tiempo de ejecución.
Nota para macOS
faiss y torch entran en conflicto por la inicialización del runtime de OpenMP en macOS,
lo que provoca un segfault en las llamadas de búsqueda de FAISS a menos que torch/numpy se importen
antes que faiss, con KMP_DUPLICATE_LIB_OK=TRUE y OMP_NUM_THREADS=1
configurados. Ambos ya están gestionados dentro de mcp_server.py y src/build_index.py.
8. Despliegue en Streamlit Community Cloud
Sube este repositorio a GitHub (público o privado — Community Cloud puede desplegar ambos para una cuenta personal).
Ve a share.streamlit.io, haz clic en New app y apúntalo a este repositorio con
streamlit_app.pycomo punto de entrada.En Settings → Secrets de la app, añade:
GEMINI_API_KEY = "your_gemini_api_key_here"Este es el mismo mecanismo que usa
.streamlit/secrets.tomllocalmente — la clave vive solo en el almacén de secretos de Streamlit, nunca en el repositorio ni en el historial de git, y la app la lee automáticamente para que los visitantes nunca tengan que escribir una clave.Despliega. El primer arranque será lento (~1-2 min) mientras descarga el modelo MiniLM y carga los índices FAISS; las cargas posteriores son rápidas.
Nota sobre el tamaño del repositorio: models/ (~110MB: el checkpoint entrenado + los índices FAISS)
está incluido para que la app desplegada no necesite reentrenar en cada arranque en frío. data/raw/ (~2,9GB de descargas crudas de HuggingFace) está en gitignore
y solo se necesita si quieres reproducir el entrenamiento desde cero.
9. Stack tecnológico
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