रनर
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 को संभालना)
स्थापना
[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)?;
RunnerConfigBuilder (अनुशंसित)
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_name | String | हाँ | अनुप्रयोग पहचानकर्ता |
agent | Arc<dyn Agent> | हाँ | निष्पादित करने के लिए मूल एजेंट |
session_service | Arc<dyn SessionService> | हाँ | सत्र भंडारण बैकएंड |
artifact_service | Option<Arc<dyn ArtifactService>> | नहीं | आर्टिफैक्ट भंडारण |
memory_service | Option<Arc<dyn Memory>> | नहीं | दीर्घकालिक स्मृति |
plugin_manager | Option<Arc<PluginManager>> | नहीं | प्लगइन जीवनचक्र हुक्स |
compaction_config | Option<EventsCompactionConfig> | नहीं | संदर्भ संपीड़न सेटिंग्स |
run_config | Option<RunConfig> | नहीं | निष्पादन विकल्प |
context_cache_config | Option<ContextCacheConfig> | नहीं | रनर-स्तरीय संदर्भ कैश जीवनचक्र (प्रायोगिक — नीचे देखें) |
cache_capable | Option<Arc<dyn CacheCapable>> | नहीं | कैश-सक्षम मॉडल संदर्भ (प्रायोगिक — नीचे देखें) |
request_context | Option<RequestContext> | No | प्रमाणीकरण middleware context |
cancellation_token | Option<CancellationToken> | No | सहकारी रद्दीकरण |
प्रॉम्प्ट कैशिंग
कैशिंग प्रदाता-स्तरीय चिंता है और इसके लिए किसी Runner कॉन्फ़िगरेशन की आवश्यकता नहीं होती। प्रत्येक प्रदाता इंटीग्रेशन इसे वहीं संभालता है जहाँ request को assembled किया जाता है:
| प्रदाता | तंत्र | डिफ़ॉल्ट |
|---|---|---|
| Anthropic / Bedrock | cache_control ब्रेकपॉइंट्स | on (AnthropicConfig::prompt_caching, with_prompt_caching(false) के साथ opt out) |
| OpenAI | server-side प्रॉम्प्ट कैशिंग, retention के लिए PromptCacheRetention | स्वचालित |
| Gemini | 2.5/3.x पर निहित कैशिंग — एक साझा प्रीफ़िक्स बिना किसी कोड परिवर्तन के छूट दिलाता है | स्वचालित |
किसी अतिरिक्त वायरिंग के बिना कैश हिट्स देखी जा सकती हैं: Gemini इंटीग्रेशन प्रत्येक response पर cachedContentTokenCount रिकॉर्ड करता है।
context_cache_configऔरcache_capableप्रयोगात्मक हैं और इन्हें unset ही छोड़ना चाहिए। ये Runner से Gemini के explicitcachedContentsAPI को संचालित करते हैं। वह 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 यह करेगा:
- event में transfer request का पता लगाएगा
- sub_agents में target agent ढूंढेगा
- session state को नए active agent के साथ अपडेट करेगा
- नए 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 →