コア型

ADK-Rustの基盤を形成するadk-coreの基本的な型とトレイト。

ContentとPart

ADK内のすべてのメッセージはContentPartを通して流れます。これらの型を理解することは、Agent、Tool、およびコールバックを扱う上で不可欠です。

Content

Contentは会話における単一のメッセージを表します。role(送信者)と、1つ以上のparts(実際のコンテンツ)を持っています。

役割:

  • "user" - ユーザーからのメッセージ
  • "model" - AIモデルからの応答
  • "tool" - Tool実行からの結果
use adk_core::Content;

// Simple text message from user
let user_msg = Content::new("user")
    .with_text("What's the weather like?");

// Model response
let model_msg = Content::new("model")
    .with_text("The weather is sunny and 72°F.");

// Multiple text parts in one message
let detailed_msg = Content::new("user")
    .with_text("Here's my question:")
    .with_text("What is the capital of France?");

マルチモーダルContent:

Contentは、テキストに加えて画像、音声、PDF、およびその他のバイナリデータを含むことができます。

// Image from bytes (e.g., read from file)
let image_bytes = std::fs::read("photo.jpg")?;
let content = Content::new("user")
    .with_text("What's in this image?")
    .with_inline_data("image/jpeg", image_bytes);

// Image from URL (model fetches it)
let content = Content::new("user")
    .with_text("Describe this image")
    .with_file_uri("image/png", "https://example.com/chart.png");

// PDF document
let pdf_bytes = std::fs::read("report.pdf")?;
let content = Content::new("user")
    .with_text("Summarize this document")
    .with_inline_data("application/pdf", pdf_bytes);

Part

Partは、メッセージ内のさまざまな種類のコンテンツを表すenumです。

pub enum Part {
    // Plain text
    Text { text: String },
    
    // Binary data embedded in the message
    InlineData { mime_type: String, data: Vec<u8> },
    
    // Reference to external file (URL or cloud storage)
    FileData { mime_type: String, file_uri: String },
    
    // Model requesting a tool call
    FunctionCall { name: String, args: Value, id: Option<String> },
    
    // Result of a tool execution
    FunctionResponse { function_response: FunctionResponseData, id: Option<String> },
}

Partを直接作成する:

use adk_core::Part;

// Text part
let text = Part::text_part("Hello, world!");

// Image from bytes
let image = Part::inline_data("image/png", png_bytes);

// Image from URL
let remote_image = Part::file_data("image/jpeg", "https://example.com/photo.jpg");

Partを検査する:

// Get text content (returns None for non-text parts)
if let Some(text) = part.text() {
    println!("Text: {}", text);
}

// Get MIME type (for InlineData and FileData)
if let Some(mime) = part.mime_type() {
    println!("MIME type: {}", mime);
}

// Get file URI (for FileData only)
if let Some(uri) = part.file_uri() {
    println!("File URI: {}", uri);
}

// Check if part contains media (image, audio, video, etc.)
if part.is_media() {
    println!("This part contains binary media");
}

Partを反復処理する:

for part in &content.parts {
    match part {
        Part::Text { text } => println!("Text: {}", text),
        Part::InlineData { mime_type, data } => {
            println!("Binary data: {} ({} bytes)", mime_type, data.len());
        }
        Part::FileData { mime_type, file_uri } => {
            println!("File: {} at {}", mime_type, file_uri);
        }
        Part::FunctionCall { name, args, .. } => {
            println!("Tool call: {}({})", name, args);
        }
        Part::FunctionResponse { function_response, .. } => {
            println!("Tool result: {} -> {}", 
                function_response.name, 
                function_response.response
            );
        }
    }
}

Agentトレイト

Agentトレイトは、ADKにおけるすべてのAgentの中核となる抽象化です。LlmAgent、ワークフローAgent、カスタムAgentなど、すべてのAgentタイプがこのトレイトを実装します。

#[async_trait]
pub trait Agent: Send + Sync {
    /// Unique identifier for this agent
    fn name(&self) -> &str;
    
    /// Human-readable description of what this agent does
    fn description(&self) -> &str;
    
    /// Child agents (for workflow agents like Sequential, Parallel)
    fn sub_agents(&self) -> &[Arc<dyn Agent>];
    
    /// Execute the agent and return a stream of events
    async fn run(&self, ctx: Arc<dyn InvocationContext>) -> Result<EventStream>;
}

主要なポイント:

  • name(): ロギング、Agent間でのやり取り、および識別のために使用されます。マルチAgentシステム内では一意である必要があります。
  • description(): LLMがAgentをToolとして使用する場合やルーティングの判断のために表示されます。
  • sub_agents(): 子Agentを返します。リーフAgent(LlmAgent)の場合は空で、コンテナ(SequentialAgent、ParallelAgent)の場合は子Agentが格納されます。
  • run(): 主要な実行メソッドです。コンテキストを受け取り、EventStreamを返します。

なぜEventStreamなのか?

Agentは単一のレスポンスではなく、EventStreamResult<Event>のストリーム)を返すのは、以下の理由によります。

  1. ストリーミング: UX向上のため、レスポンスをトークンごとにストリーミングできます。
  2. Toolの呼び出し: 実行中に複数のToolの呼び出しと応答が発生します。
  3. 状態変更: 状態の更新がイベントとして発行されます。
  4. Agent間のやり取り: Agent間のやり取りはイベントを通じて通知されます。

Tool Trait

Tool は、エージェントの会話能力を拡張します。これらを使用すると、エージェントは API、データベース、ファイルシステムと連携したり、計算を実行したりできます。

#[async_trait]
pub trait Tool: Send + Sync {
    /// ツール名(モデルからの関数呼び出しで使用されます)
    fn name(&self) -> &str;
    
    /// LLMがこのツールを使用するタイミングを判断するのに役立つ説明
    fn description(&self) -> &str;
    
    /// 期待されるパラメーターを定義するJSON Schema
    fn parameters_schema(&self) -> Option<Value> { None }
    
    /// レスポンス形式を定義するJSON Schema
    fn response_schema(&self) -> Option<Value> { None }
    
    /// このツールが非同期で実行されるかどうか(タスクIDをすぐに返します)
    fn is_long_running(&self) -> bool { false }
    
    /// 指定された引数でツールを実行します
    async fn execute(&self, ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value>;
}

重要なポイント:

  • name(): モデルがこのツールを呼び出すために使用する関数名です。短く分かりやすい名前にしてください(例: get_weathersearch_database)。
  • description(): モデルがツールを使用するタイミングを理解するために重要です。ツールの機能と使用するタイミングを具体的に記述してください。
  • parameters_schema(): モデルに提供する引数を伝えるJSON Schemaです。これがなければ、モデルは推測します。
  • execute(): 解析された引数を serde_json::Value として受け取ります。結果をJSONとして返します。

カスタムツールの実装:

use adk_core::{Tool, ToolContext, Result};
use async_trait::async_trait;
use serde_json::{json, Value};
use std::sync::Arc;

struct WeatherTool {
    api_key: String,
}

#[async_trait]
impl Tool for WeatherTool {
    fn name(&self) -> &str { 
        "get_weather" 
    }
    
    fn description(&self) -> &str { 
        "Get current weather for a city. Use this when the user asks about weather conditions."
    }
    
    fn parameters_schema(&self) -> Option<Value> {
        Some(json!({
            "type": "object",
            "properties": {
                "city": { 
                    "type": "string",
                    "description": "都市名(例:'London', 'New York')"
                },
                "units": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "description": "温度単位"
                }
            },
            "required": ["city"]
        }))
    }
    
    async fn execute(&self, _ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
        let city = args["city"].as_str().unwrap_or("Unknown");
        let units = args["units"].as_str().unwrap_or("celsius");
        
        // ここで天気APIを呼び出す...
        
        Ok(json!({
            "city": city,
            "temperature": 22,
            "units": units,
            "condition": "sunny"
        }))
    }
}

長時間実行されるツール:

長時間かかる操作(ファイル処理、外部API呼び出しなど)の場合、ツールを長時間実行としてマークします。

fn is_long_running(&self) -> bool { true }

長時間実行されるツールは、すぐにタスクIDを返します。モデルは、保留中にツールを再度呼び出さないように指示されます。


Toolset Trait

Toolsetはコンテキストに基づいてツールを動的に提供します。以下の場合に使用します。

  • ツールがユーザー権限に依存する場合
  • ツールが外部ソース(MCPサーバー)からロードされる場合
  • 実行中にツールの利用可能性が変化する場合
#[async_trait]
pub trait Toolset: Send + Sync {
    /// Toolset identifier
    fn name(&self) -> &str;
    
    /// Return available tools for the current context
    async fn tools(&self, ctx: Arc<dyn ReadonlyContext>) -> Result<Vec<Arc<dyn Tool>>>;
}

例: 権限ベースのToolset:

struct AdminToolset {
    admin_tools: Vec<Arc<dyn Tool>>,
    user_tools: Vec<Arc<dyn Tool>>,
}

#[async_trait]
impl Toolset for AdminToolset {
    fn name(&self) -> &str { "admin_toolset" }
    
    async fn tools(&self, ctx: Arc<dyn ReadonlyContext>) -> Result<Vec<Arc<dyn Tool>>> {
        if ctx.user_id().starts_with("admin_") {
            Ok(self.admin_tools.clone())
        } else {
            Ok(self.user_tools.clone())
        }
    }
}

Context Traits

Contextは、実行中にAgentやToolに情報とサービスを提供します。Context traitには階層があり、それぞれがより多くの機能を追加します。

ReadonlyContext

あらゆる場所で利用可能な基本情報:

pub trait ReadonlyContext: Send + Sync {
    /// Unique ID for this invocation
    fn invocation_id(&self) -> &str;
    
    /// Name of the currently executing agent
    fn agent_name(&self) -> &str;
    
    /// User identifier
    fn user_id(&self) -> &str;
    
    /// Application name
    fn app_name(&self) -> &str;
    
    /// Session identifier
    fn session_id(&self) -> &str;
    
    /// The user's input that triggered this invocation
    fn user_content(&self) -> &Content;
}

CallbackContext

アーティファクトへのアクセスを追加します(ReadonlyContextを拡張):

pub trait CallbackContext: ReadonlyContext {
    /// Access to artifact storage (if configured)
    fn artifacts(&self) -> Option<Arc<dyn Artifacts>>;
}

ToolContext

Toolの実行用(CallbackContextを拡張):

pub trait ToolContext: CallbackContext {
    /// ID of the function call that triggered this tool
    fn function_call_id(&self) -> &str;
    
    /// Get current event actions (transfer, escalate, etc.)
    fn actions(&self) -> EventActions;
    
    /// Set event actions (e.g., trigger a transfer)
    fn set_actions(&self, actions: EventActions);
    
    /// Search long-term memory
    async fn search_memory(&self, query: &str) -> Result<Vec<MemoryEntry>>;
}

InvocationContext

Agent実行のための完全なContext(CallbackContextを拡張):

pub trait InvocationContext: CallbackContext {
    /// The agent being executed
    fn agent(&self) -> Arc<dyn Agent>;
    
    /// Memory service (if configured)
    fn memory(&self) -> Option<Arc<dyn Memory>>;
    
    /// Current session with state and history
    fn session(&self) -> &dyn Session;
    
    /// Execution configuration
    fn run_config(&self) -> &RunConfig;
    
    /// Signal that this invocation should end
    fn end_invocation(&self);
    
    /// Check if invocation has been ended
    fn ended(&self) -> bool;
}

セッションと状態

セッションは会話を追跡します。状態はセッション内にデータを保存します。

セッション

pub trait Session: Send + Sync {
    /// ユニークなセッション識別子
    fn id(&self) -> &str;
    
    /// このセッションが属するアプリケーション
    fn app_name(&self) -> &str;
    
    /// このセッションを所有するユーザー
    fn user_id(&self) -> &str;
    
    /// 可変の状態ストレージ
    fn state(&self) -> &dyn State;
    
    /// この会話の以前のメッセージ
    fn conversation_history(&self) -> Vec<Content>;
}

状態

スコープ付きプレフィックスを持つキーバリュー型ストレージ:

pub trait State: Send + Sync {
    /// キーによる値の取得
    fn get(&self, key: &str) -> Option<Value>;
    
    /// 値の設定
    fn set(&mut self, key: String, value: Value);
    
    /// すべてのキーバリューペアの取得
    fn all(&self) -> HashMap<String, Value>;
}

状態プレフィックス:

状態キーは、スコープと永続性を制御するためにプレフィックスを使用します:

プレフィックススコープ永続性ユースケース
user:ユーザーレベルすべてのセッションでユーザー設定、設定
app:アプリケーションレベルアプリケーション全体共有設定
temp:ターンレベル各ターン後にクリア一時的な計算データ
(なし)セッションレベルこのセッションのみ会話コンテキスト
// コールバックまたはツール内
let state = ctx.session().state();

// ユーザー設定 (セッション間で永続化)
state.set("user:theme".into(), json!("dark"));

// セッション固有のデータ
state.set("current_topic".into(), json!("weather"));

// 一時的なデータ (このターン後にクリアされる)
state.set("temp:step_count".into(), json!(1));

// 値の読み取り
if let Some(theme) = state.get("user:theme") {
    println!("Theme: {}", theme);
}

エラー処理

ADK はすべての操作に統合されたエラータイプを使用します:

pub enum AdkError {
    Agent(String),      // Agent 実行エラー
    Tool(String),       // Tool 実行エラー
    Model(String),      // LLM API エラー
    Session(String),    // Session ストレージエラー
    Artifact(String),   // Artifact ストレージエラー
    Config(String),     // 設定エラー
    Io(std::io::Error), // ファイル/ネットワーク I/O エラー
    Json(serde_json::Error), // JSON パースエラー
}

pub type Result<T> = std::result::Result<T, AdkError>;

Tool におけるエラー処理:

async fn execute(&self, ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
    let city = args["city"]
        .as_str()
        .ok_or_else(|| AdkError::Tool("Missing 'city' parameter".into()))?;
    
    let response = reqwest::get(&format!("https://api.weather.com/{}", city))
        .await
        .map_err(|e| AdkError::Tool(format!("API error: {}", e)))?;
    
    Ok(json!({ "weather": "sunny" }))
}

EventStream

Agent は単一の応答ではなく、イベントのストリームを返します:

pub type EventStream = Pin<Box<dyn Stream<Item = Result<Event>> + Send>>;

イベントの処理:

use futures::StreamExt;

let mut stream = agent.run(ctx).await?;

while let Some(result) = stream.next().await {
    match result {
        Ok(event) => {
            // テキストコンテンツの確認
            if let Some(content) = event.content() {
                for part in &content.parts {
                    if let Some(text) = part.text() {
                        print!("{}", text);
                    }
                }
            }
            
            // 状態変更の確認
            for (key, value) in event.state_delta() {
                println!("State changed: {} = {}", key, value);
            }
            
            // これが最終応答であるか確認
            if event.is_final_response() {
                println!("\n[Done]");
            }
        }
        Err(e) => {
            eprintln!("Error: {}", e);
            break;
        }
    }
}

Llm トレイト

すべての LLM プロバイダーが実装するトレイトです。

#[async_trait]
pub trait Llm: Send + Sync {
    /// モデル識別子 (例: "gemini-2.0-flash", "gpt-4o")
    fn name(&self) -> &str;
    
    /// コンテンツを生成 (ストリーミングまたは非ストリーミング)
    async fn generate_content(
        &self,
        request: LlmRequest,
        stream: bool,
    ) -> Result<LlmResponseStream>;
}

LlmRequest:

pub struct LlmRequest {
    pub contents: Vec<Content>,           // 会話履歴
    pub tools: Vec<ToolDeclaration>,      // 利用可能なツール
    pub system_instruction: Option<String>, // システムプロンプト
    pub config: GenerateContentConfig,    // 温度、max_tokens など
}

LlmResponse:

pub struct LlmResponse {
    pub content: Option<Content>,         // 生成されたコンテンツ
    pub finish_reason: Option<FinishReason>, // 生成が停止した理由
    pub usage: Option<UsageMetadata>,     // トークン数
    pub partial: bool,                    // これはストリーミングのチャンクか?
    pub turn_complete: bool,              // ターンが完了したか?
}

すべてのプロバイダー (Gemini, OpenAI, Anthropic, Ollama など) がこのトレイトを実装しており、相互に交換可能です:

// 1行変更するだけでプロバイダーを切り替え
let model: Arc<dyn Llm> = Arc::new(GeminiModel::new(&key, "gemini-2.0-flash")?);
// let model: Arc<dyn Llm> = Arc::new(OpenAIClient::new(config)?);
// let model: Arc<dyn Llm> = Arc::new(AnthropicClient::new(config)?);

let agent = LlmAgentBuilder::new("assistant")
    .model(model)
    .build()?;

前へ: ← はじめに | 次へ: Runner →