Skip to main content
Glama

Pipeline de inteligencia laboral agéntico (MCP + LLM)

Un pipeline impulsado por agentes que utiliza el Model Context Protocol para orquestar herramientas externas de recuperación estructurada de datos, con una capa de puntuación LLM que clasifica descripciones de empleo no estructuradas frente a un perfil de candidato. La presión de la ventana de contexto se gestiona con una estrategia de recuperación escalonada que prioriza los metadatos (pipeline.py), y las mismas herramientas se exponen también a un agente real de llamada a herramientas con su propio bucle de planificación (agent.py) y a una API REST + WebSocket (api.py). Consulta docs/ para el documento completo.

Ejecútalo

pip install -r requirements.txt

python pipeline.py --benchmark     # token comparison, zero API calls
python pipeline.py --dry-run       # real MCP subprocess handshake, no LLM
export OPENAI_API_KEY=sk-...
python pipeline.py --top 8         # fixed 3-stage pipeline
python agent.py --dry-run          # agent tool discovery, no LLM calls
export OPENAI_API_KEY=sk-...
python agent.py --top 8            # tool-calling agent with a planning loop
python eval.py --prefilter-only    # stage-1 recall, deterministic half, no key needed
pytest -q                          # in-process MCP server, no key needed

uvicorn api:app --reload           # REST + WebSocket layer, http://localhost:8000
curl localhost:8000/health
curl -X POST localhost:8000/rank -H 'content-type: application/json' -d '{"use_llm": false}'

Resultado medido

Corpus de 150 empleos, lista corta de 8. prefilter() aplica filtros de etiqueta/título, seniority (descarta senior cuando los años del candidato < 4) y ubicación (ciudad preferida del candidato, normalizada por alias, o remoto), que reducen el número de supervivientes de 150 a 31:

Estrategia

Tokens de prompt

vs. ingenua

A — enviar las 150 descripciones completas

67,360

B — primero metadatos, luego recuperar 8

13,802

4.9× más barato

C — prefilter → metadatos → recuperar 8

6,258

10.8× más barato

Reprodúcelo con python pipeline.py --benchmark. Son números reales del data/jobs.json de este repositorio, no marcadores de posición. Los recuentos de tokens usan el codificador o200k_base de tiktoken (el que usan gpt-4o / gpt-4o-mini) — exactos, no estimados. La antigua heurística de caracteres ÷ 4 sobreestimó el coste de la estrategia ingenua en un 17.4% en este corpus; el campo heuristic_vs_real_tokens de benchmark() reproduce esa comparación.

Arquitectura

   MCP SERVER (stdio subprocess)              MCP CLIENT / pipeline.py
   ---------------------------------          ------------------------------------
   tool  list_jobs        -> metadata  <----  Stage 0  prefilter()   [0 tokens]
   tool  get_job_details  -> full text        Stage 1  shortlist     [~5k tokens]
   tool  get_candidate_profile                Stage 2  score         [~4k tokens]
   tool  corpus_stats
   resource  jobs://schema                    Meter tracks tokens per stage
   prompt    rank_jobs

El argumento de la recuperación escalonada

Ingenua: entregar al modelo cada descripción completa y pedirle que las clasifique. Tres problemas.

  1. Coste — 79k tokens de prompt por ejecución, y crece linealmente con el corpus.

  2. Límite — a partir de unos cientos de ofertas supera la ventana de contexto por completo. No lento: imposible.

  3. Calidad — la recuperación en contexto largo se degrada en medio de un prompt grande, así que la clasificación empeora a medida que añades más candidatos.

Recuperación escalonada, primero el filtro más barato:

Etapa

Mecanismo

Coste

Por qué aquí

0

Filtro determinista de etiqueta/título/ubicación en Python

gratis

Nunca dejes que un modelo lea lo que un if podría descartar. 150 → 91.

1

El LLM ve ~55 tokens de metadatos por empleo, elige los 8 mejores

~5k

Pantalla de alta recuperación (recall). Se le indica que sobre-incluya, porque la etapa 2 puede rechazar.

2

Descripciones completas solo para los 8 supervivientes

~4k

Fidelidad total, pagada una vez, solo donde cambia la respuesta.

El principio generalizable — y lo que hay que decir en voz alta en una entrevista — es cascada por coste: ordena los filtros de más barato a más caro, y ajusta el umbral de cada etapa para la recuperación (recall) en lugar de la precisión, porque una etapa posterior aún puede rechazar, pero nada puede recuperar lo que una etapa temprana descartó.

Capa de agente (agent.py)

pipeline.py es un script fijo: prefiltrar, luego siempre seleccionar la lista corta, luego siempre puntuar. agent.py entrega al modelo las mismas herramientas MCP a través de la llamada a funciones de OpenAI y le permite planificar su propio camino — un agente real de llamada a herramientas, no una secuencia fija:

  • Respuesta final estructurada como llamada a herramienta. El agente no "responde en JSON y espera" — terminar significa llamar a una herramienta sintética submit_rankings cuyo esquema de parámetros es schemas.RankingResult. Los argumentos no válidos vuelven como un error de validación que el modelo puede leer y corregir, con un número limitado de reintentos.

  • Los fallos de herramientas se degradan, no rompen. Cualquier excepción de herramienta MCP se convierte en un resultado de herramienta normal {"error": ...} que se devuelve al modelo, para que pueda sortear una llamada incorrecta en lugar de tumbar toda la ejecución.

  • Un modelo que nunca converge sigue devolviendo algo. Si agota su presupuesto de pasos/reintentos sin una salida válida, el agente recurre a la misma lógica determinista prefilter → shortlist → score que pipeline.py, e informa fallback_used: true.

  • Los errores transitorios de API tienen su propio reintento, mediante tenacity, separado del bucle de reintentos de esquema anterior — una mala conexión y una mala respuesta son modos de fallo distintos.

Consulta docs/CODE_WALKTHROUGH.md para el bucle paso a paso.

Capa REST + WebSocket (api.py)

Un servicio FastAPI envuelve las herramientas MCP, pipeline.py y agent.py para que sean accesibles por HTTP en lugar de solo como scripts CLI:

Endpoint

Qué hace

GET /health

comprobación de actividad

GET /jobs, GET /jobs/{id}

lista de metadatos / detalle completo, misma invariante sin descripción que la herramienta MCP

GET /stats

estadísticas del corpus

POST /rank

ejecuta el pipeline de clasificación (bucle de planificación agentic por defecto, o el pipeline escalonado fijo); use_llm: false ejecuta solo la mitad determinista gratuita

WS /ws/rank

igual que POST /rank, pero transmite un evento por cada paso del agente a medida que ocurre, en lugar de una única respuesta al final

Una única sesión MCP stdio se abre al inicio y se comparte detrás de un bloqueo (api.MCPSession) en lugar de lanzar un subproceso por petición — una simplificación deliberada frente a un pool de conexiones real, documentada como tal en el docstring del módulo api.py, no vendida como un sistema distribuido real. Cada petición recibe un id de correlación (request_id), que se propaga por los registros y por cada evento transmitido, de modo que una ejecución puede rastrearse a través de los saltos asíncronos.

Notas sobre MCP que conviene saber de memoria

  • Por qué existe: N modelos × M integraciones se convierte en N + M. Un protocolo, JSON-RPC 2.0 sobre stdio o Streamable HTTP.

  • Herramientas vs recursos vs prompts: controlado por el modelo / controlado por la aplicación / controlado por el usuario. Acertar con este trío es un diferenciador habitual en entrevistas.

  • Forma de CallToolResult: content (bloques), structured_content (tipado, envuelto como {"result": ...} para devoluciones que no son objetos), is_error. Ver pipeline.call().

  • El diseño de herramientas es diseño de API para un llamador no humano. list_jobs y get_job_details están separadas porque esa separación es lo que permite la recuperación escalonada. Los docstrings son la descripción de la herramienta que lee el modelo — docstring vago, elección de herramienta equivocada.

  • Mejor parámetros por lotes que escalares: get_job_details(job_ids: list[str]) cuesta un viaje de ida y vuelta; get_job_detail(job_id: str) cuesta ocho.

Limitaciones conocidas

  • La recuperación de la lista corta (¿mantiene la etapa 1 los empleos etiquetados como relevantes que sobreviven al prefilter?) necesita una OPENAI_API_KEY real para medirse — python eval.py --top 8 la ejecuta; no se ha ejecutado aquí por razones de coste.

  • El corpus es sintético. Las ofertas reales son más desordenadas — HTML, duplicados, anuncios obsoletos.

  • No hay caché entre ejecuciones, así que las invocaciones repetidas vuelven a pagar la etapa 1.

Recuperación de la etapa 1 — medida, no supuesta

data/relevance_labels.json contiene 20 identificadores de empleo que un humano consideraría relevantes para el candidato, elegidos con una rúbrica documentada y reproducible (consulta el archivo). eval.py comprueba dos cosas por separado:

  • prefilter_recall — de los 20 empleos etiquetados como relevantes, ¿cuántos sobreviven al prefilter determinista? Gratis, sin clave API: python eval.py --prefilter-only20/20, recall 1.0. La rúbrica de etiquetas es un subconjunto estricto de los propios filtros de prefilter, por lo que esto confirma que prefilter no está descartando silenciosamente el rol objetivo, en lugar de asumirlo.

  • shortlist_recall — de esos, ¿cuántos sobreviven también a la lista corta del LLM con el top que realmente usas? python eval.py --top 8 — necesita OPENAI_API_KEY, una llamada real al modelo, por lo que no se ejecuta en este repositorio; ejecútalo tú mismo cuando tengas una clave.

Tus tareas pendientes

  1. prefilter() — añadir filtros de seniority y ubicación. Volver a ejecutar --benchmark, registrar el número. Hecho: 150 → 31 supervivientes, reducción de 10.8× frente al enfoque ingenuo.

  2. Sustituir approx_tokens por el recuento real con tiktoken; anota cuánto se desviaba la heurística ÷4. Hecho: la heurística sobreestimaba en un 17.4%.

  3. Crear un conjunto etiquetado de relevancia de 20 empleos y medir la recuperación de la etapa 1. Hecho para la mitad gratuita (prefilter_recall = 1.0); la mitad de pago (shortlist_recall) está conectada en eval.py --top 8, ejecútala con tu propia clave.

  4. Conectar el servidor a la configuración MCP de Claude Desktop y llamarlo manualmente. El fragmento de configuración y las instrucciones de reinicio están en docs/OVERVIEW.md — el registro real se hace en tu propia aplicación de Claude Desktop, no es algo que este repositorio pueda hacer por ti.

Documentación

  • docs/OVERVIEW.md — qué es este proyecto, qué problema resuelve y por qué, arquitectura, conexión con Claude Desktop, prueba local, limitaciones conocidas.

  • docs/CODE_WALKTHROUGH.md — cada módulo, función por función.

-
license - not tested
-
quality - not tested
B
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 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/jaideepdnaik/mcp-job-intel'

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