Runner
O runtime de execução de adk-runner que orquestra a execução de agentes.
Visão geral
O Runner gerencia todo o ciclo de vida da execução de agentes:
- Gerenciamento de sessões (criar/recuperar sessões)
- Injeção de memória (pesquisar e injetar memórias relevantes)
- Gerenciamento de artefatos (acesso a artefatos com escopo)
- Transmissão de eventos (processar e encaminhar eventos)
- Transferências entre agentes (gerenciar handoffs entre vários agentes)
Instalação
[dependencies]
adk-runner = "2.1.0"
RunnerConfig
Configure o runner com os serviços necessários:
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 (Recomendado)
Use o construtor typestate para criar um Runner. O construtor impõe os campos obrigatórios em tempo de compilação e define valores padrão para todos os campos opcionais; assim, adicionar novos campos em versões futuras não interromperá seu código:
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()?;
O construtor exige três campos: app_name, agent e session_service. Todo o restante é opcional e possui valores padrão apropriados. O método build() só fica disponível quando os três campos obrigatórios são definidos — deixar um deles de fora gera um erro em tempo de compilação, não em tempo de execução.
Campos de configuração
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
app_name | String | Sim | Identificador da aplicação |
agent | Arc<dyn Agent> | Sim | Agente raiz a ser executado |
session_service | Arc<dyn SessionService> | Sim | Backend de armazenamento de sessão |
artifact_service | Option<Arc<dyn ArtifactService>> | Não | Armazenamento de artefatos |
memory_service | Option<Arc<dyn Memory>> | Não | Memória de longo prazo |
plugin_manager | Option<Arc<PluginManager>> | Não | Ganchos do ciclo de vida do plugin |
compaction_config | Option<EventsCompactionConfig> | Não | Configurações de compactação de contexto |
run_config | Option<RunConfig> | Não | Opções de execução |
context_cache_config | Option<ContextCacheConfig> | Não | Ciclo de vida do cache de contexto no nível do executor (experimental — veja abaixo) |
cache_capable | Option<Arc<dyn CacheCapable>> | Não | Referência de modelo compatível com cache (experimental — veja abaixo) |
request_context | Option<RequestContext> | Não | Contexto do middleware de autenticação |
cancellation_token | Option<CancellationToken> | Não | Cancelamento cooperativo |
Cache de prompts
O armazenamento em cache é uma preocupação do nível do provedor e não requer configuração do Runner. Cada integração com o provedor lida com isso quando a solicitação é montada:
| Provedor | Mecanismo | Padrão |
|---|---|---|
| Anthropic / Bedrock | cache_control breakpoints | ativado (AnthropicConfig::prompt_caching, desative com with_prompt_caching(false)) |
| OpenAI | armazenamento em cache de prompts no lado do servidor, PromptCacheRetention para retenção | automático |
| Gemini | cache implícito em 2.5/3.x — um prefixo compartilhado gera desconto sem alteração no código | automático |
As ocorrências de cache são observáveis sem qualquer configuração adicional: a integração com Gemini registra cachedContentTokenCount em cada resposta.
context_cache_configecache_capablesão experimentais e devem permanecer não definidos. Eles controlam ocachedContentsAPI explícito do Gemini a partir do Runner. Esse API exige que o cache substituasystem_instruction,toolsetool_config— enviar um cache junto com qualquer um deles é rejeitado comINVALID_ARGUMENT. O Runner seleciona um cache antes de o agente resolver suas ferramentas, portanto não consegue montar essa solicitação, e habilitar esses campos atualmente não produz ocorrências de cache. O cache garantido (em vez de baseado no melhor esforço) para o Gemini deve ficar na integração do modelo, assim como os outros provedores fazem.
Executando agentes
Execute um agente com uma entrada do usuário:
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étodo de conveniência para strings
Para pontos de chamada simples, run_str() aceita argumentos &str simples e realiza a conversão do novo tipo internamente:
let mut stream = runner.run_str(
"user-123",
"session-456",
Content::new("user").with_text("Hello!"),
).await?;
Se a string falhar na validação (estiver vazia, contiver bytes nulos ou exceder o limite de tamanho), run_str() retornará um erro antes de iniciar o loop do agente. O método run() existente com UserId/SessionId tipados permanece inalterado.
Interrupção e isolamento das execuções
Uma execução é registrada assim que run() retorna seu fluxo, e removida do registro quando
esse fluxo é descartado — inclusive quando é descartado sem nunca ter sido consultado.
| Método | Escopo |
|---|---|
interrupt(session_id) | Cancela todas as execuções em andamento para esse ID de sessão, entre aplicativos e usuários |
interrupt_identity(app_name, user_id, session_id) | Cancela as execuções de uma identidade exata |
active_runs() | A identidade de cada execução em andamento; uma identidade repetida significa execuções simultâneas |
active_session_ids() | IDs de sessão sem duplicatas das execuções em andamento |
// Cancel one tenant's run without touching another that shares the session ID
let cancelled = runner.interrupt_identity("my-app", "user-1", "session-1");
Um ID de sessão é exclusivo apenas dentro de um app e usuário, portanto, interrupt(session_id) é
a forma abrangente e interrupt_identity é a precisa. Prefira
interrupt_identity quando um único Runner atender a mais de um app ou usuário.
As execuções são rastreadas por um ID de execução exclusivo, e não pelo ID da sessão, portanto, duas execuções para a mesma identidade são rastreadas separadamente e cada uma remove o registro apenas de si mesma.
A persistência é vinculada à identidade
Cada evento que o Runner persiste — turnos do usuário, respostas do modelo, eventos de transferência,
eventos de plug-ins e eventos de compactação — é gravado por meio de
SessionService::append_event_for_identity com a
(app_name, user_id, session_id) completa. Um SessionService cuja chave natural é
composta pode, portanto, vincular cada evento ao seu tenant e rejeitar ou ignorar
completamente o caminho de append_event do ID de sessão bruto.
Fluxo de execução
┌─────────────────────────────────────────────────────────────┐
│ 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
O contexto fornecido aos agentes durante a execução:
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
Opções de execução:
pub struct RunConfig {
/// Streaming mode for responses
pub streaming_mode: StreamingMode,
// ... other fields (tool_confirmation_decisions, cached_content, etc.)
}
ToolExecutionStrategy
Controla como várias chamadas de ferramentas de uma única resposta de LLM são despachadas:
| Estratégia | Comportamento |
|---|---|
Sequential (padrão) | Execute as ferramentas uma de cada vez, na ordem retornada por LLM |
Parallel | Execute todas as ferramentas simultaneamente; o chamador é responsável pela segurança |
Auto | Execute o subconjunto seguro somente para leitura simultaneamente e, em seguida, todas as chamadas restantes sequencialmente |
Definido por agente 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()?;
No modo Auto, o loop de despacho consulta tanto is_read_only() quanto is_concurrency_safe(). As chamadas cujas ferramentas selecionadas retornam true para ambos os métodos são executadas primeiro de forma concorrente; todas as chamadas restantes são executadas sequencialmente. Parallel ignora essas verificações de metadados como uma substituição explícita do chamador. Os resultados são sempre remontados na ordem original retornada por LLM, independentemente da estratégia. Ferramentas com falha produzem uma resposta de erro JSON sem interromper o lote.
pub enum StreamingMode {
/// No streaming, return complete response
None,
/// Server-Sent Events (default)
SSE,
/// Bidirectional streaming (realtime)
Bidi,
}
Transferências entre agentes
O Runner gerencia automaticamente as transferências entre vários agentes:
// 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()
});
}
O Runner irá:
- Detectar a solicitação de transferência no evento
- Encontrar o agente de destino em sub_agents
- Atualizar o estado da sessão com o novo agente ativo
- Continuar a execução com o novo agente
Compactação de contexto
Para sessões de longa duração, habilite a compactação automática de contexto para manter limitada a janela de contexto de LLM:
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),
}),
// ...
};
Quando a compactação é acionada, os eventos mais antigos são substituídos por um evento de resumo. conversation_history() usa automaticamente o resumo em vez dos eventos originais.
Consulte Compactação de contexto para obter a documentação completa.
Integração com o Launcher
O Launcher usa Runner internamente:
// 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()?;
Uso personalizado do Runner
Para cenários avançados, use o Runner diretamente:
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)
}
Anterior: ← Tipos principais | Próximo: Launcher →