Artefactos
Los artefactos proporcionan una forma de almacenar y recuperar datos binarios (imágenes, PDFs, archivos de audio, etc.) dentro de sus aplicaciones de agente. El sistema de artefactos maneja el versionado, el alcance de los espacios de nombres y la persistencia de los datos entre sesiones.
Visión General
El sistema de artefactos en ADK-Rust consta de:
- Part: La representación de datos central que puede contener datos de texto o binarios con tipos MIME
- ArtifactService: El trait que define las operaciones de almacenamiento de artefactos
- InMemoryArtifactService: Una implementación en memoria para desarrollo y pruebas
- ScopedArtifacts: Un wrapper que simplifica las operaciones con artefactos al manejar automáticamente el contexto de la sesión
Los artefactos tienen un alcance definido por la aplicación, el usuario y la sesión, proporcionando aislamiento y organización. Los archivos pueden tener un alcance de sesión (predeterminado) o de usuario (usando el prefijo user:).
Representación de Part
El enum Part representa datos que pueden almacenarse como artefactos:
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 },
}
Para los artefactos, utilizará principalmente:
Part::Textpara datos de textoPart::InlineDatapara datos binarios con tipos MIME
Uso Básico
La forma más sencilla de trabajar con artefactos es a través del trait Artifacts, que está disponible en los contextos de agente:
use adk_rust::prelude::*;
// En una herramienta de agente o callback
async fn save_report(ctx: &ToolContext) -> Result<Value> {
let artifacts = ctx.artifacts();
// Guardar datos de texto
let version = artifacts.save(
"report.txt",
&Part::Text { text: "Report content".to_string() }
).await?;
// Guardar datos binarios
let image_data = vec![0xFF, 0xD8, 0xFF]; // Encabezado 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
El trait ArtifactService define las operaciones principales para la gestión de artifacts:
#[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>;
}
Operación Save
Guarda un artifact con versionado automático o explícito:
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, // Versión con auto-incremento
}).await?;
println!("Saved as version: {}", response.version);
Operación Load
Carga la última versión o una versión específica:
use adk_artifact::LoadRequest;
// Carga la última versión
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, // Cargar la más reciente
}).await?;
// Carga una versión específica
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), // Cargar la versión 2
}).await?;
match response.part {
Part::InlineData { mime_type, data } => {
println!("Loaded {} bytes of {}", data.len(), mime_type);
}
_ => {}
}
Operación List
Lista todos los artifacts en una 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);
}
Operación Delete
Elimina una versión específica o todas las versiones:
use adk_artifact::DeleteRequest;
// Elimina una versión específica
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), // Eliminar versión 1
}).await?;
// Elimina todas las versiones
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, // Eliminar todas las versiones
}).await?;
Operación Versions
Lista todas las versiones de un artifact:
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);
// Salida: [3, 2, 1] (ordenado del más nuevo al más antiguo)
Control de Versiones
Los Artifacts admiten el control automático de versiones:
- Al guardar sin especificar una versión, el sistema auto-incrementa desde la última versión
- Se asigna la Versión 1 al primer guardado
- Cada guardado posterior incrementa el número de versión
- Puede cargar, eliminar o consultar versiones específicas
// 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?;
Alcance de Espacio de Nombres
Los Artifacts pueden tener alcance en dos niveles:
Con Alcance de Sesión (Predeterminado)
Por defecto, los Artifacts tienen alcance para una sesión específica. Cada sesión tiene su propio espacio de nombres de Artifacts aislado:
// 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?;
// These are two separate artifacts
Con Alcance de Usuario
Los Artifacts con el prefijo user: se comparten entre todas las sesiones para un usuario:
// 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?;
El prefijo user: permite:
- Compartir datos entre múltiples conversaciones
- Preferencias de usuario persistentes
- Caché a nivel de usuario
InMemoryArtifactService
El InMemoryArtifactService proporciona una implementación en memoria adecuada para desarrollo y pruebas:
use adk_artifact::InMemoryArtifactService;
use std::sync::Arc;
let service = Arc::new(InMemoryArtifactService::new());
// Use with agents
let agent = LlmAgentBuilder::new("my_agent")
.model(model)
.build()?;
// The service can be passed to runners or used directly
Nota: Los datos no se persisten en disco. Para uso en producción, considere implementar un ArtifactService personalizado respaldado por una base de datos o almacenamiento en la nube.
ScopedArtifacts
El envoltorio ScopedArtifacts simplifica las operaciones con Artifacts al inyectar automáticamente el contexto de la sesión:
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(),
);
// Simple API - no need to specify app/user/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?;
Esta es la misma interfaz disponible a través de ToolContext::artifacts() y CallbackContext::artifacts().
Patrones Comunes
Análisis de Imágenes con Modelos Multimodales
Cuando quieres que un LLM analice una imagen almacenada como un artifact, necesitas usar un BeforeModel callback para inyectar la imagen directamente en la solicitud del LLM. Esto sigue el patrón de adk-go.
¿Por qué no usar una tool? Las respuestas de las tools en las APIs de LLM son texto JSON. Si una tool devuelve datos de imagen (incluso codificados en base64), el modelo los ve como texto, no como una imagen real. Para un verdadero análisis multimodal, la imagen debe incluirse como un Part::InlineData en el Content de la conversación.
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")?);
// Crear servicio de artifact y guardar una imagen
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(), // con ámbito de usuario para acceso entre sesiones
part: Part::InlineData {
data: image_bytes,
mime_type: "image/png".to_string(),
},
version: None,
}).await?;
// Clonar para usar en el 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)
// Usar BeforeModel callback para inyectar la imagen en la solicitud
.before_model_callback(Box::new(move |_ctx, mut request| {
let service = callback_service.clone();
Box::pin(async move {
// Cargar el artifact de imagen
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 {
// Inyectar imagen en el último contenido del usuario
if let Some(last_content) = request.contents.last_mut() {
if last_content.role == "user" {
last_content.parts.push(response.part);
}
}
}
// Continuar con la solicitud modificada
Ok(BeforeModelResult::Continue(request))
})
}))
.build()?;
// Ahora, cuando los usuarios pregunten "¿Qué hay en la imagen?", el modelo verá la imagen real
Ok(())
}
Puntos clave:
- Usa
BeforeModelResult::Continue(request)para pasar la solicitud modificada al modelo. - Usa
BeforeModelResult::Skip(response)si quieres devolver una respuesta en caché en su lugar. - La imagen se inyecta como
Part::InlineData, que Gemini interpreta como datos de imagen reales. - Usa el prefijo
user:para artifacts que deben ser accesibles entre sesiones.
Análisis de Documentos PDF
Los modelos Gemini pueden procesar documentos PDF de forma nativa utilizando el mismo patrón de BeforeModel callback. Los PDFs se inyectan con el tipo MIME application/pdf:
// Guardar PDF como 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?;
// Usar BeforeModel callback para inyectar PDF (mismo patrón que las imágenes)
.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(),
// ... otros campos
}).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))
})
}))
Capacidades de PDF de Gemini:
- Extraer y analizar contenido de texto
- Responder preguntas sobre documentos
- Resumir secciones o documentos completos
- Procesar hasta ~1000 páginas
- Soporte de OCR para documentos escaneados
Consulta examples/artifacts/chat_pdf.rs para ver un ejemplo completo y funcional.
Almacenamiento de Imágenes Generadas
async fn generate_and_save_image(ctx: &ToolContext) -> Result<Value> {
let artifacts = ctx.artifacts();
// Generar imagen (pseudo-código)
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
}))
}
Carga y Procesamiento de Documentos
async fn process_document(ctx: &ToolContext, filename: &str) -> Result<Value> {
let artifacts = ctx.artifacts();
// Cargar el documento
let part = artifacts.load(filename).await?;
match part {
Part::InlineData { mime_type, data } => {
// Procesar según el tipo MIME
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())),
}
}
Historial de Versiones
async fn show_history(ctx: &ToolContext, filename: &str) -> Result<Value> {
let artifacts = ctx.artifacts();
// Obtener todos los archivos
let files = artifacts.list().await?;
if !files.contains(&filename.to_string()) {
return Ok(json!({ "error": "File not found" }));
}
// Nota: versions() no está disponible en el trait simple Artifacts
// Necesitarías acceso al ArtifactService subyacente
Ok(json!({
"file": filename,
"exists": true
}))
}
Referencia de la API
Para la documentación completa de la API, consulte:
adk_core::Artifacts- Trait simple para uso de agentesadk_artifact::ArtifactService- Trait de servicio completoadk_artifact::InMemoryArtifactService- Implementación en memoriaadk_artifact::ScopedArtifacts- Envoltorio con ámbito
Relacionado
- Sesiones - Gestión y ciclo de vida de la sesión
- Retrollamadas - Acceso a artefactos en retrollamadas
- Herramientas - Uso de artefactos en herramientas personalizadas
Anterior: ← Retrollamadas | Siguiente: Eventos →