Exécuteur
L’environnement d’exécution de adk-runner qui orchestre l’exécution des agents.
Vue d’ensemble
Runner gère l’intégralité du cycle de vie de l’exécution des agents :
- Gestion des sessions (création/récupération des sessions)
- Injection de mémoire (recherche et injection des souvenirs pertinents)
- Gestion des artefacts (accès aux artefacts limité au périmètre défini)
- Diffusion des événements (traitement et transmission des événements)
- Transferts d’agents (gestion des transitions entre plusieurs agents)
Installation
[dependencies]
adk-runner = "2.1.0"
RunnerConfig
Configurez l’exécuteur avec les services requis :
use adk_runner::{Runner, RunnerConfig};
use adk_session::InMemorySessionService;
use adk_artifact::InMemoryArtifactService;
use std::sync::Arc;
let config = RunnerConfig {
app_name: "my_app".to_string(),
agent: Arc::new(my_agent),
session_service: Arc::new(InMemorySessionService::new()),
artifact_service: Some(Arc::new(InMemoryArtifactService::new())),
memory_service: None,
plugin_manager: None,
run_config: None,
compaction_config: None,
context_cache_config: None,
cache_capable: None,
request_context: None,
cancellation_token: None,
};
let runner = Runner::new(config)?;
RunnerConfigBuilder (Recommandé)
Utilisez le générateur typestate pour construire un Runner. Le générateur impose les champs requis au moment de la compilation et attribue des valeurs par défaut à tous les champs facultatifs. Ainsi, l’ajout de nouveaux champs dans de futures versions ne provoquera pas de rupture dans votre code :
use adk_runner::Runner;
let runner = Runner::builder()
.app_name("my_app")
.agent(Arc::new(my_agent))
.session_service(Arc::new(InMemorySessionService::new()))
// Optional fields — only set what you need
.artifact_service(Arc::new(InMemoryArtifactService::new()))
.build()?;
Le générateur requiert trois champs : app_name, agent et session_service. Tout le reste est facultatif et dispose de valeurs par défaut appropriées. La méthode build() n’est disponible qu’une fois les trois champs requis définis : l’absence de l’un d’eux provoque une erreur de compilation, et non une erreur d’exécution.
Champs de configuration
| Champ | Type | Obligatoire | Description |
|---|---|---|---|
app_name | String | Oui | Identifiant de l’application |
agent | Arc<dyn Agent> | Oui | Agent racine à exécuter |
session_service | Arc<dyn SessionService> | Oui | Backend de stockage des sessions |
artifact_service | Option<Arc<dyn ArtifactService>> | Non | Stockage des artefacts |
memory_service | Option<Arc<dyn Memory>> | Non | Mémoire à long terme |
plugin_manager | Option<Arc<PluginManager>> | Non | Hooks du cycle de vie des plugins |
compaction_config | Option<EventsCompactionConfig> | Non | Paramètres de compaction du contexte |
run_config | Option<RunConfig> | Non | Options d’exécution |
context_cache_config | Option<ContextCacheConfig> | Non | Cycle de vie du cache de contexte au niveau de l’exécuteur (expérimental — voir ci-dessous) |
cache_capable | Option<Arc<dyn CacheCapable>> | Non | Référence de modèle compatible avec le cache (expérimental — voir ci-dessous) |
request_context | Option<RequestContext> | Non | Contexte du middleware d’authentification |
cancellation_token | Option<CancellationToken> | Non | Annulation coopérative |
Mise en cache des prompts
La mise en cache relève du fournisseur et ne nécessite aucune configuration de Runner. Chaque intégration de fournisseur la gère au moment où la requête est assemblée :
| Fournisseur | Mécanisme | Par défaut |
|---|---|---|
| Anthropic / Bedrock | points d’arrêt cache_control | activé (AnthropicConfig::prompt_caching, désactivation avec with_prompt_caching(false)) |
| OpenAI | mise en cache des prompts côté serveur, PromptCacheRetention pour la conservation | automatique |
| Gemini | mise en cache implicite sur 2.5/3.x — un préfixe partagé bénéficie d’une remise sans modification du code | automatique |
Les accès au cache sont observables sans configuration supplémentaire : l’intégration Gemini enregistre cachedContentTokenCount dans chaque réponse.
context_cache_configetcache_capablesont expérimentaux et doivent rester non définis. Ils pilotent lecachedContentsexplicite de Gemini API depuis le Runner. Cette API exige que le cache remplacesystem_instruction,toolsettool_config— l’envoi d’un cache avec l’un d’eux est rejeté avecINVALID_ARGUMENT. Le Runner sélectionne un cache avant que l’agent ne résolve ses outils ; il ne peut donc pas assembler cette requête, et l’activation de ces champs ne produit actuellement aucun accès au cache. La mise en cache garantie (plutôt que dans la mesure du possible) pour Gemini relève de l’intégration du modèle, comme pour les autres fournisseurs.
Exécution des agents
Exécutez un agent avec une entrée utilisateur :
use adk_core::{Content, SessionId, UserId};
use futures::StreamExt;
let user_content = Content::new("user").with_text("Hello!");
let mut stream = runner.run(
UserId::new("user-123")?,
SessionId::new("session-456")?,
user_content,
).await?;
while let Some(event) = stream.next().await {
match event {
Ok(e) => {
if let Some(content) = e.content() {
for part in &content.parts {
if let Some(text) = part.text() {
print!("{}", text);
}
}
}
}
Err(e) => eprintln!("Error: {}", e),
}
}
Méthode pratique pour les chaînes
Pour les points d’appel simples, run_str() accepte des arguments &str simples et gère en interne la conversion vers le newtype :
let mut stream = runner.run_str(
"user-123",
"session-456",
Content::new("user").with_text("Hello!"),
).await?;
Si la chaîne échoue à la validation (elle est vide, contient des octets nuls ou dépasse la limite de longueur), run_str() renvoie une erreur avant de démarrer la boucle de l’agent. La méthode run() existante avec des UserId/SessionId typés reste inchangée.
Interruption et isolation des exécutions
Une exécution est enregistrée dès que run() renvoie son flux, et désenregistrée lorsque
ce flux est supprimé — y compris lorsqu’il est supprimé sans avoir jamais été interrogé.
| Méthode | Portée |
|---|---|
interrupt(session_id) | Annule toutes les exécutions en cours pour cet ID de session, parmi les applications et les utilisateurs |
interrupt_identity(app_name, user_id, session_id) | Annule les exécutions pour une identité exacte |
active_runs() | L’identité de chaque exécution en cours ; une identité répétée signifie que les exécutions sont concurrentes |
active_session_ids() | Identifiants de session dédupliqués des exécutions en cours |
// Cancel one tenant's run without touching another that shares the session ID
let cancelled = runner.interrupt_identity("my-app", "user-1", "session-1");
Un identifiant de session n’est unique qu’au sein d’une application et d’un utilisateur ; interrupt(session_id) est donc la forme générale et interrupt_identity la forme précise. Préférez
interrupt_identity lorsqu’un seul Runner est utilisé par plusieurs applications ou utilisateurs.
Les exécutions sont suivies par un identifiant d’exécution unique plutôt que par un identifiant de session. Ainsi, deux exécutions associées à la même identité sont suivies séparément et chacune se désenregistre uniquement elle-même.
La persistance est liée à l’identité
Chaque événement que le Runner rend persistant — tours utilisateur, réponses du modèle, événements de transfert, événements de plugin et événements de compactage — est écrit via
SessionService::append_event_for_identity avec le triplet complet
(app_name, user_id, session_id). Un SessionService dont la clé naturelle est
composite peut donc associer chaque événement à son locataire et peut rejeter ou ignorer entièrement le chemin append_event utilisant l’identifiant de session brut.
Flux d’exécution
┌─────────────────────────────────────────────────────────────┐
│ Runner.run() │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 1. Session Retrieval │
│ │
│ SessionService.get(app_name, user_id, session_id) │
│ → Creates new session if not exists │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 2. Agent Selection │
│ │
│ Check session state for active agent │
│ → Use root agent or transferred agent │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 3. Context Creation │
│ │
│ InvocationContext with: │
│ - Session (mutable) │
│ - Artifacts (scoped to session) │
│ - Memory (if configured) │
│ - Run config │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 4. Agent Execution │
│ │
│ agent.run(ctx) → EventStream │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 5. Event Processing │
│ │
│ For each event: │
│ - Update session state │
│ - Handle transfers │
│ - Forward to caller │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 6. Session Save │
│ │
│ SessionService.append_event(session, events) │
└─────────────────────────────────────────────────────────────┘
InvocationContext
Le contexte fourni aux agents pendant l’exécution :
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
fn session(&self) -> &dyn Session;
/// Execution configuration
fn run_config(&self) -> &RunConfig;
/// Signal end of invocation
fn end_invocation(&self);
/// Check if invocation has ended
fn ended(&self) -> bool;
}
RunConfig
Options d’exécution :
pub struct RunConfig {
/// Streaming mode for responses
pub streaming_mode: StreamingMode,
// ... other fields (tool_confirmation_decisions, cached_content, etc.)
}
ToolExecutionStrategy
Contrôle la manière dont plusieurs appels d’outils provenant d’une même réponse LLM sont distribués :
| Stratégie | Comportement |
|---|---|
Sequential (par défaut) | Exécute les outils un par un dans l’ordre renvoyé par LLM |
Parallel | Exécute tous les outils simultanément ; l’appelant est responsable de la sécurité |
Auto | Exécuter simultanément le sous-ensemble sûr en lecture seule, puis tous les appels restants séquentiellement |
Définissez-le par agent via LlmAgentBuilder :
use adk_core::ToolExecutionStrategy;
let agent = LlmAgentBuilder::new("fast_agent")
.model(model)
.tool_execution_strategy(ToolExecutionStrategy::Auto)
.tool(Arc::new(
search_tool
.with_read_only(true)
.with_concurrency_safe(true),
))
.tool(Arc::new(save_tool)) // runs after the concurrent safe subset
.build()?;
En mode Auto, la boucle de dispatch interroge à la fois is_read_only() et is_concurrency_safe(). Les appels dont les outils sélectionnés renvoient true pour les deux méthodes sont d’abord exécutés simultanément ; tous les autres appels sont ensuite exécutés séquentiellement. Parallel contourne ces vérifications de métadonnées en tant que surcharge explicite de l’appelant. Les résultats sont toujours réassemblés dans l’ordre d’origine renvoyé par LLM, quelle que soit la stratégie. Les outils en échec produisent une réponse d’erreur JSON sans interrompre le lot.
pub enum StreamingMode {
/// No streaming, return complete response
None,
/// Server-Sent Events (default)
SSE,
/// Bidirectional streaming (realtime)
Bidi,
}
Transferts entre agents
Le Runner gère automatiquement les transferts entre plusieurs agents :
// In an agent's tool or callback
if should_transfer {
// Set transfer in event actions
ctx.set_actions(EventActions {
transfer_to_agent: Some("specialist_agent".to_string()),
..Default::default()
});
}
Le Runner va :
- Détecter la demande de transfert dans l’événement
- Rechercher l’agent cible dans sub_agents
- Mettre à jour l’état de la session avec le nouvel agent actif
- Poursuivre l’exécution avec le nouvel agent
Compactage du contexte
Pour les sessions de longue durée, activez le compactage automatique du contexte afin de maintenir la fenêtre de contexte LLM dans des limites définies :
use adk_runner::{Runner, RunnerConfig, EventsCompactionConfig};
use adk_agent::LlmEventSummarizer;
use std::sync::Arc;
let summarizer = LlmEventSummarizer::new(model.clone());
let config = RunnerConfig {
// ... other fields ...
compaction_config: Some(EventsCompactionConfig {
compaction_interval: 3, // Compact every 3 invocations
overlap_size: 1, // Keep 1 event overlap for continuity
summarizer: Arc::new(summarizer),
}),
// ...
};
Lorsque le compactage est déclenché, les événements plus anciens sont remplacés par un événement de synthèse. conversation_history() utilise automatiquement la synthèse au lieu des événements d’origine.
Consultez Compactage du contexte pour la documentation complète.
Intégration avec Launcher
Le Launcher utilise Runner en interne :
// Launcher creates Runner with default services
Launcher::new(agent)
.app_name("my_app")
.run()
.await?;
// Equivalent to using the builder:
let runner = Runner::builder()
.app_name("my_app")
.agent(agent)
.session_service(Arc::new(InMemorySessionService::new()))
.build()?;
Utilisation personnalisée du Runner
Pour les scénarios avancés, utilisez directement Runner :
use adk_runner::Runner;
// Production configuration using the builder
let runner = Runner::builder()
.app_name("production_app")
.agent(my_agent)
.session_service(Arc::new(SqliteSessionService::new(db_pool)))
.artifact_service(Arc::new(S3ArtifactService::new(s3_client)))
.memory_service(Arc::new(QdrantMemoryService::new(qdrant_client)))
.build()?;
// Use in HTTP handler with run_str() for convenience
async fn chat_handler(runner: &Runner, request: ChatRequest) -> Response {
let stream = runner.run_str(
&request.user_id,
&request.session_id,
request.content,
).await?;
// Stream events to client
Response::sse(stream)
}
Précédent : ← Types principaux | Suivant : Launcher →