Tools sind keine Prompts: Warum Agent-Aktionen Idempotenz, Auth und Audit benötigen

Ein Tool-Call kann Geld bewegen und Konten verändern. Warum Idempotenz, Auth und Audit in Agent-Architekturen keine Kür, sondern Pflicht sind.

· 39 Min. Lesezeit
Podcast --:--
0:00 / --:--

Der Agent bestätigt die Rückerstattung. Das Geld kommt nie.

Ein Tool-Call kann Geld bewegen, Konten verändern, Daten schreiben oder Aktionen auslösen. Damit verschiebt sich die Verantwortung von “gutes Wording” zu “sicherer Ausführung”. Tools sind Contracts. Und was das für die CI bedeutet, habe ich in einem vorherigen Blogpost geschrieben.

AI Systems Architecture beginnt dort, wo Modelloutput Teil der Produktlogik wird
Wie ich aus einem GitHub Issue einen testbaren, beobachtbaren Umsetzungsplan mache
rubeen.dev/blog/ai-systems-architecture

Basierend auf einem bereits eingeführten Beispiel zu einem Multi-Agent System für Supportfälle beschäftigen wir uns in diesem Post mit der Absicherung von Agent-Tools.

Tools sind keine Prompts

Früher etwas besonderes, mittlerweile Alltag: Ich kann Tools in meine LLM-Chatfenster bringen, die Dinge in meinem Namen erledigen. OpenAI etwa bietet einen Katalog an Apps, die ich in meinen Chat einbauen kann - eigene Playlists erstellen lassen, basierend auf meinen Interessen, oder andere Dinge.

Apps in OpenAI

Sobald Tools also Aktionen ausführen - und nicht nur Informationen zusammensuchen - muss die Frage gestellt werden: Wer ist dafür verantwortlich, dass nichts im Namen des Nutzers passiert, das dieser nicht wollte? Die App-Hersteller, die diese Integrationen bereitstellen, sind dazu in der Pflicht. Und auch die LLM-Anbieter schützen den Nutzer, indem eine Anfrage an ein solches Tool mindestens beim ersten Mal genehmigt werden muss.

Doch soll es hier nicht darum gehen, wie wir eine solche Integration in den Chat eines LLM-Anbieters einbinden; nein, hier geht es darum, Tools in eigene Agent-Lösungen einzubinden. Chat-Interfaces sind da eine mögliche Ansicht - eine viel spannendere sind aber AI-Feature UIs, die viel tiefer integriert sind. Um direkt mit bereits eingeführten Konzepten mitzugehen - und weil das Beispiel so treffend ist - belassen wir es aber in diesem Post bei einer Chat-basierten Anwendung: dem refund-agent.

Evals in die CI: Wenn AI-Features aufhören, Prompts zu sein
... und anfangen, Software zu werden - mit den entsprechenden Testpflichten
rubeen.dev/blog/ai-evals-ci-pipeline
Kurze Beschreibung des Refund Agents

Der Refund Agent ist Teil eines Multi-Agent Support-Teams, orchestriert von einem Orchestrator - und zuständig für die Abwicklung von Rückgaben sowie deren Statusabfrage. Der Support-Agent hat Tools, um unter anderem die Rückgabe anzustoßen.

Refund-Agent
Multi-Agent Support-System mit Policy Gate, Tool-Sicherheit und Audit-Trail

Ein Tool-Call ist keine intelligentere Antwort, sondern der Beginn eines Zustandsübergangs.

Sobald Tool-Calls Aktionen ausführen und damit Daten verändern, verändert sich der Zustand des Nutzers - im eigenen oder im Fremdsystem.

Die Prompts formulieren die Absicht, die API hinter den Tools garantiert die korrekte Funktion. Ab diesem Punkt reicht Prompt-Qualität als Sicherheitsgrenze nicht mehr aus. Das Modell darf vorschlagen, was passieren soll - aber es darf nicht bestimmen, was tatsächlich passiert. Evals prüfen, ob sich das LLM korrekt verhält - aber nicht, ob die Tools korrekt arbeiten.

Wer dennoch Tools wie Prompts behandelt, der verwechselt die Sprachoberfläche mit Systemverantwortung.

Die MCP-Spezifikation definiert Tools explizit als Seiteneffekt-behaftete Operationen:

M
Tools - Model Context Protocol
modelcontextprotocol.io

Der teuerste Fehler heißt False Success

Ein False Success entsteht, wenn das System Erfolg meldet, obwohl keiner eingetreten ist. Ein roter Stacktrace ist selten dein schlimmstes Problem. Schlimmer ist ein grüner Chatverlauf mit kaputtem Backend-Zustand.

Ein schon im vorherigen Post angedeutetes Beispiel war: Der Refund-Agent bestätigt dem Nutzer höflich, präzise und empathisch, dass die Rückerstattung eingeleitet wurde. Nur wurde sie nicht eingeleitet - das Modell hat die Tool-Antwort falsch interpretiert, der Nutzer ist darauf hin enttäuscht - hat den Chat-Verlauf evtl. sogar aufgehoben und wendet sich dann mit diesem ans Unternehmen.

Dieses Szenario ist ein einfaches Beispiel, um den Begriff des “False Success” zu beschreiben - und das ist sicherlich einer der teuersten Fehler, die entstehen können. Denn: im Backend fliegt keine Exception, ohne Weiteres gibt es keine Timeouts, kein Retry. Einfach ein “ist erledigt”. Früher habe ich von ChatGPT oft zu hören bekommen: “Klar bereite ich dir deine 200-Seitige Ausarbeitung im Stil einer Master-Arbeit aus, ich melde mich dann zurück, wenn ich fertig bin”. Auf eine Antwort warte ich bis heute.

Mögliche Ursachen eines False Success sind unter anderem:

Zu diesen Punkten gibt es direkt sichtbare Lösungsansätze: Traces mit Request-IDs, die Verwendung fachlicher Status für Rückgabewerte, die dem Modell klar kommuniziert werden, wie für alle AI-Features in Produktion empfehlenswert: Evals.

Das eigentliche Risiko ist nicht, dass das Modell Unsinn sagt. Das Risiko ist, dass das System diesen Unsinn wie Wahrheit behandelt.

Sobald Retries, Timeouts und Netzfehler relevante Störungen werden (also die Anwendung in Produktion ist), kommt man an einer Eigenschaft nicht mehr vorbei: Idempotenz.

Idempotenz: Retries dürfen keine Doppelwirkung erzeugen

Retries können auftreten - vor allem in Produktion sollten mehrfach ausgeführte State-verändernde Aktionen immer eingeplant werden, wenn sie vom Nutzer direkt ausgeführt werden können. In der Kommunikation zwischen zwei aufeinander abgestimmten Systemen sollte zumindest darüber gesprochen werden, was bei einem Retry passiert. AI sollte in diesem Kontext als Nutzer betrachtet werden - niemals als abgestimmtes Partnersystem. In einem nichtdeterministischen System lässt sich schlicht nicht kontrollieren, was wann aufgerufen wird.

Idempotenz ist die Eigenschaft, die aus unzuverlässigem Transport verlässliche Wirkung baut. Wer hingegen Seiteneffekte ohne Idempotenz baut, hat das Netzwerk nicht ernst genommen.

Gründe für wiederholte Anfragen in deterministischen Systemen

Gründe für wiederholte Anfragen in nichtdeterministischen AI-Systemen

Besonders bei nichtdeterministischen Systemen kommt noch hinzu, dass ein Retry oft nicht identisch zum initialen Aufruf sein kann - und so wird der gleiche Endpunkt mit leicht anderen Parametern aufgerufen. Dadurch wird die Idempotenz schwieriger zu prüfen.

Wichtig ist es demnach, nicht nur das Duplikat zu erkennen, sondern die semantisch gleichwertige Antwort zu liefern, falls der Request wiederholt wurde.

Tools & Mechanismen zum Mitigieren in deterministischen Systemen

D
REL04-BP04 Make mutating operations idempotent - AWS Well-Architected Framework
An idempotent service promises that each request is processed exactly once, such that making multiple identical requests has the same effect as making a single request. This makes it easier for a client to implement retries without fear that a request is erroneously processed multiple times. To do this, clients can issue API requests with an idempotency token, which is used whenever the request is repeated. An idempotent service API uses the token to return a response identical to the response that was returned the first time that the request was completed, even if the underlying state of the system has changed.
docs.aws.amazon.com

Bei deterministischen Systemen kommen demnach bekannte Techniken zum Einsatz - bei nichtdeterministischen Systemen haben wir dennoch ebenfalls eine Reihe an Möglichkeiten:

Tools & Mechanismen zum Mitigieren in nichtdeterministischen Systemen

Der auffälligste Unterschied zur bekannten Welt ist, dass Idempotenz nicht mehr nur auf exakte Duplikate angewendet werden muss, sondern auf semantische - das Modell kann den gleichen Intent mit leicht variierenden Parametern ausdrücken.

Idempotenz ist keine Optimierung. Sie ist die Mindestvoraussetzung für sichere Mutationen.

Idempotenz sichert ab, dass Wiederholungen keinen Schaden anrichten. Doch wer darf den Aufruf überhaupt auslösen?

Auth: das Modell darf keine Berechtigungen erteilen

Blicken wir auf das eingangs eingeführte Beispiel zurück: Die Frage ist nicht, ob der Agent den ausgelösten Refund plausibel fand. Die Frage muss lauten: Darf das konkrete Subjekt [User] diese konkrete Aktion [Refund] an diesem konkreten Objekt [bestellter Artikel X] ausführen?

Die Entscheidungsgewalt liegt keineswegs beim Agent. Sie muss auf deterministischer Seite definiert und durch Authentifizierung, Autorisierung und Business-Verification-Rules abgesichert sein.

Identitätsprüfung im Chat ist keine Authentifizierung

Verify a customer's identity by checking their customer ID and email address. Must be called before processing sensitive operations for unverified customers.
System Prompt des verify_customer Tools

Das verify_customer-Tool dient dazu, den Agent mit dem Nutzer vertraut zu machen: Name, Kundennummer, Email. Aber es ersetzt niemals eine echte Authentifizierung des Nutzers gegenüber dem System. Es ist eine Kontextanreicherung für das Modell - kein Login. Der Nutzer muss sich mindestens einmal gegenüber dem System authentifizieren, bevor der Agent Zugriff auf personenbezogene Daten oder mutierende Aktionen bekommt.

Wer das verify_customer-Ergebnis als Autorisierung behandelt, hat gerade eine natürlichsprachliche Eingabe zur Sicherheitsgrenze erklärt.

Der Refund-Agent verweigert korrekt den Zugriff auf eine fremde Bestellung und das Zurücksetzen eines fremden Passworts - Autorisierung findet im Backend statt, nicht im Chat.
Autorisierung im Backend: Der Agent kann nur ausführen, was das System erlaubt - nicht, was der Nutzer fordert.

Klassische API-Risiken treffen Agent-Systeme härter

Die OWASP API Security Top 10 (2023) sind für klassische APIs geschrieben - aber drei Risiken treffen Agent-Architekturen besonders hart, weil natürlichsprachliche Interfaces die Angriffsvektoren verschleiern.

O
OWASP API Security Top 10
OWASP API Security Top 10 2023 edition
owasp.org

BOLA - Broken Object Level Authorization (API1:2023)

Der Nutzer übergibt eine order_id im Chat, das Modell schickt diese an refund_order. Ohne serverseitige Prüfung, ob diese Bestellung dem authentifizierten Nutzer gehört, entsteht ein klassischer BOLA-Fall - nur dass der Angreifer die ID nicht in einer URL manipuliert, sondern in natürlicher Sprache übergibt.

Der Agent hat legitimen Zugriff auf das refund_order-Tool. Die Frage ist ausschließlich, ob er es auf dieses Objekt anwenden darf.

BFLA - Broken Function Level Authorization (API5:2023)

Ein Support-Agent, der für Rückgaben zuständig ist, darf nicht plötzlich Administrations- oder Finanzfunktionen aufrufen können - auch nicht, wenn ein Nutzer angemeldet ist. Wenn der Orchestrator dem Agent ein breites Toolset zur Verfügung stellt, muss die Frage lauten: Welche Tools darf dieser Agent für diesen Nutzer aufrufen?

Unrestricted Access to Sensitive Business Flows (API6:2023)

Ein Agent ist Automatisierung - er kann Dutzende Tool-Calls in Sekunden absetzen, ohne dass ein Mensch jeden einzelnen prüft. Ein refund_order-Tool ohne Rate-Limiting, ohne Approval-Schwelle und ohne Business-Rule Check auf der API-Seite ist genau die Schwachstelle, die OWASP hier beschreibt - nur dass der Automatisierer dieses Mal kein Skript ist, sondern ein LLM.

MCP Security Best Practices: Was die Spezifikation verlangt

Wer MCP-Server anbindet, sollte die Security Best Practices der Spezifikation kennen - mehrere Prinzipien sind direkt auf Agent-Tool-Architekturen anwendbar.

M
Security Best Practices - Model Context Protocol
Security considerations, attack vectors, and best practices for MCP implementations
modelcontextprotocol.io

Kein Token-Passthrough

Token-Passthrough ist ein Anti-Pattern, bei dem ein MCP-Server Tokens vom Client akzeptiert, ohne zu validieren, dass diese korrekt für den MCP-Server ausgestellt wurden, und sie an die nachgelagerte API weiterreicht.

Ein MCP-Server kann die einzelnen Clients nicht identifizieren, wenn Tools mit Tokens aufgerufen werden, die für einen vorgelagerten Service ausgestellt wurden. Logs auf dem nachgelagerten Server zeigen Requests, die scheinbar von einer anderen Quelle mit anderer Identität stammen. Für Audit und Incident-Response ist das fatal. Deshalb dürfen MCP-Server keine Tokens akzeptieren, die nicht explizit für den MCP-Server ausgestellt wurden.

Übertragen auf den Refund-Agent bedeutet das: Der Agent startet mit Read-Only Zugriff. Erst wenn eine mutierende Aktion wie refund_order aufgerufen wird, wird ein Step-up durchgeführt - mit engem Scope, kurzer TTL und klarer Audit-Bindung.

Häufige Fehler laut MCP-Spezifikation: Verwendung von Wildcard-Scopes wie *, all, full-access, sowie das Bündeln von nicht zusammengehörenden Privilegien, um zukünftige Berechtigungsanfragen zu vermeiden.

SSRF-Risiken bei OAuth Discovery

Dieser Punkt betrifft eher die Client-Seite, ist aber für selbstgebaute Orchestratoren relevant: ein bösartiger MCP-Server kann OAuth Metadatenfelder mit URLs befüllen, die auf interne Ressourcen zeigen - Cloud-Metadaten Endpunkte, lokale Services oder interne Netzwerk-Adressen. Wer einen MCP-Client betreibt, der sich dynamisch mit Servern verbindet, muss diese URLs validieren, bevor sie aufgelöst werden.


Die Autorisierung in Agent-Systemen ist kein Prompt-Problem - sie ist ein Policy und API-Problem. Daraus ergeben sich konkrete Verantwortlichkeiten:

Identitätsprüfung im Chat ersetzt keine Autorisierung im Backend, je kritischer der Geschäftsfluss, desto härter muss die Autorisierung hinter dem Agent sein.

Viele klassische API-Risiken tauchen auch in Agent-Systemen auf. Sie werden durch die natürlichsprachlichen Interfaces jedoch leichter übersehen - und genau deshalb benötigen sie explizite Absicherung.

Audit: Ohne eine Beweiskette ist Automatisierung nur eine Behauptung

Sobald es um Geld, Konten oder Berechtigungen geht, reicht es nicht aus, irgendwo Logs zu haben: wer einen Agenten produktiv handeln lässt, benötigt eine Beweiskette - nicht nur Konversation.

Tracing ist kein Auditing

Die beiden Begriffe werden oft vermischt, decken aber grundverschiedene Bedürfnisse ab. Tracing dient dem Debugging: wo war der Engpass, warum kam ein Timeout, welcher Service hat wie lange gebraucht? Audit Logging dagegen dient der Verantwortlichkeit: Wer hat was, wann und wo mit welchem Ergebnis getan?

In einem Agent-System wie im Beispiel ist diese Unterscheidung noch kritischer, weil zwei Wahrheiten nebeneinander existieren:

Ein Chat-Transkript erklärt, was gesagt wurde, ein Audit-Log erklärt, was wirklich passiert ist.

Warum Chat-Transkripte keine Audit-Logs ersetzen

Ein Chatprotokoll ist die Sicht des Modells auf die Welt. Es enthält die Formulierung des Agents, nicht den tatsächlichen Systemzustand. Wenn der Agent sagt: “Ihre Rückerstattung wurde eingeleitet”, dann steht das im Transkript - unabhängig davon, ob die API tatsächlich einen 200 OK mit Status approved zurückgegeben hat, oder ob der Call ins Timeout gelaufen ist und der Agent das Fehlen einer Fehlermeldung als Erfolg interpretiert hat.

Der Kunde behauptet, der Agent habe einem Refund zugesagt, das Support-Team (menschlich) sieht im Chatverlauf die Zusage, aber keine belastbare Kette aus Freigabe, Tool-Execution und Commit. Genau hier liegt die Anwendung eines Audit-Logs.

Gerade bei Agenten ist es deshalb wichtig, die Modellbehauptungen vom bestätigten Domänenereignis zu trennen. Für mich gehören mindestens zwei Ebenen in den Logpfad: erstens der Versuch der Aktion, zweitens der fachlich committete Effekt.

Sinnvolle Bestandteile eines Audit-Eintrages:

Ich möchte nicht nur wissen, dass ein Tool aufgerufen wurde, sondern von wem, in wessen Auftrag mit welchem Freigabekontext und zu welchem Ergebnis.

Trace-Ansicht des Refund-Agents: chronologische Darstellung aller Einträge von User Message über Route Decision, Tool Calls mit Gate Allow bis zur finalen Agent Response.
Trace eines Refund-Requests: Jeder Schritt - vom Routing über Tool-Calls mit Gate-Entscheidungen bis zur Antwort - ist nachvollziehbar.
Vollständiger Request-Trace als JSON
{
  "id": "req_177464138774458839119",
  "requestId": "req_177464138774458839119",
  "startedAt": "2026-03-27T19:56:27.744Z",
  "completedAt": "2026-03-27T19:56:33.339Z",
  "userMessage": "max@example.com C001",
  "route": "refund",
  "entries": [
    {
      "id": "1bf8df49-27d3-491a-897d-046888900bcf",
      "timestamp": "2026-03-27T19:56:27.744Z",
      "type": "user_message",
      "data": { "message": "max@example.com C001" }
    },
    {
      "id": "86ab3b56-ef2d-45bc-9673-563349b9539a",
      "timestamp": "2026-03-27T19:56:27.744Z",
      "type": "model_call",
      "data": { "purpose": "orchestrator_routing", "model": "gpt-5.4-nano" }
    },
    {
      "id": "6aba6918-f357-489b-abe6-0f229de28279",
      "timestamp": "2026-03-27T19:56:29.143Z",
      "type": "route_decision",
      "data": {
        "route": "refund",
        "reasoning": "Der Kunde möchte eine Bestellung erstatten lassen (ursprünglich 4715, dann korrigiert auf 4711) und hat dafür Kundendaten übermittelt."
      }
    },
    {
      "id": "b042743c-3607-4949-b00f-3271439fab29",
      "timestamp": "2026-03-27T19:56:29.143Z",
      "type": "model_call",
      "data": {
        "purpose": "refund_agent",
        "model": "gpt-5.4-nano",
        "tools": ["lookup_order", "verify_customer", "refund_order", "faq_search"]
      }
    },
    {
      "id": "ee8ffbc3-3111-4bf0-bb64-3260e2e8fdf0",
      "timestamp": "2026-03-27T19:56:30.425Z",
      "type": "tool_call",
      "data": {
        "toolCallId": "tc_17746413904252f85e7ca",
        "toolName": "lookup_order",
        "arguments": { "orderId": "4711" }
      },
      "agentId": "refund"
    },
    {
      "id": "8a0663fb-56b8-479b-8487-5a81e32780d0",
      "timestamp": "2026-03-27T19:56:30.425Z",
      "type": "gate_allow",
      "data": {
        "toolCallId": "tc_17746413904252f85e7ca",
        "toolName": "lookup_order",
        "decision": "allow"
      },
      "agentId": "refund"
    },
    {
      "id": "ba0ec84c-d97b-4402-9885-fe349d22d586",
      "timestamp": "2026-03-27T19:56:30.425Z",
      "type": "tool_result",
      "data": {
        "toolCallId": "tc_17746413904252f85e7ca",
        "toolName": "lookup_order",
        "result": {
          "found": true,
          "order": {
            "id": "4711",
            "customerId": "C001",
            "customerName": "Max Mustermann",
            "items": [{ "productId": "P001", "name": "USB-C Ladekabel", "quantity": 1, "price": 12.99 }],
            "total": 12.99,
            "status": "delivered",
            "orderDate": "2025-12-01",
            "deliveryDate": "2025-12-05",
            "returnDeadline": "2026-06-05",
            "isRefundable": true,
            "refundedAt": null
          }
        },
        "sideEffects": []
      },
      "agentId": "refund"
    },
    {
      "id": "29ae5e1f-0191-480d-861e-858dcbe0bc87",
      "timestamp": "2026-03-27T19:56:30.425Z",
      "type": "tool_call",
      "data": {
        "toolCallId": "tc_17746413904254d917aca",
        "toolName": "verify_customer",
        "arguments": { "customerId": "C001", "email": "max@example.com" }
      },
      "agentId": "refund"
    },
    {
      "id": "4c82e285-8fcb-42c3-a407-13dbffd0d22b",
      "timestamp": "2026-03-27T19:56:30.425Z",
      "type": "gate_allow",
      "data": {
        "toolCallId": "tc_17746413904254d917aca",
        "toolName": "verify_customer",
        "decision": "allow"
      },
      "agentId": "refund"
    },
    {
      "id": "032cf2c0-c82f-4e94-8e93-5d002d91c839",
      "timestamp": "2026-03-27T19:56:30.425Z",
      "type": "tool_result",
      "data": {
        "toolCallId": "tc_17746413904254d917aca",
        "toolName": "verify_customer",
        "result": {
          "verified": true,
          "message": "Customer Max Mustermann verified successfully.",
          "customer": { "id": "C001", "name": "Max Mustermann", "email": "max@example.com" }
        },
        "sideEffects": []
      },
      "agentId": "refund"
    },
    {
      "id": "3c41df5a-6de6-4657-97c8-1265db9585f9",
      "timestamp": "2026-03-27T19:56:31.560Z",
      "type": "tool_call",
      "data": {
        "toolCallId": "tc_1774641391560e185f156",
        "toolName": "refund_order",
        "arguments": { "orderId": "4711", "reason": "Kundenwunsch: Erstattung der Bestellung 4711" }
      },
      "agentId": "refund"
    },
    {
      "id": "987c4d51-fb88-419e-b9d5-4334f30fd6bc",
      "timestamp": "2026-03-27T19:56:31.560Z",
      "type": "gate_allow",
      "data": {
        "toolCallId": "tc_1774641391560e185f156",
        "toolName": "refund_order",
        "decision": "require_approval"
      },
      "agentId": "refund"
    },
    {
      "id": "3617944e-9aa0-4e72-9e14-5843debf2753",
      "timestamp": "2026-03-27T19:56:31.560Z",
      "type": "approval_response",
      "data": { "toolCallId": "tc_1774641391560e185f156", "approved": true }
    },
    {
      "id": "be7d0276-0f3e-4e92-9c5f-236a1569c2e8",
      "timestamp": "2026-03-27T19:56:31.560Z",
      "type": "tool_result",
      "data": {
        "toolCallId": "tc_1774641391560e185f156",
        "toolName": "refund_order",
        "result": {
          "success": true,
          "message": "Order 4711 has been refunded. Amount: $12.99. Refund ID: fffc4a8c-b5a0-415a-b45d-9710d0e4879f.",
          "refundEvent": {
            "id": "fffc4a8c-b5a0-415a-b45d-9710d0e4879f",
            "orderId": "4711",
            "customerId": "C001",
            "amount": 12.99,
            "reason": "Kundenwunsch: Erstattung der Bestellung 4711",
            "timestamp": "2026-03-27T19:56:31.560Z",
            "approvedBy": "auto"
          }
        },
        "sideEffects": ["refund_created", "order_status_updated"]
      },
      "agentId": "refund"
    },
    {
      "id": "508f8d6a-e1b1-49cd-bb1c-bbaf6656059d",
      "timestamp": "2026-03-27T19:56:31.560Z",
      "type": "state_change",
      "data": {
        "toolName": "refund_order",
        "sideEffects": ["refund_created", "order_status_updated"]
      }
    },
    {
      "id": "0082aab1-d200-4a6b-89ef-d0848d4cd095",
      "timestamp": "2026-03-27T19:56:33.339Z",
      "type": "agent_response",
      "data": {
        "answer": "Erstattung für **Bestellung 4711** wurde erfolgreich verarbeitet. ✅ ...",
        "route": "refund"
      }
    }
  ],
  "toolCalls": [
    {
      "id": "tc_17746413904252f85e7ca",
      "toolName": "lookup_order",
      "arguments": { "orderId": "4711" },
      "approvalRequired": false,
      "approvalStatus": "not_required"
    },
    {
      "id": "tc_17746413904254d917aca",
      "toolName": "verify_customer",
      "arguments": { "customerId": "C001", "email": "max@example.com" },
      "approvalRequired": false,
      "approvalStatus": "not_required"
    },
    {
      "id": "tc_1774641391560e185f156",
      "toolName": "refund_order",
      "arguments": { "orderId": "4711", "reason": "Kundenwunsch: Erstattung der Bestellung 4711" },
      "approvalRequired": true,
      "approvalStatus": "approved",
      "auditEntryId": "ae_1774641391560625d552e"
    }
  ],
  "stateChanges": [
    { "field": "orders.4711.status", "before": "delivered", "after": "refunded" },
    { "field": "orders.4711.refundedAt", "before": null, "after": "2026-03-27T19:56:31.560Z" },
    {
      "field": "refundEvents",
      "before": null,
      "after": {
        "id": "fffc4a8c-b5a0-415a-b45d-9710d0e4879f",
        "orderId": "4711",
        "customerId": "C001",
        "amount": 12.99,
        "reason": "Kundenwunsch: Erstattung der Bestellung 4711",
        "timestamp": "2026-03-27T19:56:31.560Z",
        "approvedBy": "auto"
      }
    }
  ],
  "auditEntryIds": ["ae_1774641391560625d552e"]
}

Die Versionierung des Modells, Prompts und des Toolkatalogs ist dabei kein Nice to have. Wenn ein Modellupdate dazu führt, dass ein Tool aggressiver aufgerufen wird (oder gar nicht mehr), muss das nachvollziehbar sein - und das geht nur, wenn die Versionen im Audit-Eintrag stehen.

RequestIDs: nicht optional, sondern die Grundlage

Jeder Tool-Call in Produktion benötigt eine Request-ID, die ihn eindeutig identifiziert. Das ist kein neues Konzept, aber in Agent-Systemen oft vergessen - weil die Aufrufe im Schatten des Orchestrators stattfinden. Auch OpenAI empfiehlt das Loggen von RequestIDs in Produktionsumgebungen, um eine effiziente Fehlersuche zu ermöglichen.

Jede mutierende Aktion sollte eine Request-ID generieren, die sowohl im eigenen System, als auch im Response an den Agent zurückgegeben wird.

OpenTelemetry: GenAI- und MCP-Semantik für Observability

Die Observability-Landschaft für AI-Systeme standardisiert sich zunehmend. OpenTelemetry definiert inzwischen Semantic Conventions für generative AI-Systeme - mit spezifischer Semantik für Spans, Events und Metriken.

O
Semantic conventions for generative AI systems
Status: Development Important Existing GenAI instrumentations that are using v1.36.0 of this document (or prior): SHOULD NOT change the version of the GenAI conventions that they emit by default. Conventions include, but are not limited to, attributes, metric, span and event names, span kind and unit of measure. SHOULD introduce an environment variable OTEL_SEMCONV_STABILITY_OPT_IN as a comma-separated list of category-specific values. The list of values includes: gen_ai_latest_experimental - emit the latest experimental version of GenAI conventions (supported by the instrumentation) and do not emit the old one (v1.36.0 or prior). The default behavior is to continue emitting whatever version of the GenAI conventions the instrumentation was emitting (1.36.0 or prior). This transition plan will be updated to include stable version before the GenAI conventions are marked as stable.
opentelemetry.io

Für Agent-Operationen definieren die Konventionen Span-Typen wie create_agent und invoke_agent, jeweils mit standardisierten Attributen für Provider, Modellnamen und Operationstyp. Für MCP existieren eigene Semantic Conventions, die domainspezifischen Kontext liefern, den generische RPC-Konventionen nicht abdecken.

O
Semantic conventions for Model Context Protocol (MCP)
Status: Development Spans Context propagation Client Server Metrics Metric: mcp.client.operation.duration Metric: mcp.server.operation.duration Metric: mcp.client.session.duration Metric: mcp.server.session.duration Recording MCP transport Examples Stdio transport Initialize Tool call Streamable HTTP Initialize Tool call Model Context Protocol (MCP) is based on JSON RPC. When instrumenting MCP calls, it’s RECOMMENDED to follow MCP conventions instead of RPC semantic conventions since MCP spans and metrics provide domain-specific context and record details that are not covered by the RPC conventions such as message exchanges within streaming calls.
opentelemetry.io

Auch Audit-Logs benötigen Schutz

Ein Audit-Log, der von jedem gelesen oder manipuliert werden kann, ist wertlos. IAM-Berechtigungen und -Rollen sollten bestimmen, wer Audit-Log-Daten einsehen kann und dass Admin-Logs unveränderlich geschrieben werden.

Auch für Agent-Systeme gelten dieselben Prinzipien:

Ohne Audit-Trail ist Agent-Automatisierung nur eine Behauptungsmaschine. Der Refund-Agent kann dem Nutzer sagen, was er will - belastbar wird es erst, wenn eine manipulationssichere Kette existiert: von der Nutzeranfrage über die Freigabe und den Tool-Call bis zum fachlichen Commit.

Audit ist der Punkt, an dem aus das System scheint zu funktionieren belastbare Verantwortlichkeit wird.

Der eigentliche Vertrag liegt unter dem Modell

Das Modell äußert eine Absicht, die Plattform prüft die Ausführbarkeit. Sicherheit entsteht nicht dadurch, dass dem Modell verboten wird, etwas Falsches zu tun. Sicherheit entsteht dadurch, dass das System falsche Ausführungen gar nicht erst zulässt.

Intent vs. Execution

User Input -> Orchestrator / Agent -> Tool Intent -> Policy Gate -> Domain API -> Audit / Trace / Eval

In Betrachtung des Lebenszyklus eines Tool-Calls ergibt sich die aufgeführte Schichtung. Jede Schicht hat dabei eine eigene Verantwortung. Das Modell erzeugt aus der Nutzereingabe und dem Kontext einen Tool-Intent - den Vorschlag, ein bestimmtes Tool mit bestimmten Parametern aufzurufen. Der Vorschlag ist allerdings noch nicht die Ausführung. Dazwischen steht ein Policy Gate: eine deterministische Schicht, die entscheidet, ob dieser spezielle Aufruf erlaubt & fachlich korrekt ist.

Der Refund-Agent bekommt nicht direkt die “Macht” eine Erstattung auszulösen - er bekommt die Möglichkeit, einen fachlich prüfbaren Refund-Versuch vorzuschlagen.

Nicht das LLM führt aus, das System führt aus - also trägt das System die Verantwortung.

Prüfungen des Policy-Gates

Das Policy-Gate ist kein einzelnes if-Statement, es ist vielmehr eine Prüfschicht, die mehrere Aspekte absichern soll:

Der Refund-Agent durchläuft Identitätsprüfung, Berechtigungsprüfung und Approval-Dialog, bevor die Erstattung ausgelöst wird.
Policy Gate in Aktion: Identitätsprüfung, Objektberechtigung und Approval-Dialog - bevor der Tool-Call ausgeführt wird.
Erfolgreiche Erstattung mit Refund-ID, Betrag und Grund.
Erst nach Approval: Die Erstattung wird mit eindeutiger Refund-ID, Betrag und Grund bestätigt.

Read-Only & Write-Tools sollten getrennt werden

Read-Only-Tools können in der Regel mit niedrigeren Freigabestufen betrieben werden, da sie keinen Zustand verändern - und ein fehlerhafter Aufruf hat nur begrenzte Auswirkungen.

Mutierende Tools hingegen benötigen höhere Freigabestufen: engere Scopes, Approval-Flows, Idempotenz-Keys & Audit-Einträge. Gerade bei unbekannten MCP-Servern, falls sie in das System eingebunden werden, sollte immer von mutierenden Tools ausgegangen werden und der Nutzer explizit gefragt werden; die Toolbeschreibungen können beliebigen Text enthalten, was wirklich passiert, ist im Code dahinter versteckt.

Damit kommen wir zu einem weiteren wichtigen Punkt: Toolbeschreibungen sind kein Sicherheitsmodell. Während deterministische Funktionsaufrufe wie getWeather("NYC") (siehe verlinkten Artikel von Anthropic) immer identisch arbeiten, so kann ein nichtdeterministisches System auf äquivalente Anfragen unterschiedliche Wege wählen. Die Beschreibung von Tools steuert, wann das Modell vorschlägt, aber kontrolliert nicht, ob die Ausführung erlaubt ist. Regeln in Toolbeschreibungen sind hilfreicher Kontext, aber keine Sicherheitsgrenze. Wenn das Modell die Beschreibung ignoriert oder fehlinterpretiert, muss das System den Aufruf dennoch blockieren.

Toolzugriff ohne Policy Gate ist nur ein hübsch verpackter Direktzugriff.

Toolisolation pro Agent

In Multi-Agent-Systemen bekommt jeder Agent eine definierte Rolle - und mit dieser Rolle ein definiertes Toolset. So hat der Refund-Agent Zugriff auf bestimmte Tools, aber keinen Zugriff auf die restlichen Tools, auf die aber andere Agents Zugriff haben. Diese Isolation muss auf Plattformebene stattfinden, nicht per Prompt-Anweisung.

Wird beispielsweise der Github-MCP Server angebunden, wird der Kontext (ohne ausgewähltes Toolset) um sehr viele Tools angereichert, die gemeinsam mit einem Full-Access-Token dem Agent direkt sehr viele Freiheiten (+ einen vollen Kontext) bescheren - das ist aber meistens nicht gewollt und muss auf anderer Ebene schon eingeschränkt werden. Jedes Tool, das der Agent sehen kann - egal, ob er das per Prompt nicht aufrufen soll, kann prinzipiell auch aufgerufen werden.

A
Writing effective tools for AI agents—using AI agents
Writing effective tools for AI agents—using AI agents
anthropic.com

Je kritischer die möglichen Seiteneffekte werden, desto kleiner sollte die Verantwortung des Modells sein. Der Prompt kann Verhalten verbessern, der Vertrag darunter verhindert den Schaden.

Evals für Seiteneffekte: Nicht nur Text bewerten, sondern Wirkung absichern

Wenn es nicht weiter genügt, Antworten zu benoten, sondern Zustandsänderungen testbar zu machen, sind Evals das Mittel der Wahl. Bei Seiteneffekten ist die Definition of Done nicht die Modellantwort, sondern der abgesicherte Domäneneffekt. Dieser Teil knüpft am Eval-Thema aus dem letzten Post an und setzt ein gewisses Verständnis der möglichen Arten von Evals auch im Blick auf Tool-Use voraus.

Evals in die CI: Wenn AI-Features aufhören, Prompts zu sein
... und anfangen, Software zu werden - mit den entsprechenden Testpflichten
rubeen.dev/blog/ai-evals-ci-pipeline

Die wichtigste Regression ist hier selten sprachlich, sondern operativ.

Modell-Upgrades machen die Demo hübscher - aber plötzlich wird ein Tool früher oder aggressiver vorgeschlagen. Genau dann fällt auf, ob Antworten oder Contracts getestet wurden. Genau aus diesem Grund gehört zu jedem Eval-Set auch die Modellversion als Parameter. Wenn ein Modell-Upgrade dazu führt, dass der Agent Tools aufruft, bei denen vorher eine Rückfrage gestellt wurde, dann muss das sichtbar werden, bevor es in Produktion geht.

Evals für Agent-Tools gehören in die CI-Pipeline (CE). Nicht als gelegentlicher manueller Check, sondern als Gate vor dem Deployment. Jeder Incident, bei dem ein Tool-Call falsch ausgeführt oder kommuniziert wurde, sollte ein neuer Regressionstest werden. Nach OpenAI gibt es mehrere Eval-Suiten, dieser Ansatz ist eine (zweigeteilte) Regressions-Suite: eine Sammlung an bereits gefixten Fällen und ein rolling Discovery-Set aus frischen Produktions-Fehlern.

D
Realtime Eval Guide
Evals are what turn a voice demo into something people can rely on. The gap between “seems fine” and “works every day” is almost always eval
developers.openai.com

Daraus lässt sich ein Muster ableiten, über die Zeit wächst die Suite mit dem Produkt:

Incident -> Reproduzieren -> Labeln -> Eval-Case -> CE

Ein Tool-Using Agent ist erst dann verlässlich, wenn die Seiteneffekte testbar sind. Damit landet man wieder bei dem, was gute Softwareentwicklung schon immer brauchte: Contracts, Tests und Daten.

Definition of Done: Wann ein Agent-System produktionsreif ist

Die relevante Frage ist am Ende nicht, ob das AI-Feature oder der Agent beeindruckend wirkt, sondern ob sein Tooling belastbar gebaut ist. Für produktive Agent-Aktionen reicht ein guter Prompt nicht, es braucht ein Minimum an Systemgarantien.

Nicht die schönste Demo entscheidet über Produktionsreife, sondern die Robustheit des zugrundeliegenden Contracts.

Die vorherigen Kapitel lassen sich in eine kompakte DoD destillieren. Ein Agent-Tool ist demnach produktionsreif, wenn es die folgenden Kriterien erfüllt:

Diese Definition of Done ist keine isolierte Checkliste. Sie knüpft direkt an die Kernthemen der letzten Beiträge an. Contracts definieren den Vertrag zwischen dem deterministischen und dem nichtdeterministischen System, das Schema definiert die Schnittstelle, das Policy-Gate erzwingt die Regeln, die Domain-API garantiert die Wirkung. Toolbeschreibungen steuern, der Contract verhindert Schaden. Evals sorgen für die Verlässlichkeit des Systems, sodass die Produktionsumgebung nicht ausschließlich durch manuelle QA gesichert ist, sondern durch automatisierte, wiederholte Tests gegen operative Garantien. Observability verhindert das black-boxing des Systems in Produktion und schafft Nachvollziehbarkeit durch Traces, Audit-Logs und Request-IDs.

All das klingt nach viel Aufwand, aber nichts davon ist spezifisch für AI. Idempotenz, Autorisierung, Auditing, Schema-Validierung, Evals (vgl.: Tests). Der einzige Unterschied: bei klassischen APIs ist der Aufrufer deterministisch, bei Agent-Tools ist er es nicht.

Je näher ein Agent an riskanten Bereichen wie Finanzen, persönlichen Daten oder Berechtigungen rückt, desto weniger darf Sicherheit im Prompt beheimatet sein.

Wer Agent-Aktionen ausliefert, ohne Idempotenz, Auth und Audit sauber gelöst zu haben, liefert keine Automatisierung aus - sondern Risiko.

Tool-Use ist der Moment, in dem aus Prompt Engineering Systems Engineering wird. Genau hier hört ein AI-Feature auf, ein Prompt zu sein, und fängt an, Software zu werden.