Hallo zusammen, Leo hier von agntdev.com. Ich hoffe, ihr habt alle eine produktive Woche!
Heute möchte ich etwas erkunden, das mich in letzter Zeit sehr beschäftigt, insbesondere seitdem ich an einigen persönlichen Projekten gearbeitet habe, die kompliziertere und mehrstufige Agenten-Workflows beinhalten. Wir sprechen viel über den Aufbau von Agenten, über die LLMs selbst und über die coolen Dinge, die sie tun können. Aber wie sieht es mit dem weniger glamourösen, aber absolut entscheidenden Aspekt aus, sicherzustellen, dass unsere Agenten über die Zeit zuverlässig und effizient funktionieren?
Genauer gesagt spreche ich über die Beobachtbarkeit von Agenten. Es geht nicht nur um Logging; es geht darum, wirklich zu verstehen, was euer Agent tut, warum er es tut und Probleme zu erkennen, bevor sie unüberschaubar werden. In einer Welt, in der Agenten mit externen APIs interagieren, Entscheidungen basierend auf dynamischen Eingaben treffen und potenziell über lange Zeiträume funktionieren können, blind zu navigieren, ist ein Rezept für das Desaster. Das habe ich auf die harte Tour gelernt, wie ich gleich erklären werde.
Der „Mysteriöse Bug“, Der Mir Alles Beigebracht Hat
Vor einigen Monaten habe ich einen persönlichen Assistenten-Agenten entwickelt. Nennen wir ihn „Projekt Chronos.“ Seine Aufgabe war es, meinen Kalender, meine Nachrichtenströme und spezifische Slack-Kanäle zu überwachen und proaktiv Meetingzeiten vorzuschlagen, wichtige Updates zusammenzufassen oder sogar erste Antworten auf häufige Fragen zu verfassen. Auf den ersten Blick ein ziemlich standardmäßiger Betrieb.
Ich habe ihn gebaut, mit ein paar Szenarien getestet, und es schien alles richtig zu funktionieren. Ich hatte ihn so eingestellt, dass er über Nacht arbeitet, in der Hoffnung, mit einer perfekt organisierten Zusammenfassung aufzuwachen. Stattdessen wachte ich auf und fand… nichts. Oder besser gesagt, eine teilweise Zusammenfassung, die abrupt endete, gefolgt von einer kryptischen Fehlermeldung in meinen Systemprotokollen, die im Grunde sagte: „Irgendwas ist schiefgegangen.“
Das Debuggen war ein Albtraum. Chronos sollte mehrere Dinge tun: Kalenderereignisse abrufen, eine Nachrichten-API abfragen, eine Slack-API ansprechen, die Daten verarbeiten und dann eine Zusammenfassung generieren. Welcher Schritt ist fehlgeschlagen? Warum? Hat es überhaupt alle Schritte versucht? War es eine API-Rate-Limitierung? Ein fehlerhafter Prompt? Eine Zeitüberschreitung? Ich hatte keinen blassen Schimmer.
Mein ursprüngliches Logging war grundlegend: „Schritt X gestartet,“ „Schritt Y abgeschlossen,“ und dann die endgültige Ausgabe oder einen Fehler. Das war nicht genug. Es war, als würde man versuchen, ein Autoproblem zu diagnostizieren, indem man nur weiß, dass es gestartet ist und dann gestoppt hat, ohne Informationen über die Motortemperatur, den Kraftstoffdruck oder elektrische Fehler.
Diese Erfahrung hat einen Punkt hervorgehoben: Wenn ihr ernsthaft Agenten entwickeln wollt, braucht ihr von Anfang an eine gute Beobachtbarkeit. Das ist keine nachträgliche Überlegung; es ist eine grundlegende Komponente.
Über Basis-Logging hinaus: Was Bedeutet „Beobachtbarkeit“ für Agenten?
Für mich lässt sich die Beobachtbarkeit von Agenten in einige zentrale Bereiche unterteilen, die jeweils eine andere Perspektive darauf bieten, wie euer Agent funktioniert:
1. Schritt-für-Schritt-Ausführungsverfolgung
Das ist das kritischste. Ihr müsst genau wissen, was euer Agent in jedem Schritt seiner Ausführung macht. Denkt daran wie an einen detaillierten Faden von Ariadne. Für das Projekt Chronos musste ich sehen:
- Wann er begonnen hat, die Kalenderereignisse abzurufen.
- Die Parameter, die er für den API-Aufruf des Kalenders verwendet hat (z. B. der Datumsbereich).
- Die rohe Antwort der Kalender-API.
- Wie er diese Antwort verarbeitet hat.
- Den genauen Prompt, den er an das LLM geschickt hat, um die Informationen des Kalenders zusammenzufassen.
- Die Antwort des LLM.
- Alle aufgerufenen Tools, mit ihren Eingaben und Ausgaben.
- Die Fehlermeldungen, nicht nur „etwas ist schiefgegangen“, sondern spezifische Fehlermeldungen mit Kontext (z. B. „Die Kalender-API hat 401 Unauthorized für den Benutzer X zurückgegeben“).
Dieses Detailniveau ist unschätzbar, um Probleme zu reproduzieren und die Entscheidungsfindung besser zu verstehen. Mein ursprüngliches Logging sagte nur „Abrufen von Kalenderdaten…“ dann „Zusammenfassen der Kalenderdaten…“ ohne irgendetwas dazwischen. Nicht nützlich, wenn bereits das Abrufen der Daten stillschweigend fehlgeschlagen ist.
2. Verfolgung von Prompts und Antworten
Das LLM ist das Gehirn eures Agenten. Wenn ihr nicht wisst, welche Prompts es erhält und welche Antworten es gibt, navigiert ihr blind. Dazu gehört:
- Der vollständige Prompt, der an das LLM gesendet wurde (System, Benutzer und alle Funktionsaufruf-Beschreibungen).
- Die Temperatur, top_p und andere Generierungsparameter.
- Die rohe Antwort des LLM, einschließlich aller Toolaufrufe, die es entschieden hat zu machen.
- Nutzung der Tokens (Eingabe, Ausgabe, insgesamt) zur Kostenverfolgung und Leistungsanalyse.
Dies ist entscheidend für die Prompt-Engineering. Wenn ein Agent unsinnige Antworten gibt, hilft es, zu sehen, welcher Prompt genau gesendet wurde, um zu debuggen, ob der Eingabekontext falsch war oder ob der Prompt selbst schlecht strukturiert war.
3. Verfolgung von Toolaufrufen
Agenten interagieren häufig mit externen Tools oder APIs. Jede Interaktion stellt einen potenziellen Fehlerpunkt oder unerwartetes Verhalten dar. Ihr müsst aufzeichnen:
- Welches Tool aufgerufen wurde.
- Die genauen Argumente, die an das Tool übergeben wurden.
- Die rohe Ausgabe des Tools.
- Alle Fehler, die vom Tool oder während seiner Ausführung zurückgegeben wurden.
Für Chronos, wenn er versuchte, die Slack-API anzurufen, um eine Zusammenfassung zu posten, musste ich wissen, welcher Kanal angesprochen wurde, der Inhalt der Nachricht und ob die API möglicherweise mit einem 403 Forbidden-Fehler antwortete. Meine vorherige Konfiguration sagte mir lediglich: „Versuch, auf Slack zu posten.“
4. Statusaufzeichnungen
Viele Agenten erhalten einen internen Status – eine Notiz, ein Gedächtnis, eine Liste von Fakten, die sie gesammelt haben. Regelmäßige Erfassungen dieses Status können unglaublich nützlich für das Debuggen sein. Wenn ein Agent in einer Schleife stecken bleibt oder eine falsche Entscheidung trifft, können die Einblicke in seine „internen Gedanken“ zu verschiedenen Zeitpunkten offenbaren, wo sein Verständnis aus dem Ruder gelaufen ist.
Es geht weniger um das Logging jeder Variablenänderung als vielmehr um die Erfassung entscheidungsrelevanter Zustände. Für Chronos könnte das „Derzeitiges Verständnis des Zeitplans des Nutzers“ oder „Wichtige Punkte der Nachrichtenströme bis jetzt“ sein.
Praktische Ansätze: Beobachtbarkeit integrieren
Okay, wie setzen wir das um, ohne in den Logs zu ertrinken? Hier sind einige praktische Strategien und Codeausschnitte.
Strategie 1: Strukturiertes Logging mit Kontext
Vergesst die `print()`-Anweisungen. Verwendet eine geeignete Logging-Bibliothek (wie das `logging`-Modul von Python). Es ist entscheidend, eure Logging-Nachrichten mit strukturierten Daten (JSON, Dictionaries) zu erweitern, anstatt mit einfachen Zeichenfolgen. Dadurch werden die Logs analysierbar, durchsuchbar und viel nützlicher.
Hier ist ein vereinfachtes Beispiel in Python:
import logging
import json
import uuid
from datetime import datetime
# Grundkonfiguration des Loggers (in einer echten Anwendung würden Sie ihn robuster konfigurieren)
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
formatter = logging.Formatter('%(levelname)s: %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
def log_agent_step(agent_id: str, step_name: str, status: str, details: dict = None):
log_data = {
"timestamp": datetime.now().isoformat(),
"agent_id": agent_id,
"step_name": step_name,
"status": status, # zum Beispiel, "started", "completed", "failed"
"details": details if details is not None else {}
}
logger.info(json.dumps(log_data))
class MyAgent:
def __init__(self, agent_id: str = None):
self.agent_id = agent_id if agent_id else str(uuid.uuid4())
self.memory = [] # Einfache interne Erinnerung
def _fetch_calendar_events(self, user_id: str, date_range: str):
log_agent_step(self.agent_id, "fetch_calendar_events", "started",
{"user_id": user_id, "date_range": date_range})
try:
# Simuliere einen API-Aufruf
if "error" in date_range:
raise ValueError("Simuliertes Kalender-API-Fehler")
events = [
{"title": "Teammeeting", "time": "10:00 AM"},
{"title": "Kundenmeeting", "time": "02:00 PM"}
]
log_agent_step(self.agent_id, "fetch_calendar_events", "completed",
{"num_events": len(events), "data_preview": events[0]})
self.memory.append(f"Kalenderereignisse: {events}")
return events
except Exception as e:
log_agent_step(self.agent_id, "fetch_calendar_events", "failed",
{"error": str(e), "traceback": "..."}) # Im realen Leben: traceback erfassen
raise
def _summarize_with_llm(self, prompt_text: str):
log_agent_step(self.agent_id, "summarize_with_llm", "started",
{"prompt_length": len(prompt_text), "prompt_preview": prompt_text[:100]})
try:
# Simuliere einen LLM-Aufruf
if "fail_llm" in prompt_text:
raise RuntimeError("Simuliertes LLM-API-Fehler")
response = f"LLM-Zusammenfassung von: {prompt_text[:50]}..."
token_usage = {"input": len(prompt_text) // 4, "output": len(response) // 4}
log_agent_step(self.agent_id, "summarize_with_llm", "completed",
{"response_length": len(response), "token_usage": token_usage,
"llm_response_preview": response[:100]})
self.memory.append(f"Von LLM produziertes Zusammenfassung: {response}")
return response
except Exception as e:
log_agent_step(self.agent_id, "summarize_with_llm", "failed",
{"error": str(e), "traceback": "..."})
raise
def run_daily_briefing(self, user_id: str):
log_agent_step(self.agent_id, "run_daily_briefing", "started", {"user_id": user_id})
try:
calendar_data = self._fetch_calendar_events(user_id, "today")
news_summary = self._summarize_with_llm("Fasse die wichtigsten Nachrichten von heute zusammen...")
final_briefing_prompt = (
f"Erstelle ein tägliches Briefing basierend auf:\n"
f"Kalender: {json.dumps(calendar_data)}\n"
f"Nachrichten: {news_summary}"
)
final_briefing = self._summarize_with_llm(final_briefing_prompt)
log_agent_step(self.agent_id, "run_daily_briefing", "completed",
{"final_briefing_length": len(final_briefing)})
return final_briefing
except Exception as e:
log_agent_step(self.agent_id, "run_daily_briefing", "failed",
{"error": str(e), "current_memory": self.memory}) # Erinnerung im Fehlerfall erfassen
raise
# Beispiel für die Verwendung
if __name__ == "__main__":
agent = MyAgent()
print(f"\n--- Ausführung des Agenten {agent.agent_id} (Erfolgsfall) ---")
try:
briefing = agent.run_daily_briefing("leo_g")
print(f"Briefing: {briefing[:100]}...")
except Exception as e:
print(f"Die Ausführung des Agenten ist fehlgeschlagen: {e}")
agent_fail = MyAgent()
print(f"\n--- Ausführung des Agenten {agent_fail.agent_id} (Kalenderfehlerm fall) ---")
try:
# Simuliere einen Kalenderfehler, indem du "error" in date_range übergibst
agent_fail._fetch_calendar_events("leo_g", "error_today")
except Exception as e:
print(f"Die Ausführung des Agenten ist wie erwartet fehlgeschlagen: {e}")
agent_llm_fail = MyAgent()
print(f"\n--- Ausführung des Agenten {agent_llm_fail.agent_id} (LLM-Fehlerfall) ---")
try:
# Simuliere einen LLM-Fehler
agent_llm_fail._summarize_with_llm("fail_llm_please")
except Exception as e:
print(f"Die Ausführung des Agenten ist wie erwartet fehlgeschlagen: {e}")
Beachten Sie, wie `log_agent_step` die Agenten-ID, den Schrittname, den Status und ein Dictionary relevanter Details erfasst. Dies erleichtert das Filtern der Protokolle nach Agenten-ID, das Verfolgen einer einzelnen Ausführung oder das Suchen nach allen “fehlgeschlagenen” Schritten.
Strategie 2: Zentrale Beobachtbarkeit mit einer dedizierten Bibliothek/Dienst
Für komplexere Agenten oder Produktionsumgebungen werden Sie schnell über einfaches Datei-Logging hinausgehen. Hier glänzen spezialisierte Werkzeuge. Bibliotheken wie `LangSmith` von LangChain (oder ähnliche für andere Frameworks) bieten integriertes Tracing, Visualisierung und Debugging für LLM-Anwendungen.
Selbst wenn Sie LangChain nicht verwenden, ist das Konzept übertragbar. Sie können Ihr eigenes Wrapper um die Ausführung Ihres Agenten erstellen, das strukturierte Ereignisse an einen Logging-Dienst (Datadog, Splunk, ELK-Stack oder sogar ein einfaches S3-Bucket mit Lambda-Verarbeitung) sendet. Das Wesentliche ist, das Schema der Ereignisse zu standardisieren.
Mein verbessertes Chronos-Projekt verwendet jetzt eine benutzerdefinierte `TraceManager`-Klasse, die kritische Operationen umschließt. Dieser Manager sendet strukturierte Ereignisse an eine lokale Datenbank für die Entwicklung und an einen Cloud-Logging-Dienst in der Produktion. Dadurch kann ich einen vollständigen “Trace” jeder Agentenausführung sehen, mit geschachtelten Schritten und allen zugehörigen Daten (Eingabeaufforderung, Antworten, Eingaben/Ausgaben von Werkzeugen, Fehler).
Strategie 3: Abfangen von LLM- und Werkzeugaufrufen
Viele LLM-SDKs ermöglichen es Ihnen, Rückrufe oder Interceptor für API-Aufrufe einzurichten. Nutzen Sie sie! Anstatt manuell vor und nach jeder LLM-Aufforderung zu protokollieren, können Sie einen einzigen Interceptor haben, der automatisch protokolliert:
- Den genauen verwendeten API-Endpunkt.
- Die Header und den Body der Anfrage (insbesondere die Eingabeaufforderung).
- Die Header und den Body der Antwort (die Vervollständigung).
- Die Latenz.
- Alle Ausnahmen.
Ebenso kapseln Sie Ihre Werkzeugaufrufe ein. Wenn Sie ein `search_web`-Werkzeug haben, sollte der Wrapper die Suchanfrage, die verwendete Suchmaschine und die N besten zurückgegebenen Ergebnisse protokollieren, sowie alle Fehler.
Umsetzbare Tipps für Ihr nächstes Agentenprojekt
- Entwerfen Sie zuerst für die Beobachtbarkeit: Betrachten Sie es nicht als nachträgliche Überlegung. Denken Sie darüber nach, was Sie für das Debuggen benötigen würden, noch bevor Sie Ihren ersten Agentenschritt schreiben.
- Übernehmen Sie strukturiertes Logging: Lassen Sie `print()` und `console.log()` in Produktionscode hinter sich. Verwenden Sie eine geeignete Logging-Bibliothek und geben Sie strukturierte Daten (JSON) für jedes bedeutende Ereignis aus.
- Protokollieren Sie alles, was wichtig ist: Protokollieren Sie den Beginn und das Ende jedes größeren Schrittes, alle LLM-Aufforderungen und -Antworten (einschließlich der Parameter und Token-Zählungen) und jeden Werkzeugaufruf mit seinen Eingaben und Ausgaben.
- Stellen Sie den Zustand im Fehlerfall fest: Wenn ein Agent fehlschlägt, protokollieren Sie seinen internen Zustand oder seine Erinnerung zu diesem Zeitpunkt. Dies liefert einen entscheidenden Kontext für das Verständnis, warum er fehlgeschlagen ist.
- Verwenden Sie agentenspezifische IDs: Weisen Sie jeder Agentenausführung eine eindeutige ID zu (z. B. eine UUID). Dadurch können Sie einfach einen einzigartigen Ausführungspfad in Ihren Protokollen filtern und verfolgen.
- Visualisieren Sie Ihre Traces: Wenn möglich, verwenden oder erstellen Sie ein Tool, das diese strukturierten Protokolle als Ereignisfolge visualisieren kann. Den Fluss zu sehen, macht das Debuggen unendlich einfacher, als mit Rohtext umzugehen. LangSmith macht das großartig, aber selbst ein benutzerdefiniertes Skript kann einen einfachen HTML-Zeitstrahl erstellen.
- Überwachen Sie die Kosten: Die Nutzung von LLM-Token verursacht direkte Kosten. Protokollieren Sie das. Dies hilft Ihnen zu verstehen, wohin Ihr Geld fließt, und Ihre Eingaben zu optimieren.
Agenten zu bauen ist spannend, aber zuverlässige Agenten zu bauen, ist die echte Arbeit (und der echte Wert). Und Zuverlässigkeit beginnt damit, zu wissen, was hinter den Kulissen passiert. Meine schmerzhaften Erfahrungen mit dem Chronos-Projekt haben mir diese Lektion gut beigebracht. Warten Sie nicht auf Ihren eigenen “mysteriösen Fehler”, um überzeugt zu werden. Beginnen Sie noch heute, intelligent zu protokollieren.
Was sind Ihre bevorzugten Beobachtbarkeitsstrategien für Agenten? Schreiben Sie mir in den Kommentaren oder in den sozialen Medien. Ich bin immer interessiert zu erfahren, wie andere diese Herausforderungen bewältigen!
🕒 Published: