ADK-Rust के साथ मल्टी-एजेंट सिस्टम बनाना
Rust की टाइप सुरक्षा और प्रदर्शन के साथ विभिन्न मल्टी-एजेंट पैटर्न—सरल समन्वय से लेकर जटिल ग्राफ़-आधारित ऑर्केस्ट्रेशन तक—कब और कैसे उपयोग करें, जानें।
1. परिचय
समस्या: AI जो एक दीवार से टकराता है
आपने अपना पहला AI एजेंट बनाया है। यह प्रभावशाली है—यह सवालों के जवाब दे सकता है, दस्तावेज़ों का सारांश बना सकता है, शायद कोड भी लिख सकता है। लेकिन फिर वास्तविकता सामने आती है:
"क्या आप मेरा पिछला चालान देख सकते हैं और मुझे API सेट करने में भी मदद कर सकते हैं?"
आपका एजेंट संघर्ष करता है। इसे बिलिंग सिस्टम और डेवलपर डॉक्स और समस्या निवारण वर्कफ़्लो पर प्रशिक्षित नहीं किया गया था। इसका निर्देश प्रॉम्प्ट पहले से ही 2000 टोकन का है जो सब कुछ कवर करने की कोशिश कर रहा है। प्रतिक्रिया की गुणवत्ता कम हो जाती है।
यह एकल-एजेंट की सीमा है। जैसे-जैसे आपका एप्लिकेशन बढ़ता है, आपको दर्दनाक समझौतों का सामना करना पड़ता है:
- फूले हुए प्रॉम्प्ट: हर नई क्षमता का मतलब है लंबे निर्देश, अधिक विलंबता, और मॉडल के लिए अधिक भ्रम
- हरफनमौला: एक एजेंट जो बिलिंग, समर्थन और बिक्री को संभालता है, तीनों में औसत दर्जे का हो जाता है
- असंभव रखरखाव: बिलिंग लॉजिक बदलने से आपके समर्थन प्रवाह के टूटने का जोखिम नहीं होना चाहिए
- कोई विशेषज्ञता नहीं: आपका गणित एजेंट कैलकुलेटर टूल नहीं रख सकता जबकि आपका शोध एजेंट वेब खोज रखता है—वे सब कुछ साझा करते हैं
समाधान: विशेषज्ञ एजेंट एक साथ काम कर रहे हैं
बहु-एजेंट सिस्टम जटिल कार्यों को विशेष भूमिकाओं में विभाजित करके इसे हल करते हैं। एक अभिभूत सामान्यवादी के बजाय, आप केंद्रित विशेषज्ञ बनाते हैं:
- ग्राहक सेवा: एक coordinator उपयोगकर्ताओं को बिलिंग, तकनीकी सहायता, या बिक्री विशेषज्ञों के पास भेजता है—प्रत्येक केंद्रित प्रशिक्षण और उपकरणों के साथ
- सामग्री निर्माण: एक शोध एजेंट तथ्य एकत्र करता है, एक writer कथा तैयार करता है, एक संपादक उसे निखारता है—प्रत्येक एजेंट एक कौशल में महारत हासिल करता है
- कोड जनरेशन: एक योजनाकार वास्तुकला डिज़ाइन करता है, एक coder लागू करता है, एक समीक्षक बग पकड़ता है—विभिन्न दृष्टिकोण गुणवत्ता में सुधार करते हैं
परिणाम? प्रत्येक एजेंट केंद्रित रहता है, प्रॉम्प्ट प्रबंधनीय रहते हैं, और आप समर्थन को छुए बिना बिलिंग लॉजिक को अपडेट कर सकते हैं। यह AI के लिए माइक्रोसर्विसेज है।
आप क्या सीखेंगे
ADK-Rust मल्टी-एजेंट ऑर्केस्ट्रेशन के लिए तीन उत्तरोत्तर शक्तिशाली पैटर्न प्रदान करता है। यह ट्यूटोरियल आपको सिखाएगा:
- अपनी आवश्यकताओं के आधार पर प्रत्येक पैटर्न का उपयोग कब करें
- उत्पादन-तैयार Rust कोड के साथ उन्हें कैसे लागू करें
- आपके विशिष्ट उपयोग के मामले के लिए वास्तुशिल्प ट्रेडऑफ़ क्यों मायने रखते हैं
2. सही पैटर्न चुनना
कोड में गोता लगाने से पहले, आइए समझते हैं कि प्रत्येक पैटर्न क्या प्रदान करता है:
| पैटर्न | इसके लिए सबसे अच्छा | नियंत्रण स्तर | जटिलता |
|---|---|---|---|
| समन्वयक | बातचीत सौंपना | LLM तय करता है | कम |
| AgentTool | प्रतिक्रिया प्रसंस्करण | समन्वयक प्रक्रियाएँ | मध्यम |
| पर्यवेक्षक ग्राफ | जटिल वर्कफ़्लो | पूर्ण स्थिति प्रबंधन | उच्च |
3. पैटर्न 1: समन्वयक (उप-एजेंट)
🎯 उपयोग का मामला: ग्राहक सेवा रूटिंग
आप एक ग्राहक सेवा बॉट बना रहे हैं। उपयोगकर्ता बिलिंग के बारे में पूछ सकते हैं, तकनीकी सहायता का अनुरोध कर सकते हैं, या नई सुविधाओं के बारे में पूछताछ कर सकते हैं। प्रत्येक डोमेन को विशेष ज्ञान की आवश्यकता होती है, लेकिन उपयोगकर्ताओं को यह जानने की आवश्यकता नहीं होनी चाहिए कि किस विभाग से संपर्क करना है।
समन्वयक पैटर्न स्वचालित एजेंट स्थानांतरण का उपयोग करता है। जब आप .sub_agent() के माध्यम से उप-एजेंट जोड़ते हैं, तो ADK-Rust एक transfer_to_agent टूल इंजेक्ट करता है। LLM बातचीत के आधार पर यह तय करता है कि कब सौंपना है।
मुख्य विशेषताएँ
- निर्बाध हस्तांतरण: उपयोगकर्ता स्वाभाविक रूप से विशेषज्ञ के साथ जारी रहता है
- LLM-संचालित रूटिंग: coordinator बातचीत के संदर्भ के आधार पर निर्णय लेता है
- बातचीत की निरंतरता: सत्र इतिहास स्थानांतरणों में बनाए रखा जाता है
- कोई प्रतिक्रिया प्रसंस्करण नहीं: विशेषज्ञ स्थानांतरण के बाद सीधे उपयोगकर्ता से बात करता है
कार्यान्वयन
use adk_rust::prelude::*;
use std::sync::Arc;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let api_key = std::env::var("GOOGLE_API_KEY")?;
let model = Arc::new(GeminiModel::new(&api_key, "gemini-2.0-flash")?);
// Specialist: Billing Agent
// Clear description helps the coordinator know when to transfer
let billing_agent = LlmAgentBuilder::new("billing_agent")
.description(
"Handles all billing questions: invoices, payments, refunds, subscription plans, and account charges. Transfer here for any money-related questions."
)
.instruction(
"You are a billing specialist. Answer questions about invoices, payments, and subscription plans. Be concise and accurate. If asked about technical issues, suggest transferring to support."
)
.model(model.clone())
.build()?;
// Specialist: Technical Support Agent
let support_agent = LlmAgentBuilder::new("support_agent")
.description(
"Provides technical support: troubleshooting, bug reports, feature questions, and integration help. Transfer here for any technical problems."
)
.instruction(
"You are a technical support specialist. Help users troubleshoot issues step by step. Ask clarifying questions when needed. Be patient and thorough."
)
.model(model.clone())
.build()?;
// Coordinator: Routes to specialists
let coordinator = LlmAgentBuilder::new("coordinator")
.description("Customer service coordinator")
.instruction(
"You are a friendly customer service coordinator. Your job is to:\n 1. Greet users warmly\n 2. Understand their needs\n 3. Route to the right specialist:\n - Billing questions → transfer to billing_agent\n - Technical issues → transfer to support_agent\n 4. Handle general questions yourself\n\n Always explain who you're connecting them with."
)
.model(model.clone())
.sub_agent(Arc::new(billing_agent))
.sub_agent(Arc::new(support_agent))
.build()?;
// Run with the built-in launcher
Launcher::new(Arc::new(coordinator))
.run()
.await?;
Ok(())
}उदाहरण बातचीत
उपयोगकर्ता: नमस्ते, मेरे बिल के बारे में एक सवाल है
[coordinator]: नमस्ते! मुझे आपके बिलिंग प्रश्न में मदद करने में खुशी होगी। मुझे आपको हमारे बिलिंग विशेषज्ञ से जोड़ने दें जो आपकी सहायता कर सकते हैं।
सिस्टम: 🔄 को हस्तांतरित करें: billing_agent
[billing_agent]: नमस्ते! मैं बिलिंग विशेषज्ञ हूँ। मैं चालान, भुगतान और सदस्यता संबंधी प्रश्नों में मदद कर सकता हूँ। आप अपने बिल के बारे में क्या जानना चाहेंगे?
उपयोगकर्ता: मुझे इस महीने दो बार शुल्क क्यों लिया गया?
[billing_agent]: मैं आपके लिए उस दोहरे शुल्क की जाँच करूँगा...
✅ समन्वयक का उपयोग कब करें
- उपयोगकर्ता को सीधे विशेषज्ञों के साथ बातचीत करनी चाहिए
- रूटिंग के निर्णय सीधे होते हैं
- आपको विशेषज्ञ के जवाबों को संसाधित करने की आवश्यकता नहीं है
- बातचीत का प्रवाह रैखिक होता है (एक समय में एक विशेषज्ञ)
4. पैटर्न 2: उपकरण के रूप में एजेंट (AgentTool)
🎯 उपयोग का मामला: ज्ञान एकत्रीकरण
आप एक स्मार्ट सहायक बना रहे हैं जो कई डोमेन में फैले प्रश्नों का उत्तर देता है। एक उपयोगकर्ता पूछता है "250 का 15% कितना है, और इतिहास में यह संख्या क्यों महत्वपूर्ण है?" आपको एक गणित विशेषज्ञ को बुलाना होगा, फिर एक सामान्य ज्ञान विशेषज्ञ को, और उनके उत्तरों को संयोजित करना होगा।
AgentTool पैटर्न एजेंटों को कॉल करने योग्य उपकरणों के रूप में लपेटता है। उप-एजेंटों के विपरीत, coordinator विशेषज्ञों को प्रोग्रामेटिक रूप से आमंत्रित करता है और उपयोगकर्ता को जवाब देने से पहले उनके जवाबों को संसाधित या संयोजित करने के लिए प्राप्त करता है।
समन्वयक से मुख्य अंतर
समन्वयक (उप-एजेंट)
- • विशेषज्ञ सीधे उपयोगकर्ता से बात करता है
- • एक समय में एक विशेषज्ञ
- • कोई प्रतिक्रिया प्रसंस्करण नहीं
AgentTool
- • समन्वयक प्रतिक्रियाएँ प्राप्त करता है
- • कई विशेषज्ञों को बुला सकता है
- • एकत्रित करता है और सारांशित करता है
कार्यान्वयन
use adk_agent::LlmAgentBuilder;
use adk_tool::{AgentTool, FunctionTool};
use adk_core::ToolContext;
use serde_json::{json, Value};
use std::sync::Arc;
// Calculator tool for the math agent
async fn calculator(
_ctx: Arc<dyn ToolContext>,
args: Value
) -> Result<Value, adk_core::AdkError> {
let operation = args["operation"].as_str().unwrap_or("add");
let a = args["a"].as_f64().unwrap_or(0.0);
let b = args["b"].as_f64().unwrap_or(0.0);
let result = match operation {
"add" => a + b,
"multiply" => a * b,
"percent" => a * (b / 100.0),
_ => return Err(adk_core::AdkError::Tool(
format!("Unknown operation: {}", operation)
)),
};
Ok(json!({ "result": result }))
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let api_key = std::env::var("GOOGLE_API_KEY")?;
let model = Arc::new(GeminiModel::new(&api_key, "gemini-2.5-flash")?);
// Create calculator tool
let calc_tool = FunctionTool::new(
"calculator",
"Performs arithmetic: add, multiply, percent. Args: operation (string), a (number), b (number)",
calculator,
);
// Math Expert agent - has its own tools
let math_agent = LlmAgentBuilder::new("math_expert")
.description(
"A math expert that performs calculations. Use for any math-related questions, percentages, or numerical analysis."
)
.instruction(
"You are a math expert. Use the calculator tool for calculations. Show your work step by step. Be precise."
)
.model(model.clone())
.tool(Arc::new(calc_tool))
.build()?;
// Trivia Expert agent - uses LLM knowledge
let trivia_agent = LlmAgentBuilder::new("trivia_expert")
.description(
"A trivia and history expert. Use for questions about historical facts, pop culture, science facts, and trivia."
)
.instruction(
"You are a trivia expert with vast knowledge across domains. Answer questions accurately and include interesting related facts."
)
.model(model.clone())
.build()?;
// Wrap agents as tools with configuration
let math_tool = AgentTool::new(Arc::new(math_agent))
.skip_summarization(false) // Summarize lengthy responses
.forward_artifacts(true); // Pass through any generated files
let trivia_tool = AgentTool::new(Arc::new(trivia_agent))
.skip_summarization(false);
// Coordinator uses agents as tools
let coordinator = LlmAgentBuilder::new("coordinator")
.description("Smart assistant that combines expert knowledge")
.instruction(
"You are a helpful assistant with access to expert agents:\n - math_expert: For calculations and math problems\n - trivia_expert: For facts, history, and trivia\n\n When questions span multiple domains, call multiple experts and synthesize their responses into a cohesive answer."
)
.model(model)
.tool(Arc::new(math_tool))
.tool(Arc::new(trivia_tool))
.build()?;
Launcher::new(Arc::new(coordinator)).run().await?;
Ok(())
}उदाहरण: बहु-डोमेन प्रश्न
उपयोगकर्ता: 250 का 15% कितना है, और क्या वह संख्या इतिहास में महत्वपूर्ण है?
सिस्टम: // समन्वयक math_expert उपकरण को कॉल करता है
[math_expert जवाब देता है]: 250 का 15% 37.5 है
सिस्टम: // समन्वयक trivia_expert टूल को कॉल करता है
[trivia_expert जवाब देता है]: 37 और 38 ऐतिहासिक रूप से कम उल्लेखनीय हैं, लेकिन 37.5°C मानव शरीर का तापमान है...
सिस्टम: // समन्वयक संश्लेषण करता है
[coordinator]: 250 का 15% 37.5 के बराबर होता है। दिलचस्प बात यह है कि 37.5°C (99.5°F) मानव शरीर के औसत तापमान 37°C के करीब है, जो इसे चिकित्सकीय रूप से एक महत्वपूर्ण संख्या बनाता है!
✅ AgentTool का उपयोग कब करें
- आपको कई विशेषज्ञों से प्रतिक्रियाओं को संयोजित करने की आवश्यकता है
- समन्वयक को विशेषज्ञ आउटपुट का सारांश या फ़िल्टर करना चाहिए
- विशेषज्ञों के पास अपने स्वयं के उपकरण (नेस्टेड क्षमताएं) होते हैं
- आप एजेंट आह्वान पर प्रोग्रामेटिक नियंत्रण चाहते हैं
5. पैटर्न 3: सुपरवाइजर ग्राफ
🎯 उपयोग का मामला: सामग्री निर्माण पाइपलाइन
आप एक सामग्री निर्माण प्रणाली बना रहे हैं। एक विषय दिए जाने पर, आपको यह करना होगा: (1) उस पर शोध करें, (2) एक लेख लिखें, (3) कोड उदाहरण जोड़ें। supervisor कार्य के आधार पर गतिशील रूप से क्रम तय करता है, और कार्यकर्ता संशोधनों के लिए वापस चक्र कर सकते हैं।
सुपरवाइजर ग्राफ पैटर्न ADK-Rust की ग्राफ-आधारित वर्कफ़्लो प्रणाली का उपयोग करता है। एक supervisor एजेंट पूर्ण स्थिति प्रबंधन और चक्रीय निष्पादन समर्थन के साथ श्रमिकों को गतिशील रूप से रूट करता है।
ग्राफ का उपयोग क्यों करें?
- गतिशील रूटिंग: सुपरवाइजर वर्तमान स्थिति के आधार पर अगले कार्यकर्ता का निर्णय लेता है
- चक्रीय निष्पादन: कार्यकर्ता पुनरावृत्तियों के लिए वापस लूप कर सकते हैं
- साझा स्थिति: सभी नोड एक सामान्य स्थिति ऑब्जेक्ट पर पढ़ते/लिखते हैं
- सशर्त किनारे: LLM निर्णयों के आधार पर विभिन्न पथ
- पुनरावृत्ति सीमाएँ: अनंत लूप को रोकें
कार्यान्वयन
use adk_agent::LlmAgentBuilder;
use adk_graph::{
StateGraph,
edge::{START, END},
node::{AgentNode, ExecutionConfig, NodeOutput},
state::State,
};
use adk_model::GeminiModel;
use serde_json::json;
use std::sync::Arc;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let api_key = std::env::var("GOOGLE_API_KEY")?;
let model = Arc::new(GeminiModel::new(&api_key, "gemini-2.0-flash")?);
// Supervisor: Decides which worker should act next
let supervisor = LlmAgentBuilder::new("supervisor")
.description("Routes tasks to specialized workers")
.instruction(
"You are a task supervisor. Based on the task and work done so far, decide who should work next.\n\n Workers available:\n - researcher: Gathers information and facts\n - writer: Writes content based on research\n - coder: Creates code examples\n\n Respond with ONLY one word: 'researcher', 'writer', 'coder', or 'done'."
)
.model(model.clone())
.build()?;
// Workers with specialized roles
let researcher = LlmAgentBuilder::new("researcher")
.instruction("Research the topic. Provide key facts as bullet points.")
.model(model.clone())
.build()?;
let writer = LlmAgentBuilder::new("writer")
.instruction("Write engaging content based on the research provided.")
.model(model.clone())
.build()?;
let coder = LlmAgentBuilder::new("coder")
.instruction("Write clean, documented code examples for the topic.")
.model(model.clone())
.build()?;
// Create AgentNodes with input/output mappers
let supervisor_node = AgentNode::new(Arc::new(supervisor))
.with_input_mapper(|state| {
let task = state.get("task").and_then(|v| v.as_str()).unwrap_or("");
let history = state.get("history")
.and_then(|v| v.as_array())
.map(|arr| arr.iter()
.filter_map(|h| h.get("agent").and_then(|a| a.as_str()))
.map(|s| format!("- {} completed", s))
.collect::<Vec<_>>()
.join("\n"))
.unwrap_or_default();
adk_core::Content::new("user").with_text(format!(
"Task: {}\n\nWork completed:\n{}\n\nWho next?",
task,
if history.is_empty() { "None yet" } else { &history }
))
})
.with_output_mapper(|events| {
let mut updates = std::collections::HashMap::new();
for event in events {
if let Some(content) = event.content() {
let text: String = content.parts.iter()
.filter_map(|p| p.text())
.collect();
let next = if text.to_lowercase().contains("researcher") {
"researcher"
} else if text.to_lowercase().contains("writer") {
"writer"
} else if text.to_lowercase().contains("coder") {
"coder"
} else {
"done"
};
updates.insert("next_agent".to_string(), json!(next));
}
}
updates
});
// Build the graph
let graph = StateGraph::with_channels(&[
"task", "next_agent", "history",
"research_output", "written_content", "code_output"
])
.add_node(supervisor_node)
.add_node(AgentNode::new(Arc::new(researcher)))
.add_node(AgentNode::new(Arc::new(writer)))
.add_node(AgentNode::new(Arc::new(coder)))
// Finalize node compiles all outputs
.add_node_fn("finalize", |ctx| async move {
let research = ctx.get("research_output").and_then(|v| v.as_str());
let content = ctx.get("written_content").and_then(|v| v.as_str());
let code = ctx.get("code_output").and_then(|v| v.as_str());
let result = format!(
"=== FINAL OUTPUT ===\n\n{}\n\n{}\n\n{}",
research.unwrap_or("No research"),
content.unwrap_or("No content"),
code.unwrap_or("No code")
);
Ok(NodeOutput::new().with_update("final_result", json!(result)))
})
// Graph structure
.add_edge(START, "supervisor")
.add_conditional_edges(
"supervisor",
|state| state.get("next_agent")
.and_then(|v| v.as_str())
.unwrap_or("done")
.to_string(),
[
("researcher", "researcher"),
("writer", "writer"),
("coder", "coder"),
("done", "finalize"),
],
)
// Workers cycle back to supervisor
.add_edge("researcher", "supervisor")
.add_edge("writer", "supervisor")
.add_edge("coder", "supervisor")
.add_edge("finalize", END)
.compile()?
.with_recursion_limit(15); // Prevent infinite loops
// Execute
let mut input = State::new();
input.insert("task".to_string(), json!("Create a guide about Rust error handling"));
input.insert("history".to_string(), json!([]));
let result = graph.invoke(input, ExecutionConfig::new("content-thread")).await?;
println!("{}", result.get("final_result").and_then(|v| v.as_str()).unwrap_or(""));
Ok(())
}✅ सुपरवाइजर ग्राफ का उपयोग कब करें
- वर्कफ़्लो क्रम गतिशील और LLM-निर्धारित होता है
- श्रमिकों को दोहराने या वापस जाने की आवश्यकता हो सकती है
- एजेंटों के बीच जटिल स्थिति साझा करने की आवश्यकता है
- आपको चेकपॉइंटिंग या फिर से शुरू किए जा सकने वाले वर्कफ़्लो की आवश्यकता है
- कार्य विघटन के लिए कई अनुक्रमिक चरणों की आवश्यकता होती है
6. पैटर्न तुलना
| विशेषता | समन्वयक | AgentTool | पर्यवेक्षक ग्राफ़ |
|---|---|---|---|
| उपयोगकर्ता बात करता है | विशेषज्ञ से सीधे | केवल समन्वयक | अंतिम आउटपुट |
| बहु-एजेंट कॉल | ❌ एक बार में एक | ✅ समानांतर संभव | ✅ व्यवस्थित |
| प्रतिक्रिया प्रसंस्करण | ❌ | ✅ | ✅ |
| चक्रीय वर्कफ़्लो | ❌ | ❌ | ✅ |
| साझा स्थिति | केवल सत्र | केवल सत्र | पूर्ण ग्राफ़ स्थिति |
| सेटअप जटिलता | 🟢 कम | 🟡 मध्यम | 🔴 उच्च |
7. निष्कर्ष
मल्टी-एजेंट सिस्टम आपको विशेष एजेंटों को मिलाकर परिष्कृत एआई एप्लिकेशन बनाने देते हैं। अपनी आवश्यकताओं के आधार पर अपना पैटर्न चुनें:
- समन्वयक: सेटअप करने में तेज़, ग्राहक सेवा रूटिंग के लिए बढ़िया
- AgentTool: जब आपको प्रतिक्रियाओं को संसाधित या संयोजित करने की आवश्यकता हो
- पर्यवेक्षक ग्राफ़: जटिल, गतिशील, बहु-चरणीय वर्कफ़्लो