Zum Hauptinhalt springen
Connic
Platform

Logs & Debugging

Ausgaben aus eigenem Code, Run-Kontext, Traces und Run Again helfen bei der Fehleranalyse. Run-Metadaten lassen sich an externe Dienste senden.

Zuletzt aktualisiert

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

CodeAnzeige
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:

QuelleErzeugt durch
ToolEin benutzerdefiniertes Tool, gekennzeichnet mit seinem Tool-Namen.
MiddlewareBefore- und After-Middleware, gekennzeichnet als before oder after.
HookEin Tool Hook, gekennzeichnet mit dem zugehörigen Tool.
GuardrailEine benutzerdefinierte Guardrail, gekennzeichnet mit ihrem Guardrail-Namen.

Beispiel

tools/lookup_customer.py
"""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 record

Das 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.

Erfassungslimits und Verhalten
  • 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.
  • StopProcessing und AbortTool sind 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.

Durchsuchbarer Run-Kontext

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.

middleware/invoice-processor.py
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 content
1

Fehlermuster isolieren

Beginne in Agent Runs und filtere nach dem Agenten. Grenze fehlgeschlagene Runs über den Status ein. Nutze bei inhaltlich falschen Antworten trotz erfolgreichem Run Ausdrücke für den Kontext, um Runs mit einer bestimmten Eingabestruktur, einem Kundensegment, Deployment oder Workflow zu finden.
2

Fehlerhafte und erfolgreiche Runs vergleichen

Öffne einen Run mit falschem Ergebnis und einen fehlerfreien Run nebeneinander. Wähle denselben Agenten und dasselbe Deployment mit ähnlichen Inputs und ähnlichem Kontext und vergleiche dann ihre Traces. Der Fokus liegt auf dem ersten Span, an dem sie voneinander abweichen.
3

Entscheidungspfad untersuchen

LLM-Spans zeigen Modell-Request und -Response, Tool-Auswahl, Argumente und Thoughts, wenn der Provider Reasoning-Inhalte zurückgibt. Tool-, Middleware-, Hook- und Guardrail-Spans zeigen ihre Inputs, Outputs, Status, Dauer und Fehlerdetails.
4

Run Again

Verwende bei einem Run Run Again, um einen weiteren Run mit demselben Input zu starten und seinen Trace zu untersuchen.

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üsselBeschreibung
run_idEindeutiger Bezeichner des Runs.
agent_nameName des ausgeführten Agenten.
duration_msAktive Ausführungszeit ohne Wartezeiten auf Approvals und untergeordnete Sequential-Runs.
token_usageAnzahl der Input-, Output-, Thinking-, Cached-Input- und gesamten Tokens des Runs.
middleware/invoice-processor.py
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 response
Werte aus before() 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.