KI‑Agenten containerisieren (damit sie beim Deploy nicht sterben)

Agents sicher containerisieren und shippen: Runtime‑Config, Secrets, Timeouts, Health Checks und failure‑freundliche Rollouts. Python + JS Beispiele.
Auf dieser Seite
  1. Problem (dein Agent lief… bis zum Deploy)
  2. Warum das in Prod scheitert
  3. Diagramm: was du wirklich deployest
  4. Echter Code: container‑freundlicher Entrypoint (Python + JS)
  5. Dockerfile (multi-stage, keine Secrets im Image)
  6. Realer Ausfall (incident-style, mit Zahlen)
  7. Abwägungen
  8. Wann NICHT containerisieren
  9. Copy/Paste Checkliste
  10. Safe default config (YAML)
  11. In OnceOnly umsetzen (optional)
  12. FAQ (3–5)
  13. Verwandte Seiten (3–6 Links)

Problem (dein Agent lief… bis zum Deploy)

Notebook‑Agent: okay.

Deploy‑Agent: hier lebt der Schmerz:

  • er erreicht das Netzwerk nicht, das du „angenommen“ hast
  • er OOMKill’t, weil jemand „full trace logging“ anmacht
  • er retried sich in eine Rate‑Limit‑Storm
  • Secrets sind im Image (bitte nicht)

Containerizing ist kein Dockerfile‑Theater. Es ist der Moment, wo du den Agent zwingst, sich wie ein Service zu benehmen.

Warum das in Prod scheitert

Agents sind unangenehme Workloads:

  • bursty Traffic (Traffic‑Spikes = Token‑Spikes)
  • viel I/O (Tools) + Hänger bei Timeouts
  • lange Tails (p95 okay, p99 Chaos)

Wenn dein Container Budgets/Timeouts nicht enforced, macht Production das. Nur eben via 504s, OOMKills und Rechnungen.

Diagramm: was du wirklich deployest

Echter Code: container‑freundlicher Entrypoint (Python + JS)

Langweilig ist gut:

  • Config aus Env
  • Budgets/Timeouts enforced
  • Health Endpoint
PYTHON
import os
import time
from dataclasses import dataclass
from typing import Any, Dict


@dataclass(frozen=True)
class Budgets:
  max_steps: int
  max_tool_calls: int
  max_seconds: int


def load_budgets() -> Budgets:
  return Budgets(
      max_steps=int(os.getenv("AGENT_MAX_STEPS", "25")),
      max_tool_calls=int(os.getenv("AGENT_MAX_TOOL_CALLS", "12")),
      max_seconds=int(os.getenv("AGENT_MAX_SECONDS", "60")),
  )


def run_request(task: str, *, budgets: Budgets) -> Dict[str, Any]:
  t0 = time.time()
  steps = 0
  tool_calls = 0

  while True:
      steps += 1
      if steps > budgets.max_steps:
          return {"output": "", "stop_reason": "max_steps"}
      if tool_calls > budgets.max_tool_calls:
          return {"output": "", "stop_reason": "max_tool_calls"}
      if time.time() - t0 > budgets.max_seconds:
          return {"output": "", "stop_reason": "max_seconds"}

      return {"output": "ok", "stop_reason": "finish"}


def health() -> Dict[str, str]:
  return {"ok": "true"}
JAVASCRIPT
export function loadBudgets() {
return {
  maxSteps: Number(process.env.AGENT_MAX_STEPS ?? 25),
  maxToolCalls: Number(process.env.AGENT_MAX_TOOL_CALLS ?? 12),
  maxSeconds: Number(process.env.AGENT_MAX_SECONDS ?? 60),
};
}

export function runRequest(task, { budgets }) {
const t0 = Date.now();
let steps = 0;
let toolCalls = 0;

while (true) {
  steps += 1;
  if (steps > budgets.maxSteps) return { output: "", stop_reason: "max_steps" };
  if (toolCalls > budgets.maxToolCalls) return { output: "", stop_reason: "max_tool_calls" };
  if ((Date.now() - t0) / 1000 > budgets.maxSeconds) return { output: "", stop_reason: "max_seconds" };

  return { output: "ok", stop_reason: "finish" };
}
}

export function health() {
return { ok: true };
}

Dockerfile (multi-stage, keine Secrets im Image)

DOCKERFILE
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci

FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
COPY --from=deps /app/node_modules ./node_modules
COPY . .
EXPOSE 3000
CMD ["npm","run","start"]

Realer Ausfall (incident-style, mit Zahlen)

Wir haben “debug logging” default‑on deployed. Es hat komplette Tool Results geloggt.

Impact an einem Nachmittag:

  • Memory stieg bis OOMKill
  • Retries verstärkten Load (Client retried, Agent retried Tools)
  • ~12% request failure rate
  • On‑Call: ~3 Stunden (weil Logs riesig und trotzdem nutzlos waren)

Fix:

  1. sampled logging + Redaction default (/de/observability-monitoring/agent-logging)
  2. Budgets im Runtime enforce’n (max seconds + max tool calls)
  3. Kill‑Switch‑Config, um teure Tools im Incident abzuschalten

Abwägungen

  • Tight Timeouts reduzieren Tail‑Latency und können Quality senken.
  • Mehr Logging hilft Debugging und tut Datenschutz/Kosten weh.
  • “One container per agent” ist simpel und teuer. Shared Services sind billiger und schwerer.

Wann NICHT containerisieren

Wenn du das nicht als Service betreibst (kein Traffic, keine SLOs), überbau’s nicht. Aber sobald echte User das triggern können, betreibst du einen Service. Glückwunsch.

Copy/Paste Checkliste

  • [ ] Budgets aus Env laden und im Runtime enforce’n
  • [ ] Tool‑Gateway: timeouts/retries/allowlists enforced
  • [ ] Health + readiness checks
  • [ ] Secrets via Platform injecten (nicht baked)
  • [ ] Kill switch config (disable tools / disable writes)
  • [ ] Logs strukturiert + sampled; PII‑Redaction default‑on

Safe default config (YAML)

YAML
runtime:
  env:
    AGENT_MAX_STEPS: 25
    AGENT_MAX_TOOL_CALLS: 12
    AGENT_MAX_SECONDS: 60
tools:
  allowlist: ["search.read", "http.get"]
  timeouts_ms: { default: 8000 }
  retries: { max: 2, backoff_ms: [200, 800] }
observability:
  sampled_tool_results: true
  result_sample_rate: 0.01
rollout:
  canary_percent: 10
  rollback_on_error_rate: 0.05

In OnceOnly umsetzen (optional)

In OnceOnly umsetzen
Budgets + Tool‑Gateway Defaults, die Deployments überleben.
In OnceOnly nutzen
# onceonly-python: tool allowlist + governed tool call
import os
from onceonly import OnceOnly

client = OnceOnly(
    api_key=os.environ["ONCEONLY_API_KEY"],
    timeout=5.0,
    max_retries_429=2,
)

agent_id = "billing-agent"

client.gov.upsert_policy({
    "agent_id": agent_id,
    "allowed_tools": ["search.read", "http.get"],
    "max_actions_per_hour": 200,
    "max_spend_usd_per_day": 10.0,
})

res = client.ai.run_tool(
    agent_id=agent_id,
    tool="http.get",
    args={"url": "https://example.com/health"},
    spend_usd=0.001,
)
if not res.allowed:
    raise RuntimeError(res.policy_reason)

FAQ (3–5)

Soll ich Prompts/Modelle ins Image backen?
Code ja, Secrets nein. Prompts können versioniert im Repo sein. Modelle sind Config. Secrets nur zur Runtime.
Häufigster Deploy‑Fail?
Timeouts + Retries, die sich gegenseitig verstärken. 504s und Storms. Retries an einer Stelle, Budgets cap’en.
Brauche ich Kubernetes?
Nicht zwingend. Du brauchst Budgets, Observability und Rollback. Das geht auch auf einfacheren Plattformen.
Wie rolle ich sicher zurück?
Kill switch + vorheriges Image/Prompt‑Version ready. Rollback auf error‑rate + spend spikes.

Nicht sicher, ob das dein Fall ist?

Agent gestalten ->
⏱️ 5 Min. LesezeitAktualisiert Mär, 2026Schwierigkeit: ★★★
Integriert: Production ControlOnceOnly
Guardrails für Tool-Calling-Agents
Shippe dieses Pattern mit Governance:
  • Budgets (Steps / Spend Caps)
  • Tool-Permissions (Allowlist / Blocklist)
  • Kill switch & Incident Stop
  • Idempotenz & Dedupe
  • Audit logs & Nachvollziehbarkeit
Integrierter Hinweis: OnceOnly ist eine Control-Layer für Production-Agent-Systeme.
Autor

Diese Dokumentation wird von Engineers kuratiert und gepflegt, die AI-Agenten in der Produktion betreiben.

Die Inhalte sind KI-gestützt, mit menschlicher redaktioneller Verantwortung für Genauigkeit, Klarheit und Produktionsrelevanz.

Patterns und Empfehlungen basieren auf Post-Mortems, Failure-Modes und operativen Incidents in produktiven Systemen, auch bei der Entwicklung und dem Betrieb von Governance-Infrastruktur für Agenten bei OnceOnly.