Skip to main content
Glama

Agentic Job Intelligence Pipeline (MCP + LLM)

Eine agentengetriebene Pipeline, die das Model Context Protocol nutzt, um externe Werkzeuge für strukturierten Datenabruf zu orchestrieren, mit einer LLM-Bewertungsebene, die unstrukturierte Stellenbeschreibungen gegen ein Kandidatenprofil rankt. Der Druck auf das Kontextfenster wird durch eine abgestufte Metadaten-zuerst-Abrufstrategie (pipeline.py) bewältigt, und dieselben Werkzeuge werden auch einem echten Tool-Calling-Agenten mit eigener Planungsschleife (agent.py) sowie einer REST + WebSocket-API (api.py) bereitgestellt. Die vollständige Ausarbeitung finden Sie in docs/.

Ausführen

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}'

Gemessenes Ergebnis

Corpus mit 150 Jobs, Shortlist von 8. prefilter() wendet Tag-/Titel-, Senioritäts- (verwirft senior, wenn die Berufsjahre des Kandidaten < 4 sind) und Standort-Gates (bevorzugte Stadt des Kandidaten, alias-normalisiert, oder Remote) an, wodurch die Zahl der Überlebenden von 150 auf 31 sinkt:

Strategie

Prompt tokens

vs. naiv

A — alle 150 vollständigen Beschreibungen senden

67,360

B — Metadaten zuerst, dann 8 abrufen

13,802

4.9× günstiger

C — Prefilter → Metadaten → 8 abrufen

6,258

10.8× günstiger

Reproduzierbar mit python pipeline.py --benchmark. Echte Zahlen aus dem heutigen data/jobs.json dieses Repos, keine Platzhalter. Token-Zählungen verwenden den o200k_base-Encoder von tiktoken (den gpt-4o / gpt-4o-mini tatsächlich verwenden) – exakt, nicht geschätzt. Die alte Heuristik Zeichen ÷ 4 hat die Kosten der naiven Strategie bei diesem Corpus um 17.4% überschätzt; das Feld heuristic_vs_real_tokens von benchmark() reproduziert diesen Vergleich.

Architektur

   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

Das Argument für den gestuften Abruf

Naiv: Jede vollständige Beschreibung dem Modell geben und es bitten, zu ranken. Drei Probleme.

  1. Kosten — 79k Prompt-Token pro Lauf, und es wächst linear mit dem Corpus.

  2. Obergrenze — ab ein paar hundert Einträgen überschreitet es das Kontextfenster schlichtweg. Nicht langsam, sondern unmöglich.

  3. Qualität — der Long-Context-Recall nimmt in der Mitte eines großen Prompts ab, sodass das Ranking schlechter wird, je mehr Kandidaten Sie hinzufügen.

Gestufter Abruf, günstigster Filter zuerst:

Stufe

Mechanismus

Kosten

Warum hier

0

Deterministischer Tag-/Titel-/Standort-Filter in Python

kostenlos

Lassen Sie ein Modell nie lesen, was if verwerfen könnte. 150 → 91.

1

LLM sieht ~55 Token Metadaten pro Job, wählt die Top 8

~5k

High-Recall-Sieb. Angewiesen, großzügig einzuschließen, weil Stufe 2 ablehnen kann.

2

Vollständige Beschreibungen nur für die 8 Überlebenden

~4k

Volle Genauigkeit, einmal bezahlt, nur dort, wo es die Antwort ändert.

Das verallgemeinerbare Prinzip — und das, was man in einem Vorstellungsgespräch laut sagen sollte — ist Kaskade nach Kosten: Ordnen Sie Ihre Filter vom günstigsten zum teuersten, und setzen Sie die Schwelle jeder Stufe auf Recall statt auf Präzision, denn eine spätere Stufe kann zwar noch ablehnen, aber nichts kann wiederherstellen, was eine frühe Stufe verworfen hat.

Agentische Ebene (agent.py)

pipeline.py ist ein festes Skript: Prefilter, dann immer Shortlist, dann immer Score. agent.py reicht dem Modell dieselben MCP-Tools über OpenAI-Funktionsaufrufe und lässt es seinen eigenen Weg planen — ein echter Tool-Calling-Agent, keine fest verdrahtete Sequenz:

  • Strukturierte Endantwort als Tool-Aufruf. Der Agent antwortet nicht „in JSON und hofft“ — fertig zu sein bedeutet, ein synthetisches Tool submit_rankings aufzurufen, dessen Parameterschema ist schemas.RankingResult. Ungültige Argumente kommen als Validierungsfehler zurück, den das Modell lesen und korrigieren kann, für eine begrenzte Anzahl von Wiederholungen.

  • Tool-Fehler führen zu Degradierung, nicht zum Absturz. Jede MCP-Tool-Ausnahme wird zu einem normalen Tool-Ergebnis {"error": ...}, das dem Modell zurückgegeben wird, sodass es um einen fehlerhaften Aufruf herum navigieren kann, statt den gesamten Lauf zum Absturz zu bringen.

  • Ein Modell, das nie konvergiert, gibt trotzdem etwas zurück. Wenn es sein Schritt-/Wiederholungsbudget ohne gültige Ausgabe erschöpft, greift der Agent auf dieselbe deterministische Logik prefilter → shortlist → score wie pipeline.py zurück und meldet fallback_used: true.

  • Transiente API-Fehler erhalten einen eigenen Wiederholungsversuch über tenacity, getrennt von der obigen Schema-Wiederholungsschleife — eine schlechte Verbindung und eine schlechte Antwort sind verschiedene Fehlermodi.

Den Schritt-für-Schritt-Ablauf finden Sie in docs/CODE_WALKTHROUGH.md.

REST + WebSocket-Ebene (api.py)

Ein FastAPI-Dienst kapselt die MCP-Tools, pipeline.py und agent.py, sodass sie über HTTP erreichbar sind und nicht nur als CLI-Skripte:

Endpunkt

Funktion

GET /health

Liveness-Check

GET /jobs, GET /jobs/{id}

Metadatenliste / vollständige Details, dieselbe Invariante ohne Beschreibung wie das MCP-Tool

GET /stats

Corpus-Statistiken

POST /rank

die Ranking-Pipeline ausführen (Standard: agentic Planungsschleife, oder die feste gestufte Pipeline); use_llm: false führt nur die kostenlose deterministische Hälfte aus

WS /ws/rank

dasselbe wie POST /rank, streamt jedoch ein Ereignis pro Agentenschritt, sobald es passiert, statt einer einzelnen Antwort am Ende

Eine MCP-stdio-Sitzung wird beim Start einmal geöffnet und hinter einer Sperre gemeinsam genutzt (api.MCPSession), anstatt pro Anfrage einen Subprozess zu erzeugen — eine bewusste Vereinfachung gegenüber einem echten Connection-Pool, die im Modul-Docstring von api.py als solche dokumentiert ist und nicht als echtes verteiltes System überverkauft wird. Jede Anfrage erhält eine Korrelations-ID (request_id), die durch Logs und jedes gestreamte Ereignis hindurchläuft, sodass ein Lauf über die asynchronen Hops hinweg verfolgt werden kann.

MCP-Hinweise, die man in- und auswendig kennen sollte

  • Warum es existiert: N Modelle × M Integrationen wird zu N + M. Ein Protokoll, JSON-RPC 2.0 über stdio oder Streamable HTTP.

  • Tools vs. Ressourcen vs. Prompts: modellgesteuert / anwendungsgesteuert / benutzergesteuert. Dieses Dreiergespann richtig hinzubekommen, ist ein häufiges Unterscheidungsmerkmal in Vorstellungsgesprächen.

  • CallToolResult-Form: content (Blöcke), structured_content (typisiert, als {"result": ...} verpackt bei Nicht-Objekt-Rückgaben), is_error. Siehe pipeline.call().

  • Tool-Design ist API-Design für einen nicht-menschlichen Aufrufer. list_jobs und get_job_details sind getrennt, weil genau diese Trennung den gestuften Abruf ermöglicht. Docstrings sind die Tool-Beschreibung, die das Modell liest — vager Docstring, falsche Tool-Wahl.

  • Batch-Parameter statt skalarer Parameter: get_job_details(job_ids: list[str]) kostet einen Round Trip; get_job_detail(job_id: str) kostet acht.

Bekannte Grenzen

  • Shortlist-Recall (Behält Stufe 1 die als relevant markierten Jobs, die den Prefilter überleben?) benötigt einen echten OPENAI_API_KEY, um gemessen zu werden — python eval.py --top 8 führt ihn aus; hier aus Kostengründen nicht ausgeführt.

  • Der Corpus ist synthetisch. Echte Stellenausschreibungen sind unordentlicher — HTML, Duplikate, veraltete Einträge.

  • Kein Caching zwischen Läufen, daher zahlt man bei wiederholten Aufrufen erneut für Stufe 1.

Stufe-1-Recall — gemessen, nicht angenommen

data/relevance_labels.json enthält 20 Job-IDs, die ein Mensch als für den Kandidaten relevant bezeichnen würde, ausgewählt anhand einer dokumentierten, reproduzierbaren Bewertungsrichtlinie (siehe Datei). eval.py prüft zwei Dinge getrennt:

  • prefilter_recall — Wie viele der 20 als relevant markierten Jobs überleben den deterministischen Prefilter? Kostenlos, kein API-Schlüssel: python eval.py --prefilter-only20/20, Recall 1.0. Die Label-Richtlinie ist eine echte Teilmenge der Prefilter-Gates selbst, daher bestätigt dies, dass der Prefilter die Zielrolle nicht stillschweigend verwirft, anstatt dies anzunehmen.

  • shortlist_recall — Wie viele davon überleben auch die LLM-Shortlist bei dem top, mit dem Sie tatsächlich arbeiten? python eval.py --top 8 — benötigt OPENAI_API_KEY, einen echten Modellaufruf, wird daher in diesem Repo nicht ausgeführt; führen Sie es selbst aus, wenn Sie einen Schlüssel haben.

Ihre TODOs

  1. prefilter() — Senioritäts- und Standort-Gates hinzufügen. --benchmark erneut ausführen und die Zahl notieren. Erledigt: 150 → 31 Überlebende, 10.8× Reduktion gegenüber naiv.

  2. approx_tokens durch echte tiktoken-Zählung ersetzen; festhalten, wie weit die ÷4-Heuristik danebenlag. Erledigt: Die Heuristik überschätzte um 17.4%.

  3. Einen markierten Relevanzsatz mit 20 Jobs erstellen und den Stufe-1-Recall messen. Erledigt für die kostenlose Hälfte (prefilter_recall = 1.0); die kostenpflichtige Hälfte (shortlist_recall) ist in eval.py --top 8 verdrahtet, führen Sie sie mit Ihrem eigenen Schlüssel aus.

  4. Den Server in die MCP-Konfiguration von Claude Desktop einbinden und von Hand aufrufen. Das Konfigurations-Snippet und die Neustartanweisungen finden Sie in docs/OVERVIEW.md — die tatsächliche Registrierung erfolgt in Ihrer eigenen Claude-Desktop-App, was dieses Repo nicht für Sie erledigen kann.

Dokumentation

  • docs/OVERVIEW.md — was dieses Projekt ist, welches Problem es löst und warum, Architektur, Einbindung von Claude Desktop, lokale Testbarkeit, bekannte Einschränkungen.

  • docs/CODE_WALKTHROUGH.md — jedes Modul, Funktion für Funktion.

-
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