Artéfacts
Les Artéfacts permettent de stocker et de récupérer des données binaires (images, PDF, fichiers audio, etc.) au sein de vos applications d'agent. Le système d'artéfacts gère le versionnement, la portée des espaces de noms et la persistance des données entre les sessions.
Aperçu
Le système d'artéfacts dans adk-rust se compose de :
- Part : La représentation de données principale qui peut contenir du texte ou des données binaires avec des types MIME
- ArtifactService : Le trait définissant les opérations de stockage d'artéfacts
- InMemoryArtifactService : Une implémentation en mémoire pour le développement et les tests
- ScopedArtifacts : Un wrapper qui simplifie les opérations d'artéfacts en gérant automatiquement le contexte de session
Les Artéfacts sont gérés par application, utilisateur et session, offrant isolation et organisation. Les fichiers peuvent être limités à la session (par défaut) ou à l'utilisateur (en utilisant le préfixe user:).
Représentation de Part
L'énumération Part représente les données qui peuvent être stockées comme des artéfacts :
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 },
}
Pour les artéfacts, vous utiliserez principalement :
Part::Textpour les données textuellesPart::InlineDatapour les données binaires avec des types MIME
Utilisation de base
La manière la plus simple de travailler avec les artéfacts est d'utiliser le trait Artifacts, qui est disponible dans les contextes d'agent :
use adk_rust::prelude::*;
// Dans un outil d'agent ou un rappel
async fn save_report(ctx: &ToolContext) -> Result<Value> {
let artifacts = ctx.artifacts();
// Enregistrer des données textuelles
let version = artifacts.save(
"report.txt",
&Part::Text { text: "Report content".to_string() }
).await?;
// Enregistrer des données binaires
let image_data = vec![0xFF, 0xD8, 0xFF]; // Entête JPEG
artifacts.save(
"chart.jpg",
&Part::InlineData {
mime_type: "image/jpeg".to_string(),
data: image_data,
}
).await?;
Ok(json!({ "saved": true, "version": version }))
}
Trait ArtifactService
Le trait ArtifactService définit les opérations essentielles pour la gestion des artefacts :
#[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>;
}
Opération de Sauvegarde
Sauvegarder un artefact avec un versionnement automatique ou explicite :
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);
Opération de Chargement
Charger la dernière version ou une version spécifique :
use adk_artifact::LoadRequest;
// Charger la dernière 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, // Charger la dernière
}).await?;
// Charger une version spécifique
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), // Charger la version 2
}).await?;
match response.part {
Part::InlineData { mime_type, data } => {
println!("Loaded {} bytes of {}", data.len(), mime_type);
}
_ => {}
}
Opération de Liste
Lister tous les artefacts d'une session :
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);
}
Opération de Suppression
Supprimer une version spécifique ou toutes les versions :
use adk_artifact::DeleteRequest;
// Supprimer une version spécifique
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), // Supprimer la version 1
}).await?;
// Supprimer toutes les 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, // Supprimer toutes les versions
}).await?;
Opération de Versions
Lister toutes les versions d'un artefact :
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);
// Sortie : [3, 2, 1] (triées de la plus récente à la plus ancienne)
Gestion des versions
Les artefacts prennent en charge la gestion automatique des versions :
- Lors de la sauvegarde sans spécifier de version, le système incrémente automatiquement à partir de la dernière version
- La version 1 est attribuée à la première sauvegarde
- Chaque sauvegarde ultérieure incrémente le numéro de version
- Vous pouvez charger, supprimer ou interroger des versions spécifiques
// First save - becomes 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);
// Second save - becomes 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);
// Load latest (version 2)
let latest = service.load(LoadRequest {
file_name: "data.json".to_string(),
version: None,
// ... other fields
}).await?;
// Load specific version
let old = service.load(LoadRequest {
file_name: "data.json".to_string(),
version: Some(1),
// ... other fields
}).await?;
Portée des espaces de noms
Les artefacts peuvent être délimités à deux niveaux :
Lié à la Session (Par Défaut)
Par défaut, les artefacts sont liés à une session spécifique. Chaque session possède son propre espace de noms d'artefacts isolé :
// Session 1
service.save(SaveRequest {
session_id: "session_1".to_string(),
file_name: "notes.txt".to_string(),
// ... other fields
}).await?;
// Session 2 - different artifact with same name
service.save(SaveRequest {
session_id: "session_2".to_string(),
file_name: "notes.txt".to_string(),
// ... other fields
}).await?;
// Ce sont deux artefacts distincts
Lié à l'Utilisateur
Les artefacts avec le préfixe user: sont partagés entre toutes les sessions pour un utilisateur :
// Save in session 1
service.save(SaveRequest {
session_id: "session_1".to_string(),
file_name: "user:profile.jpg".to_string(), // user: prefix
// ... other fields
}).await?;
// Load in session 2 - same artifact
let profile = service.load(LoadRequest {
session_id: "session_2".to_string(),
file_name: "user:profile.jpg".to_string(),
// ... other fields
}).await?;
Le préfixe user: permet :
- Le partage de données entre plusieurs conversations
- Des préférences utilisateur persistantes
- La mise en cache au niveau de l'utilisateur
InMemoryArtifactService
Le InMemoryArtifactService fournit une implémentation en mémoire adaptée au développement et aux tests :
use adk_artifact::InMemoryArtifactService;
use std::sync::Arc;
let service = Arc::new(InMemoryArtifactService::new());
// Utiliser avec des agents
let agent = LlmAgentBuilder::new("my_agent")
.model(model)
.build()?;
// Le service peut être passé aux Runner ou utilisé directement
Remarque : Les données ne sont pas persistées sur le disque. Pour une utilisation en production, envisagez d'implémenter un ArtifactService personnalisé adossé à une base de données ou à un stockage cloud.
ScopedArtifacts
L'encapsuleur ScopedArtifacts simplifie les opérations sur les artefacts en injectant automatiquement le contexte de session :
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(),
);
// API simple - pas besoin de spécifier l'application/l'utilisateur/la session
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?;
C'est la même interface disponible via ToolContext::artifacts() et CallbackContext::artifacts().
Modèles Courants
Analyse d'images avec des modèles multimodaux
Lorsque vous souhaitez qu'un LLM analyse une image stockée comme un artifact, vous devez utiliser un callback BeforeModel pour injecter l'image directement dans la requête LLM. Cela suit le modèle adk-go.
Pourquoi ne pas utiliser un outil ? Les réponses des outils dans les API LLM sont du texte JSON. Si un outil renvoie des données d'image (même encodées en base64), le modèle les voit comme du texte, et non comme une image réelle. Pour une véritable analyse multimodale, l'image doit être incluse comme un Part::InlineData dans le contenu de la conversation.
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(())
}
Points clés :
- Utilisez
BeforeModelResult::Continue(request)pour passer la requête modifiée au model - Utilisez
BeforeModelResult::Skip(response)si vous souhaitez retourner une réponse mise en cache à la place - L'image est injectée en tant que
Part::InlineData, que Gemini interprète comme des données d'image réelles - Utilisez le préfixe
user:pour les artifacts qui doivent être accessibles à travers les sessions
Analyse de documents PDF
Les modèles Gemini peuvent traiter les documents PDF nativement en utilisant le même modèle de callback BeforeModel. Les PDF sont injectés avec le type MIME application/pdf :
// 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))
})
}))
Capacités PDF de Gemini :
- Extraire et analyser le contenu textuel
- Répondre à des questions sur des documents
- Résumer des sections ou des documents entiers
- Traiter jusqu'à ~1000 pages
- Support OCR pour les documents numérisés
Consultez examples/artifacts/chat_pdf.rs pour un exemple complet et fonctionnel.
Stockage des images générées
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
}))
}
Chargement et traitement des documents
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())),
}
}
Historique des versions
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
}))
}
Référence d'API
Pour une documentation API complète, voir :
adk_core::Artifacts- Trait simple pour l'utilisation par l'agentadk_artifact::ArtifactService- Trait de service completadk_artifact::InMemoryArtifactService- Implémentation en mémoireadk_artifact::ScopedArtifacts- Wrapper scopé
Articles Similaires
- Sessions - Gestion et cycle de vie des sessions
- Callbacks - Accès aux artifacts dans les callbacks
- Tools - Utilisation des artifacts dans des outils personnalisés
Précédent: ← Callbacks | Suivant: Événements →