मुख्य प्रकार

adk-core से मूलभूत प्रकार और traits जो ADK-Rust की नींव बनाते हैं।

Content और Part

ADK में प्रत्येक संदेश Content और Part के माध्यम से प्रवाहित होता है। Agent, Tool और callbacks के साथ काम करने के लिए इन प्रकारों को समझना आवश्यक है।

Content

Content एक बातचीत में एक एकल संदेश का प्रतिनिधित्व करता है। इसमें एक role (किसने भेजा) और एक या अधिक parts (वास्तविक सामग्री) होते हैं।

Roles:

  • "user" - उपयोगकर्ता से संदेश
  • "model" - AI मॉडल से प्रतिक्रियाएँ
  • "tool" - Tool निष्पादन के परिणाम
use adk_core::Content;

// Simple text message from user
let user_msg = Content::new("user")
    .with_text("What's the weather like?");

// Model response
let model_msg = Content::new("model")
    .with_text("The weather is sunny and 72°F.");

// Multiple text parts in one message
let detailed_msg = Content::new("user")
    .with_text("Here's my question:")
    .with_text("What is the capital of France?");

बहुमोडल Content:

Content में पाठ के साथ-साथ छवियां, ऑडियो, PDFs और अन्य बाइनरी डेटा भी शामिल हो सकते हैं:

// Image from bytes (e.g., read from file)
let image_bytes = std::fs::read("photo.jpg")?;
let content = Content::new("user")
    .with_text("What's in this image?")
    .with_inline_data("image/jpeg", image_bytes);

// Image from URL (model fetches it)
let content = Content::new("user")
    .with_text("Describe this image")
    .with_file_uri("image/png", "https://example.com/chart.png");

// PDF document
let pdf_bytes = std::fs::read("report.pdf")?;
let content = Content::new("user")
    .with_text("Summarize this document")
    .with_inline_data("application/pdf", pdf_bytes);

Part

Part एक enum है जो एक संदेश के भीतर विभिन्न प्रकार की सामग्री का प्रतिनिधित्व करता है:

pub enum Part {
    // Plain text
    Text { text: String },
    
    // Binary data embedded in the message
    InlineData { mime_type: String, data: Vec<u8> },
    
    // Reference to external file (URL or cloud storage)
    FileData { mime_type: String, file_uri: String },
    
    // Model requesting a tool call
    FunctionCall { name: String, args: Value, id: Option<String> },
    
    // Result of a tool execution
    FunctionResponse { function_response: FunctionResponseData, id: Option<String> },
}

सीधे Part बनाना:

use adk_core::Part;

// Text part
let text = Part::text_part("Hello, world!");

// Image from bytes
let image = Part::inline_data("image/png", png_bytes);

// Image from URL
let remote_image = Part::file_data("image/jpeg", "https://example.com/photo.jpg");

Parts का निरीक्षण करना:

// Get text content (returns None for non-text parts)
if let Some(text) = part.text() {
    println!("Text: {}", text);
}

// Get MIME type (for InlineData and FileData)
if let Some(mime) = part.mime_type() {
    println!("MIME type: {}", mime);
}

// Get file URI (for FileData only)
if let Some(uri) = part.file_uri() {
    println!("File URI: {}", uri);
}

// Check if part contains media (image, audio, video, etc.)
if part.is_media() {
    println!("This part contains binary media");
}

Parts पर Iterate करना:

for part in &content.parts {
    match part {
        Part::Text { text } => println!("Text: {}", text),
        Part::InlineData { mime_type, data } => {
            println!("Binary data: {} ({} bytes)", mime_type, data.len());
        }
        Part::FileData { mime_type, file_uri } => {
            println!("File: {} at {}", mime_type, file_uri);
        }
        Part::FunctionCall { name, args, .. } => {
            println!("Tool call: {}({})", name, args);
        }
        Part::FunctionResponse { function_response, .. } => {
            println!("Tool result: {} -> {}", 
                function_response.name, 
                function_response.response
            );
        }
    }
}

एजेंट ट्रेट

Agent trait ADK में सभी agents के लिए मुख्य एब्स्ट्रैक्शन है। हर agent प्रकार—एलएलएम एजेंट, वर्कफ़्लो एजेंट, कस्टम एजेंट—इस trait को इम्प्लीमेंट करता है।

#[async_trait]
pub trait Agent: Send + Sync {
    /// Unique identifier for this agent
    fn name(&self) -> &str;
    
    /// Human-readable description of what this agent does
    fn description(&self) -> &str;
    
    /// Child agents (for workflow agents like Sequential, Parallel)
    fn sub_agents(&self) -> &[Arc<dyn Agent>];
    
    /// Execute the agent and return a stream of events
    async fn run(&self, ctx: Arc<dyn InvocationContext>) -> Result<EventStream>;
}

मुख्य बातें:

  • name(): लॉगिंग, ट्रांसफर और पहचान के लिए उपयोग किया जाता है। एक मल्टी-एजेंट सिस्टम में अद्वितीय होना चाहिए।
  • description(): LLM को तब दिखाया जाता है जब agent का उपयोग टूल के रूप में या रूटिंग निर्णयों के लिए किया जाता है।
  • sub_agents(): चाइल्ड एजेंट लौटाता है। लीफ एजेंट (LlmAgent) के लिए खाली होता है, कंटेनर (SequentialAgent, ParallelAgent) के लिए पॉपुलेट किया जाता है।
  • run(): मुख्य निष्पादन विधि। संदर्भ प्राप्त करता है और इवेंट की एक स्ट्रीम लौटाता है।

EventStream क्यों?

Agents एक single response के बजाय EventStream (Result<Event> की एक स्ट्रीम) लौटाते हैं क्योंकि:

  1. स्ट्रीमिंग: बेहतर UX के लिए प्रतिक्रियाओं को टोकन-बाय-टोकन स्ट्रीम किया जा सकता है
  2. टूल कॉल: निष्पादन के दौरान कई टूल कॉल और प्रतिक्रियाएं होती हैं
  3. स्टेट परिवर्तन: स्टेट अपडेट्स को इवेंट के रूप में एमिट किया जाता है
  4. स्थानांतरण: Agent transfers को इवेंट के माध्यम से संकेतित किया जाता है

टूल ट्रेट

टूल बातचीत से परे Agent की क्षमताओं का विस्तार करते हैं। वे Agent को API, डेटाबेस, फाइल सिस्टम के साथ इंटरैक्ट करने या गणनाएं करने की सुविधा देते हैं।

#[async_trait]
pub trait Tool: Send + Sync {
    /// Tool name (used in function calls from the model)
    fn name(&self) -> &str;
    
    /// Description shown to the LLM to help it decide when to use this tool
    fn description(&self) -> &str;
    
    /// JSON Schema defining the expected parameters
    fn parameters_schema(&self) -> Option<Value> { None }
    
    /// JSON Schema defining the response format
    fn response_schema(&self) -> Option<Value> { None }
    
    /// Whether this tool runs asynchronously (returns task ID immediately)
    fn is_long_running(&self) -> bool { false }
    
    /// Execute the tool with given arguments
    async fn execute(&self, ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value>;
}

मुख्य बिंदु:

  • name(): यह वह फ़ंक्शन नाम है जिसका उपयोग मॉडल इस टूल को कॉल करने के लिए करता है। इसे छोटा और वर्णनात्मक रखें (उदाहरण के लिए, get_weather, search_database)।
  • description(): यह मॉडल के लिए महत्वपूर्ण है कि वह यह समझे कि इस टूल का उपयोग कब करना है। यह क्या करता है और इसका उपयोग कब करना है, इसके बारे में विशिष्ट रहें।
  • parameters_schema(): JSON Schema जो मॉडल को बताता है कि कौन से तर्क (arguments) प्रदान करने हैं। इसके बिना, मॉडल अनुमान लगाता है।
  • execute(): पार्स किए गए तर्कों को serde_json::Value के रूप में प्राप्त करता है। परिणाम को JSON के रूप में लौटाएँ।

एक कस्टम टूल लागू करना:

use adk_core::{Tool, ToolContext, Result};
use async_trait::async_trait;
use serde_json::{json, Value};
use std::sync::Arc;

struct WeatherTool {
    api_key: String,
}

#[async_trait]
impl Tool for WeatherTool {
    fn name(&self) -> &str { 
        "get_weather" 
    }
    
    fn description(&self) -> &str { 
        "Get current weather for a city. Use this when the user asks about weather conditions."
    }
    
    fn parameters_schema(&self) -> Option<Value> {
        Some(json!({
            "type": "object",
            "properties": {
                "city": { 
                    "type": "string",
                    "description": "City name (e.g., 'London', 'New York')"
                },
                "units": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "description": "Temperature units"
                }
            },
            "required": ["city"]
        }))
    }
    
    async fn execute(&self, _ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
        let city = args["city"].as_str().unwrap_or("Unknown");
        let units = args["units"].as_str().unwrap_or("celsius");
        
        // Call weather API here...
        
        Ok(json!({
            "city": city,
            "temperature": 22,
            "units": units,
            "condition": "sunny"
        }))
    }
}

Long-Running Tools:

उन ऑपरेशनों के लिए जिनमें लंबा समय लगता है (फाइल प्रोसेसिंग, बाहरी API कॉल), टूल को long-running के रूप में चिह्नित करें:

fn is_long_running(&self) -> bool { true }

Long-running tools तुरंत एक कार्य ID लौटाते हैं। मॉडल को निर्देश दिया जाता है कि जब तक यह लंबित है, तब तक टूल को फिर से कॉल न करे।


Toolset ट्रेट

Toolset संदर्भ के आधार पर गतिशील रूप से उपकरण प्रदान करता है। इसका उपयोग तब करें जब:

  • उपकरण उपयोगकर्ता अनुमतियों पर निर्भर करते हैं
  • उपकरण बाहरी स्रोतों (MCP सर्वर) से लोड होते हैं
  • निष्पादन के दौरान उपकरण की उपलब्धता बदल जाती है
#[async_trait]
pub trait Toolset: Send + Sync {
    /// Toolset पहचानकर्ता
    fn name(&self) -> &str;
    
    /// वर्तमान संदर्भ के लिए उपलब्ध उपकरण लौटाएं
    async fn tools(&self, ctx: Arc<dyn ReadonlyContext>) -> Result<Vec<Arc<dyn Tool>>>;
}

उदाहरण: अनुमति-आधारित Toolset:

struct AdminToolset {
    admin_tools: Vec<Arc<dyn Tool>>,
    user_tools: Vec<Arc<dyn Tool>>,
}

#[async_trait]
impl Toolset for AdminToolset {
    fn name(&self) -> &str { "admin_toolset" }
    
    async fn tools(&self, ctx: Arc<dyn ReadonlyContext>) -> Result<Vec<Arc<dyn Tool>>> {
        if ctx.user_id().starts_with("admin_") {
            Ok(self.admin_tools.clone())
        } else {
            Ok(self.user_tools.clone())
        }
    }
}

Context ट्रेट

Contexts निष्पादन के दौरान Agent और Tool को जानकारी और सेवाएँ प्रदान करते हैं। Context ट्रेट का एक पदानुक्रम है, जिसमें से प्रत्येक अधिक क्षमताएँ जोड़ता है।

ReadonlyContext

हर जगह उपलब्ध बुनियादी जानकारी:

pub trait ReadonlyContext: Send + Sync {
    /// इस इनवोकेशन के लिए अद्वितीय ID
    fn invocation_id(&self) -> &str;
    
    /// वर्तमान में निष्पादित हो रहे Agent का नाम
    fn agent_name(&self) -> &str;
    
    /// उपयोगकर्ता पहचानकर्ता
    fn user_id(&self) -> &str;
    
    /// एप्लिकेशन का नाम
    fn app_name(&self) -> &str;
    
    /// सेशन पहचानकर्ता
    fn session_id(&self) -> &str;
    
    /// उपयोगकर्ता का इनपुट जिसने इस इनवोकेशन को ट्रिगर किया
    fn user_content(&self) -> &Content;
}

CallbackContext

Artifact तक पहुँच जोड़ता है (ReadonlyContext का विस्तार करता है):

pub trait CallbackContext: ReadonlyContext {
    /// Artifact स्टोरेज तक पहुँच (यदि कॉन्फ़िगर किया गया हो)
    fn artifacts(&self) -> Option<Arc<dyn Artifacts>>;
}

ToolContext

Tool निष्पादन के लिए (CallbackContext का विस्तार करता है):

pub trait ToolContext: CallbackContext {
    /// फंक्शन कॉल की ID जिसने इस Tool को ट्रिगर किया
    fn function_call_id(&self) -> &str;
    
    /// वर्तमान इवेंट एक्शन प्राप्त करें (जैसे, transfer, escalate, आदि)
    fn actions(&self) -> EventActions;
    
    /// इवेंट एक्शन सेट करें (जैसे, transfer ट्रिगर करना)
    fn set_actions(&self, actions: EventActions);
    
    /// लंबी अवधि की memory खोजें
    async fn search_memory(&self, query: &str) -> Result<Vec<MemoryEntry>>;
}

InvocationContext

Agent निष्पादन के लिए पूर्ण संदर्भ (CallbackContext का विस्तार करता है):

pub trait InvocationContext: CallbackContext {
    /// निष्पादित किया जा रहा Agent
    fn agent(&self) -> Arc<dyn Agent>;
    
    /// Memory सेवा (यदि कॉन्फ़िगर की गई हो)
    fn memory(&self) -> Option<Arc<dyn Memory>>;
    
    /// स्थिति और इतिहास के साथ वर्तमान Session
    fn session(&self) -> &dyn Session;
    
    /// निष्पादन कॉन्फ़िगरेशन
    fn run_config(&self) -> &RunConfig;
    
    /// संकेत दें कि यह इनवोकेशन समाप्त होना चाहिए
    fn end_invocation(&self);
    
    /// जांचें कि क्या इनवोकेशन समाप्त हो गया है
    fn ended(&self) -> bool;
}

सेशन और स्टेट

सेशन वार्तालापों को ट्रैक करते हैं। स्टेट सेशन के भीतर डेटा संग्रहीत करता है।

सेशन

pub trait Session: Send + Sync {
    /// अद्वितीय सेशन पहचानकर्ता
    fn id(&self) -> &str;
    
    /// वह एप्लिकेशन जिससे यह सेशन संबंधित है
    fn app_name(&self) -> &str;
    
    /// वह उपयोगकर्ता जिसके पास यह सेशन है
    fn user_id(&self) -> &str;
    
    /// परिवर्तनीय स्टेट स्टोरेज
    fn state(&self) -> &dyn State;
    
    /// इस वार्तालाप में पिछले संदेश
    fn conversation_history(&self) -> Vec<Content>;
}

स्टेट

स्कोप किए गए उपसर्गों के साथ कुंजी-मान स्टोरेज:

pub trait State: Send + Sync {
    /// कुंजी द्वारा एक मान प्राप्त करें
    fn get(&self, key: &str) -> Option<Value>;
    
    /// एक मान सेट करें
    fn set(&mut self, key: String, value: Value);
    
    /// सभी कुंजी-मान युग्म प्राप्त करें
    fn all(&self) -> HashMap<String, Value>;
}

स्टेट उपसर्ग:

स्टेट कुंजियाँ स्कोप और स्थायित्व को नियंत्रित करने के लिए उपसर्गों का उपयोग करती हैं:

उपसर्गस्कोपस्थायित्वउपयोग का मामला
user:उपयोगकर्ता-स्तरसभी सेशनों मेंउपयोगकर्ता प्राथमिकताएँ, सेटिंग्स
app:एप्लिकेशन-स्तरएप्लिकेशन-व्यापीसाझा कॉन्फ़िगरेशन
temp:टर्न-स्तरप्रत्येक टर्न पर साफ़ किया गयाअस्थायी गणना डेटा
(कोई नहीं)सेशन-स्तरकेवल यह सेशनवार्तालाप संदर्भ
// एक कॉलबैक या टूल में
let state = ctx.session().state();

// उपयोगकर्ता प्राथमिकता (सभी सेशनों में बनी रहती है)
state.set("user:theme".into(), json!("dark"));

// सेशन-विशिष्ट डेटा
state.set("current_topic".into(), json!("weather"));

// अस्थायी डेटा (इस टर्न के बाद साफ़ किया गया)
state.set("temp:step_count".into(), json!(1));

// मान पढ़ें
if let Some(theme) = state.get("user:theme") {
    println!("Theme: {}", theme);
}

त्रुटि प्रबंधन

ADK सभी ऑपरेशनों के लिए एक एकीकृत त्रुटि प्रकार का उपयोग करता है:

pub enum AdkError {
    Agent(String),      // Agent निष्पादन त्रुटियाँ
    Tool(String),       // Tool निष्पादन त्रुटियाँ
    Model(String),      // LLM API त्रुटियाँ
    Session(String),    // Session स्टोरेज त्रुटियाँ
    Artifact(String),   // Artifact स्टोरेज त्रुटियाँ
    Config(String),     // कॉन्फ़िगरेशन त्रुटियाँ
    Io(std::io::Error), // फ़ाइल/नेटवर्क I/O त्रुटियाँ
    Json(serde_json::Error), // JSON पार्सिंग त्रुटियाँ
}

pub type Result<T> = std::result::Result<T, AdkError>;

Tools में त्रुटि प्रबंधन:

async fn execute(&self, ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
    let city = args["city"]
        .as_str()
        .ok_or_else(|| AdkError::Tool("Missing 'city' parameter".into()))?;
    
    let response = reqwest::get(&format!("https://api.weather.com/{}", city))
        .await
        .map_err(|e| AdkError::Tool(format!("API error: {}", e)))?;
    
    Ok(json!({ "weather": "sunny" }))
}

इवेंटस्ट्रीम

Agent एक एकल प्रतिक्रिया के बजाय घटनाओं की एक स्ट्रीम लौटाते हैं:

pub type EventStream = Pin<Box<dyn Stream<Item = Result<Event>> + Send>;

इवेंट्स को प्रोसेस करना:

use futures::StreamExt;

let mut stream = agent.run(ctx).await?;

while let Some(result) = stream.next().await {
    match result {
        Ok(event) => {
            // टेक्स्ट कंटेंट की जाँच करें
            if let Some(content) = event.content() {
                for part in &content.parts {
                    if let Some(text) = part.text() {
                        print!("{}", text);
                    }
                }
            }
            
            // स्टेट परिवर्तनों की जाँच करें
            for (key, value) in event.state_delta() {
                println!("State changed: {} = {}", key, value);
            }
            
            // जाँच करें कि क्या यह अंतिम प्रतिक्रिया है
            if event.is_final_response() {
                println!("\n[Done]");
            }
        }
        Err(e) => {
            eprintln!("Error: {}", e);
            break;
        }
    }
}

Llm ट्रेट

वह ट्रेट जिसे सभी Llm प्रदाता लागू करते हैं:

#[async_trait]
pub trait Llm: Send + Sync {
    /// Model identifier (e.g., "gemini-2.0-flash", "gpt-4o")
    fn name(&self) -> &str;
    
    /// Generate content (streaming or non-streaming)
    async fn generate_content(
        &self,
        request: LlmRequest,
        stream: bool,
    ) -> Result<LlmResponseStream>;
}

LlmRequest:

pub struct LlmRequest {
    pub contents: Vec<Content>,           // Conversation history
    pub tools: Vec<ToolDeclaration>,      // Available tools
    pub system_instruction: Option<String>, // System prompt
    pub config: GenerateContentConfig,    // Temperature, max_tokens, etc.
}

LlmResponse:

pub struct LlmResponse {
    pub content: Option<Content>,         // Generated content
    pub finish_reason: Option<FinishReason>, // Why generation stopped
    pub usage: Option<UsageMetadata>,     // Token counts
    pub partial: bool,                    // Is this a streaming chunk?
    pub turn_complete: bool,              // Is the turn finished?
}

सभी प्रदाता (Gemini, OpenAI, Anthropic, Ollama, आदि) इस ट्रेट को लागू करते हैं, जिससे वे एक-दूसरे के लिए बदली जा सकने वाली बन जाती हैं:

// Switch providers by changing one line
let model: Arc<dyn Llm> = Arc::new(GeminiModel::new(&key, "gemini-2.0-flash")?);
// let model: Arc<dyn Llm> = Arc::new(OpenAIClient::new(config)?);
// let model: Arc<dyn Llm> = Arc::new(AnthropicClient::new(config)?);

let agent = LlmAgentBuilder::new("assistant")
    .model(model)
    .build()?;

पिछला: ← Introduction | अगला: Runner →