ML On-Call Agent
ML On-Call Agent
„Warum hat der gestrige Lauf Regressionen verursacht?" — ein Multi-Agenten-System, das diese Frage beantwortet, indem es einen Drift-Report, einen Eval-Run und ein Deploy-Log korreliert, und jede Behauptung mit einer Quelle belegt.
Bereitgestellt über MCP, sodass die Tools aus jedem MCP-Client funktionieren.
Status: 65 Tests. Die Diagnose wird deterministisch aus gewichteten Beweisen berechnet, daher ist jede Zahl unten eine Assertion, keine Demo.
Das eigentliche Problem
Ein ML-System verschlechtert sich lautlos. Wenn es schließlich jemand bemerkt, sind die Beweise über drei Orte verstreut, die nicht miteinander kommunizieren:
ein Drift-Report — hat sich die Eingabeverteilung verschoben?
ein Eval-Run — haben sich die Offline-Scores verschlechtert, und auf welchen Slices?
ein Deploy-Log — hat jemand etwas ausgerollt?
Sie zu korrelieren ist eine Aufgabe, und es ist eine Aufgabe, die Menschen um 3 Uhr morgens schlecht erledigen, weil es bedeutet, drei JSON-Dateien gleichzeitig im Kopf zu halten.
Diese drei Artefakte sind nicht für dieses Repository erfunden. Sie sind das, was
model-drift-monitor,
llm-eval-pipeline
und ai-code-review-bot
bereits produzieren. Der Reader wurde gegen ein echtes Artefakt geschrieben, nicht gegen Dokumentation.
Das Ergebnis
python -m oncall.cli evaluate scenario truth verdict conf margin steps action
healthy healthy healthy 0.57 2.0 9 no action
data_shift data_shift data_shift 0.71 8.0 9 retrain on recent data
bad_deploy bad_deploy bad_deploy 0.66 8.5 9 roll back
concept_drift concept_drift concept_drift 0.81 9.0 9 retrain with fresh labels
pipeline_break pipeline_break pipeline_break 0.62 5.0 9 fix the upstream pipeline
flaky_eval noise noise 0.62 3.0 9 rerun the evaluation
task success : 100%
action accuracy : 100%
mean steps : 9.0Die Ground Truth ist konstruktionsbedingt bekannt, daher wird der Agent bewertet statt bewundert. „Der Agent hat einen plausiblen Incident-Report erzeugt" ist kein Ergebnis — plausibel ist das, was Sprachmodelle tun, auch wenn die Beweise nichts hergeben.
Das Szenario, um das es geht
concept_drift: Die Eingaben sind statistisch identisch, nichts wurde ausgerollt, und das Ranking des Modells hat sich umgekehrt (AUC 0,729 → 0,326, schlechter als Zufall).
Es gibt nichts, worauf man zeigen könnte. Die Antwort ergibt sich aus der Kombination von zwei Negativen und einem Positiven — kein Drift, kein Deploy, Ranking kollabiert — was genau das ist, was eine Zusammenfassung eines einzelnen Artefakts übersehen würde. Es ist auch der blinde Fleck, den model-drift-monitor über sich selbst dokumentiert, eine Ebene höher diagnostiziert.
Die Designentscheidung, auf der alles beruht
Die Diagnose ist deterministisch. Das Sprachmodell erzählt nur.
Der naheliegende Bau wäre, drei JSON-Dateien in einen Prompt einzufügen und zu fragen, was schiefgelaufen ist. Das erzeugt jedes Mal etwas Flüssiges — auch wenn die Beweise nichts hergeben — und es ist untestbar, weil man nicht auf Prosa asserten oder eine richtige Antwort von einer glücklichen unterscheiden kann.
Daher ist die Schlussfolgerung gewöhnliches Python:
jeder Spezialist liest ein Artefakt und gibt
Findings ausjedes Finding trägt eine Zitation — Datei, Feld, Wert.
Findingverlangt eine, sodass eine Behauptung ohne Quelle nicht konstruiert werden kannjedes Finding benennt, welche Root Causes es stützt und welche es ausschließt
diagnose()summiert die Gewichte — eine reine Funktion, unit-getestet gegen die Wahrheit
| finding | source |
|------------------------------------------------------------|----------------------------|
| input drift is 'none': the scored population is | `drift:severity=none` |
| statistically the same as training | |
| roc_auc is 0.326 - WORSE THAN RANDOM. The model's ranking | `evals:metrics.roc_auc |
| has inverted, which is a changed relationship | =0.326` |
| 1 change(s) landed but none touch model behaviour | `changes:changes[].files` |test_the_narration_does_not_change_the_diagnosis assertet, dass das Urteil mit und ohne Modell identisch ist. Falls es jemals fehlschlägt, hat das Modell begonnen, die Schlussfolgerung zu übernehmen — und die Schlussfolgerung hört in dem Moment auf, testbar zu sein.
Negative Beweise sind der Ort, an dem der meiste diagnostische Wert liegt. „Kein Drift" und „nichts wurde ausgerollt" sind Findings mit Gewichten. Ein LLM, das das Drift-JSON zusammenfasst, würde sie überspringen, weil nichts passiert ist.
Der Graph
SUPERVISOR ──► drift ──┐
▲ ├──► evals ───┤ one specialist per artifact, each consulted once
│ └──► changes ─┤
│ ▼
│ DIAGNOSE sum the weighted findings
│ ▼
└────────────── CRITIC "is this conclusion supported?"
(bounded) ▼
REPORTZwei Eigenschaften, die eine gerade Pipeline nicht hat:
Der Kritiker kann Arbeit zurückschicken. Wenn das Urteil auf zwei Artefakten beruht, während ein drittes nie gelesen wurde, kehrt die Kontrolle zum Supervisor zurück, statt eine Schlussfolgerung zu veröffentlichen, die aus der Hälfte der Beweise gezogen wurde.
Der Zyklus ist begrenzt. MAX_REVISIONS = 2 begrenzt ihn, und recursion_limit fängt alles ab, was entkommt. Ein Agent, der loopen kann, ist ein Agent, der für immer loopen kann — und ein außer Kontrolle geratener On-Call-Agent erzeugt Seiten, statt sie zu beantworten.
Er läuft ohne LLM und ohne API-Key — der Supervisor routet über eine einfache Python-Regel — sodass das Routing, die Delegation und das Loop-back des Kritikers alle offline unit-getestet sind. test_the_graph_and_the_plain_loop_agree assertet, dass ein LangGraph-Lauf und eine einfache for-Schleife in jedem Szenario zu identischen Urteilen kommen, was beweist, dass der Graph Orchestrierung hinzufügt, nicht Schlussfolgerung.
Was jedes Artefakt tatsächlich wert ist
python -m oncall.cli ablateBeweise | Aufgaben-Erfolg | Aktions-Genauigkeit |
alle drei | 100 % | 100 % |
ohne Drift | 67 % | 67 % |
ohne Evals | 67 % | 67 % |
ohne Changes | 100 % | 100 % |
Ein ehrliches negatives Ergebnis, und es ist assertet, damit es nicht stillschweigend vergessen wird. Das Entfernen des Change-Logs kostet nichts bei diesem Szenario-Set — bad_deploy ist bereits allein aus den Drift- und Eval-Beweisen separierbar. Das Change-Log verdient seinen Platz durch die Benennung des Commits, was ein Mensch braucht, um zu handeln, nicht durch eine Änderung der Diagnose.
test_the_change_log_currently_changes_no_verdicts fixiert das. Wenn ein zukünftiges Szenario es tragend macht, schlägt der Test fehl, und diese Tabelle muss sich ändern. Dafür ist die Assertion da.
Ablation ist der einzige Weg, um zu entdecken, dass eine Quelle, für deren Integration du eine Woche gebraucht hast, keine Urteile verändert.
Und wenn ein Artefakt fehlt
Jobs schlagen fehl, Buckets sind leer, Pfade ändern sich. Die interessante Frage ist nicht, ob der Agent weiterhin antwortet — das tut er —, sondern ob er es bemerkt:
missing drift -> verdict bad_deploy flagged=True
missing evals -> verdict bad_deploy flagged=True
missing changes -> verdict bad_deploy flagged=TrueEin Agent, der stillschweigend aus zwei von drei Dateien diagnostiziert, ist schlechter als einer, der sich weigert, weil niemand weiß, dass man ihm misstrauen sollte.
Der MCP-Server
python -m oncall.mcp_server # stdio, for Claude Desktop et al
python -m oncall.mcp_server --http --port 8931Sieben Tools — list_incidents, get_drift_report, get_eval_run,
get_changes, investigate_incident, compare_incidents, list_specialists
— plus eine Resource und eine templated Resource.
Die Tools geben Beweise zurück, keine Prosa. Jedes gibt das Artefakt oder die strukturierte Diagnose zurück, sodass das Modell des Clients über Daten mit Zitationen schlussfolgert, statt über einen Absatz, den jemand bereits zusammengefasst hat. Eine Zusammenfassung ist der Ort, an dem das Detail stirbt.
Geschrieben gegen mcp 2.0.0, das ein breaking Rewrite ist
Erwähnenswert, weil fast alles Veröffentlichte 1.x ist und nicht funktioniert. Jedes davon wurde durch Ausführen verifiziert:
1.x | 2.0.0 |
| weg — |
| weg — der Low-Level- |
|
|
|
|
Zwei weitere, die echte Zeit gekostet haben:
@server.tool()muss aufgerufen werden. Bloßes@server.toolwirft einenTypeError, der genau das sagt.Ein bloßes
-> dicterzeugt überhaupt keinstructuredContent. Der Textinhalt ist trotzdem da, also sieht es in einem Chat-Client gut aus und bricht stillschweigend jeden Client, der das strukturierte Feld liest. Jedes Tool hier gibt aus diesem Grund einen parametrisierten Generic (Dict[str, Any]) zurück, und es gibt einen Test dafür.
Testen eines Protokoll-Servers ohne Subprozess
Client(server) akzeptiert direkt ein Server-Objekt, sodass der gesamte Protokoll-Roundtrip im Prozess läuft — kein Subprozess, kein Port, keine Flakiness durch beides. Diese Möglichkeit ist der einzige Grund, warum Protokoll-Tests hier billig sind.
Schnellstart
git clone https://github.com/kanishqtanwar35-hub/ml-oncall-agent
cd ml-oncall-agent
pip install -r requirements.txt
export PYTHONPATH=src
python -m oncall.cli incidents # what can be investigated
python -m oncall.cli investigate concept_drift # the full report
python -m oncall.cli evaluate # score it against truth
python -m oncall.cli ablate # what each artifact is worth
pytest -q # 65 testsZeige es auf echte Artefakte:
python -m oncall.cli write ./artifacts --scenario bad_deploy
# then: Evidence.load("./artifacts") reads a real pipeline's output unchangedAlles läuft ohne API-Key. investigate --narrate fügt einen vom Modell geschriebenen Eröffnungsabsatz hinzu, wenn GEMINI_API_KEY gesetzt ist, und ändert sonst nichts.
Bugs, die lesenswert sind
Eine hartkodierte Toleranz verschluckte den Flaky-Eval-Fall. regressions() verwendete einen 0,02-Schwellenwert, sodass eine 0,006-Bewegung nie registriert wurde und der Noise-Check nie lief — der Agent sagte healthy, wo die Wahrheit noise war. Das ist genau der Folklore-Schwellenwert-Fehler, gegen den model-drift-monitor argumentiert, ein Repository weiter reproduziert. Erkennung und Signifikanz sind zwei Fragen: Der Boden ist jetzt 0,005 („alles, was ein Dashboard zeigen würde"), und das selbst gemessene noise_std der Harness übernimmt die Unterscheidung.
Der Recovery-Test konnte nicht fehlschlagen. Er täuschte einen übersprungenen Spezialisten vor, indem er consulted vorab befüllte — aber der Kritiker vergleicht available gegen consulted, sodass das Markieren von etwas als konsultiert die Lücke vor genau der Prüfung verbarg, die getestet wurde. Er meldete recovered: False und sah aus wie ein kaputter Kritiker. Der Fehler muss in das Routing injiziert werden, nicht in die Buchhaltung; build_graph(skip_first_pass=...) macht das, und der Kritiker fängt die Lücke nun nachweislich und schickt den Supervisor zurück.
Einschränkungen, klar benannt
Die Szenarien sind synthetisch. Bewusst — echte Incidents kommen nicht mit einer beschrifteten Root Cause, genau deshalb ist ihre Diagnose schwer. Die Zahlen charakterisieren die Methode, nicht ein Produktionssystem.
Sechs Szenarien sind eine kleine Menge. 100 % bei sechs ist nicht 100 % im Allgemeinen, und das nächste hinzugefügte Szenario bricht es eher, als es zu bestätigen.
Kalibrierung ist hier nicht messbar. Ohne falsche Antworten gibt es nichts, womit man Konfidenz vergleichen könnte. Das CLI gibt
n/aaus und sagt warum, statt eine Zahl zu erfinden —test_calibration_is_honestly_unmeasurable_herefixiert das.Die Gewichte sind von Hand gesetzt. Sie kodieren mein Urteil darüber, was Beweise wert sind, und ein größerer beschrifteter Incident-Satz würde es erlauben, sie stattdessen anzupassen — mit einem zurückgehaltenen Split, weil das Anpassen an sechs Szenarien Memorisation wäre.
Die Tool-Auswahl-Genauigkeit ist trivial 1,0 bei drei immer vorhandenen Artefakten. Die Metrik ist hier, weil sie beim vierten Tool anfängt, wichtig zu werden, und das nachträgliche Hinzufügen ist der Weg, um zu entdecken, dass der Agent seit Monaten alles aufruft.
Keine Live-Integrationen. Es liest Artefakte von der Festplatte. Die Verbindung mit einem echten Warehouse, CI-System und Git-Host ist Deployment-Arbeit, keine Schlussfolgerungs-Arbeit.
Der Kritiker prüft Vollständigkeit und Unterstützung, nicht Korrektheit. Er kann dir nicht sagen, dass die Gewichte falsch sind, nur dass die Beweise dünn waren.
Roadmap
Ein größerer beschrifteter Incident-Satz, und die Gewichte auf einem zurückgehaltenen Split anpassen.
Mehr Spezialisten — Serving-Latenz, Kosten-Ledger, Feature-Store-Frische — wo die Tool-Auswahl-Genauigkeit anfängt, etwas zu bedeuten.
Den MCP-Server mit echten Artefakt-Quellen verbinden, statt mit einem Szenario-Builder.
Multi-Incident-Korrelation: Drei Dienste, die gleichzeitig Regressionen haben, sind ein Incident, nicht drei.
Lizenz
MIT.
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 Connectors
MCP server providing access to the Scorecard API to evaluate and optimize LLM systems.
Monitor MCP servers, API contracts and AI outputs for schema drift. Alerts on breaking changes.
MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.
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/kanishqtanwar35-hub/ml-oncall-agent'
If you have feedback or need assistance with the MCP directory API, please join our Discord server