アーティファクト
アーティファクトは、エージェントアプリケーション内でバイナリデータ(画像、PDF、オーディオファイルなど)を保存および取得する方法を提供します。artifact systemは、バージョン管理、namespace scoping、およびsessions間でのデータのpersistenceを処理します。
概要
ADK-Rust の artifact system は以下で構成されます。
- Part: MIME typesを持つテキストまたはバイナリデータを保持できるコアデータ表現
- ArtifactService: artifactストレージ操作を定義するtrait
- InMemoryArtifactService: 開発およびテスト用のin-memory実装
- ScopedArtifacts: session contextを自動的に処理することでartifact operationsを簡素化するwrapper
Artifacts は、application、user、sessionによってスコープ化され、分離と整理を提供します。ファイルは、session-scoped(デフォルト)またはuser-scoped(user: プレフィックスを使用)にすることができます。
Partの表現
Part enum は、artifacts として保存できるデータを表します。
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 },
}
artifactsの場合、主に以下を使用します。
Part::Textはテキストデータ用Part::InlineDataはMIME typesを持つバイナリデータ用
基本的な使い方
artifacts を操作する最も簡単な方法は、agent contexts で利用可能な Artifacts trait を使用することです。
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 トレイト
ArtifactService トレイトは、アーティファクト管理の中核となる操作を定義します。
#[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>;
}
保存操作
自動または明示的なバージョン管理でアーティファクトを保存します。
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);
読み込み操作
最新バージョンまたは特定のバージョンを読み込みます。
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);
}
_ => {}
}
リスト操作
セッション内のすべてのアーティファクトをリストします。
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);
}
削除操作
特定のバージョンまたはすべてのバージョンを削除します。
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?;
バージョン操作
アーティファクトのすべてのバージョンをリストします。
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)
バージョン管理
Artifact は自動バージョン管理をサポートしています。
- バージョンを指定せずに保存すると、システムは最新バージョンから自動的にインクリメントします
- 最初の保存にはバージョン1が割り当てられます
- その後の保存ごとにバージョン番号がインクリメントされます
- 特定のバージョンをロード、削除、またはクエリできます
// 最初の保存 - バージョン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);
// 2回目の保存 - バージョン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);
// 最新をロード (バージョン2)
let latest = service.load(LoadRequest {
file_name: "data.json".to_string(),
version: None,
// ... other fields
}).await?;
// 特定のバージョンをロード
let old = service.load(LoadRequest {
file_name: "data.json".to_string(),
version: Some(1),
// ... other fields
}).await?;
名前空間のスコープ設定
Artifact は2つのレベルでスコープ設定できます。
セッションスコープ (デフォルト)
デフォルトでは、Artifact は特定の Session にスコープされます。各 Session は独自の分離された Artifact 名前空間を持ちます。
// セッション1
service.save(SaveRequest {
session_id: "session_1".to_string(),
file_name: "notes.txt".to_string(),
// ... other fields
}).await?;
// セッション2 - 同じ名前の異なる Artifact
service.save(SaveRequest {
session_id: "session_2".to_string(),
file_name: "notes.txt".to_string(),
// ... other fields
}).await?;
// これらは2つの異なる Artifact です
ユーザースコープ
user:プレフィックスを持つ Artifact は、ユーザーのすべての Session で共有されます。
// セッション1で保存
service.save(SaveRequest {
session_id: "session_1".to_string(),
file_name: "user:profile.jpg".to_string(), // user: プレフィックス
// ... other fields
}).await?;
// セッション2でロード - 同じ Artifact
let profile = service.load(LoadRequest {
session_id: "session_2".to_string(),
file_name: "user:profile.jpg".to_string(),
// ... other fields
}).await?;
user:プレフィックスにより、以下が可能になります。
- 複数の会話にわたるデータ共有
- 永続的なユーザー設定
- ユーザーレベルのキャッシング
InMemoryArtifactService
InMemoryArtifactServiceは、開発およびテストに適したインメモリ実装を提供します。
use adk_artifact::InMemoryArtifactService;
use std::sync::Arc;
let service = Arc::new(InMemoryArtifactService::new());
// Agent と一緒に使用
let agent = LlmAgentBuilder::new("my_agent")
.model(model)
.build()?;
// サービスは Runner に渡すことも、直接使用することもできます
注: データはディスクに永続化されません。本番環境での使用には、データベースまたはクラウドストレージによってサポートされるカスタムの ArtifactService の実装を検討してください。
ScopedArtifacts
ScopedArtifactsラッパーは、Session コンテキストを自動的に挿入することで、Artifact 操作を簡素化します。
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 - 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?;
これは、ToolContext::artifacts()およびCallbackContext::artifacts()を介して利用できるのと同じインターフェースです。
一般的なパターン
マルチモーダルモデルによる画像分析
LLMにartifactとして保存された画像を分析させたい場合、BeforeModel callbackを使用して、画像をLLMリクエストに直接挿入する必要があります。これはadk-goのパターンに倣っています。
なぜtoolを使わないのか? LLM APIにおけるtoolの応答はJSONテキストです。toolが画像データ(base64エンコードされたものでも)を返した場合、モデルはそれを実際の画像としてではなく、テキストとして認識します。真のマルチモーダル分析には、会話コンテンツに画像をPart::InlineDataとして含める必要があります。
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(())
}
重要なポイント:
- 修正されたリクエストをモデルに渡すには、
BeforeModelResult::Continue(request)を使用します - 代わりにキャッシュされた応答を返したい場合は、
BeforeModelResult::Skip(response)を使用します - 画像は
Part::InlineDataとして挿入され、Geminiはこれを実際の画像データとして解釈します - セッションをまたいでアクセス可能にするartifactには
user:プレフィックスを使用します
PDFドキュメント分析
Geminiモデルは、同じBeforeModel callbackパターンを使用して、PDFドキュメントをネイティブに処理できます。PDFは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))
})
}))
GeminiのPDF機能:
- テキストコンテンツの抽出と分析
- ドキュメントに関する質問への回答
- セクションまたはドキュメント全体の要約
- 最大約1000ページを処理
- スキャンされたドキュメントのOCRサポート
完全な動作例はexamples/artifacts/chat_pdf.rsを参照してください。
生成された画像の保存
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
}))
}
ドキュメントの読み込みと処理
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())),
}
}
バージョン履歴
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リファレンス
完全なAPIドキュメントについては、以下を参照してください:
adk_core::Artifacts- エージェントで使用するためのシンプルなトレイトadk_artifact::ArtifactService- フルサービス・トレイトadk_artifact::InMemoryArtifactService- インメモリ実装adk_artifact::ScopedArtifacts- スコープ付きラッパー
関連
前へ: ← Callbacks | 次へ: Events →