Runner
Die Laufzeitumgebung aus adk-runner, die die Agent-Ausführung orchestriert.
Übersicht
Runner verwaltet den vollständigen Lebenszyklus der Agent-Ausführung:
- Sitzungsverwaltung (Sitzungen erstellen/abrufen)
- Speicherinjektion (relevante Erinnerungen suchen und injizieren)
- Artefaktverwaltung (bereichsbezogener Artefaktzugriff)
- Ereignis-Streaming (Ereignisse verarbeiten und weiterleiten)
- Agent-Übertragungen (Übergaben zwischen mehreren Agenten verwalten)
Installation
[dependencies]
adk-runner = "2.1.0"
RunnerConfig
Konfigurieren Sie den Runner mit den erforderlichen Diensten:
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 (Empfohlen)
Verwenden Sie den typestate builder, um einen Runner zu erstellen. Der Builder erzwingt erforderliche Felder zur Compile-Zeit und versieht alle optionalen Felder mit Standardwerten. Dadurch wird Ihr Code beim Hinzufügen neuer Felder in zukünftigen Versionen nicht beeinträchtigt:
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()?;
Der Builder erfordert drei Felder: app_name, agent und session_service. Alles andere ist optional und verfügt über sinnvolle Standardwerte. Die Methode build() ist erst verfügbar, sobald alle drei erforderlichen Felder gesetzt sind – ein fehlendes Feld führt zu einem Fehler zur Compile-Zeit und nicht erst zur Laufzeit.
Konfigurationsfelder
| Feld | Typ | Erforderlich | Beschreibung |
|---|---|---|---|
app_name | String | Ja | Anwendungskennung |
agent | Arc<dyn Agent> | Ja | Auszuführender Stammagent |
session_service | Arc<dyn SessionService> | Ja | Backend für Sitzungsspeicher |
artifact_service | Option<Arc<dyn ArtifactService>> | Nein | Artefaktspeicher |
memory_service | Option<Arc<dyn Memory>> | Nein | Langzeitgedächtnis |
plugin_manager | Option<Arc<PluginManager>> | Nein | Hooks für den Plugin-Lebenszyklus |
compaction_config | Option<EventsCompactionConfig> | Nein | Einstellungen zur Kontextkomprimierung |
run_config | Option<RunConfig> | Nein | Ausführungsoptionen |
context_cache_config | Option<ContextCacheConfig> | Nein | Lebenszyklus des Kontext-Caches auf Runner-Ebene (experimentell — siehe unten) |
cache_capable | Option<Arc<dyn CacheCapable>> | Nein | Referenz auf ein cachefähiges Modell (experimentell — siehe unten) |
request_context | Option<RequestContext> | Nein | Kontext der Authentifizierungs-Middleware |
cancellation_token | Option<CancellationToken> | Nein | Kooperative Abbruchbehandlung |
Prompt-Caching
Caching ist eine Angelegenheit auf Anbieterebene und erfordert keine Runner-Konfiguration. Jede Anbieterintegration behandelt dies dort, wo die Anfrage zusammengestellt wird:
| Anbieter | Mechanismus | Standard |
|---|---|---|
| Anthropic / Bedrock | cache_control Breakpoints | aktiviert (AnthropicConfig::prompt_caching, deaktivieren mit with_prompt_caching(false)) |
| OpenAI | serverseitiges Prompt-Caching, PromptCacheRetention für die Aufbewahrung | automatisch |
| Gemini | Implizites Caching auf 2.5/3.x – ein gemeinsames Präfix bringt ohne Codeänderung einen Rabatt | automatisch |
Cache-Treffer sind ohne zusätzliche Verdrahtung beobachtbar: Die Gemini-Integration erfasst cachedContentTokenCount bei jeder Antwort.
context_cache_configundcache_capablesind experimentell und sollten nicht gesetzt werden. Sie steuerncachedContentsAPI von Gemini aus dem Runner. Dieser API erfordert, dass der Cachesystem_instruction,toolsundtool_configersetzt — das Senden eines Caches zusammen mit einem dieser Felder wird mitINVALID_ARGUMENTabgelehnt. Der Runner wählt einen Cache aus, bevor der Agent seine Tools auflöst, und kann diese Anfrage daher nicht zusammenstellen. Außerdem erzeugt die Aktivierung dieser Felder derzeit keine Cache-Treffer. Garantiertes Caching für Gemini (statt Best-Effort-Caching) gehört in die Modellintegration, ebenso wie die entsprechende Umsetzung bei den anderen Anbietern.
Agenten ausführen
Einen Agenten mit Benutzereingabe ausführen:
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),
}
}
Komfortmethode für Zeichenfolgen
Für einfache Aufrufstellen akzeptiert run_str() einfache &str-Argumente und übernimmt die Umwandlung in den Newtype-Typ intern:
let mut stream = runner.run_str(
"user-123",
"session-456",
Content::new("user").with_text("Hello!"),
).await?;
Wenn die Zeichenfolge die Validierung nicht besteht (leer ist, Nullbytes enthält oder das Längenlimit überschreitet), gibt run_str() einen Fehler zurück, bevor die Agentenschleife gestartet wird. Die vorhandene Methode run() mit typisierten UserId/SessionId bleibt unverändert.
Unterbrechung und Laufisolierung
Ein Lauf wird registriert, sobald run() seinen Stream zurückgibt, und deregistriert, wenn
dieser Stream verworfen wird — auch wenn er verworfen wird, ohne jemals abgefragt worden zu sein.
| Methode | Geltungsbereich |
|---|---|
interrupt(session_id) | Bricht jeden laufenden Durchlauf für diese Sitzungs-ID über Apps und Benutzer hinweg ab |
interrupt_identity(app_name, user_id, session_id) | Bricht Durchläufe für eine exakt übereinstimmende Identität ab |
active_runs() | Die Identität jedes laufenden Durchlaufs; eine wiederholte Identität bedeutet parallele Durchläufe |
active_session_ids() | Deduplizierte Sitzungs-IDs laufender Durchläufe |
// Cancel one tenant's run without touching another that shares the session ID
let cancelled = runner.interrupt_identity("my-app", "user-1", "session-1");
Eine Sitzungs-ID ist nur innerhalb einer App und eines Benutzers eindeutig, daher ist interrupt(session_id) die allgemeine Form und interrupt_identity die präzise Form. Bevorzugen Sie interrupt_identity, wenn ein einzelnes Runner mehr als eine App oder einen Benutzer bedient.
Ausführungen werden anhand einer eindeutigen Ausführungs-ID und nicht anhand der Sitzungs-ID verfolgt. Daher werden zwei Ausführungen für dieselbe Identität separat verfolgt, und jede meldet nur sich selbst ab.
Persistenz ist an die Identität gebunden
Jedes Ereignis, das der Runner persistent speichert — Benutzeraktionen, Modellantworten, Übertragungsereignisse, Plug-in-Ereignisse und Komprimierungsereignisse — wird über SessionService::append_event_for_identity mit dem vollständigen (app_name, user_id, session_id)-Tripel geschrieben. Ein SessionService, dessen natürlicher Schlüssel zusammengesetzt ist, kann daher jedes Ereignis an seinen Mandanten binden und den Pfad append_event mit der rohen Sitzungs-ID vollständig ablehnen oder ignorieren.
Ausführungsablauf
┌─────────────────────────────────────────────────────────────┐
│ 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
Der Kontext, der Agenten während der Ausführung bereitgestellt wird:
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
Ausführungsoptionen:
pub struct RunConfig {
/// Streaming mode for responses
pub streaming_mode: StreamingMode,
// ... other fields (tool_confirmation_decisions, cached_content, etc.)
}
ToolExecutionStrategy
Steuert, wie mehrere Tool-Aufrufe aus einer einzelnen LLM-Antwort verteilt werden:
| Strategie | Verhalten |
|---|---|
Sequential (Standard) | Tools nacheinander in der von LLM zurückgegebenen Reihenfolge ausführen |
Parallel | Alle Tools gleichzeitig ausführen; der Aufrufer ist für die Sicherheit verantwortlich |
Auto | Die sichere schreibgeschützte Teilmenge gleichzeitig ausführen, anschließend alle verbleibenden Aufrufe sequenziell |
Pro Agent festgelegt über 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()?;
Im Modus Auto fragt die Dispatch-Schleife sowohl is_read_only() als auch is_concurrency_safe() ab. Aufrufe, deren ausgewählte Tools für beide Methoden true zurückgeben, werden zuerst gleichzeitig ausgeführt; alle verbleibenden Aufrufe werden anschließend sequenziell ausgeführt. Parallel umgeht diese Metadatenprüfungen als explizite Überschreibung durch den Aufrufer. Die Ergebnisse werden unabhängig von der Strategie immer in der ursprünglichen, von LLM zurückgegebenen Reihenfolge wieder zusammengesetzt. Fehlgeschlagene Tools erzeugen eine JSON-Fehlerantwort, ohne den Batch abzubrechen.
pub enum StreamingMode {
/// No streaming, return complete response
None,
/// Server-Sent Events (default)
SSE,
/// Bidirectional streaming (realtime)
Bidi,
}
Agent-Übergaben
Der Runner verarbeitet Übergaben zwischen mehreren Agents automatisch:
// 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()
});
}
Der Runner wird:
- Die Übergabeanforderung im Ereignis erkennen
- Den Ziel-Agenten in sub_agents finden
- Den Sitzungsstatus mit dem neuen aktiven Agenten aktualisieren
- Die Ausführung mit dem neuen Agenten fortsetzen
Kontextkomprimierung
Aktivieren Sie für Sitzungen mit langer Laufzeit die automatische Kontextkomprimierung, um das Kontextfenster von LLM begrenzt zu halten:
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),
}),
// ...
};
Wenn die Komprimierung ausgelöst wird, werden ältere Ereignisse durch ein Zusammenfassungsereignis ersetzt. conversation_history() verwendet automatisch die Zusammenfassung anstelle der ursprünglichen Ereignisse.
Siehe Kontextkomprimierung für die vollständige Dokumentation.
Integration mit Launcher
Der Launcher verwendet intern Runner:
// 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()?;
Benutzerdefinierte Runner-Verwendung
Für fortgeschrittene Szenarien verwenden Sie Runner direkt:
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)
}
Zurück: ← Core Types | Weiter: Launcher →