Skip to main content
Glama

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-v2 del 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 score

3. 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

recommend_for_user(user_id, k)

Recomendaciones personalizadas top-k, excluye los ítems con los que el usuario ya interactuó

similar_items(item_id, k)

Similitud ítem-a-ítem mediante las incrustaciones entrenadas de la torre de ítem

search_items(query_text, k)

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)

explain_recommendation(user_id, item_id)

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.py

Si .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

  1. Sube este repositorio a GitHub (público o privado — Community Cloud puede desplegar ambos para una cuenta personal).

  2. Ve a share.streamlit.io, haz clic en New app y apúntalo a este repositorio con streamlit_app.py como punto de entrada.

  3. 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.toml localmente — 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.

  4. 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.

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