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::Textfür TextdatenPart::InlineDatafü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::InlineDatainjiziert, 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-Nutzungadk_artifact::ArtifactService- Vollständiger Service-Traitadk_artifact::InMemoryArtifactService- In-Memory-Implementierungadk_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 →