LlmAgent

LlmAgent は、ADK-Rust における、大規模言語モデルを推論と意思決定に利用するコアエージェントタイプです。

クイックスタート

新しいプロジェクトを作成します:

cargo new llm_agent
cd llm_agent

Cargo.toml に依存関係を追加します:

[dependencies]
adk-rust = "0.2.0"
tokio = { version = "1.40", features = ["full"] }
dotenvy = "0.15"
serde_json = "1.0"

APIキーを使用して .env を作成します:

echo 'GOOGLE_API_KEY=your-api-key' > .env

src/main.rs を置き換えます:

use adk_rust::prelude::*;
use adk_rust::Launcher;
use std::sync::Arc;

#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    dotenvy::dotenv().ok();
    
    let api_key = std::env::var("GOOGLE_API_KEY")?;
    let model = GeminiModel::new(&api_key, "gemini-2.5-flash")?;

    let agent = LlmAgentBuilder::new("my_agent")
        .instruction("You are a helpful assistant.")
        .model(Arc::new(model))
        .build()?;

    Launcher::new(Arc::new(agent)).run().await?;
    Ok(())
}

実行します:

cargo run

Agent との対話

対話型のプロンプトが表示されます:

🤖 Agent ready! Type your questions (or 'exit' to quit).

You: Hello! What can you help me with?
Assistant: Hello! I'm a helpful assistant. I can help you with:
- Answering questions on various topics
- Explaining concepts clearly
- Having a conversation

What would you like to know?

You: exit
👋 Goodbye!

エージェントの挙動をインストラクションで形成する

instruction() メソッドは、エージェントの個性と挙動を定義します。これは、すべての応答をガイドするシステムプロンプトです。

// A formal business assistant
let formal_agent = LlmAgentBuilder::new("formal_assistant")
    .instruction("You are a professional business consultant. \
                 Use formal language. Be concise and data-driven.")
    .model(Arc::new(model))
    .build()?;

// A friendly coding tutor
let tutor_agent = LlmAgentBuilder::new("code_tutor")
    .instruction("You are a friendly coding tutor for beginners. \
                 Explain concepts simply. Use examples. \
                 Encourage questions. Never make the user feel bad for not knowing.")
    .model(Arc::new(model))
    .build()?;

// A creative storyteller
let story_agent = LlmAgentBuilder::new("storyteller")
    .instruction("You are a creative storyteller. \
                 Craft engaging narratives with vivid descriptions. \
                 Use plot twists and memorable characters.")
    .model(Arc::new(model))
    .build()?;

出力例

ユーザープロンプト: "What is Rust?"

公式ビジネスアシスタント:

Rust is a systems programming language focused on performance, memory safety, 
and concurrency. Key metrics: 0 runtime overhead, 0 garbage collection. 
Adoption growing 30% YoY in enterprise. Recommended for performance-critical 
applications where reliability is paramount.

フレンドリーなコーディングチューター:

Great question! 🎉 Rust is a programming language that helps you write fast 
and safe code. Think of it like a helpful friend who stops you from making 
mistakes before they happen!

Here's a simple example:
  let name = "Alice";  // This creates a variable

What makes Rust special is that it catches bugs at compile time, not when 
your program is running. Pretty cool, right? Want me to explain more?

クリエイティブなストーリーテラー:

In the twilight of 2010, in a secret Mozilla laboratory, a lone developer 
named Graydon Hoare breathed life into something extraordinary—a language 
that would challenge the old gods of C and C++.

They called it Rust, named for a fungus that survives against all odds. 
Like a phoenix rising from the ashes of segmentation faults and memory 
leaks, Rust promised a new era: speed without sacrifice, power without peril.

And so, the revolution began...

インストラクションのテンプレート化

インストラクションは、{var} 構文を使用した変数インジェクションをサポートしています。変数は実行時にセッションステートから解決されます。

let agent = LlmAgentBuilder::new("personalized")
    .instruction("You are helping {user_name}. Their role is {user_role}. \
                 Tailor your responses to their expertise level.")
    .model(Arc::new(model))
    .build()?;

テンプレート化を使用するためのステップバイステップガイド:

  1. インストラクションにテンプレート変数を含めてエージェントを作成します
  2. ステートを管理するためにRunnerとSessionServiceを設定します
  3. テンプレートに一致するステート変数を持つセッションを作成します
  4. エージェントを実行します - テンプレートは自動的に置き換えられます

完全な動作例を次に示します。

use adk_rust::prelude::*;
use adk_rust::runner::{Runner, RunnerConfig};
use adk_rust::session::{CreateRequest, InMemorySessionService, SessionService};
use adk_rust::futures::StreamExt;
use serde_json::json;
use std::collections::HashMap;
use std::sync::Arc;

#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    dotenvy::dotenv().ok();
    let api_key = std::env::var("GOOGLE_API_KEY")?;
    let model = GeminiModel::new(&api_key, "gemini-2.5-flash")?;

    // 1. テンプレート化されたインストラクションを持つエージェント
    let agent = LlmAgentBuilder::new("personalized")
        .instruction("You are helping {user_name}. Their role is {user_role}. \
                     Tailor your responses to their expertise level.")
        .model(Arc::new(model))
        .build()?;

    // 2. セッションサービスとRunnerを作成
    let session_service = Arc::new(InMemorySessionService::new());
    let runner = Runner::new(RunnerConfig {
        app_name: "templating_demo".to_string(),
        agent: Arc::new(agent),
        session_service: session_service.clone(),
        artifact_service: None,
        memory_service: None,
        run_config: None,
    })?;

    // 3. ステート変数を持つセッションを作成
    let mut state = HashMap::new();
    state.insert("user_name".to_string(), json!("Alice"));
    state.insert("user_role".to_string(), json!("Senior Developer"));

    let session = session_service.create(CreateRequest {
        app_name: "templating_demo".to_string(),
        user_id: "user123".to_string(),
        session_id: None,
        state,
    }).await?;

    // 4. エージェントを実行 - インストラクションは次のようになります:
    // 「あなたはAliceを助けています。彼女の役割はSenior Developerです...」
    let mut response_stream = runner.run(
        "user123".to_string(),
        session.id().to_string(),
        Content::new("user").with_text("Explain async/await in Rust"),
    ).await?;

    // レスポンスを出力
    while let Some(event) = response_stream.next().await {
        let event = event?;
        if let Some(content) = event.content() {
            for part in &content.parts {
                if let Part::Text { text } = part {
                    print!("{}", text);
                }
            }
        }
    }

    Ok(())
}

テンプレート変数の型:

PatternExampleSource
{var}{user_name}セッションステート
{prefix:var}{user:name}, {app:config}プレフィックス付きステート
{var?}{user_name?}オプション (存在しない場合は空)
{artifact.file}{artifact.resume.pdf}アーティファクトコンテンツ

出力例:

テンプレート: "You are helping {user_name}. Their role is {user_role}."
になります: "You are helping Alice. Their role is Senior Developer."

エージェントは、ユーザーの名前と専門知識のレベルに基づいてパーソナライズされたコンテンツで応答します!


ツールの追加

ツールは、エージェントに会話以上の能力を与えます。データを取得したり、計算を実行したり、ウェブを検索したり、外部APIを呼び出したりできます。LLMは、ユーザーのリクエストに基づいていつツールを使用するかを決定します。

ツールの仕組み

  1. エージェントがユーザーメッセージを受信 → 「東京の天気はどうですか?」
  2. LLMがツールを呼び出すことを決定{"city": "Tokyo"}get_weatherを選択
  3. ツールが実行{"temperature": "22°C", "condition": "sunny"}を返す
  4. LLMが応答をフォーマット → 「東京の天気は晴れで、気温は22°Cです。」

FunctionToolを使ったツールの作成

FunctionToolはツールを作成する最も簡単な方法です。任意のasync Rust関数をラップするだけで、LLMがそれを呼び出すことができます。名前、説明、JSON引数を受け取りJSON結果を返すハンドラー関数を提供します。

let weather_tool = FunctionTool::new(
    "get_weather",                              // LLMが使用するツール名
    "Get the current weather for a city",       // 説明(LLMがいつ使用するかを判断するのに役立つ)
    |_ctx, args| async move {                   // ハンドラー関数
        let city = args.get("city")             // JSONから引数を抽出
            .and_then(|v| v.as_str())
            .unwrap_or("unknown");
        Ok(json!({ "city": city, "temperature": "22°C" }))  // JSON結果を返す
    },
);

⚠️ 注意: 現在の制限事項: GoogleSearchToolのような組み込みツールは、現在、同じAgent内でFunctionToolと互換性がありません。組み込みツールまたはカスタムFunctionToolのいずれかを使用し、両方を同時に使用しないでください。 💡 回避策: それぞれ独自のツールタイプを持つ個別のsubagentを作成し、master LLMAgent、ワークフローagent、またはマルチagentパターンを使用してそれらを連携させます。

マルチツールAgentの構築

新しいプロジェクトを作成します。

cargo new tool_agent
cd tool_agent

Cargo.tomlに依存関係を追加します。

[dependencies]
adk-rust = { version = "0.2.0", features = ["tools"] }
tokio = { version = "1.40", features = ["full"] }
dotenvy = "0.15"
serde_json = "1.0"

.envを作成します。

echo 'GOOGLE_API_KEY=your-api-key' > .env

src/main.rsを、3つのツールを持つagentに置き換えます。

use adk_rust::prelude::*;
use adk_rust::Launcher;
use serde_json::json;
use std::sync::Arc;

#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    dotenvy::dotenv().ok();
    let api_key = std::env::var("GOOGLE_API_KEY")?;
    let model = GeminiModel::new(&api_key, "gemini-2.5-flash")?;

    // ツール1: 天気検索
    let weather_tool = FunctionTool::new(
        "get_weather",
        "Get the current weather for a city. Parameters: city (string)",
        |_ctx, args| async move {
            let city = args.get("city").and_then(|v| v.as_str()).unwrap_or("unknown");
            Ok(json!({ "city": city, "temperature": "22°C", "condition": "sunny" }))
        },
    );

    // ツール2: 計算機
    let calculator = FunctionTool::new(
        "calculate",
        "Perform arithmetic. Parameters: a (number), b (number), operation (add/subtract/multiply/divide)",
        |_ctx, args| async move {
            let a = args.get("a").and_then(|v| v.as_f64()).unwrap_or(0.0);
            let b = args.get("b").and_then(|v| v.as_f64()).unwrap_or(0.0);
            let op = args.get("operation").and_then(|v| v.as_str()).unwrap_or("add");
            let result = match op {
                "add" => a + b,
                "subtract" => a - b,
                "multiply" => a * b,
                "divide" => if b != 0.0 { a / b } else { 0.0 },
                _ => 0.0,
            };
            Ok(json!({ "result": result }))
        },
    );

    // ツール3: 組み込みのGoogle Search(注意: 現在adk-rustでは非サポート)
    // let search_tool = GoogleSearchTool::new();

    // 天気と計算機のツールを持つagentを構築
    let agent = LlmAgentBuilder::new("multi_tool_agent")
        .instruction("You are a helpful assistant. Use tools when needed: \
                     - get_weather for weather questions \
                     - calculate for math")
        .model(Arc::new(model))
        .tool(Arc::new(weather_tool))
        .tool(Arc::new(calculator))
        // .tool(Arc::new(search_tool))  // 現在非サポート
        .build()?;

    Launcher::new(Arc::new(agent)).run().await?;
    Ok(())
}

agentを実行します。

cargo run

対話例

あなた: 250の15%は何ですか?
アシスタント: [calculateツールをa=250, b=0.15, operation=multiplyで利用中]
250の15%は37.5です。

あなた: 東京の天気はどうですか?
アシスタント: [get_weatherツールをcity=Tokyoで利用中]
東京の天気は晴れで、気温は22°Cです。

あなた: 最新のRust機能について検索してください
アシスタント: 現時点では検索機能にアクセスできませんが、Rustに関する他の質問にお答えしたり、計算を実行したりすることはできます!

JSONスキーマによる構造化出力

構造化データが必要なアプリケーションでは、output_schema() を使用します。

use adk_rust::prelude::*;
use serde_json::json;
use std::sync::Arc;

#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    dotenvy::dotenv().ok();
    let api_key = std::env::var("GOOGLE_API_KEY")?;
    let model = GeminiModel::new(&api_key, "gemini-2.5-flash")?;

    let extractor = LlmAgentBuilder::new("entity_extractor")
        .instruction("Extract entities from the given text.")
        .model(Arc::new(model))
        .output_schema(json!({
            "type": "object",
            "properties": {
                "people": {
                    "type": "array",
                    "items": { "type": "string" }
                },
                "locations": {
                    "type": "array",
                    "items": { "type": "string" }
                },
                "dates": {
                    "type": "array",
                    "items": { "type": "string" }
                }
            },
            "required": ["people", "locations", "dates"]
        }))
        .build()?;

    println!("Entity extractor ready!");
    Ok(())
}

JSON出力例

入力: "John met Sarah in Paris on December 25th"

出力:

{
  "people": ["John", "Sarah"],
  "locations": ["Paris"],
  "dates": ["December 25th"]
}

高度な機能

コンテンツの包含

会話履歴の可視性を制御します。

// 完全な履歴 (デフォルト)
.include_contents(IncludeContents::Default)

// ステートレス - 現在の入力のみを参照
.include_contents(IncludeContents::None)

出力キー

エージェントの応答をセッションステートに保存します。

.output_key("summary")  // 応答は state["summary"] に保存されます

動的な指示

実行時に指示を計算します。

.instruction_provider(|ctx| {
    Box::pin(async move {
        let user_id = ctx.user_id();
        Ok(format!("You are assisting user {}.", user_id))
    })
})

コールバック

エージェントの動作をインターセプトします。

.before_model_callback(|ctx, request| {
    Box::pin(async move {
        println!("About to call LLM with {} messages", request.contents.len());
        Ok(BeforeModelResult::Continue)
    })
})

ビルダーリファレンス

メソッド説明
new(name)エージェント名でビルダーを作成します
model(Arc<dyn Llm>)LLM を設定します (必須)
description(text)エージェントの説明
instruction(text)システムプロンプト
tool(Arc<dyn Tool>)ツールを追加します
output_schema(json)構造化出力用のJSONスキーマ
output_key(key)応答をステートに保存します
include_contents(mode)履歴の可視性
build()エージェントを作成します

完全な例

複数のツール(天気、計算機、検索)を備え、セッション状態に結果を保存する本番環境対応のAgent:

use adk_rust::prelude::*;
use adk_rust::Launcher;
use serde_json::json;
use std::sync::Arc;

#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
    dotenvy::dotenv().ok();
    let api_key = std::env::var("GOOGLE_API_KEY")?;
    let model = GeminiModel::new(&api_key, "gemini-2.5-flash")?;

    // Weather tool
    let weather = FunctionTool::new(
        "get_weather",
        "Get weather for a city. Parameters: city (string)",
        |_ctx, args| async move {
            let city = args.get("city").and_then(|v| v.as_str()).unwrap_or("unknown");
            Ok(json!({
                "city": city,
                "temperature": "22°C",
                "humidity": "65%",
                "condition": "partly cloudy"
            }))
        },
    );

    // Calculator tool
    let calc = FunctionTool::new(
        "calculate",
        "Math operations. Parameters: expression (string like '2 + 2')",
        |_ctx, args| async move {
            let expr = args.get("expression").and_then(|v| v.as_str()).unwrap_or("0");
            Ok(json!({ "expression": expr, "result": "computed" }))
        },
    );

    // Build the full agent
    let agent = LlmAgentBuilder::new("assistant")
        .description("A helpful assistant with weather and calculation abilities")
        .instruction("You are a helpful assistant. \
                     Use the weather tool for weather questions. \
                     Use the calculator for math. \
                     Be concise and friendly.")
        .model(Arc::new(model))
        .tool(Arc::new(weather))
        .tool(Arc::new(calc))
        // .tool(Arc::new(GoogleSearchTool::new()))  // Currently unsupported with FunctionTool
        .output_key("last_response")
        .build()?;

    println!("✅ Agent '{}' ready!", agent.name());
    Launcher::new(Arc::new(agent)).run().await?;
    Ok(())
}

これらのプロンプトを試してください:

ユーザー: 25かける4は何ですか?
アシスタント: 100です。

ユーザー: ニューヨークの天気はどうですか?
アシスタント: ニューヨークの天気は、一部曇りで、気温22°C、湿度65%です。

ユーザー: 85ドルに15%のチップを計算してください
アシスタント: 85ドルに15%のチップは12.75ドルで、合計は97.75ドルになります。

関連


前へ: クイックスタート | 次へ: ワークフローAgent →