Runner
Le runtime d’exécution de adk-runner qui orchestre l’exécution de l’agent.
Vue d’ensemble
Le Runner gère le cycle de vie complet de l’exécution de l’agent :
- Gestion des sessions (créer/récupérer des sessions)
- Injection de mémoire (rechercher et injecter les mémoires pertinentes)
- Gestion des artefacts (accès aux artefacts à portée limitée)
- Flux d’événements (traiter et transmettre les événements)
- Transferts d’agent (gérer les passations entre plusieurs agents)
Installation
[dependencies]
adk-runner = "2.0.0"
RunnerConfig
Configurez le runner 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 à la compilation et attribue des valeurs par défaut à tous les champs optionnels, de sorte que l’ajout de nouveaux champs dans les futures versions ne casse pas 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 exige trois champs : app_name, agent et session_service. Tout le reste est facultatif et dispose de valeurs par défaut raisonnables. La méthode build() n’est disponible qu’une fois les trois champs requis définis — en manquer un provoque une erreur à la compilation, et non à l’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 de session |
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 du runner (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 est une préoccupation au niveau du fournisseur et ne nécessite aucune configuration de Runner. Chaque intégration de fournisseur la gère à l’endroit 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ésactivable avec with_prompt_caching(false)) |
| OpenAI | mise en cache des prompts côté serveur, PromptCacheRetention pour la rétention | automatique |
| Gemini | mise en cache implicite sur 2.5/3.x — un préfixe partagé bénéficie d’une remise sans changement de code | automatique |
Les hits de cache sont observables sans câblage supplémentaire : l’intégration Gemini enregistre cachedContentTokenCount sur chaque réponse.
context_cache_configetcache_capablesont expérimentaux et doivent rester non définis. Ils pilotent lecachedContentsAPI explicite de Gemini depuis le Runner. Ce API exige que le cache remplacesystem_instruction,toolsettool_config— envoyer 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 pas de hits de cache. Un cache garanti (plutôt qu’au mieux possible) pour Gemini relève de l’intégration du modèle, de la même manière que le font les autres fournisseurs.
Exécution des agents
Exécutez un agent avec l’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 appels simples, run_str() accepte des arguments &str bruts et gère la conversion du nouveautype en interne :
let mut stream = runner.run_str(
"user-123",
"session-456",
Content::new("user").with_text("Hello!"),
).await?;
Si la chaîne échoue à la validation (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 jamais avoir été interrogé.
| Méthode | Portée |
|---|---|
interrupt(session_id) | Annule toute exécution en cours pour cet ID de session, dans toutes les applications et pour tous 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 des exécutions concurrentes |
active_session_ids() | IDs 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’à l’intérieur d’une application et d’un utilisateur, donc interrupt(session_id) est
la forme large et interrupt_identity la forme précise. Préférez
interrupt_identity lorsqu’une seule Runner sert plus d’une application ou d’un utilisateur.
Les exécutions sont suivies par un identifiant d’exécution unique plutôt que par un identifiant de session, donc deux exécutions pour la même identité sont suivies séparément et chacune ne se désenregistre qu’elle-même.
La persistance est liée à l’identité
Chaque événement que le Runner persiste — tours utilisateur, réponses du modèle, événements de transfert, événements de plugin et événements de compaction — est écrit via
SessionService::append_event_for_identity avec le triplet complet
(app_name, user_id, session_id). Une SessionService dont la clé naturelle est
composite peut donc lier chaque événement à son locataire, et peut rejeter ou ignorer entièrement le chemin append_event de 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’outil provenant d’une seule réponse LLM sont dispatché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écutez le sous-ensemble sécurisé en lecture seule simultanément, puis toutes les autres appels séquentiellement |
Définir 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 s’exécutent d’abord en parallèle ; tous les appels restants s’exécutent ensuite séquentiellement. Parallel contourne ces vérifications de métadonnées en tant que surcharge explicite du caller. 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 d’agent
Le Runner gère automatiquement les transferts multi-agent :
// 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
- Trouver l’agent cible dans sub_agents
- Mettre à jour l’état de session avec le nouvel agent actif
- Continuer l’exécution avec le nouvel agent
Compaction du contexte
Pour les sessions de longue durée, activez la compaction automatique du contexte afin de maintenir la fenêtre de contexte LLM bornée :
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 la compaction se déclenche, les événements plus anciens sont remplacés par un événement de résumé. conversation_history() utilise automatiquement le résumé à la place des événements d’origine.
Voir Compaction 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 de Runner
Pour des scénarios avancés, utilisez Runner directement :
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 de base | Suivant : Launcher →