Skip to main content
Glama

pdml-agent

Ein MCP-Server und Tool-Calling-Agent für die property-driven-ml-Experiment-Pipeline, mit einem Human-in-the-Loop-Gate für alles, das Rechenleistung verbraucht, und einer strukturierten Ablaufverfolgung jedes Aufrufs.

Property-driven ML trainiert Klassifikatoren gegen formal-logische Constraints, daher wird ein Lauf durch einen Constraint, einen Datensatz, eine differenzierbare Logik und einen Seed definiert und erzeugt Metriken pro Epoche für sowohl Vorhersageleistung als auch Constraint-Sicherheit. Das macht es zu einer wirklich werkzeugförmigen Domäne und nicht zu einer Demo-Domäne: Experimente können aufgelistet, Konfigurationen abgerufen, Ergebnisse gelesen, Läufe verglichen und neue Läufe geplant, genehmigt und ausgeführt werden.

Status: im Rahmen vollständig. Server, Agent, Gate, Ablaufverfolgung. Echte Ausführung auf CPU demonstriert.

Architektur

┌──────────────────────────────────────────────────────────────┐
│  agent.py                       (Anthropic SDK tool runner)  │
│                                                              │
│   claude-opus-5 ──► pending tool_use ──► ToolLedger.wrap     │
│        ▲                                   │  memoise (RO)  │
│        │                                   │  gate (compute)│
│        │ tool_result                       │  trace (JSONL) │
│        └───────────────────────────────────┘        │        │
└─────────────────────────────┬───────────────────────┼────────┘
                   MCP over stdio                     ▼
┌─────────────────────────────┴────────────────┐   traces/*.jsonl
│  server.py           (mcp MCPServer, thin)   │
│   list_experiments  get_experiment_config    │
│   get_results       compare_runs             │
│   search_logic_definitions                   │
│   run_experiment ──► PDML_ALLOW_EXECUTE=1 ?  │
└────┬───────────┬──────────────┬──────────────┘
     ▼           ▼              ▼
experiments.py  logic_defs.py  runner.py ──► subprocess: main.py
(read CSVs)     (parse source)  (plan/execute)   in property-driven-ml

agent.py weiß nichts über die Domäne. Es verbindet sich wie jeder andere MCP-Client über stdio mit dem Server und arbeitet nur mit den Werkzeugen, die der Server bereitstellt. Die Domänenmodule haben keine MCP-Abhängigkeit und sind durch Import testbar. server.py registriert nur Werkzeuge und delegiert.

Layout

pdml_agent/
  experiments.py   reading and comparing runs
  logic_defs.py    searching the logic implementations
  runner.py        validating, planning and executing runs
  server.py        the MCP layer, deliberately thin
  agent.py         the agent: runner, gate, memoisation, tracing
scripts/
  make_fixtures.py generate sample runs
  smoke_test.py    start the server, exercise every tool, check refusals
  demo.py          run the agent on five tasks
fixtures/results/  sample runs, so nothing needs a GPU to demo
demo_output/       what the agent said and did, one JSON per task
traces/            one JSONL per run, every turn and every call

Werkzeuge

Werkzeug

Rückgabe

list_experiments

Läufe, filterbar nach Constraint, Datensatz oder Logik

get_experiment_config

Die Konfiguration, mit der ein Lauf tatsächlich trainiert wurde

get_results

Metriken für eine Epoche, standardmäßig die letzte

compare_runs

Config- und Metrik-Diff zwischen zwei Läufen

search_logic_definitions

Logik-Klassen, deren Operatoren und Docstrings

run_experiment

Mit dry_run=true ein validierter Befehlsplan; mit dry_run=false Ausführung hinter zwei Toren

Das Gate

run_experiment ist das einzige Werkzeug, das Rechenleistung verbraucht, und zwei unabhängige Dinge stehen davor.

Der Server führt nichts aus, es sei denn, er wurde mit PDML_ALLOW_EXECUTE=1 gestartet. Dies ist eine Entscheidung desjenigen, der den Server ausführt, und keine Anfrage kann sie ändern. Ohne diese Einstellung gibt dry_run=false status: refused mit dem angehängten Plan zurück, und es ist kein Fehler.

Der Agent sendet keine Ausführungsanfrage, ohne dass ein Betreiber den genauen Aufruf genehmigt. Der Genehmigungsdialog zeigt den Werkzeugnamen und die vollständigen Argumente als JSON, nicht eine Zusammenfassung. Eine Ablehnung gibt ein normales Ergebnis mit declined_by_operator zurück, und das Modell wird angewiesen, dies zu melden und anzuhalten, anstatt es erneut zu versuchen.

Jede Schicht allein würde einen unerwünschten Lauf stoppen. Beide zusammen bedeuten, dass keine perfekt sein muss. Die Richtlinie, die entscheidet, was einer Genehmigung bedarf, ist eine Funktion, needs_approval, klein genug, um auf einen Blick gelesen zu werden.

Die Ablaufverfolgung

Jeder Lauf hängt an traces/<timestamp>-<question>.jsonl an. Eine Zeile pro Ereignis, niemals überschrieben.

turn-Einträge führen die Schrittnummer, den Stop-Grund des Modells, dessen Text- und Denkzusammenfassung, die Aufrufe, die es gerade tätigen wird, und die Token-Nutzung dieses Turns auf. tool_call-Einträge führen das Werkzeug, seine Argumente, ob der Aufruf erfolgreich war, aus dem Cache kam oder gegatet wurde, seine Latenz, eine Zusammenfassung des Ergebnisses und den eigenen angegebenen Grund des Modells, entnommen aus dem Satz, den es neben dem Aufruf geschrieben hat. gate-Einträge führen die Entscheidung auf. run_start und run_end klammern dies mit Summen ein.

Die Systemaufforderung bittet das Modell, in einem Satz zu erklären, warum es jeden Aufruf tätigt – und es tut es. Aus der Ablehnungspfad-Ablaufverfolgung:

turn 1  "I'll start by finding the existing YG runs to confirm identifiers."
turn 2  "No results with those filters; let me broaden."
turn 3  "The constraint is named `standard-robustness`. Let me get the seed-0 run's config and results."
turn 4  "Now the dry-run plan for the requested run (matching epsilon 0.3 from the seed-0 baseline)."
turn 5  "Plan validated. Now executing it."          ← gate: declined
turn 6  "The training run was not executed: the operator declined ..."

Diese Ablaufverfolgung hat auch einen Fehler in den eigenen Werkzeugen dieses Repositorys aufgedeckt. Turn 1 lieferte ein leeres Ergebnis, weil list_experiments nach dem Ergebnisordnernamen filterte, während run_experiment den Klassennamen nahm – zwei Vokabulare für ein Konzept. Das Modell hat sich selbstständig erholt, auf Kosten eines Turns, und sein Grund in Turn 3 sagt genau, was es herausgefunden hat. list_experiments akzeptiert jetzt beide Schreibweisen.

Was die Demos gezeigt haben

Fünf Aufgaben, keine mit einer einzigen Antwort zu beantworten. Vollständige Transkripte in demo_output/, vollständige Ablaufverfolgungen in traces/.

A. Beste Logik innerhalb eines Genauigkeitsbudgets. Drei Turns. Läufe aufgelistet, alle vier Ergebnisse in einem parallelen Turn abgerufen, YG bei 0,9981 Sicherheit für 0,76 Punkte Genauigkeit geantwortet und gesagt, dass nichts ausgeführt wurde.

B. Variante eines bestehenden Laufs planen. Vier Turns. Konfiguration, Vergleich und Logikdefinition in einem parallelen Turn abgerufen, run_experiment mit dry_run=true aufgerufen, den Plan und den genauen Befehl gemeldet, und da ein passender Lauf existierte, diese verglichen.

C. Gegen einen nicht existierenden Lauf vergleichen. Drei Turns. Zuerst aufgelistet statt geraten, bestätigt, dass STL eine echte Logik ist, die einfach keinen Lauf hat, und dies mitgeteilt.

D. Trainieren, Betreiber lehnt ab. Sechs Turns. Zuerst mit einem Dry Run geplant, wie in der Werkzeugbeschreibung verlangt, dann die Ausführung angefordert. Der Betreiber hat abgelehnt. Das Modell meldete, dass es nicht ausgeführt wurde, und versuchte es nicht erneut, gab den Plan und antwortete mit dem, was existierte.

E. Trainieren, Betreiber genehmigt. Sechs Turns und ein echter Trainingslauf. Dieselbe Plan-dann-Ausführungssequenz; der Betreiber akzeptierte; der Server, gestartet mit aktivierter Ausführung, führte main.py für eine Epoche auf CPU in 28,6 Sekunden aus und schrieb fixtures/results/standard-robustness/mnist/1/YG.csv. Der Agent rief dann get_results und compare_runs für den neuen Lauf auf und meldete endgültiges Test-P-Metric 0,9160 und Test-C-Sec-self 0,5482. Beide stimmen mit der CSV überein. Unaufgefordert listete er die Störfaktoren gegen den Seed-0-Vergleich auf (eine Epoche versus zehn, Verzögerung, ein absichtlich geschwächtes Angriffsbudget) und stellte aus der Epoche-0-Zeile fest, dass die Constraint-Sicherheit bei einem untrainierten Modell trivialerweise 1,0 beträgt und nur zusammen mit einer konvergierten Genauigkeit etwas bedeutet. Dies ist eine korrekte Interpretation der Metrik.

Diese Seed-1-CSV ist ein echter Lauf und wird absichtlich neben den synthetischen Fixtures aufbewahrt. Ihre erste Zeile ist der argv, mit dem sie trainiert wurde, wie bei jedem anderen Lauf.

Zwei wissenswerte Dinge über die Daten

Epoche 0 ist eine Vor-Trainings-Evaluierung. Ein mit --epochs 10 konfigurierter Lauf schreibt elf Zeilen, nummeriert 0 bis 10. Zeilenanzahl und letzte Epoche werden getrennt gemeldet, da die Bezeichnung der Zeilenanzahl als „Epochen“ das Training um eins überschätzt.

Das Trainingsskript schreibt -1 für Metriken, die es nicht evaluiert hat. get_results normalisiert diese auf null, damit ein Sentinel nicht als Messung gelesen werden kann. Ein Basislauf hat überhaupt keine Constraint-Metriken, und er sollte dies so angeben, anstatt minus eins zu melden.

Grenzen, damit nicht übertrieben wird

Das Modell ist im Live-Betrieb über fünf Aufgaben hinweg nie auf ein is_error-Tool-Ergebnis gestoßen, da es der Anweisung folgte, vor dem Vertrauen auf eine Kennung aufzulisten. Der Fehlerpfad wird auf Protokollebene in smoke_test.py und auf Wrapperebene getestet, aber die Live-Erholung von einem Tool-Fehler während der Aufgabe wurde nicht demonstriert.

Memoisation wurde live nie aktiv. Das Modell wiederholte in keinem Lauf einen identischen Aufruf. Es ist getestet und in jeder Ablaufverfolgung im Leerlauf.

Prompt-Caching ist nicht konfiguriert. cache_read_input_tokens ist in jeder Ablaufverfolgung null, und die Eingabe-Token-Anzahlen (11k bis 46k pro Aufgabe) sind größtenteils erneut gesendeter Kontext. Cache-Breakpoints bei den Werkzeugdefinitionen und der Systemaufforderung würden dies erheblich reduzieren und sind die offensichtliche nächste Verbesserung.

Die Ausführung eines Laufs erforderte einen Checkout, dessen main.py parst. Im Upstream-main tut es dies nicht: --epsilon und --delta sind jeweils zweimal definiert, und argparse lehnt das Duplikat ab, bevor ein Argument gelesen wird, sodass python main.py --help fehlschlägt. Dies wurde im fix/duplicate-argparse-flags-Branch des Forks behoben, mit einem Regressionstest, und die Demo zeigte PDML_REPO_DIR auf diesen Checkout.

Probieren Sie es aus

uv sync
uv run python scripts/make_fixtures.py
uv run python scripts/smoke_test.py

Der Smoke-Test startet den Server über stdio, listet die Werkzeuge auf, ruft jedes auf, prüft, ob die Ausführung ohne PDML_ALLOW_EXECUTE verweigert wird, und prüft, ob eine unbekannte Experiment-ID einen Fehler ergibt, anstatt stillschweigend erfolgreich zu sein. Es kostet nichts.

Um den Agenten etwas zu fragen, mit gesetzter ANTHROPIC_API_KEY:

uv run python -m pdml_agent.agent "Which mnist run has the best constraint security?"
uv run python scripts/demo.py A B C D

Um ihn tatsächlich trainieren zu lassen, weisen Sie ihn auf einen Checkout von property-driven-ml, dessen main.py parst, und auf einen Interpreter mit torch hin, und übergeben Sie das Flag, das die Ausführung aktiviert:

export PDML_REPO_DIR=~/property-driven-ml
export PDML_PYTHON=~/property-driven-ml/.venv/bin/python
uv run python -m pdml_agent.agent --allow-execute "Train a one-epoch YG run on mnist at seed 2 ..."
uv run python scripts/demo.py E

Ihnen wird der genaue Aufruf gezeigt, und Sie werden gebeten, ihn zu genehmigen.

Umgebungsvariablen, die der Server liest: PDML_RESULTS_DIR (wo Läufe leben, Standard fixtures/results), PDML_REPO_DIR (der property-driven-ml-Checkout), PDML_PYTHON (Interpreter für main.py, andernfalls das .venv des Repositorys), PDML_ALLOW_EXECUTE (1 zur Erlaubnis der Ausführung), PDML_EXECUTE_TIMEOUT (Sekunden, Standard 3600).

-
license - not tested
-
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 Connectors

  • MCP server providing access to the Scorecard API to evaluate and optimize LLM systems.

  • MCP server for generating rough-draft project plans from natural-language prompts.

  • The MCP server for Azure DevOps, bringing the power of Azure DevOps directly to your agents.

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/HappyHackingOrange/pdml-agent'

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