रनर

adk-runner से निष्पादन runtime जो agent निष्पादन को orchestrate करता है।

अवलोकन

Runner agent निष्पादन के पूर्ण lifecycle को प्रबंधित करता है:

  • Session प्रबंधन (sessions बनाना/पुनः प्राप्त करना)
  • Memory injection (संबंधित memories को खोजना और inject करना)
  • Artifact handling (scoped artifact access)
  • Event streaming (events को process और forward करना)
  • Agent transfers (multi-agent handoffs को संभालना)
Rendering architecture…

स्थापना

[dependencies]
adk-runner = "2.0.0"

RunnerConfig

आवश्यक services के साथ runner को configure करें:

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

Runner बनाने के लिए typestate builder का उपयोग करें। Builder compile time पर आवश्यक fields को enforce करता है और सभी वैकल्पिक fields के लिए default देता है, इसलिए future releases में नए fields जोड़ने से आपका 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()?;

Builder को तीन fields की आवश्यकता होती है: app_name, agent, और session_service। बाकी सब कुछ वैकल्पिक है और उनके लिए उपयुक्त defaults हैं। build() method केवल तब उपलब्ध होती है जब सभी तीन आवश्यक fields set हो जाते हैं — एक भी missing होने पर compile-time error होता है, runtime error नहीं।

कॉन्फ़िगरेशन फ़ील्ड्स

फ़ील्डप्रकारआवश्यकविवरण
app_nameStringहाँअनुप्रयोग पहचानकर्ता
agentArc<dyn Agent>हाँनिष्पादित करने के लिए मूल एजेंट
session_serviceArc<dyn SessionService>हाँसत्र भंडारण बैकएंड
artifact_serviceOption<Arc<dyn ArtifactService>>नहींआर्टिफैक्ट भंडारण
memory_serviceOption<Arc<dyn Memory>>नहींदीर्घकालिक स्मृति
plugin_managerOption<Arc<PluginManager>>नहींप्लगइन जीवनचक्र हुक्स
compaction_configOption<EventsCompactionConfig>नहींसंदर्भ संपीड़न सेटिंग्स
run_configOption<RunConfig>नहींनिष्पादन विकल्प
context_cache_configOption<ContextCacheConfig>नहींरनर-स्तरीय संदर्भ कैश जीवनचक्र (प्रायोगिक — नीचे देखें)
cache_capableOption<Arc<dyn CacheCapable>>नहींकैश-सक्षम मॉडल संदर्भ (प्रायोगिक — नीचे देखें)
request_contextOption<RequestContext>Noप्रमाणीकरण middleware context
cancellation_tokenOption<CancellationToken>Noसहकारी रद्दीकरण

प्रॉम्प्ट कैशिंग

कैशिंग प्रदाता-स्तरीय चिंता है और इसके लिए किसी Runner कॉन्फ़िगरेशन की आवश्यकता नहीं होती। प्रत्येक प्रदाता इंटीग्रेशन इसे वहीं संभालता है जहाँ request को assembled किया जाता है:

प्रदातातंत्रडिफ़ॉल्ट
Anthropic / Bedrockcache_control ब्रेकपॉइंट्सon (AnthropicConfig::prompt_caching, with_prompt_caching(false) के साथ opt out)
OpenAIserver-side प्रॉम्प्ट कैशिंग, retention के लिए PromptCacheRetentionस्वचालित
Gemini2.5/3.x पर निहित कैशिंग — एक साझा प्रीफ़िक्स बिना किसी कोड परिवर्तन के छूट दिलाता हैस्वचालित

किसी अतिरिक्त वायरिंग के बिना कैश हिट्स देखी जा सकती हैं: Gemini इंटीग्रेशन प्रत्येक response पर cachedContentTokenCount रिकॉर्ड करता है।

context_cache_config और cache_capable प्रयोगात्मक हैं और इन्हें unset ही छोड़ना चाहिए। ये Runner से Gemini के explicit cachedContents API को संचालित करते हैं। वह API cache से अपेक्षा करता है कि वह system_instruction, tools, और tool_config को replace करे — इनमें से किसी के साथ cache भेजना INVALID_ARGUMENT के साथ अस्वीकार कर दिया जाता है। Runner agent के अपने tools resolve करने से पहले एक cache चुनता है, इसलिए वह उस request को assemble नहीं कर सकता, और इन fields को enable करने से वर्तमान में cache hits उत्पन्न नहीं होते। Gemini के लिए guaranteed (best-effort के बजाय) caching model integration में होनी चाहिए, उसी तरह जैसे अन्य providers इसे करते हैं।

Agents चलाना

user input के साथ एक agent execute करें:

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

String सुविधा विधि

simple call sites के लिए, run_str() plain &str arguments स्वीकार करता है और newtype conversion को आंतरिक रूप से संभालता है:

let mut stream = runner.run_str(
    "user-123",
    "session-456",
    Content::new("user").with_text("Hello!"),
).await?;

यदि string validation में विफल होती है (empty, null bytes शामिल हैं, या length limit से अधिक है), तो run_str() agent loop शुरू करने से पहले एक error लौटाता है। typed UserId/SessionId के साथ मौजूदा run() विधि अपरिवर्तित रहती है।

Interruption और Run Isolation

जैसे ही run() अपनी stream लौटाता है, एक run registered हो जाता है, और जब वह stream dropped होती है तब deregister हो जाता है — इसमें वह स्थिति भी शामिल है जब उसे कभी poll किए बिना drop कर दिया जाता है।

विधिदायरा
interrupt(session_id)उस session ID के लिए, apps और users के across हर in-flight run को cancel करता है
interrupt_identity(app_name, user_id, session_id)एक exact identity के लिए runs को cancel करता है
active_runs()प्रत्येक in-flight run की पहचान; एक दोहराई गई पहचान का अर्थ concurrent runs है
active_session_ids()in-flight runs के deduplicated session IDs
// Cancel one tenant's run without touching another that shares the session ID
let cancelled = runner.interrupt_identity("my-app", "user-1", "session-1");

एक session ID केवल किसी app और user के भीतर ही unique होता है, इसलिए interrupt(session_id) व्यापक रूप है और interrupt_identity सटीक रूप है। जब एक single Runner एक से अधिक app या user की सेवा करता हो, तब interrupt_identity को प्राथमिकता दें।

Runs को session ID के बजाय एक unique run ID द्वारा tracked किया जाता है, इसलिए same identity के लिए दो runs अलग-अलग tracked होते हैं और हर एक केवल स्वयं को deregister करता है।

Persistence Is Identity-Bound

Runner द्वारा persist किया गया हर event — user turns, model responses, transfer events, plugin events, और compaction events — SessionService::append_event_for_identity के माध्यम से full (app_name, user_id, session_id) triple के साथ लिखा जाता है। इसलिए SessionService, जिसका natural key composite है, हर event को उसके tenant से bind कर सकता है, और raw-session-ID append_event path को पूरी तरह reject या ignore कर सकता है।

Execution Flow

┌─────────────────────────────────────────────────────────────┐
│                     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

Execution के दौरान agents को प्रदान किया गया context:

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

Execution options:

pub struct RunConfig {
    /// Streaming mode for responses
    pub streaming_mode: StreamingMode,
    // ... other fields (tool_confirmation_decisions, cached_content, etc.)
}

ToolExecutionStrategy

यह नियंत्रित करता है कि एक single LLM response से आने वाली multiple tool calls कैसे dispatched होती हैं:

रणनीतिव्यवहार
Sequential (डिफ़ॉल्ट)LLM-द्वारा लौटाए गए क्रम में उपकरणों को एक-एक करके निष्पादित करें
Parallelसभी उपकरणों को एक साथ निष्पादित करें; सुरक्षा की ज़िम्मेदारी caller की है
Autoसुरक्षित केवल-पठन उपसमूह को समानांतर रूप से निष्पादित करें, फिर शेष सभी कॉल क्रमिक रूप से

प्रति-एजेंट सेट करें 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()?;

Auto मोड में, dispatch loop दोनों is_read_only() और is_concurrency_safe() को क्वेरी करता है। जिन कॉल्स के चयनित tools दोनों methods के लिए true लौटाते हैं, वे पहले समानांतर रूप से चलती हैं; फिर शेष सभी कॉल्स क्रम से चलती हैं। Parallel इन metadata checks को एक स्पष्ट caller override के रूप में बायपास करता है। Results हमेशा मूल LLM-returned क्रम में, strategy की परवाह किए बिना, फिर से assembled किए जाते हैं। Failed tools batch को abort किए बिना एक JSON error response उत्पन्न करते हैं।

pub enum StreamingMode {
    /// No streaming, return complete response
    None,
    /// Server-Sent Events (default)
    SSE,
    /// Bidirectional streaming (realtime)
    Bidi,
}

Agent Transfers

Runner multi-agent transfers को स्वचालित रूप से संभालता है:

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

Runner यह करेगा:

  1. event में transfer request का पता लगाएगा
  2. sub_agents में target agent ढूंढेगा
  3. session state को नए active agent के साथ अपडेट करेगा
  4. नए agent के साथ execution जारी रखेगा

Context Compaction

लंबे समय तक चलने वाली sessions के लिए, LLM context window को bounded रखने के लिए automatic context compaction सक्षम करें:

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),
    }),
    // ...
};

जब compaction trigger होता है, तो पुराने events को एक summary event से बदल दिया जाता है। conversation_history() automatically original events के बजाय summary का उपयोग करता है।

पूर्ण documentation के लिए Context Compaction देखें।

Integration with Launcher

Launcher आंतरिक रूप से 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()?;

Custom Runner Usage

उन्नत scenarios के लिए, 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)
}

Previous: ← Core Types | Next: Launcher →

रनर - ADK-Rust दस्तावेज़ीकरण | ADK-Rust