LLM Observability ohne großes Budget: Der minimale Stack zum Debuggen von KI-Features in der Produktion

#LLM Observability Stack
Sandor Farkas - Founder & Lead Developer at Wolf-Tech

Sandor Farkas

Gründer & Lead Developer

Experte für Softwareentwicklung und Legacy-Code-Optimierung

Dein APM-Tool sagt dir, dass alles in Ordnung ist. P99-Latenz ist gesund, Fehlerrate ist flach, die Datenbank schwitzt nicht. Gleichzeitig hat ein Kunde gerade ein Ticket geöffnet, weil dein KI-Assistent selbstbewusst einen Erstattungsbetrag generiert hat, der um 800 Euro zu hoch war -- und du hast absolut keine Ahnung warum.

Das ist das zentrale Problem mit LLM Observability: Die Fehlermuster sind semantischer, nicht systemischer Natur. Standard-Monitoring-Tools wurden entwickelt, um Abstürze, langsame Abfragen und Speicherlecks zu erkennen. Sie sind nicht darauf ausgelegt, ein Modell zu erkennen, das nach einer Änderung der Prompt-Vorlage zu halluzinieren begann, oder ein Kontextfenster, das den wichtigsten Teil der Nutzereingabe stillschweigend abgeschnitten hat. Wer KI-Features ohne einen zweckgebundenen LLM-Observability-Stack ausliefert, fliegt blind.

Die gute Nachricht: Du brauchst keinen sechsstelligen Datadog-Vertrag oder ein dediziertes ML-Platform-Team für sinnvolle Transparenz. Dieser Beitrag beschreibt den minimalen LLM-Observability-Stack für ein kleines Entwicklungsteam -- was zu erfassen ist, wie Traces zu strukturieren sind, wo sie kostengünstig gehostet werden können und welche Alarmierungsregeln nutzersichtbare Vorfälle tatsächlich vorhersagen, bevor deine Support-Warteschlange volläuft.

Warum Standard-APM für LLM-Features nicht ausreicht

Wenn ein traditioneller API-Endpunkt ausfällt, ist das Signal eindeutig: Eine Exception wird geworfen, ein Statuscode ändert sich, die Latenz steigt über einen Schwellenwert. Der Debugging-Pfad ist deterministisch -- Stack-Trace, Log-Zeile, fehlerhafte Abfrage.

LLM-Ausfälle funktionieren nicht so. Ein Prompt, der das Kontextfenster des Modells stillschweigend überschritten hat, gibt einen 200 OK mit einer plausibel aussehenden Antwort zurück. Ein Modell, das nach einer Bearbeitung des System-Prompts begann, Nutzeranfragen übermäßig abzulehnen, zeigt keinerlei Fehlerratenänderung. Token-Kosten, die sich verdreifacht haben, weil ein Template-Injection-Fehler begann, bei jedem Aufruf den gesamten Nutzerverlauf einzubeziehen, sehen in deinem APM-Dashboard gut aus -- bis die AWS-Rechnung eintrifft.

Was du tatsächlich beobachten musst:

  • Die genaue Prompt-Payload, die an das Modell gesendet wird, einschließlich des gerenderten System-Prompts und jedes injizierten Kontexts
  • Die rohe Modellausgabe, vor jeder Nachverarbeitung
  • Welche Modellversion, Temperatur und Parameter verwendet wurden
  • Token-Anzahl aufgeschlüsselt nach Eingabe, Ausgabe und -- falls zutreffend -- zwischengespeicherten Tokens
  • Welcher Mandant oder Nutzer den Aufruf ausgelöst hat, damit du Kosten zuordnen und Missbrauch erkennen kannst
  • Den vollständigen Trace einer mehrstufigen agentischen Kette, einschließlich der durchgeführten Tool-Aufrufe und ihrer Reihenfolge
  • Ausgabequalitätssignale: Wurde die Antwort erfolgreich geparst, haben Guardrails ausgelöst, hat der Nutzer sofort wiederholt?

Nichts davon erscheint standardmäßig in einem herkömmlichen APM-Tool.

Auswahl eines selbst gehosteten Observability-Backends

Für kleine Teams sind Langfuse und OpenLLMetry die zwei stärksten Optionen für selbst gehostete LLM Observability.

Langfuse ist speziell für LLM-Tracing entwickelt. Es bietet eine übersichtliche UI zum Durchsuchen von Traces, ein Prompt-Management-System mit Versionshistorie und einen Dataset/Evaluation-Workflow für Offline-Evaluierungen anhand erfasster Traces. Die selbst gehostete Version ist ein einzelner Docker-Compose-Stack -- PostgreSQL, Redis und der Anwendungsserver. Auf einem Hetzner-VPS für 20 Euro im Monat kannst du problemlos Zehntausende von Traces pro Tag verarbeiten. Für ein kleines Entwicklungsteam, das ein oder zwei KI-Features ausliefert, ist das mehr als ausreichend.

OpenLLMetry verfolgt einen anderen Ansatz: Es ist ein OpenTelemetry-basiertes SDK, das deine LLM-Aufrufe auf Bibliotheksebene instrumentiert (OpenAI SDK, Anthropic SDK, LangChain und andere) und Standard-OTEL-Spans ausgibt. Das bedeutet, deine LLM-Traces landen in dem Backend, das du bereits verwendest -- Jaeger, Grafana Tempo, Honeycomb oder sogar eine selbst gehostete OpenObserve-Instanz. Wenn du bereits eine OTEL-Pipeline hast, ist OpenLLMetry oft der Weg mit dem geringsten Aufwand, da kein zusätzlicher Datenspeicher hinzugefügt wird.

Wenn du von Grund auf neu anfängst und die reichhaltigste LLM-spezifische UI möchtest, nimm Langfuse. Wenn du eine bestehende OTEL-Infrastruktur hast und LLM-Traces neben deinen anderen Traces haben möchtest, nimm OpenLLMetry.

OpenTelemetry-Span-Konventionen für LLM-Aufrufe

Ob du Langfuse, OpenLLMetry oder eine eigene Instrumentierungsschicht verwendest -- deine LLM-Spans sollten den OpenTelemetry GenAI-Semantikkonventionen folgen. Die wichtigsten Attribute, die auf jedem Span zu erfassen sind:

gen_ai.system          # "openai", "anthropic", "mistral" usw.
gen_ai.request.model   # exakter Modell-String einschließlich Version
gen_ai.request.max_tokens
gen_ai.request.temperature
gen_ai.response.model  # tatsächlich verwendetes Modell (kann von angefordertem abweichen)
gen_ai.usage.input_tokens
gen_ai.usage.output_tokens
gen_ai.usage.cache_read_input_tokens   # für Anthropic Prompt Caching
gen_ai.finish_reason   # "stop", "length", "tool_calls", "content_filter"

Über die Standardattribute hinaus solltest du benutzerdefinierte Attribute für deinen eigenen Kontext hinzufügen:

app.tenant_id          # für Kostenzuordnung pro Mandant
app.feature_name       # welches KI-Feature den Aufruf ausgelöst hat
app.prompt_version     # Hash oder Versionskennung der Prompt-Vorlage
app.user_id            # falls relevant und von deiner Datenschutzrichtlinie erlaubt

Die Attribute app.feature_name und app.prompt_version werden dir beim Debugging die meiste Zeit sparen. Wenn du einen Anstieg schlechter Ausgaben siehst, kannst du sofort nach Feature und Prompt-Version filtern, um festzustellen, ob eine Vorlagenänderung die Regression verursacht hat.

Für eine Symfony-Anwendung, die das Anthropic PHP SDK oder einen OpenAI-kompatiblen Client verwendet, kannst du einen Middleware- oder Event-Listener hinzufügen, der jeden LLM-Aufruf in einen OTEL-Span einschließt und diese Attribute automatisch befüllt. Das hält die Instrumentierung aus deiner Geschäftslogik heraus und macht es trivial einfach, neue LLM-Aufrufe hinzuzufügen, ohne daran denken zu müssen, sie zu instrumentieren.

Erfassung von Prompt-Payloads und PII-Scrubbing

Die Erfassung von Prompt-Payloads ist die einzelne wertvollste Maßnahme in deinem gesamten Observability-Setup. Ohne sie bedeutet das Debuggen einer schlechten Ausgabe Raten. Mit ihr kannst du die genaue Interaktion, die den Fehler verursacht hat, wiedergeben und sie isoliert beheben.

Das Problem ist, dass Prompts häufig personenbezogene Daten enthalten. Ein Kundenservice-KI empfängt Nachrichten mit Namen, Bestellnummern, Kontodetails und manchmal Zahlungsinformationen. Ein Dokumentenanalyse-Feature verarbeitet Verträge mit personenbezogenen Daten. Das wörtliche Erfassen von Payloads und ihr Versenden an ein Logging-Backend ohne angemessene Kontrollen schafft ein DSGVO-Compliance-Problem.

Das richtige Muster ist Scrubbing an der Grenze, bevor die Payload deine Anwendung verlässt:

Zuerst einen PII-Erkennungsdurchlauf über den Prompt ausführen, bevor er an den Span angehängt wird. Für strukturierte Felder (E-Mail-Adressen, Telefonnummern, Kreditkartenmuster) ist Regex-basierte Erkennung schnell und zuverlässig. Für unstrukturierten Text kann ein leichtes lokales Modell oder ein deterministischer Entity-Recognizer wie spaCy Namen und Adressen erfassen.

Dann erkannte PII durch typisierte Platzhalter ersetzen: [EMAIL], [TELEFON], [PERSONENNAME]. Das bewahrt die semantische Struktur des Prompts -- die du zum Debuggen benötigst -- ohne die tatsächlichen Werte zu speichern.

Schließlich die rohe Payload separat, verschlüsselt gespeichert, mit kurzer Aufbewahrungsfrist (7-30 Tage), nur für Engineering mit explizitem Audit-Log zugänglich, aufbewahren. Das ist deine Break-Glass-Option für Vorfälle, die den echten Inhalt erfordern.

Dieser zweistufige Ansatz gibt dir aussagekräftige Observability-Daten in deiner Trace-UI, während deine langfristige Speicherung sauber und compliant bleibt.

Kostenzuordnung pro Mandant

Die Token-Kostenzuordnung ist häufig das Zweite, das Teams rückblickend wünschten, von Anfang an instrumentiert zu haben (das Erste ist die Prompt-Payload-Erfassung). Ohne sie weißt du, dass deine KI-Kosten 3.000 Euro pro Monat betragen, hast aber keine Ahnung, ob das von drei Power-Usern angetrieben wird, die deinen Assistenten-Feature nutzen, einem einzigen Mandanten, der automatisierte Massenanfragen ausführt, oder deiner eigenen internen Staging-Umgebung, die in den Produktionsverkehr einläuft.

Die Mechanik ist unkompliziert. Auf jedem LLM-Span die Mandanten-ID aufzeichnen. In einem Nachverarbeitungsschritt -- entweder eine Langfuse-Annotation oder eine benutzerdefinierte Aggregationsabfrage -- Token-Anzahlen mit dem Preis pro Token des Modells multiplizieren und nach Mandanten summieren. Das als Metrik in internen Dashboards bereitstellen.

Für Symfony-Anwendungen ist die sauberste Implementierung ein request-scopeter Service, der den aktuellen Mandantenkontext hält. Deine LLM-Middleware liest von diesem Service und stempelt jeden Span automatisch. Wenn du an einer mandantenfähigen SaaS-Plattform arbeitest, findest du mehr Details zu Mandantentrennungsmustern in unserem Leitfaden zu Multi-Tenant-SaaS-Architektur.

Zwei Schwellenwert-Alarme, die es sich sofort lohnt einzurichten: Alarm, wenn die Tagesausgaben eines einzelnen Mandanten das 3-Fache seines 7-Tage-Durchschnitts überschreiten, und Alarm, wenn die gesamten Tagesausgaben dein monatliches Budget geteilt durch 28 überschreiten. Das Erste erkennt Missbrauch oder unkontrollierte Automatisierung; das Zweite erkennt Abrechnungsüberraschungen, bevor sie zu CFO-Gesprächen werden.

Tracing mehrstufiger agentischer Ketten

Einzelne LLM-Aufrufe sind relativ einfach zu tracen. Agentische Features -- bei denen das Modell entscheidet, welche Tools aufzurufen sind, die Ergebnisse interpretiert und zusätzliche Tools oder Modelle aufruft -- erfordern verschachtelte Trace-Strukturen, um verständlich zu sein.

Das OpenTelemetry-Modell passt hier gut: ein Root-Span für die gesamte Agenten-Aufruf, Child-Spans für jeden LLM-Aufruf und weitere Child-Spans für jeden Tool-Aufruf, den das Modell auslöst. Das gibt dir ein vollständiges Bild davon, was der Agent beschlossen hat zu tun und in welcher Reihenfolge, was für das Debuggen von Fehlern in mehrstufigen Workflows unerlässlich ist.

Die Attribute, die auf Tool-Call-Ebene am meisten zählen, sind der Tool-Name, die Korrelations-ID aus der Modellantwort, die Größe in Token des an das Modell zurückgegebenen Ergebnisses und wie lange der Tool-Aufruf dauerte. Die Ergebnisgröße ist besonders wichtig. Wenn dein Agent ein Retrieval-Tool aufruft und das abgerufene Dokument 8.000 Token groß ist, fließt dieser Inhalt zurück in das Kontextfenster und wird als Eingabe-Token beim nächsten LLM-Aufruf berechnet. Das Verfolgen der Ergebnisgröße pro Tool-Aufruf ermöglicht es dir, Kontext-Bloat zu erkennen, bevor er deine Kosten treibt und deine Ausgabequalität senkt.

Alarmierungsregeln, die tatsächlich Vorfälle vorhersagen

Die meisten Teams beginnen mit offensichtlichen Alarmen: Modell-API-Fehler über einem Schwellenwert, LLM-Aufruf-Latenz über einem bestimmten Perzentil. Das sind Grundvoraussetzungen, aber sie erkennen nur die offensichtlichsten Ausfälle. Die Alarme, die tatsächlich nutzersichtbare Vorfälle verhindern, sind subtiler.

Finish-Reason-Drift: Alarm, wenn der Anteil der Aufrufe, die mit length (abgeschnittene Ausgabe) enden, im Vergleich zur 7-Tage-Baseline für dieses Feature erheblich zunimmt. Abschneiden bedeutet, dass das Modell keine Token mehr hatte, bevor es seine Antwort abschließen konnte -- ein stiller Qualitätsfehler, der häufig darauf hinweist, dass dein Kontextfenster aufgrund eines Fehlers im Gesprächsverlaufs-Management unbegrenzt wächst.

Content-Filter-Rate: Alarm, wenn der Anteil der Aufrufe, die einen content_filter-Finish-Reason zurückgeben, zunimmt. Ein Anstieg hier bedeutet normalerweise, dass eine Prompt-Vorlagenänderung versehentlich den Sicherheitsklassifikator des Modells ausgelöst hat, was Nutzer als schweigendes Ablehnen des Features erleben.

Ausgabe-Parse-Fehlerrate: Wenn dein Code strukturierte Ausgaben erwartet (JSON-Modus, Tool-Use oder Regex-extrahierte Felder) und die Nachverarbeitung fehlschlägt, ein Span-Ereignis für jeden Fehler protokollieren. Alarm, wenn die Parse-Fehlerrate für ein bestimmtes Feature 5% überschreitet. Das ist fast immer ein Signal dafür, dass das Ausgabeformat des Modells gedriftet ist, was häufig nach einer Modellversionsänderung passiert.

Anruf-Volumen-Anomalien pro Mandant: Ein 3-facher Anstieg über dem 7-Tage-Durchschnitt für einen einzigen Mandanten ist ein zuverlässiger Missbrauchs- oder Runaway-Automation-Indikator.

Token-Effizienz-Verhältnis: Das Verhältnis von Ausgabe-Token zu Eingabe-Token über die Zeit pro Feature verfolgen. Ein plötzlicher Abfall dieses Verhältnisses bedeutet häufig, dass deine Prompts gewachsen sind, ohne eine entsprechende Verbesserung der Ausgabequalität, was ein früher Indikator für überfüllte Kontextfenster ist.

Alles zusammenbringen: Ein praktischer Einstiegspunkt

Für eine Symfony- oder Next.js-Anwendung, die ihre ersten KI-Features ausliefert, ist hier die Reihenfolge der Operationen, die am schnellsten den größten Mehrwert liefert.

Mit Langfuse selbst gehostet auf einem kleinen VPS beginnen. Die Prompt-Payload-Erfassung zuerst in der Staging-Umgebung zum Laufen bringen -- du musst dein PII-Scrubbing überprüfen, bevor es Produktionsdaten berührt. Die Mandanten-ID und Feature-Name-Attribute zu jedem Span hinzufügen. Die Finish-Reason-Drift- und Ausgabe-Parse-Fehler-Alarme einrichten. Das sind 80% des Mehrwerts in zwei bis drei Arbeitstagen.

Sobald diese Basislinie läuft, Kostenzuordnungs-Dashboards hinzufügen. Das bringt typischerweise innerhalb der ersten Woche eine Überraschung zutage -- ein Feature, das im Test günstig aussah, sich unter realen Nutzungsmustern als deutlich teurer herausstellt, oft weil Produktionseingaben länger oder vielfältiger sind als Testdaten.

Die letzte Schicht sind Ausgabequalitätssignale: Guardrail-Auslöseraten, Nutzer-Wiederholungsraten (ein Nutzer, der dieselbe Frage erneut einreicht, ist ein starkes implizites Qualitätssignal) und alle expliziten Feedback-Mechanismen, die du in deiner UI hast. Diese brauchen länger zur Kalibrierung, werden aber zu deinem primären Input, wenn du entscheidest, wann Modellversionen gewechselt, Prompt-Vorlagen überarbeitet oder in Fine-Tuning investiert werden soll.

Den Stack von Anfang an richtig aufbauen

Ein LLM-Observability-Stack zu bauen ist keine optionale Maßnahme, wenn du KI-Features für zahlende Kunden auslieferst. Die Fehlermuster sind zu subtil und die Debugging-Erfahrung ohne ihn zu schmerzhaft. Die minimale Version -- Langfuse selbst gehostet, OpenTelemetry-Spans mit korrekten Attributen, PII-gescrubbte Payload-Erfassung und eine Handvoll Anomalie-Alarme -- ist in einem fokussierten Sprint erreichbar und kostet fast nichts zu betreiben.

Wenn du KI-Features in eine bestehende Symfony- oder Next.js-Anwendung integrierst und die Observability-Schicht richtig aufbauen möchtest, bevor du in Produktion gehst, melde dich unter hello@wolf-tech.io oder besuche wolf-tech.io. Die Instrumentierung von Anfang an richtig zu machen ist deutlich günstiger als sie nach einem Produktionsvorfall zu rekonstruieren.