Prädiktive KI: Geben Sie Ihrem Agenten ein Docker-Labor zum Ausführen von Modellen
Inhaltsverzeichnis
Jede KI-Agent-Demo zeigt dasselbe: “Fragen Sie Ihren Agenten zu Ihren Daten.” Der Agent fragt eine Datenbank ab, fasst Ergebnisse zusammen, erstellt vielleicht ein Diagramm.
Aber versuchen Sie mal, ihn zu bitten, eine polynomiale Regression auf 24 Monaten Verkaufsdaten auszuführen und die nächsten 6 Monate vorherzusagen. Plötzlich stößt Ihr Chat-basierter Agent an eine Wand. Er kann kein import sklearn machen. Er kann kein Python ausführen. Er kann darüber nachdenken, welches Modell er verwenden soll, aber er kann keines tatsächlich ausführen.
Die offensichtliche Lösung: Geben Sie ihm eine Python-Umgebung. Aber hier ist die Frage, über die niemand spricht - sollte diese Umgebung die Standard-Laufzeitumgebung des Agenten sein (wie Claude Code), oder sollte sie ein Werkzeug sein, das der Agent bei Bedarf wählt?
Wir haben eine Demo gebaut, die diese Frage beantwortet.
TL;DR
- Das “Environment as a Tool”-Muster lässt Ihren KI-Agenten selektiv eine Docker-Sandbox nur bei Bedarf nutzen und vermeidet Overhead bei einfachen Abfragen.
- Der Sub-Agent-Delegationsansatz hält den Hauptagenten sauber - er beschreibt was vorhergesagt werden soll, und der Sub-Agent findet heraus, wie der Python-Code geschrieben wird.
- Strukturierte Pydantic-Ausgabe für Charts schlägt Bildgenerierung - senden Sie Daten, lassen Sie das Frontend mit Chart.js rendern.
- WebSocket-Streaming mit
agent.iter()bietet Echtzeit-Einblick in Text, Tool-Aufrufe und Ergebnisse. - Der schwierigste Teil ist die Frontend-Integration - das Zusammenführen von Chart-Serien mit unterschiedlichen Datumsbereichen, das Abfangen von Tool-Ausgaben und das Streaming über WebSocket.
Die Demo: Prädiktiver Analytics-Agent
Wir haben eine Full-Stack-Demo-App gebaut - einen Chat-basierten Analytik-Assistenten, der:
- Daten abfragen kann - monatliche Verkaufsdaten filtern und aggregieren (3 Produkte, 3 Regionen, 24 Monate)
- Vorhersagen ausführen kann - ein Sub-Agent schreibt und führt Python (sklearn, pandas) in einem isolierten Docker-Container aus
- Charts generieren kann - strukturierte Pydantic-Ausgabe, gerendert als Chart.js-Liniendiagramme im Browser
Der Hauptagent hat drei Werkzeuge. Zwei sind einfach (JSON abfragen, Chart-Daten zurückgeben). Das dritte wird interessant - es startet einen Sub-Agenten, der vollen Zugriff auf eine Docker-Sandbox hat.
Die Architektur: Umgebung als Werkzeug
Hier ist die entscheidende Design-Entscheidung: Die Docker-Sandbox ist ein Werkzeug, nicht die Standard-Umgebung des Agenten.
analytics_agent: Agent[AnalyticsDeps, str] = Agent( "openai:gpt-4.1", deps_type=AnalyticsDeps,)Der Hauptagent ist ein regulärer Pydantic AI Agent. Er lebt nicht innerhalb von Docker. Er hat drei Werkzeuge registriert mit @analytics_agent.tool:
query_data- liest eine JSON-Datei, filtert Datensätze, gibt Ergebnisse zurück. Kein Docker nötig.predict- erstellt einen Sub-Agenten mit Docker-Zugriff, delegiert die Vorhersageaufgabe.generate_chart- gibt strukturiertesLineChartData(ein Pydantic-Modell) zurück, das das Frontend als Chart.js-Diagramm rendert.
Der Agent entscheidet wann er Docker nutzt. Wenn Sie fragen “zeige mir die Gesamtverkäufe nach Produkt”, ruft er query_data auf - schnell, kein Overhead. Wenn Sie fragen “sage die Widget-Alpha-Verkäufe für die nächsten 6 Monate voraus”, ruft er predict auf - der einen Sub-Agenten innerhalb von Docker startet.
Das Predict-Werkzeug: Sub-Agent mit Docker-Labor
Das ist das Kernmuster. Das predict-Werkzeug führt keinen Code selbst aus - es delegiert an einen Sub-Agenten, der vollen Zugriff auf einen Docker-Container hat:
@analytics_agent.toolasync def predict( ctx: RunContext[AnalyticsDeps], task_description: str,) -> str: """Run a prediction using Python in a Docker sandbox.""" sandbox = ctx.deps.sandbox
# Write sales data into the Docker container sandbox.write("/workspace/sales_data.json", data_content)
# Create a sub-agent with Docker tools console_toolset = create_console_toolset( include_execute=True, require_write_approval=False, require_execute_approval=False, )
sub_agent: Agent[SandboxDeps, str] = Agent( "openai:gpt-4.1", system_prompt="You are a data science code executor...", deps_type=SandboxDeps, toolsets=[console_toolset], )
result = await sub_agent.run( f"Perform this prediction task:\n\n{task_description}", deps=SandboxDeps(backend=sandbox), ) return result.outputWas passiert Schritt für Schritt:
- Hauptagent empfängt: “Sage die Widget-Alpha-Verkäufe für die nächsten 6 Monate voraus”
- Hauptagent ruft
predict(task_description="...")auf - Verkaufsdaten werden in den Docker-Container unter
/workspace/sales_data.jsongeschrieben - Ein neuer Sub-Agent wird mit
create_console_toolset()erstellt - mitls,read,write,executeund anderen Dateioperationen - Der Sub-Agent schreibt ein Python-Skript mit pandas + sklearn
- Der Sub-Agent führt das Skript innerhalb von Docker aus
- Ergebnisse fließen zurück zum Hauptagenten, der sie dem Benutzer erklärt
Der Sub-Agent weiß nicht, dass er ein Sub-Agent ist. Er sieht nur einen System-Prompt, der sagt “du bist ein Data-Science-Code-Executor” und Werkzeuge zum Lesen/Schreiben/Ausführen von Dateien. Die Docker-Sandbox ist komplett transparent.
Strukturierte Charts mit Pydantic
Das dritte Werkzeug - generate_chart - demonstriert strukturierte Ausgabe. Statt rohen Text zurückzugeben, gibt es ein Pydantic-Modell zurück:
class DataPoint(BaseModel): x: str # e.g. "2024-01" y: float
class ChartSeries(BaseModel): name: str data_points: list[DataPoint]
class LineChartData(BaseModel): title: str x_label: str y_label: str series: list[ChartSeries]Das generate_chart-Werkzeug nimmt Chart-Parameter vom LLM und gibt serialisiertes LineChartData mit einem speziellen Präfix (CHART_DATA:) zurück. Der Server fängt diesen Präfix im WebSocket-Stream ab und sendet ihn als chart_data-Nachricht an das Frontend:
if result_str.startswith(CHART_DATA_PREFIX): chart_json = result_str[len(CHART_DATA_PREFIX):] await websocket.send_json( {"type": "chart_data", "data": json.loads(chart_json)} )Das Frontend empfängt es und rendert es mit Chart.js. Keine Bilder, kein base64, kein matplotlib - nur strukturierte Daten, die vom Agenten zum Browser fließen.
Die Umgebungsfrage: Werkzeug vs. Standard
Das ist die Design-Frage, die ich am Anfang erwähnt habe. Es gibt zwei Wege, einem Agenten eine Code-Ausführungsumgebung zu geben:
Option A: Umgebung als Werkzeug (was wir gebaut haben)
Der Agent lebt außerhalb von Docker. Er hat ein predict-Werkzeug, das an einen Sub-Agenten innerhalb von Docker delegiert. Der Agent entscheidet wann er es nutzt.
Option B: Standard-Umgebung (wie Claude Code) Der Agent lebt innerhalb von Docker. Jeder Befehl, den er ausführt, jede Datei, die er liest - alles ist in der Sandbox. Die Umgebung ist immer da.
Wann jede Option sinnvoll ist:
| Umgebung als Werkzeug | Standard-Umgebung | |
|---|---|---|
| Am besten für | Domänenspezifische Aufgaben (Vorhersagen, Datenanalyse, Code-Review) | Allzweck-Coding-Agenten |
| Agent-Kontrolle | Agent entscheidet, wann die Sandbox genutzt wird | Agent läuft immer in der Sandbox |
| Overhead | Zahlt Docker-Kosten nur bei Bedarf | Immer aktiv |
| Flexibilität | Kann Werkzeuge frei mischen | Alles geht durch die Sandbox |
| Komplexität | Braucht Sub-Agent-Delegationsmuster | Einfacher - Agent hat einfach Werkzeuge |
Für unsere prädiktive Analytik-Demo ist Option A klar die richtige Wahl. Der Agent beantwortet hauptsächlich Fragen zu Daten (kein Docker nötig) und führt Docker nur aus, wenn er sklearn-Code ausführen muss. Docker als Standard-Umgebung zu verwenden, würde unnötige Latenz zu jeder Interaktion hinzufügen.
Aber für einen Coding-Agenten wie Claude Code macht Option B Sinn - die gesamte Aufgabe des Agenten ist Lesen, Schreiben und Ausführen von Code. Die Umgebung ist das Produkt.
Echte Ergebnisse
Hier ist, was die Demo tatsächlich produziert. Fragen Sie sie: “Analysiere die saisonalen Muster von Widget Beta und sage die nächsten 12 Monate voraus”:
Der Sub-Agent wählte Holt-Winters exponentielle Glättung (passend für saisonale Daten), führte sie innerhalb von Docker aus und gab strukturierte Vorhersagen zurück. Der Hauptagent rief dann generate_chart mit historischen und prognostizierten Daten als separate Serien auf.
Der gesamte Ablauf - von der Benutzernachricht bis zum gerenderten Chart - geschieht über eine einzige WebSocket-Verbindung mit Echtzeit-Streaming von Text, Tool-Aufrufen und Chart-Daten.
Das WebSocket-Streaming-Protokoll
Der Server nutzt Pydantic AIs agent.iter() für Echtzeit-Streaming. Jedes Modell-Token, jeder Tool-Aufruf und jedes Tool-Ergebnis wird an das Frontend gestreamt:
async with analytics_agent.iter( user_message, deps=deps, message_history=message_history,) as run: async for node in run: if Agent.is_model_request_node(node): # Stream text deltas and tool call deltas async with node.stream(run.ctx) as stream: async for event in stream: if isinstance(event, PartDeltaEvent): if isinstance(event.delta, TextPartDelta): await ws.send_json({ "type": "text_delta", "content": event.delta.content_delta }) elif Agent.is_call_tools_node(node): # Stream tool execution events ...Das Frontend zeigt Tool-Karten, die sich öffnen, um Argumente und Ergebnisse anzuzeigen, Text-Streaming Token für Token und Charts inline gerendert - alles über einen WebSocket.
Ein Stolperstein: Chart.js Multi-Serien mit unterschiedlichen Bereichen
Wir sind während der Entwicklung auf einen interessanten Bug gestoßen. Beim Zeichnen von “Historical” (2024-01 bis 2025-12) neben “Forecast” (2026-01 bis 2026-06) verwendete Chart.js nur Labels der ersten Serie. Prognosepunkte wurden auf historische Daten abgebildet.
Die Lösung: alle einzigartigen x-Labels über alle Serien zusammenführen, dann eine Lookup-Map pro Serie mit null für fehlende Daten verwenden:
const allLabels = [...new Set( chartData.series.flatMap((s) => s.data_points.map((dp) => dp.x)))].sort();
const datasets = chartData.series.map((s, i) => { const lookup = new Map(s.data_points.map((dp) => [dp.x, dp.y])); return { label: s.name, data: allLabels.map((x) => lookup.get(x) ?? null), spanGaps: false, // ...styling };});Kleinigkeit, aber es ist die Art von Bug, die Ihre Prognose komplett falsch aussehen lässt, während die Daten tatsächlich korrekt sind.
Wichtigste Erkenntnisse
- “Umgebung als Werkzeug” ist das richtige Muster, wenn Ihr Agent nur manchmal Code-Ausführung braucht. Zahlen Sie nicht den Docker-Overhead bei jeder Interaktion.
- Sub-Agent-Delegation hält den Hauptagenten sauber. Der Hauptagent beschreibt was vorhergesagt werden soll. Der Sub-Agent findet heraus, wie der Python-Code geschrieben wird.
- Strukturierte Pydantic-Ausgabe für Charts schlägt Bildgenerierung. Senden Sie Daten, lassen Sie das Frontend rendern. Einfacher zu stylen, interaktiv und keine base64-Blobs.
- WebSocket-Streaming mit
agent.iter()gibt Ihnen Echtzeit-Einblick in das, was der Agent tut - Text, Tool-Aufrufe und Ergebnisse. - Der schwierigste Teil ist nicht der Agent - es ist die Frontend-Integration (Zusammenführen von Chart-Serien mit unterschiedlichen Datumsbereichen, Abfangen von Tool-Ausgaben, Streaming über WebSocket).
Selbst ausprobieren
pydantic-ai-backend - Docker-Sandbox, Console-Toolset und Backend-Abstraktionen für Pydantic AI Agenten
Die vollständige Demo ist in examples/predictive_analytics/:
pip install pydantic-ai-backend[docker,console]export OPENAI_API_KEY=your-keyuvicorn examples.predictive_analytics.server:app --port 8000Verwandte Artikel
Von create-react-app zu create-ai-app: Der neue Standard für KI-Anwendungen
2016 standardisierte create-react-app, wie wir Frontends bauen. 2026 brauchen KI-Anwendungen denselben Moment — und er i...
AGENTS.md: So machen Sie Ihre Codebasis KI-Agenten-freundlich (Copilot, Cursor, Codex, Claude Code)
Jedes KI-Coding-Tool liest Ihr Repository anders. So gibt AGENTS.md — der aufkommende Tool-agnostische Standard — ihnen...
Von 0 zum produktionsreifen KI-Agenten in 30 Minuten — Full-Stack-Template mit 5 KI-Frameworks
Schritt-fuer-Schritt-Anleitung: Web-Konfigurator, Preset waehlen, KI-Framework auswaehlen, 75+ Optionen konfigurieren, d...