LlmAgent

Le LlmAgent est le type d'agent principal dans adk-rust qui utilise un Large Language Model pour le raisonnement et la prise de décision.

Démarrage rapide

Créez un nouveau projet :

cargo new llm_agent
cd llm_agent

Ajoutez les dépendances à Cargo.toml :

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

Créez un fichier .env avec votre clé API :

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

Remplacez 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(())
}

Exécutez-le :

cargo run

Interagir avec votre Agent

Vous verrez une invite interactive :

🤖 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!

Façonner le comportement de l'Agent avec des instructions

La méthode instruction() définit la personnalité et le comportement de votre Agent. C'est le system prompt qui guide chaque réponse :

// 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()?;

Exemples de sorties

Requête utilisateur : "What is Rust?"

Assistant d'affaires formel :

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.

Tuteur de code amical :

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?

Conteur créatif :

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...

Modélisation des instructions

Les instructions prennent en charge l'injection de variables à l'aide de la syntaxe {var}. Les variables sont résolues à partir de l'état de la Session au moment de l'exécution :

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()?;

Guide étape par étape pour utiliser la modélisation :

  1. Créer l'Agent avec des variables de modèle dans l'instruction
  2. Configurer le Runner et le SessionService pour gérer l'état
  3. Créer une Session avec des variables d'état qui correspondent à votre modèle
  4. Exécuter l'Agent - les modèles sont remplacés automatiquement

Voici un exemple complet et fonctionnel :

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. Agent with templated instruction
    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. Create session service and 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. Create session with state variables
    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. Run the agent - instruction becomes:
    // "You are helping Alice. Their role is 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?;

    // Print the response
    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(())
}

Types de variables de modèle :

ModèleExempleSource
{var}{user_name}État de la Session
{prefix:var}{user:name}, {app:config}État préfixé
{var?}{user_name?}Optionnel (vide si manquant)
{artifact.file}{artifact.resume.pdf}Contenu de l'Artifact

Exemple de sortie :

Modèle : \"You are helping {user_name}. Their role is {user_role}.\"
Devient : \"Vous aidez Alice. Leur rôle est Senior Developer.\"

L'Agent répondra ensuite avec un contenu personnalisé basé sur le nom de l'utilisateur et son niveau d'expertise !


Ajout d'Outils

Les Tools confèrent à votre Agent des capacités au-delà de la conversation — ils peuvent récupérer des données, effectuer des calculs, rechercher sur le web ou appeler des API externes. Le LLM décide quand utiliser un Tool en fonction de la demande de l'utilisateur.

Comment les Outils Fonctionnent

  1. L'Agent reçoit le message de l'utilisateur → "Quel temps fait-il à Tokyo ?"
  2. Le LLM décide d'appeler un Tool → Sélectionne get_weather avec {"city": "Tokyo"}
  3. Le Tool s'exécute → Renvoie {"temperature": "22°C", "condition": "sunny"}
  4. Le LLM formate la réponse → "Le temps à Tokyo est ensoleillé avec une température de 22 °C."

Création d'un Outil avec FunctionTool

FunctionTool est le moyen le plus simple de créer un Tool — enveloppez n'importe quelle fonction async Rust et le LLM pourra l'appeler. Vous fournissez un nom, une description et une fonction de gestionnaire qui reçoit des arguments JSON et renvoie un résultat JSON.

let weather_tool = FunctionTool::new(
    "get_weather",                              // Nom du Tool (utilisé par le LLM)
    "Obtenir la météo actuelle pour une ville", // Description (aide le LLM à décider quand l'utiliser)
    |_ctx, args| async move {                   // Fonction de gestionnaire
        let city = args.get("city")             // Extrait les arguments du JSON
            .and_then(|v| v.as_str())
            .unwrap_or("unknown");
        Ok(json!({ "city": city, "temperature": "22°C" }))  // Renvoie le résultat JSON
    },
);

⚠️ Note : Limitation Actuelle : Les Tools intégrés comme GoogleSearchTool sont actuellement incompatibles avec FunctionTool au sein du même Agent. Utilisez soit les Tools intégrés, soit les FunctionTools personnalisés, mais pas les deux ensemble. 💡 Solution de contournement : Créez des subagents séparés, chacun avec son propre type de Tool, et coordonnez-les à l'aide d'un LlmAgent maître, d'Agents de workflow ou de schémas multi-agents.

Construire un Agent Multi-Tool

Créez un nouveau projet :

cargo new tool_agent
cd tool_agent

Ajoutez les dépendances à Cargo.toml :

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

Créez .env :

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

Remplacez src/main.rs par un Agent qui possède trois Tools :

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")?;

    // Tool 1 : Recherche météo
    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" }))
        },
    );

    // Tool 2 : Calculatrice
    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 }))
        },
    );

    // Tool 3 : Google Search intégré (Note : Actuellement non pris en charge dans ADK-Rust)
    // let search_tool = GoogleSearchTool::new();

    // Construire l'Agent avec les Tools de météo et de calculatrice
    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))  // Actuellement non pris en charge
        .build()?;

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

Exécutez votre Agent :

cargo run

Exemple d'Interaction

Vous : Qu'est-ce que 15 % de 250 ?
Assistant : [Utilise l'outil calculate avec a=250, b=0.15, operation=multiply]
15 % de 250 est 37,5.

Vous : Quel temps fait-il à Tokyo ?
Assistant : [Utilise l'outil get_weather avec city=Tokyo]
Le temps à Tokyo est ensoleillé avec une température de 22 °C.

Vous : Cherche les dernières fonctionnalités de Rust
Assistant : Je n'ai pas accès à la fonctionnalité de recherche pour le moment, mais je peux vous aider avec d'autres questions sur Rust ou effectuer des calculs !

Sortie structurée avec JSON Schema

Pour les applications qui nécessitent des données structurées, utilisez 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(())
}

Exemple de sortie JSON

Entrée: "John met Sarah in Paris on December 25th"

Sortie:

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

Fonctionnalités avancées

Inclure le contenu

Contrôlez la visibilité de l'historique de conversation:

// Historique complet (par défaut)
.include_contents(IncludeContents::Default)

// Sans état - ne voit que l'entrée actuelle
.include_contents(IncludeContents::None)

Clé de sortie

Enregistre les réponses de l'Agent dans l'état de session:

.output_key("summary")  // La réponse est enregistrée dans state["summary"]

Instructions dynamiques

Calcule les instructions à l'exécution:

.instruction_provider(|ctx| {
    Box::pin(async move {
        let user_id = ctx.user_id();
        Ok(format!("Vous assistez l'utilisateur {}.", user_id))
    })
})

Callbacks

Intercepte le comportement de l'Agent:

.before_model_callback(|ctx, request| {
    Box::pin(async move {
        println!("Sur le point d'appeler le LLM avec {} messages", request.contents.len());
        Ok(BeforeModelResult::Continue)
    })
})

Référence du constructeur

MéthodeDescription
new(name)Crée le constructeur avec le nom de l'Agent
model(Arc<dyn Llm>)Définit le LLM (obligatoire)
description(text)Description de l'Agent
instruction(text)Prompt système
tool(Arc<dyn Tool>)Ajoute un Tool
output_schema(json)Schéma JSON pour la sortie structurée
output_key(key)Enregistre la réponse dans l'état
include_contents(mode)Visibilité de l'historique
build()Crée l'Agent

Exemple complet

Un agent prêt pour la production avec plusieurs outils (météo, calculatrice, recherche) et une sortie enregistrée dans l'état de la session :

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(())
}

Essayez ces invites :

You: What's 25 times 4?
Assistant: It's 100.

You: How's the weather in New York?
Assistant: The weather in New York is partly cloudy with a temperature of 22°C and 65% humidity.

You: Calculate 15% tip on $85
Assistant: A 15% tip on $85 is $12.75, making the total $97.75.

Rubriques connexes


Précédent: Démarrage rapide | Suivant: Agents de workflow →