Ereignisse

Ereignisse sind die grundlegenden Bausteine der Gesprächshistorie in ADK-Rust. Jede Interaktion mit einem Agenten — sei es eine Nutzernachricht, eine Agentenantwort oder eine Tool-Ausführung — wird als Ereignis aufgezeichnet. Ereignisse bilden ein unveränderliches Protokoll, das die vollständige Ausführungsspur einer Agentensitzung erfasst.

Überblick

Das Ereignissystem erfüllt mehrere kritische Zwecke:

  • Gesprächshistorie: Ereignisse bilden die chronologische Aufzeichnung aller Interaktionen in einer Sitzung
  • Zustandsverwaltung: Ereignisse tragen Zustandsänderungen über das Feld state_delta
  • Artefaktverfolgung: Ereignisse protokollieren Artefaktoperationen über das Feld artifact_delta
  • Agentenkoordination: Ereignisse ermöglichen Agentenübergaben und Eskalationen
  • Debugging & Beobachtbarkeit: Ereignisse liefern eine vollständige Prüfspur des Agentenverhaltens

Ereignisstruktur

Ein Event repräsentiert eine einzelne Interaktion in einer Unterhaltung. ADK-Rust verwendet einen vereinheitlichten Event-Typ, der LlmResponse einbettet und dem in ADK-Go verwendeten Entwurfsmuster entspricht:

pub struct Event {
    pub id: String,                    // Unique event identifier (UUID)
    pub timestamp: DateTime<Utc>,      // When the event occurred
    pub invocation_id: String,         // Links related events in a single invocation
    pub branch: String,                // For future branching support
    pub author: String,                // Who created this event (user, agent name, tool name)
    pub llm_response: LlmResponse,     // Contains content and LLM metadata
    pub actions: EventActions,         // Side effects and metadata
    pub long_running_tool_ids: Vec<String>,  // IDs of long-running tools
    pub provider_metadata: HashMap<String, String>,  // Provider-specific metadata
}

Die Struktur LlmResponse enthält:

pub struct LlmResponse {
    pub content: Option<Content>,      // The message content (text, parts, etc.)
    pub usage_metadata: Option<UsageMetadata>,
    pub finish_reason: Option<FinishReason>,
    pub partial: bool,                 // True for streaming partial responses
    pub turn_complete: bool,           // True when the turn is complete
    pub interrupted: bool,             // True if generation was interrupted
    pub error_code: Option<String>,
    pub error_message: Option<String>,
}

Auf den Inhalt wird konsistent über event.llm_response.content zugegriffen:

if let Some(content) = &event.llm_response.content {
    for part in &content.parts {
        if let Part::Text { text } = part {
            println!("{}", text);
        }
    }
}

Wichtige Felder

  • id: Eine eindeutige UUID, die dieses spezifische Ereignis identifiziert. Wird für das Abrufen und die Sortierung von Ereignissen verwendet.

  • timestamp: Der Zeitstempel der UTC, zu dem das Ereignis erstellt wurde. Ereignisse werden in der Sitzung chronologisch geordnet.

  • invocation_id: Gruppiert Ereignisse, die zur gleichen Agentenaufruf gehören. Wenn ein Agent eine Nachricht verarbeitet, teilen alle erzeugten Ereignisse (Agentenantwort, Tool-Aufrufe, Unteragenten-Aufrufe) dieselbe invocation_id.

  • branch: Der Gesprächszweig, auf dem das Ereignis erzeugt wurde. ParallelAgent platziert jeden Unteragenten auf seinem eigenen Zweig ({parent}.{parallel_agent}.{sub_agent}) und versieht die von ihm ausgegebenen Ereignisse mit diesem Zweig, sodass Lesezugriffe auf die Historie eines Zweigs nicht das enthalten, was Geschwisterzweige erzeugt haben. Ein Ereignis ist von einem Zweig aus sichtbar, wenn sein eigener Zweig diesem Zweig entspricht oder ein Vorfahr davon ist; Geschwister und verschachtelte Nachfahren sind es nicht. Ein leerer Zweig bedeutet „nicht eingegrenzt“ und bleibt überall sichtbar, sodass Ereignisse ohne einen solchen Zweig unbeeinflusst bleiben. Siehe Workflow Agents.

  • author: Identifiziert, wer das Ereignis erstellt hat:

    • Nutzernachrichten: typischerweise "user" oder eine Nutzerkennung
    • Agentenantworten: der Name des Agenten
    • Tool-Ausführungen: der Name des Tools
    • Systemereignisse: "system"
  • llm_response: Enthält den Nachrichteninhalt und die LLM-Metadaten. Greife über event.llm_response.content auf den Inhalt zu. Der Typ Content kann Text, multimodale Bestandteile (Bilder, Audio) oder strukturierte Daten enthalten. Manche Ereignisse (wie reine Zustandsaktualisierungen) können content: None haben.

  • llm_response.error_code / error_message: Werden gesetzt, wenn der Provider für den Turn einen terminalen Fehler gemeldet hat. Provider können einen Fehler innerhalb eines ansonsten erfolgreichen Stream-Elements melden — die Stream-Fehler von adk-anthropic, das Fehlerereignis von OpenAI Responses und der OpenAI-Transport über websocket tun dies alle — daher unterscheiden diese Felder einen fehlgeschlagenen Turn von einem leeren. LlmAgent erzeugt zuerst das Ereignis, das sie trägt, und beendet dann den Lauf mit einem AdkError, dessen Code model.provider_error ist, wobei der eigene Code des Providers in den Fehlerdetails unter provider_error_code steht. Das Ereignis wird zuerst ausgegeben, damit der fehlgeschlagene Turn weiterhin beobachtbar und persistent bleibt, anstatt in einem Fehler zu verschwinden.

    Eine Kürzung wird nicht auf diese Weise gemeldet: Eine durch ein Token-Limit abgeschnittene Antwort trägt finish_reason: FinishReason::MaxTokens und keinen Fehlercode.

  • llm_response.interrupted: Die Generierung wurde unterbrochen (zum Beispiel ein Realtime-Turn, der vom Benutzer abgebrochen wurde, oder eine Tool-Bestätigung, die den Turn pausiert). Dies wird im Ereignis festgehalten, ist aber für sich genommen kein terminaler Fehler, sodass der Lauf nicht fehlschlägt.

// Distinguishing a failed turn from an empty one
if let Some(code) = &event.llm_response.error_code {
    let detail = event.llm_response.error_message.as_deref().unwrap_or("no message");
    eprintln!("provider reported {code}: {detail}");
}

EventActions

Die Struktur EventActions enthält Metadaten und Nebeneffekte, die mit einem Ereignis verbunden sind:

pub struct EventActions {
    pub state_delta: HashMap<String, Value>,    // State changes to apply
    pub artifact_delta: HashMap<String, i64>,   // Artifact version changes
    pub skip_summarization: bool,               // Skip this event in summaries
    pub transfer_to_agent: Option<String>,      // Transfer control to another agent
    pub escalate: bool,                         // Escalate to human or supervisor
    pub tool_confirmation: Option<ToolConfirmationRequest>,      // Pending tool confirmation
    pub tool_confirmation_decision: Option<ToolConfirmationDecision>, // Approval/denial
    pub compaction: Option<EventCompaction>,     // Context compaction summary
}

state_delta

Das Feld state_delta enthält Schlüssel-Wert-Paare, die Änderungen am Sitzungszustand darstellen. Wenn ein Ereignis an eine Sitzung angehängt wird, werden diese Änderungen in den Zustand der Sitzung übernommen.

Statusschlüssel können Präfixe verwenden, um den Geltungsbereich zu steuern:

  • app:key - Anwendungsbezogener Zustand (gemeinsam für alle Benutzer)
  • user:key - Benutzerbezogener Zustand (gemeinsam für alle Sitzungen eines Benutzers)
  • temp:key - Temporärer Zustand (zwischen Aufrufen gelöscht)
  • Kein Präfix - Sitzungsbezogener Zustand (Standard)

Beispiel:

let mut actions = EventActions::default();
actions.state_delta.insert("user_name".to_string(), json!("Alice"));
actions.state_delta.insert("temp:current_step".to_string(), json!(3));

artifact_delta

Das Feld artifact_delta verfolgt Änderungen an Artefakten. Schlüssel sind Artefaktnamen, und Werte sind Versionsnummern. Dadurch kann das System nachverfolgen, welche Artefakte während eines Ereignisses erstellt oder geändert wurden.

Beispiel:

actions.artifact_delta.insert("report.pdf".to_string(), 1);
actions.artifact_delta.insert("chart.png".to_string(), 2);

skip_summarization

Wenn true, wird dieses Ereignis aus Gesprächszusammenfassungen ausgeschlossen. Nützlich für interne Ereignisse, Debugging-Informationen oder ausführliche Tool-Ausgaben, die nicht Teil des Hauptgesprächsflusses sein sollten.

transfer_to_agent

Wenn auf einen Agentennamen gesetzt, wird die Kontrolle an diesen Agenten übertragen. Dies ermöglicht Multi-Agenten-Workflows, bei denen ein Agent an einen anderen übergeben kann. Der Ziel-Agent muss als Unteragent konfiguriert sein.

Beispiel:

actions.transfer_to_agent = Some("specialist_agent".to_string());

escalate

Wenn true, signalisiert dies, dass das Gespräch an einen menschlichen Bediener oder einen Supervisor-Agenten eskaliert werden soll. Das genaue Eskalationsverhalten hängt von der Implementierung deiner Anwendung ab.

Bildung der Gesprächshistorie

Ereignisse bilden die Gesprächshistorie, indem sie sich innerhalb einer Sitzung in chronologischer Reihenfolge ansammeln. Wenn ein Agent eine Anfrage verarbeitet:

  1. Ereignis für Nutzernachricht: Ein neues Ereignis wird mit der Eingabe des Benutzers erstellt
  2. Agentenverarbeitung: Der Agent erhält die Gesprächshistorie (alle vorherigen Ereignisse)
  3. Ereignis für Agentenantwort: Die Antwort des Agenten wird als neues Ereignis aufgezeichnet
  4. Ereignisse für Tool-Ausführungen: Jeder Tool-Aufruf kann zusätzliche Ereignisse erzeugen
  5. Zustandsaktualisierungen: Zustandsdeltas aus allen Ereignissen werden in den Sitzungszustand übernommen

Die Gesprächshistorie wird aufgebaut durch:

  • Abrufen aller Ereignisse aus der Sitzung in chronologischer Reihenfolge
  • Umwandeln des Inhalts jedes Ereignisses in das geeignete Format für das LLM
  • Einbeziehen von Zustandsinformationen aus angesammelten Zustands-Deltas
  • Herausfiltern von Ereignissen, die mit skip_summarization markiert sind, wenn angemessen

Ereignisablauf-Beispiel

Session Start
  ↓
[Event 1] User: "What's the weather in Tokyo?"
  ↓
[Event 2] Agent: "Let me check that for you."
  ↓
[Event 3] Tool (weather_api): {"temp": 22, "condition": "sunny"}
  ↓
[Event 4] Agent: "It's 22°C and sunny in Tokyo."
  ↓
Session State Updated

Jedes Ereignis baut auf den vorherigen auf und erzeugt so einen vollständigen Prüfpfad der Unterhaltung.

Arbeiten mit Ereignissen

Ereignisse aus einer Sitzung aufrufen

use adk_rust::session::{SessionService, GetRequest};

// Retrieve a session with its events
let session = session_service.get(GetRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: session_id.clone(),
    num_recent_events: None,  // Get all events
    after: None,
}).await?;

// Access the events
let events = session.events();
println!("Total events: {}", events.len());

// Iterate through events (note: session events use llm_response.content)
for i in 0..events.len() {
    if let Some(event) = events.at(i) {
        println!("Event {}: {} by {} at {}",
            event.id,
            event.llm_response.content.as_ref().map(|_| "has content").unwrap_or("no content"),
            event.author,
            event.timestamp
        );
    }
}

Ereignisdetails untersuchen

// Get a specific event from session (uses llm_response.content)
if let Some(event) = events.at(0) {
    // Check the author
    println!("Author: {}", event.author);

    // Check content (session events use llm_response.content)
    if let Some(content) = &event.llm_response.content {
        for part in &content.parts {
            if let Part::Text { text } = part {
                println!("Text: {}", text);
            }
        }
    }

    // Check for state changes
    if !event.actions.state_delta.is_empty() {
        println!("State changes:");
        for (key, value) in &event.actions.state_delta {
            println!("  {} = {}", key, value);
        }
    }

    // Check for agent transfers
    if let Some(target) = &event.actions.transfer_to_agent {
        println!("Transfers to: {}", target);
    }

    // Check for artifacts
    if !event.actions.artifact_delta.is_empty() {
        println!("Artifacts modified:");
        for (name, version) in &event.actions.artifact_delta {
            println!("  {} (v{})", name, version);
        }
    }
}

Ereignishistorie begrenzen

Bei langen Unterhaltungen möchten Sie möglicherweise nur die neuesten Ereignisse abrufen:

// Get only the last 10 events
let session = session_service.get(GetRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: session_id.clone(),
    num_recent_events: Some(10),
    after: None,
}).await?;

Wie Ereignisse ablaufen: Generierung und Verarbeitung

Zu verstehen, wie Ereignisse erstellt und verarbeitet werden, hilft zu verdeutlichen, wie das Framework Aktionen und Historie verwaltet.

Generierungsquellen

Ereignisse werden an unterschiedlichen Punkten im Lebenszyklus der Agentenausführung erstellt:

  1. Benutzereingabe: Der Runner verpackt Benutzernachrichten in ein Event mit author = "user"
  2. Agentenantworten: Agents liefern Event-Objekte zurück (setzen author = agent.name()), um Antworten zu übermitteln
  3. LLM-Ausgabe: Die Integrationsschicht des Modells übersetzt die Ausgabe von LLM (Text, Funktionsaufrufe) in Event-Objekte
  4. Tool-Ergebnisse: Nach der Ausführung eines Tools erzeugt das Framework ein Event, das die Tool-Antwort enthält

Verarbeitungsablauf

Wenn ein Ereignis generiert wird, folgt es diesem Verarbeitungspfad:

  1. Generierung: Ein Ereignis wird von seiner Quelle erstellt und ausgegeben (Agent, Tool oder Benutzereingabe-Handler)
  2. Runner empfängt: Der Runner, der den Agenten ausführt, empfängt das Ereignis
  3. SessionService-Verarbeitung: Der Runner sendet das Ereignis an das SessionService, das:
    • Deltas anwenden: Mergt state_delta in den Sitzungszustand und aktualisiert Artefaktaufzeichnungen
    • Metadaten finalisieren: Weist eindeutige id zu, falls nicht vorhanden, und setzt timestamp
    • In Historie persistieren: Hängt das Ereignis an session.events an
  4. Stream-Ausgabe: Der Runner gibt das verarbeitete Ereignis an die aufrufende Anwendung zurück

Dieser Ablauf stellt sicher, dass Zustandsänderungen und Historie gemeinsam mit dem Kommunikationsinhalt konsistent aufgezeichnet werden.

// Conceptual flow
User Input → Runner → Agent → LLM → Event Generated
                                         ↓
                                   SessionService
                                   - Apply state_delta
                                   - Record in history
                                         ↓
                                   Event Stream → Application

Ereignistypen identifizieren

Wenn Sie Ereignisse vom Runner verarbeiten, sollten Sie identifizieren, mit welchem Ereignistyp Sie es zu tun haben:

Nach Autor

Das Feld author sagt Ihnen, wer das Ereignis erstellt hat:

match event.author.as_str() {
    "user" => println!("User input"),
    agent_name => println!("Response from agent: {}", agent_name),
}

Nach Inhaltstyp

Prüfen Sie das Feld llm_response.content, um den Payload-Typ zu bestimmen:

if let Some(content) = &event.llm_response.content {
    // Check for text content
    let has_text = content.parts.iter().any(|part| {
        matches!(part, Part::Text { .. })
    });

    // Check for function calls (tool requests)
    let has_function_call = content.parts.iter().any(|part| {
        matches!(part, Part::FunctionCall { .. })
    });

    // Check for function responses (tool results)
    let has_function_response = content.parts.iter().any(|part| {
        matches!(part, Part::FunctionResponse { .. })
    });

    if has_text {
        println!("Text message");
    } else if has_function_call {
        println!("Tool call request");
    } else if has_function_response {
        println!("Tool result");
    }
}

Nach Aktionen

Prüfen Sie das Feld actions auf Steuersignale und Nebenwirkungen:

// State changes
if !event.actions.state_delta.is_empty() {
    println!("Event contains state changes");
}

// Agent transfer
if let Some(target) = &event.actions.transfer_to_agent {
    println!("Transfer to agent: {}", target);
}

// Escalation signal
if event.actions.escalate {
    println!("Escalation requested");
}

// Context compaction
if let Some(compaction) = &event.actions.compaction {
    println!("Compacted events from {} to {}",
        compaction.start_timestamp, compaction.end_timestamp);
}

// Tool confirmation
if let Some(confirmation) = &event.actions.tool_confirmation {
    println!("Tool {} awaiting confirmation", confirmation.tool_name);
}

// Skip summarization
if event.actions.skip_summarization {
    println!("Skip this event in summaries");
}

Arbeiten mit Ereignis-Streams

Wenn Sie einen Agenten ausführen, erhalten Sie einen Stream von Ereignissen. So verarbeiten Sie ihn effektiv:

Ereignisse vom Runner verarbeiten

use adk_core::{SessionId, UserId};
use futures::StreamExt;

let mut stream = runner.run(
    UserId::new("user_123")?,
    SessionId::new("session_id")?,
    user_input,
).await?;

while let Some(event_result) = stream.next().await {
    match event_result {
        Ok(event) => {
            // Process the event
            println!("Event from: {}", event.author);

            // Extract text content
            if let Some(content) = &event.llm_response.content {
                for part in &content.parts {
                    if let Part::Text { text } = part {
                        print!("{}", text);
                    }
                }
            }

            // Check for state changes
            if !event.actions.state_delta.is_empty() {
                println!("\nState updated: {:?}", event.actions.state_delta);
            }
        }
        Err(e) => {
            eprintln!("Error: {}", e);
            break;
        }
    }
}

Funktionsaufrufe extrahieren

Wenn das LLM ein Tool anfordert, enthält das Ereignis Informationen zum Funktionsaufruf:

if let Some(content) = &event.llm_response.content {
    for part in &content.parts {
        if let Part::FunctionCall { name, args, thought_signature, .. } = part {
            println!("Tool requested: {}", name);
            println!("Arguments: {}", args);
            if let Some(sig) = thought_signature {
                println!("Thought signature: {} bytes", sig.len());
            }

            // Your application might dispatch tool execution here
            // based on the tool name and arguments
        }
    }
}

Funktionsantworten extrahieren

Nachdem ein Tool ausgeführt wurde, wird das Ergebnis in einer Funktionsantwort zurückgegeben:

if let Some(content) = &event.llm_response.content {
    for part in &content.parts {
        if let Part::FunctionResponse { function_response, .. } = part {
            println!("Tool result from: {}", function_response.name);
            println!("Response: {}", function_response.response);

            // Process the tool result
            // The LLM will use this to continue the conversation
        }
    }
}

Das Durchlaufen von content.parts und das Abgleichen von Part-Varianten funktioniert, aber beim Aufbau von UIs stellt der Event-Typ typisierte, renderbereite Accessors bereit, sodass Sie nie Teil- Interna berühren. Sie sind der empfohlene Weg, Tool-Aktivität aus einem EventStream zu konsumieren:

// A model requested one or more tools.
for call in event.tool_calls() {
    println!("→ {} {}", call.name, call.args);     // call.call_id correlates this call
}

// A tool finished — surfaces the output of ANY tool, streaming or not.
for result in event.tool_results() {
    println!("✓ {} returned {}", result.name, result.response);
}

tool_calls() gibt Vec<ToolCallView> zurück und tool_results() gibt Vec<ToolResultView> zurück:

pub struct ToolCallView<'a>   { pub call_id: Option<&'a str>, pub name: &'a str, pub args: &'a Value }
pub struct ToolResultView<'a> { pub call_id: Option<&'a str>, pub name: &'a str, pub response: &'a Value }

Beide Ansichten sind für Ereignisse leer, die keine Tool-Aktivität enthalten, sodass eine UI sie bei jedem Ereignis bedingungslos aufrufen kann. tool_results() ist das, was die Ausgabe eines Tools erstklassig macht: Ein One-Shot-Tool (read_file, ein Wetter-API) hat keine Streaming-Ausgabe, aber sein Ergebnis kommt dennoch als gewöhnliches Ereignis an, das Sie rendern können — nicht nur bash-artige Tools.

Korrelation. Verwenden Sie call_id, um ein Ergebnis (und alle Fortschritts-Chunks, siehe unten) seinem ursprünglichen Aufruf zuzuordnen. Anbieter, die keine Call-IDs angeben (z. B. Gemini), lassen es auf None; fallen Sie in diesem Fall auf name zurück.

Streaming-Tool-Fortschritt

Langlaufende Tools (ein Shell-Befehl, ein Build, ein Download) können der UI Zwischen- ausgabe während ihrer Ausführung senden, statt den Benutzer auf das finale Ergebnis warten zu lassen. Ein Tool ruft ToolContext::emit_progress auf:

// inside Tool::execute, as output arrives:
ctx.emit_progress("stdout", &format!("{line}\n")).await;
ctx.emit_progress("stderr", &warning).await;

Das Framework leitet jeden Chunk als Teilereignis auf demselben EventStream wie alles andere weiter — kein separater Kanal, kein Scraping von Logs. Erkennen und leiten Sie diese Ereignisse mit tool_progress_stream() weiter:

while let Some(event) = stream.next().await {
    let event = event?;
    if let Some(stream_name) = event.tool_progress_stream() {   // "stdout" | "stderr" | custom
        // render a live terminal line from event.llm_response.content (role "tool")
        continue;
    }
    // ... handle tool_calls(), tool_results(), and model text as usual
}

Fortschrittsereignisse sind als partial = true markiert (damit sie niemals mit einer finalen Antwort verwechselt werden) und tragen die ursprüngliche Call-ID in provider_metadata (Schlüssel adk_core::TOOL_PROGRESS_CALL_ID_KEY), sodass Sie sie mit dem richtigen tool_calls() / tool_results()-Eintrag gruppieren können. Die Standard-emit_progress- Implementierung ist ein No-Op, daher sind Tools, die nicht streamen, nicht betroffen.

Zusammengefügt sieht ein vollständiger Tool-Lebenszyklus im Stream so aus:

tool_calls()          →  open a card  (name + args, correlated by call_id)
tool_progress_stream()→  live stdout/stderr lines stream into the card
tool_results()        →  finalize the card with the tool's output

Genau dieses Muster verwendet das streaming_bash-Beispiel, um sowohl Streaming-Tools (bash) als auch One-Shot-Tools (read_file, grep, glob) aus einem einzigen Datenstrom über websocket darzustellen.

Häufige Ereignismuster

Hier sind typische Ereignisfolgen, auf die Sie stoßen werden:

Einfache Text-Interaktion

[Event 1] author="user", content=Text("Hello")
[Event 2] author="assistant", content=Text("Hi! How can I help?")

Ablauf der Tool-Verwendung

[Event 1] author="user", content=Text("What's the weather?")
[Event 2] author="assistant", content=FunctionCall(name="get_weather", args={...})
[Event 3] author="assistant", content=FunctionResponse(name="get_weather", response={...})
[Event 4] author="assistant", content=Text("It's sunny and 72°F")

Zustandsaktualisierung

[Event 1] author="assistant", content=Text("I've saved your preference")
           actions.state_delta={"user_theme": "dark"}

Agenten-Übergabe

[Event 1] author="router", content=Text("Transferring to specialist")
           actions.transfer_to_agent=Some("specialist_agent")
[Event 2] author="specialist_agent", content=Text("I can help with that")

Ereignis-Metadaten und Kennungen

Ereignis-ID

Jedes Ereignis hat eine eindeutige id (UUID) zur präzisen Identifikation:

println!("Event ID: {}", event.id);

Aufruf-ID

Die invocation_id gruppiert alle Ereignisse von einer einzelnen Benutzeranfrage bis hin zur finalen Antwort:

// All events in one interaction share the same invocation_id
println!("Invocation: {}", event.invocation_id);

// Use this for logging and tracing
log::info!("Processing event {} in invocation {}", event.id, event.invocation_id);

Zeitstempel

Ereignisse werden für die chronologische Reihenfolge mit Zeitstempeln versehen:

println!("Event occurred at: {}", event.timestamp.format("%Y-%m-%d %H:%M:%S"));

Bewährte Vorgehensweisen

  1. Unveränderlichkeit von Ereignissen: Ereignisse sollten nach ihrer Erstellung niemals geändert werden. Sie bilden ein unveränderliches Prüfprotokoll.

  2. Zustandsverwaltung: Verwenden Sie state_delta für alle Zustandsänderungen, statt den Zustand direkt zu ändern. Dadurch wird sichergestellt, dass Änderungen im Ereignisprotokoll nachverfolgt werden.

  3. Aussagekräftige Autoren: Setzen Sie klare, beschreibende Autorennamen, damit Ereignisprotokolle leichter zu verstehen sind.

  4. Selektive Zusammenfassung: Verwende skip_summarization für ausführliche oder interne Ereignisse, die den Gesprächsverlauf aufblähen würden.

  5. Gruppierung von Aufrufen: Behalte für alle Ereignisse, die während eines einzelnen Agentenaufrufs erzeugt werden, dieselbe invocation_id bei, um die logische Gruppierung zu erhalten.

  6. Artefaktverfolgung: Aktualisiere artifact_delta immer, wenn Artefakte erstellt oder geändert werden, um die Konsistenz zu wahren.

  7. Stream-Verarbeitung: Behandle Fehler bei der Verarbeitung von Ereignisströmen immer. Ereignisse können aufgrund von LLM-Fehlern, Tool-Fehlern oder Netzwerkproblemen fehlschlagen.

  8. Inhaltsprüfung: Prüfe immer, ob llm_response.content Some ist, bevor du auf Teile zugreifst. Manche Ereignisse (wie reine Statusaktualisierungen) haben möglicherweise keinen Inhalt.

  9. Pattern Matching: Verwende Rusts Pattern Matching, um verschiedene Ereignistypen und Inhaltsteile elegant zu behandeln.

  10. Protokollierung: Verwende invocation_id, um alle Ereignisse innerhalb einer einzelnen Benutzerinteraktion für Debugging und Beobachtbarkeit zu korrelieren.


Vorherige: ← Artefakte | Nächste: Telemetry →