Logs & Debugging
Ausgaben aus eigenem Code, Run-Kontext, Traces und Run Again helfen bei der Fehleranalyse. Run-Metadaten lassen sich an externe Dienste senden.
Auf dieser Seite
Logging aus benutzerdefiniertem Code
Alle Ausgaben über print oder Logging in einem Tool, einer Middleware, einem Hook oder einer Guardrail werden je Run erfasst. Logs erscheinen im Tab Logs des Projekts, mit Filtern über mehrere Runs hinweg, sowie in der Detailansicht des einzelnen Runs. Ein Connic-spezifischer Logger ist nicht erforderlich: stdout, stderr und das Python-Standardmodul logging werden automatisch erfasst.
Erfasste Ausgaben
| Code | Anzeige |
|---|---|
| print("hello") | Eine Log-Zeile auf dem Level info. |
| print("boom", file=sys.stderr) | Eine Log-Zeile auf dem Level error. |
| logging.getLogger("tools.x").info(...) | Eine Zeile mit dem gewählten Log-Level debug, info, warning oder error. |
Namen von Standard-Library-Loggern müssen mit tools., middleware., hooks. oder guardrails. beginnen. Das übliche Muster logging.getLogger(__name__) erzeugt diese Namen bereits, da Connic Dateien unter den passenden Package Roots importiert.
Quellkennzeichnungen
Jede Zeile wird mit dem Codepfad gekennzeichnet, der sie ausgegeben hat:
| Quelle | Erzeugt durch |
|---|---|
| Tool | Ein benutzerdefiniertes Tool, gekennzeichnet mit seinem Tool-Namen. |
| Middleware | Before- und After-Middleware, gekennzeichnet als before oder after. |
| Hook | Ein Tool Hook, gekennzeichnet mit dem zugehörigen Tool. |
| Guardrail | Eine benutzerdefinierte Guardrail, gekennzeichnet mit ihrem Guardrail-Namen. |
Beispiel
"""Ein benutzerdefiniertes Tool, dessen Logs Connic im Dashboard anzeigt."""
import logging
import sys
from typing import Any, Dict
log = logging.getLogger(__name__) # -> "tools.lookup_customer"
async def lookup_customer(args: Dict[str, Any], context: Dict[str, Any]) -> Dict[str, Any]:
customer_id = args["customer_id"]
# Einfaches print -> wird als Log-Zeile auf Info-Level erfasst
print(f"Kunde {customer_id} wird gesucht")
# stdlib logging -> wird auf dem aufgerufenen Level erfasst
log.warning("Cache Miss für %s", customer_id)
try:
record = await fetch_from_crm(customer_id)
except Exception:
# Ausgabe auf stderr -> wird als Log-Zeile auf Error-Level erfasst
print(f"CRM-Lookup für {customer_id} fehlgeschlagen", file=sys.stderr)
raise
return recordDas erzeugt eine Info-Zeile aus print, eine Warnung aus dem Standard-Logger und bei einem fehlgeschlagenen CRM-Aufruf einen Fehler aus stderr. Die Logs-Tabelle zeigt die Quelle tool mit dem Detail lookup_customer und behält die Ausgabereihenfolge der Zeilen bei.
- Connic behält bis zu 500 Log-Zeilen pro Run; weitere Zeilen werden verworfen.
- Nicht behandelte Exceptions aus Tools, Middleware, Hooks und Guardrails werden mit ihrem Traceback geloggt, bevor die normale Fehlerbehandlung fortgesetzt wird.
StopProcessingundAbortToolsind beabsichtigter Kontrollfluss und werden nicht als Fehler geloggt.- Verwende
logging.exception(...)innerhalb einer abgefangenen Exception, um den Traceback beizubehalten. - Jeder Zeilenumbruch schließt einen Log-Eintrag ab. Ausgaben ohne abschließenden Zeilenumbruch können im Puffer bleiben, bis die Funktion endet.
- Serialisiere Werte für strukturierte Logs mit
json.dumps(...)und logge den resultierenden String.
Einen Agenten debuggen
Filtere Agent Runs so, dass vergleichbare Ausführungen übrig bleiben, und öffne anschließend einzelne Traces, um Inputs, Outputs, Kontext, Modell-Requests, Tool Calls und Fehler zu untersuchen.
Kennzeichne Runs in der Middleware mit einheitlichen Kontextfeldern wie customer_id, request_type, plan, locale oder region. Agent Runs unterstützt Ausdrücke wie context.request_type == 'invoice_validation'; die Logs-Suche gleicht Nachrichten, Quellen und Agent-Namen ab.
from typing import Any, Dict
async def before(content: Dict[str, Any], context: Dict[str, Any]) -> Dict[str, Any]:
payload = context.get("payload", {})
context["request_type"] = "invoice_validation"
context["customer_tier"] = payload.get("customer_tier", "unknown")
context["region"] = payload.get("region", "unknown")
context["debug_bucket"] = "high-risk"
return contentFehlermuster isolieren
Fehlerhafte und erfolgreiche Runs vergleichen
Entscheidungspfad untersuchen
Run Again
Im Code auf Run-Daten zugreifen
Der Middleware Hook after() erhält Systemfelder, nachdem der Agent fertig ist. Nutze sie, um Run-Metadaten an einen eigenen Monitoring- oder Analytics-Service zu senden.
| Kontextschlüssel | Beschreibung |
|---|---|
| run_id | Eindeutiger Bezeichner des Runs. |
| agent_name | Name des ausgeführten Agenten. |
| duration_ms | Aktive Ausführungszeit ohne Wartezeiten auf Approvals und untergeordnete Sequential-Runs. |
| token_usage | Anzahl der Input-, Output-, Thinking-, Cached-Input- und gesamten Tokens des Runs. |
import httpx
from typing import Any, Dict
async def after(response: str, context: Dict[str, Any]) -> str:
"""Run-Metadaten an ein externes Monitoring-System senden."""
async with httpx.AsyncClient() as client:
result = await client.post(
"https://monitoring.example.com/events",
json={
"run_id": context.get("run_id"),
"agent": context.get("agent_name"),
"duration_ms": context.get("duration_ms"),
"tokens": context.get("token_usage", {}),
"request_type": context.get("request_type"),
}
)
result.raise_for_status()
return responsebefore() sind auch in after() verfügbar. Nimm Bezeichner wie request_type oder customer_id in ausgehende Monitoring Events auf. Die vollständige Referenz steht unter Kontext.