← Alle Playbooks
Playbook· build

Sehen was Dein Agent wirklich getan hat, Tracing in 10 Schritten mit OpenTelemetry

Ein Agent läuft, macht zwanzig Tool-Calls und liefert am Ende irgendwas. Warum? Mit Tracing machst Du die Blackbox auf und siehst jeden Schritt als Zeitachse. So richtest Du es ein.

Du hast einen Agenten gebaut, er ruft ein paar Tools auf, denkt zwischendrin nach und liefert am Ende ein Ergebnis. Wenn das Ergebnis gut ist, freust Du Dich. Wenn es falsch ist, stehst Du im Dunkeln. Welches Tool hat gefeuert? Was kam zurück? Wo ist die Kette gekippt? Bei einem einzelnen Prompt kannst Du das noch im Log nachlesen. Bei einem Agenten mit zwanzig Schritten über drei MCP-Server hinweg nicht mehr.

Tracing löst genau das. Du machst jeden Schritt sichtbar als einen Eintrag auf einer Zeitachse, mit Dauer, Ein- und Ausgabe, verschachtelt so wie die Aufrufe wirklich passiert sind. Das ist keine exotische Technik, das ist der gleiche Standard mit dem große Web-Systeme seit Jahren beobachtet werden. Und seit die MCP-Spec vom 28. Juli 2026 W3C Trace Context fest verankert hat, reist so ein Trace sauber von Deiner Host-App durch den Client, über den Server bis ins letzte Tool. Ich gehe die zehn Schritte durch die Du brauchst um von blind zu sehend zu kommen.

Schritt 1, der Unterschied zwischen Logs und Traces

Logs sind Zettel die Du irgendwo hinwirfst. "Tool search aufgerufen." "Antwort erhalten." Jede Zeile steht für sich, keine weiß von der anderen. Bei einem Agenten mit vielen parallelen und verschachtelten Aufrufen wird das schnell zu einem Wust in dem Du nichts mehr zuordnest.

Ein Trace ist eine Zeitachse mit Struktur. Ein Trace umfasst einen kompletten Durchlauf, zum Beispiel eine ganze Agent-Anfrage. Darin sitzen Spans, und jeder Span ist ein einzelner Abschnitt mit Anfang, Ende und Eltern-Span. Der Agent-Lauf ist der äußere Span, jeder Tool-Call ein Kind-Span darin, jeder LLM-Call auch. Am Ende siehst Du einen Baum statt einer Liste, und in dem Baum liest Du sofort ab wo die Zeit hingeht und wo etwas schiefging.

Schritt 2, das Vokabular das Du wirklich brauchst

Drei Begriffe, mehr nicht für den Anfang. Ein Trace ist der gesamte Vorgang und hat eine Trace-ID. Ein Span ist ein Abschnitt darin und hat eine eigene Span-ID plus einen Verweis auf seinen Eltern-Span. Ein Attribut ist ein Schlüssel-Wert-Paar das Du an einen Span hängst, etwa welches Modell lief oder wie viele Tokens es kostete.

Das reicht um alles andere zu verstehen. OpenTelemetry, kurz OTel, ist der offene Standard der diese drei Dinge definiert. Er ist herstellerneutral, das heißt Du instrumentierst einmal und kannst die Traces später in jedes kompatible Backend schicken ohne Deinen Code anzufassen.

Schritt 3, warum der Trace über Systemgrenzen hält

Der Trick an OTel ist die Weitergabe der IDs. Wenn Dein Agent einen MCP-Server aufruft, gibt er die Trace-ID mit. Der Server öffnet seinen eigenen Span, hängt ihn als Kind an und gibt die ID an das Tool weiter. So entsteht ein einziger Baum, obwohl drei getrennte Prozesse beteiligt waren.

Der Standard dafür heißt W3C Trace Context. Er transportiert die Information in einem Header namens traceparent, dazu kommen tracestate und baggage für Zusatzinfos. Das Format ist fix, ein Beispiel sieht illustrativ so aus:

traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01

Vier Teile, getrennt durch Bindestriche. Version, dann die Trace-ID, dann die ID des aufrufenden Spans, dann ein Flag ob dieser Trace aufgezeichnet wird. Du musst das nie von Hand bauen, die Bibliothek macht es. Aber es hilft zu wissen dass genau diese Zeichenkette dafür sorgt dass alles zusammenbleibt.

Schritt 4, was die neue MCP-Spec daran ändert

Bis vor kurzem war die Frage wo diese IDs in einem MCP-Request stehen sollten Auslegungssache. Jeder Server machte es ein bisschen anders, und ein Trace riss oft an der Grenze zwischen Client und Server ab. Die Spec-Version 2026-07-28 räumt das auf. traceparent, tracestate und baggage haben jetzt feste Plätze im _meta Feld jedes Requests. Das ist SEP-414.

Für Dich heißt das, wenn Du einen aktuellen MCP-Server oder Client benutzt, wird die Trace-Kontext-Weitergabe entweder schon mitgeliefert oder Du kannst Dich darauf verlassen dass die Key-Namen stimmen. Kein Raten mehr wo die ID hinsoll. Wer die Details der Spec noch nicht kennt, findet sie im Playbook zur 2026-07-28 Spec.

Schritt 5, ein Backend wählen wo die Traces landen

Instrumentieren ist die eine Hälfte, angucken die andere. Du brauchst ein Ziel das die Traces empfängt und als Zeitachse darstellt. Für LLM- und Agent-Anwendungen ist Langfuse eine gute erste Wahl, weil es genau auf diesen Fall zugeschnitten ist. Es zeigt pro Span die Prompts, die Antworten, die Token-Zahlen und die Kosten, nicht nur nackte Zeitbalken.

Langfuse lässt sich selbst hosten und nimmt Traces im OpenTelemetry-Format entgegen. Das ist der Punkt der Dir Freiheit gibt. Du schreibst Deinen Code gegen OTel, und wenn Dir Langfuse später nicht passt, schickst Du die gleichen Traces woanders hin. Alternativen die ebenfalls OTel sprechen sind Jäger für den rein technischen Blick oder ein gemanagtes Backend wenn Du nichts selbst betreiben willst.

Schritt 6, den ersten Span von Hand setzen

Fang klein an. Nimm eine einzige Funktion, den äußersten Punkt Deines Agenten, und wickle sie in einen Span. In Python mit dem OTel-SDK sieht das im Kern so aus:

from opentelemetry import trace

tracer = trace.get_tracer("mein-agent")

with tracer.start_as_current_span("agent-run") as span:
    span.set_attribute("agent.task", task_beschreibung)
    ergebnis = agent.run(task_beschreibung)
    span.set_attribute("agent.status", "ok")

Das ist der ganze Zauber. start_as_current_span öffnet den Span, alles was innerhalb des with-Blocks passiert hängt automatisch darunter, und am Ende schließt er sich. Die Attribute die Du setzt tauchen später im Backend auf und machen den Span lesbar. In TypeScript und anderen Sprachen ist das Muster identisch, nur die Syntax unterscheidet sich.

Schritt 7, die Tool-Calls einzeln sichtbar machen

Ein Span um den ganzen Lauf zeigt Dir die Gesamtdauer, mehr nicht. Der Wert kommt wenn jeder Tool-Call sein eigener Kind-Span wird. Du legst also um jeden Tool-Aufruf ebenfalls einen Span, mit dem Namen des Tools und den wichtigen Argumenten als Attribute.

with tracer.start_as_current_span("tool-call") as span:
    span.set_attribute("tool.name", tool_name)
    span.set_attribute("tool.args", str(argumente)[:500])
    antwort = tool.call(argumente)
    span.set_attribute("tool.result_len", len(str(antwort)))

Ein Detail das Dich sonst beißt. Schneide lange Werte ab, hier auf 500 Zeichen. Wenn Du komplette Tool-Antworten mit zehntausend Zeichen als Attribut speicherst, blähst Du Deine Traces auf und machst das Backend langsam. Der Sinn ist die Struktur zu sehen, nicht jedes Byte zu archivieren.

Schritt 8, keine Geheimnisse in den Trace schreiben

Das ist die Stelle an der Tracing gefährlich wird wenn Du nicht aufpasst. Ein Trace fängt Ein- und Ausgaben ein, und da stehen schnell Dinge drin die nicht in ein Beobachtungs-Backend gehören. API-Schlüssel in einem Argument. Eine Kundenmail in einer Tool-Antwort. Ein ganzer Prompt mit personenbezogenen Daten.

Bau Dir eine kleine Funktion die vor dem Setzen eines Attributs bekannte Muster ersetzt, Schlüssel, Mailadressen, Tokens. Und ueberleg bei jedem Attribut ob Du diesen Wert wirklich brauchst um ein Problem zu debuggen. Meistens reicht die Länge oder ein Hash statt des Inhalts. Wer beim Thema Datensparsamkeit tiefer einsteigen will, findet die Grundlagen im Playbook Datenschutz mit KI-Tools.

Schritt 9, aus dem Trace eine Frage beantworten

Instrumentierung ist Mittel zum Zweck. Der Zweck ist eine konkrete Frage schnell zu beantworten. Drei die immer wieder kommen. Erstens, wo geht die Zeit hin. Du öffnest den langsamsten Lauf und siehst sofort welcher Span den Balken füllt, oft ist es ein einziger Tool-Call der eine externe API blockiert.

Zweitens, wo kippt die Logik. Ein Lauf liefert Unsinn, Du gehst die Kind-Spans der Reihe nach durch und findest die Stelle an der ein Tool schon die falsche Antwort zurückgab, bevor das Modell darauf aufbaute. Drittens, was kostet ein Lauf wirklich. Wenn Du Token-Zahlen als Attribute mitschreibst, summierst Du sie über den Trace und siehst die echten Kosten pro Anfrage statt einer Monatsrechnung ohne Aufschlüsselung. Für die reine Kostenseite lohnt sich zusätzlich das Playbook Was kostet KI wirklich.

Schritt 10, Tracing zur Gewohnheit machen

Ein einmal instrumentierter Agent bringt wenig wenn Du nur draufschaust wenn schon etwas brennt. Der Nutzen kommt aus der Routine. Schau Dir ein paar Traces an nachdem Du eine Änderung deployt hast, nicht erst wenn sich jemand beschwert. Leg Dir zwei, drei Attribute fest die Du bei jedem Agenten mitschreibst, Task, Modell, Status, Token-Zahl, damit Deine Traces vergleichbar bleiben.

Und trenne sauber zwischen Tracing und Evaluierung. Ein Trace sagt Dir was passiert ist, ein Eval sagt Dir ob das Ergebnis gut war. Beides brauchst Du, aber es sind zwei verschiedene Werkzeuge. Wenn Du beim Eval-Teil weitermachen willst, ist das Playbook Agent-Eval in 60 Minuten der passende nächste Schritt.

Was als nächstes

Wenn Du noch keinen eigenen Agenten laufen hast, an dem sich Tracing lohnt, bau erst einen mit dem Claude Agent SDK und komm dann hierher zurück. Wer schon MCP-Server betreibt und wissen will was die neue Spec sonst noch ändert, liest MCP wird stateless. Und für den großen Bogen über Agent-Qualität gehört Agent-Eval in 60 Minuten als Zwilling neben dieses Playbook.

Quelle

  • W3C Trace Context in der MCP-Spec (SEP-414), offizielle Ankündigung: https://blog.modelcontextprotocol.io/posts/2026-07-28-release-candidate/
  • OpenTelemetry Konzepte (Traces, Spans, Context Propagation): https://opentelemetry.io/docs/concepts/
  • W3C Trace Context Standard (traceparent Format): https://www.w3.org/TR/trace-context/
  • Langfuse, OpenTelemetry-kompatibles Tracing für LLM-Anwendungen: https://langfuse.com/docs
Sehen was Dein Agent wirklich getan hat, Tracing in 10 Schritten mit OpenTelemetry — StudioMeyer Academy