Artefakte

Artefakte bieten eine Möglichkeit, Binärdaten (Bilder, PDFs, Audiodateien usw.) innerhalb Ihrer Agentenanwendungen zu speichern und abzurufen. Das Artefaktsystem handhabt die Versionierung, das Scoping von Namensbereichen und die Persistenz von Daten über mehrere Sessions hinweg.

Übersicht

Das Artefaktsystem in adk-rust besteht aus:

  • Part: Die Kerndarstellung von Daten, die Text- oder Binärdaten mit MIME-Typen halten kann
  • ArtifactService: Der Trait, der Artefakt-Speichervorgänge definiert
  • InMemoryArtifactService: Eine In-Memory-Implementierung für Entwicklung und Tests
  • ScopedArtifacts: Ein Wrapper, der Artefaktoperationen durch automatische Handhabung des Session-Kontextes vereinfacht

Artefakte werden nach Anwendung, Benutzer und Session gescoped, was Isolation und Organisation bietet. Dateien können Session-gescoped (Standard) oder Benutzer-gescoped (unter Verwendung des user:-Präfixes) sein.

Part-Darstellung

Das Part-Enum repräsentiert Daten, die als Artefakte gespeichert werden können:

pub enum Part {
    Text { text: String },
    InlineData { mime_type: String, data: Vec<u8> },
    FunctionCall { name: String, args: serde_json::Value },
    FunctionResponse { name: String, response: serde_json::Value },
}

Für Artefakte werden Sie hauptsächlich verwenden:

  • Part::Text für Textdaten
  • Part::InlineData für Binärdaten mit MIME-Typen

Grundlegende Verwendung

Der einfachste Weg, mit Artefakten zu arbeiten, ist über den Artifacts-Trait, der in Agent-Kontexten verfügbar ist:

use adk_rust::prelude::*;

// In an agent tool or callback
async fn save_report(ctx: &ToolContext) -> Result<Value> {
    let artifacts = ctx.artifacts();
    
    // Save text data
    let version = artifacts.save(
        "report.txt",
        &Part::Text { text: "Report content".to_string() }
    ).await?;
    
    // Save binary data
    let image_data = vec![0xFF, 0xD8, 0xFF]; // JPEG header
    artifacts.save(
        "chart.jpg",
        &Part::InlineData {
            mime_type: "image/jpeg".to_string(),
            data: image_data,
        }
    ).await?;
    
    Ok(json!({ "saved": true, "version": version }))
}

ArtifactService Trait

Das ArtifactService Trait definiert die Kernoperationen für das Artefaktmanagement:

#[async_trait]
pub trait ArtifactService: Send + Sync {
    async fn save(&self, req: SaveRequest) -> Result<SaveResponse>;
    async fn load(&self, req: LoadRequest) -> Result<LoadResponse>;
    async fn delete(&self, req: DeleteRequest) -> Result<()>;
    async fn list(&self, req: ListRequest) -> Result<ListResponse>;
    async fn versions(&self, req: VersionsRequest) -> Result<VersionsResponse>;
}

Save Operation

Speichern Sie ein Artefakt mit automatischer oder expliziter Versionierung:

use adk_artifact::{InMemoryArtifactService, SaveRequest};
use adk_core::Part;

let service = InMemoryArtifactService::new();

let response = service.save(SaveRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: "session_456".to_string(),
    file_name: "document.pdf".to_string(),
    part: Part::InlineData {
        mime_type: "application/pdf".to_string(),
        data: pdf_bytes,
    },
    version: None, // Auto-increment version
}).await?;

println!("Saved as version: {}", response.version);

Load Operation

Laden Sie die neueste Version oder eine spezifische Version:

use adk_artifact::LoadRequest;

// Load latest version
let response = service.load(LoadRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: "session_456".to_string(),
    file_name: "document.pdf".to_string(),
    version: None, // Load latest
}).await?;

// Load specific version
let response = service.load(LoadRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: "session_456".to_string(),
    file_name: "document.pdf".to_string(),
    version: Some(2), // Load version 2
}).await?;

match response.part {
    Part::InlineData { mime_type, data } => {
        println!("Loaded {} bytes of {}", data.len(), mime_type);
    }
    _ => {}
}

List Operation

Listen Sie alle Artefakte in einer Session auf:

use adk_artifact::ListRequest;

let response = service.list(ListRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: "session_456".to_string(),
}).await?;

for file_name in response.file_names {
    println!("Found artifact: {}", file_name);
}

Delete Operation

Löschen Sie eine spezifische Version oder alle Versionen:

use adk_artifact::DeleteRequest;

// Delete specific version
service.delete(DeleteRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: "session_456".to_string(),
    file_name: "document.pdf".to_string(),
    version: Some(1), // Delete version 1
}).await?;

// Delete all versions
service.delete(DeleteRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: "session_456".to_string(),
    file_name: "document.pdf".to_string(),
    version: None, // Delete all versions
}).await?;

Versions Operation

Listen Sie alle Versionen eines Artefakts auf:

use adk_artifact::VersionsRequest;

let response = service.versions(VersionsRequest {
    app_name: "my_app".to_string(),
    user_id: "user_123".to_string(),
    session_id: "session_456".to_string(),
    file_name: "document.pdf".to_string(),
}).await?;

println!("Available versions: {:?}", response.versions);
// Output: [3, 2, 1] (sorted newest first)

Versionierung

Artefakte unterstützen automatische Versionierung:

  • Beim Speichern ohne Angabe einer Version erhöht das System die Versionsnummer automatisch basierend auf der neuesten Version.
  • Version 1 wird der ersten Speicherung zugewiesen.
  • Jede nachfolgende Speicherung erhöht die Versionsnummer.
  • Sie können spezifische Versionen laden, löschen oder abfragen.
// Erste Speicherung – wird zu Version 1
let v1 = service.save(SaveRequest {
    file_name: "data.json".to_string(),
    part: Part::Text { text: "v1 data".to_string() },
    version: None,
    // ... other fields
}).await?;
assert_eq!(v1.version, 1);

// Zweite Speicherung – wird zu Version 2
let v2 = service.save(SaveRequest {
    file_name: "data.json".to_string(),
    part: Part::Text { text: "v2 data".to_string() },
    version: None,
    // ... other fields
}).await?;
assert_eq!(v2.version, 2);

// Neueste Version (Version 2) laden
let latest = service.load(LoadRequest {
    file_name: "data.json".to_string(),
    version: None,
    // ... other fields
}).await?;

// Spezifische Version laden
let old = service.load(LoadRequest {
    file_name: "data.json".to_string(),
    version: Some(1),
    // ... other fields
}).await?;

Namensraum-Geltungsbereich

Artefakte können auf zwei Ebenen segmentiert werden:

Sitzungsbezogen (Standard)

Standardmäßig sind Artefakte auf eine spezifische Session beschränkt. Jede Session verfügt über ihren eigenen isolierten Artefakt-Namensraum:

// Sitzung 1
service.save(SaveRequest {
    session_id: "session_1".to_string(),
    file_name: "notes.txt".to_string(),
    // ... other fields
}).await?;

// Sitzung 2 – anderes Artefakt mit gleichem Namen
service.save(SaveRequest {
    session_id: "session_2".to_string(),
    file_name: "notes.txt".to_string(),
    // ... other fields
}).await?;

// Dies sind zwei separate Artefakte

Benutzerbezogen

Artefakte mit dem user:-Präfix werden über alle Sessions für einen Benutzer hinweg geteilt:

// In Sitzung 1 speichern
service.save(SaveRequest {
    session_id: "session_1".to_string(),
    file_name: "user:profile.jpg".to_string(), // user:-Präfix
    // ... other fields
}).await?;

// In Sitzung 2 laden – gleiches Artefakt
let profile = service.load(LoadRequest {
    session_id: "session_2".to_string(),
    file_name: "user:profile.jpg".to_string(),
    // ... other fields
}).await?;

Das user:-Präfix ermöglicht:

  • Teilen von Daten über mehrere Konversationen hinweg
  • Persistente Benutzereinstellungen
  • Caching auf Benutzerebene

InMemoryArtifactService

Der InMemoryArtifactService bietet eine In-Memory-Implementierung, die für Entwicklung und Tests geeignet ist:

use adk_artifact::InMemoryArtifactService;
use std::sync::Arc;

let service = Arc::new(InMemoryArtifactService::new());

// Mit Agents verwenden
let agent = LlmAgentBuilder::new("my_agent")
    .model(model)
    .build()?;

// Der Dienst kann an Runner übergeben oder direkt verwendet werden

Hinweis: Daten werden nicht auf der Festplatte gespeichert. Für den Produktionseinsatz sollten Sie die Implementierung eines benutzerdefinierten ArtifactService in Betracht ziehen, der durch eine Datenbank oder Cloud-Speicher unterstützt wird.

ScopedArtifacts

Der ScopedArtifacts-Wrapper vereinfacht Artefaktoperationen, indem er den Sitzungskontext automatisch injiziert:

use adk_artifact::{ScopedArtifacts, InMemoryArtifactService};
use std::sync::Arc;

let service = Arc::new(InMemoryArtifactService::new());

let artifacts = ScopedArtifacts::new(
    service,
    "my_app".to_string(),
    "user_123".to_string(),
    "session_456".to_string(),
);

// Einfache API – keine Notwendigkeit, App/Benutzer/Sitzung anzugeben
let version = artifacts.save("file.txt", &Part::Text {
    text: "content".to_string()
}).await?;

let part = artifacts.load("file.txt").await?;
let files = artifacts.list().await?;

Dies ist dieselbe Schnittstelle, die über ToolContext::artifacts() und CallbackContext::artifacts() verfügbar ist.

Häufige Muster

Bildanalyse mit multimodalen Modellen

Wenn Sie möchten, dass ein LLM ein als Artifact gespeichertes Bild analysiert, müssen Sie einen BeforeModel callback verwenden, um das Bild direkt in die LLM-Anfrage einzufügen. Dies folgt dem adk-go Muster.

Warum kein Tool verwenden? Tool-Antworten in LLM APIs sind JSON-Text. Wenn ein Tool Bilddaten zurückgibt (selbst base64-kodiert), sieht das Modell diese als Text und nicht als tatsächliches Bild. Für eine echte multimodale Analyse muss das Bild als Part::InlineData im Konversationsinhalt enthalten sein.

use adk_rust::prelude::*;
use adk_rust::artifact::{ArtifactService, InMemoryArtifactService, SaveRequest, LoadRequest};
use std::sync::Arc;

#[tokio::main]
async fn main() -> Result<()> {
    let api_key = std::env::var("GOOGLE_API_KEY")?;
    let model = Arc::new(GeminiModel::new(&api_key, "gemini-2.5-flash")?);

    // Create artifact service and save an image
    let artifact_service = Arc::new(InMemoryArtifactService::new());
    let image_bytes = std::fs::read("photo.png")?;

    artifact_service.save(SaveRequest {
        app_name: "image_app".to_string(),
        user_id: "user".to_string(),
        session_id: "init".to_string(),
        file_name: "user:photo.png".to_string(),  // user-scoped for cross-session access
        part: Part::InlineData {
            data: image_bytes,
            mime_type: "image/png".to_string(),
        },
        version: None,
    }).await?;

    // Clone for use in callback
    let callback_service = artifact_service.clone();

    let agent = LlmAgentBuilder::new("image_analyst")
        .description("Analyzes images")
        .instruction("You are an image analyst. Describe what you see in the image.")
        .model(model)
        // Use BeforeModel callback to inject image into the request
        .before_model_callback(Box::new(move |_ctx, mut request| {
            let service = callback_service.clone();
            Box::pin(async move {
                // Load the image artifact
                if let Ok(response) = service.load(LoadRequest {
                    app_name: "image_app".to_string(),
                    user_id: "user".to_string(),
                    session_id: "init".to_string(),
                    file_name: "user:photo.png".to_string(),
                    version: None,
                }).await {
                    // Inject image into the last user content
                    if let Some(last_content) = request.contents.last_mut() {
                        if last_content.role == "user" {
                            last_content.parts.push(response.part);
                        }
                    }
                }

                // Continue with the modified request
                Ok(BeforeModelResult::Continue(request))
            })
        }))
        .build()?;

    // Now when users ask "What's in the image?", the model will see the actual image
    Ok(())
}

Wichtige Punkte:

  • Verwenden Sie BeforeModelResult::Continue(request), um die modifizierte Anfrage an das Modell weiterzuleiten
  • Verwenden Sie BeforeModelResult::Skip(response), wenn Sie stattdessen eine zwischengespeicherte Antwort zurückgeben möchten
  • Das Bild wird als Part::InlineData injiziert, was Gemini als tatsächliche Bilddaten interpretiert
  • Verwenden Sie das Präfix user: für Artifacts, die sitzungsübergreifend zugänglich sein sollen

PDF-Dokumentenanalyse

Gemini models können PDF-Dokumente nativ mit demselben BeforeModel callback Muster verarbeiten. PDFs werden mit dem MIME type application/pdf injiziert:

// Save PDF as artifact
artifact_service.save(SaveRequest {
    app_name: "my_app".to_string(),
    user_id: "user".to_string(),
    session_id: "init".to_string(),
    file_name: "user:document.pdf".to_string(),
    part: Part::InlineData {
        data: pdf_bytes,
        mime_type: "application/pdf".to_string(),
    },
    version: None,
}).await?;

// Use BeforeModel callback to inject PDF (same pattern as images)
.before_model_callback(Box::new(move |_ctx, mut request| {
    let service = callback_service.clone();
    Box::pin(async move {
        if let Ok(response) = service.load(LoadRequest {
            file_name: "user:document.pdf".to_string(),
            // ... other fields
        }).await {
            if let Some(last_content) = request.contents.last_mut() {
                if last_content.role == "user" {
                    last_content.parts.push(response.part);
                }
            }
        }
        Ok(BeforeModelResult::Continue(request))
    })
}))

Gemini PDF-Funktionen:

  • Extrahieren und Analysieren von Textinhalten
  • Beantworten von Fragen zu Dokumenten
  • Zusammenfassen von Abschnitten oder ganzen Dokumenten
  • Verarbeitung von bis zu ~1000 Seiten
  • OCR-Unterstützung für gescannte Dokumente

Siehe examples/artifacts/chat_pdf.rs für ein vollständiges, funktionierendes Beispiel.

Speichern generierter Bilder

async fn generate_and_save_image(ctx: &ToolContext) -> Result<Value> {
    let artifacts = ctx.artifacts();
    
    // Generate image (pseudo-code)
    let image_bytes = generate_image().await?;
    
    let version = artifacts.save(
        "generated_image.png",
        &Part::InlineData {
            mime_type: "image/png".to_string(),
            data: image_bytes,
        }
    ).await?;
    
    Ok(json!({
        "message": "Image saved",
        "file": "generated_image.png",
        "version": version
    }))
}

Laden und Verarbeiten von Dokumenten

async fn process_document(ctx: &ToolContext, filename: &str) -> Result<Value> {
    let artifacts = ctx.artifacts();
    
    // Load the document
    let part = artifacts.load(filename).await?;
    
    match part {
        Part::InlineData { mime_type, data } => {
            // Process based on MIME type
            let result = match mime_type.as_str() {
                "application/pdf" => process_pdf(&data)?,
                "image/jpeg" | "image/png" => process_image(&data)?,
                _ => return Err(AdkError::Artifact("Unsupported type".into())),
            };
            
            Ok(json!({ "result": result }))
        }
        _ => Err(AdkError::Artifact("Expected binary data".into())),
    }
}

Versionshistorie

async fn show_history(ctx: &ToolContext, filename: &str) -> Result<Value> {
    let artifacts = ctx.artifacts();
    
    // Get all files
    let files = artifacts.list().await?;
    
    if !files.contains(&filename.to_string()) {
        return Ok(json!({ "error": "File not found" }));
    }
    
    // Note: versions() is not available on the simple Artifacts trait
    // You would need access to the underlying ArtifactService
    
    Ok(json!({
        "file": filename,
        "exists": true
    }))
}

API-Referenz

Die vollständige API-Dokumentation finden Sie unter:

  • adk_core::Artifacts - Einfacher Trait für die Agenten-Nutzung
  • adk_artifact::ArtifactService - Vollständiger Service-Trait
  • adk_artifact::InMemoryArtifactService - In-Memory-Implementierung
  • adk_artifact::ScopedArtifacts - Gekapselter Wrapper

Verwandt

  • Sitzungen - Sitzungsverwaltung und Lebenszyklus
  • Rückrufe - Zugriff auf artifacts in Rückrufen
  • Tools - Verwenden von artifacts in benutzerdefinierten Tools

Zurück: ← Rückrufe | Weiter: Ereignisse →