विकास दिशानिर्देश
यह दस्तावेज़ adk-rust में योगदान करने वाले डेवलपर्स के लिए व्यापक दिशानिर्देश प्रदान करता है। इन मानकों का पालन करने से पूरे प्रोजेक्ट में कोड की गुणवत्ता, निरंतरता और रखरखाव सुनिश्चित होता है।
विषय-सूची
- शुरू करना
- प्रोजेक्ट संरचना
- कोड शैली
- त्रुटि प्रबंधन
- एसिंक्रोनस पैटर्न
- परीक्षण
- दस्तावेज़
- पुल रिक्वेस्ट प्रक्रिया
- सामान्य कार्य
शुरू करना
पूर्वापेक्षाएँ
- Rust: 1.75 या उच्चतर (
rustc --versionसे जांचें) - Cargo: नवीनतम स्टेबल
- Git: संस्करण नियंत्रण के लिए
अपना वातावरण स्थापित करना
# Clone the repository
git clone https://github.com/zavora-ai/adk-rust.git
cd adk-rust
# Build the project
cargo build
# Run all tests
cargo test --all
# Check for lints
cargo clippy --all-targets --all-features
# Format code
cargo fmt --all
वातावरण चर
एपीआई कुंजी की आवश्यकता वाले उदाहरणों और परीक्षणों को चलाने के लिए:
# Gemini (default provider)
export GOOGLE_API_KEY="your-api-key"
# OpenAI (optional)
export OPENAI_API_KEY="your-api-key"
# Anthropic (optional)
export ANTHROPIC_API_KEY="your-api-key"
प्रोजेक्ट संरचना
adk-rust कई क्रेट के साथ एक Cargo कार्यस्थान के रूप में व्यवस्थित है:
adk-rust/
├── adk-core/ # मूलभूत ट्रेट्स और प्रकार (Agent, Tool, Llm, Event)
├── adk-telemetry/ # OpenTelemetry एकीकरण
├── adk-model/ # LLM प्रदाता (GeminiModel, OpenAIClient, AnthropicClient)
├── adk-tool/ # Tool सिस्टम (FunctionTool, MCP, AgentTool)
├── adk-session/ # Session प्रबंधन (इन-मेमोरी, SQLite)
├── adk-artifact/ # बाइनरी आर्टिफैक्ट स्टोरेज
├── adk-memory/ # खोज के साथ दीर्घकालिक मेमोरी
├── adk-agent/ # Agent इम्प्लीमेंटेशन (LlmAgent, वर्कफ़्लो एजेंट)
├── adk-runner/ # निष्पादन रनटाइम
├── adk-server/ # REST API और A2A प्रोटोकॉल
├── adk-cli/ # कमांड-लाइन लॉन्चर
├── adk-realtime/ # आवाज/ऑडियो स्ट्रीमिंग एजेंट
├── adk-graph/ # LangGraph-शैली के वर्कफ़्लो
├── adk-browser/ # ब्राउज़र ऑटोमेशन टूल
├── adk-eval/ # Agent मूल्यांकन फ्रेमवर्क
├── adk-rust/ # अंब्रेला क्रेट (सभी को पुनः निर्यात करता है)
└── examples/ # कार्यशील उदाहरण
क्रेट निर्भरताएँ
क्रेट्स को निर्भरता क्रम में प्रकाशित किया जाना चाहिए:
adk-core(कोई आंतरिक निर्भरता नहीं)adk-telemetryadk-modeladk-tooladk-sessionadk-artifactadk-memoryadk-agentadk-runneradk-serveradk-cliadk-realtimeadk-graphadk-browseradk-evaladk-rust(अंब्रेला)
कोड शैली
सामान्य सिद्धांत
- स्पष्टता चतुराई से बेहतर: ऐसा कोड लिखें जिसे पढ़ना और समझना आसान हो
- स्पष्ट अंतर्निहित से बेहतर: स्पष्ट प्रकारों और त्रुटि प्रबंधन को प्राथमिकता दें
- छोटे फ़ंक्शन: फ़ंक्शन को केंद्रित रखें और जब संभव हो तो 50 लाइनों से कम रखें
- अर्थपूर्ण नाम: वर्णनात्मक वेरिएबल और फ़ंक्शन नामों का उपयोग करें
फ़ॉर्मेटिंग
डिफ़ॉल्ट सेटिंग्स के साथ rustfmt का उपयोग करें:
cargo fmt --all
CI पाइपलाइन फ़ॉर्मेटिंग को लागू करती है। कमिट करने से पहले हमेशा cargo fmt चलाएँ।
नामकरण परंपराएँ
| प्रकार | परंपरा | उदाहरण |
|---|---|---|
| Crates | adk-* (kebab-case) | adk-core, adk-agent |
| Modules | snake_case | llm_agent, function_tool |
| Types/Traits | PascalCase | LlmAgent, ToolContext |
| Functions | snake_case | execute_tool, run_agent |
| Constants | SCREAMING_SNAKE_CASE | KEY_PREFIX_APP |
| Type parameters | Single uppercase or PascalCase | T, State |
इंपोर्ट
इंपोर्ट को इस क्रम में व्यवस्थित करें:
// 1. मानक लाइब्रेरी
use std::collections::HashMap;
use std::sync::Arc;
// 2. बाहरी क्रेट्स
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use tokio::sync::RwLock;
// 3. आंतरिक क्रेट्स (adk-*)
use adk_core::{Agent, Event, Result};
// 4. स्थानीय मॉड्यूल
use crate::config::Config;
use super::utils;
Clippy
सभी कोड को बिना किसी चेतावनी के clippy पास करना चाहिए:
cargo clippy --all-targets --all-features
clippy चेतावनियों को दबाने के बजाय उन्हें संबोधित करें। यदि दमन आवश्यक है, तो कारण दस्तावेज़ करें:
#[allow(clippy::too_many_arguments)]
// बिल्डर पैटर्न में कई पैरामीटर की आवश्यकता होती है; रीफैक्टरिंग से उपयोगिता कम हो जाएगी
fn complex_builder(...) { }
त्रुटि प्रबंधन
adk_core::AdkError का उपयोग करें
सभी त्रुटियों को केंद्रीकृत त्रुटि प्रकार का उपयोग करना चाहिए:
use adk_core::{AdkError, Result};
// Result<T> (Result<T, AdkError> का उपनाम) लौटाएं
pub async fn my_function() -> Result<String> {
// प्रसार के लिए ? का उपयोग करें
let data = fetch_data().await?;
// उपयुक्त वेरिएंट के साथ त्रुटियां बनाएं
if data.is_empty() {
return Err(AdkError::Tool("No data found".into()));
}
Ok(data)
}
त्रुटि वेरिएंट
उपयुक्त त्रुटि वेरिएंट का उपयोग करें:
| वेरिएंट | उपयोग का मामला |
|---|---|
AdkError::Agent(String) | एजेंट निष्पादन त्रुटियां |
AdkError::Model(String) | LLM प्रदाता त्रुटियां |
AdkError::Tool(String) | टूल निष्पादन त्रुटियां |
AdkError::Session(String) | सेशन प्रबंधन त्रुटियां |
AdkError::Artifact(String) | आर्टिफैक्ट स्टोरेज त्रुटियां |
AdkError::Config(String) | कॉन्फ़िगरेशन त्रुटियां |
AdkError::Network(String) | HTTP/नेटवर्क त्रुटियां |
त्रुटि संदेश
स्पष्ट, कार्यवाही योग्य त्रुटि संदेश लिखें:
// अच्छा: विशिष्ट और कार्यवाही योग्य
Err(AdkError::Config("API key not found. Set GOOGLE_API_KEY environment variable.".into()))
// बुरा: अस्पष्ट
Err(AdkError::Config("Invalid config".into()))
एसिंक्रोनस पैटर्न
Tokio का उपयोग करें
सभी async कोड Tokio runtime का उपयोग करते हैं:
use tokio::sync::{Mutex, RwLock};
// Prefer RwLock for read-heavy data
let state: Arc<RwLock<State>> = Arc::new(RwLock::new(State::default()));
// Use Mutex for write-heavy or simple cases
let counter: Arc<Mutex<u32>> = Arc::new(Mutex::new(0));
एसिंक्रोनस ट्रेड्स
async trait मेथड्स के लिए async_trait का उपयोग करें:
use async_trait::async_trait;
#[async_trait]
pub trait MyTrait: Send + Sync {
async fn do_work(&self) -> Result<()>;
}
स्ट्रीमिंग
स्ट्रीमिंग रिस्पॉन्स के लिए EventStream का उपयोग करें:
use adk_core::EventStream;
use async_stream::stream;
use futures::Stream;
fn create_stream() -> EventStream {
let s = stream! {
yield Ok(Event::new("inv-1"));
yield Ok(Event::new("inv-2"));
};
Box::pin(s)
}
थ्रेड सुरक्षा
सभी सार्वजनिक प्रकार Send + Sync होने चाहिए:
// Good: Thread-safe
pub struct MyAgent {
name: String,
tools: Vec<Arc<dyn Tool>>, // Arc for shared ownership
}
// Verify with compile-time checks
fn assert_send_sync<T: Send + Sync>() {}
fn _check() {
assert_send_sync::<MyAgent>();
}
परीक्षण
परीक्षण संगठन
crate/
├── src/
│ ├── lib.rs # फाइल के निचले भाग में यूनिट परीक्षण
│ └── module.rs # मॉड्यूल-विशिष्ट परीक्षण
└── tests/
└── integration.rs # एकीकरण परीक्षण
यूनिट परीक्षण
यूनिट परीक्षण को कोड वाली उसी फाइल में रखें:
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(2, 3), 5);
}
#[tokio::test]
async fn test_async_function() {
let result = async_function().await;
assert!(result.is_ok());
}
}
एकीकरण परीक्षण
tests/ डायरेक्टरी में रखें:
// tests/integration_test.rs
use adk_core::*;
#[tokio::test]
async fn test_full_workflow() {
// Setup
let service = InMemorySessionService::new();
// Execute
let session = service.create(request).await.unwrap();
// Assert
assert_eq!(session.id(), "test-session");
}
मॉक परीक्षण
API कॉल्स के बिना परीक्षण के लिए MockLlm का उपयोग करें:
use adk_model::MockLlm;
#[tokio::test]
async fn test_agent_with_mock() {
let mock = MockLlm::new(vec![
"First response".to_string(),
"Second response".to_string(),
]);
let agent = LlmAgentBuilder::new("test")
.model(Arc::new(mock))
.build()
.unwrap();
// Test agent behavior
}
परीक्षण कमांड
# सभी परीक्षण चलाएँ
cargo test --all
# विशिष्ट क्रेट परीक्षण चलाएँ
cargo test --package adk-core
# आउटपुट के साथ चलाएँ
cargo test --all -- --nocapture
# अनदेखे परीक्षण चलाएँ (API कुंजियों की आवश्यकता है)
cargo test --all -- --ignored
डॉक्यूमेंटेशन
डॉक टिप्पणियाँ
सार्वजनिक आइटमों के लिए /// का उपयोग करें:
/// Creates a new LLM agent with the specified configuration.
///
/// # Arguments
///
/// * `name` - इस एजेंट के लिए एक अद्वितीय पहचानकर्ता
/// * `model` - तर्क के लिए उपयोग करने वाला LLM प्रोवाइडर
///
/// # Examples
///
/// ```rust
/// use adk_agent::LlmAgentBuilder;
///
/// let agent = LlmAgentBuilder::new("assistant")
/// .model(Arc::new(model))
/// .build()?;
/// ```
///
/// # Errors
///
/// यदि मॉडल सेट नहीं है तो `AdkError::Agent` रिटर्न करता है।
pub fn new(name: impl Into<String>) -> Self {
// ...
}
मॉड्यूल डॉक्यूमेंटेशन
lib.rs के शीर्ष पर मॉड्यूल-स्तरीय डॉक्स जोड़ें:
//! # adk-core
//!
//! ADK-Rust के लिए मुख्य प्रकार और ट्रेड्स।
//!
//! ## अवलोकन
//!
//! यह क्रेट मूलभूत प्रकार प्रदान करता है...
README फाइलें
प्रत्येक क्रेट में README.md होना चाहिए जिसमें ये शामिल हों:
- संक्षिप्त विवरण
- इंस्टॉलेशन निर्देश
- त्वरित उदाहरण
- पूर्ण डॉक्यूमेंटेशन का लिंक
डॉक्यूमेंटेशन परीक्षण
सुनिश्चित करें कि डॉक उदाहरण संकलित होते हैं:
cargo test --doc --all
पुल रिक्वेस्ट प्रक्रिया
सबमिट करने से पहले
-
पूरी टेस्ट सूट चलाएँ:
cargo test --all -
क्लिप्पी चलाएँ:
cargo clippy --all-targets --all-features -
कोड फॉर्मेट करें:
cargo fmt --all -
यदि सार्वजनिक API जोड़ रहे/बदल रहे हैं तो डॉक्यूमेंटेशन अपडेट करें
-
नई कार्यक्षमता के लिए टेस्ट जोड़ें
PR दिशानिर्देश
- शीर्षक: परिवर्तन का स्पष्ट, संक्षिप्त विवरण
- विवरण: बताएं कि क्या और क्यों (कैसे नहीं)
- आकार: PR को केंद्रित रखें; बड़े परिवर्तनों को विभाजित करें
- टेस्ट: नई कार्यक्षमता के लिए टेस्ट शामिल करें
- ब्रेकिंग परिवर्तन: विवरण में स्पष्ट रूप से डॉक्यूमेंट करें
कमिट संदेश
पारंपरिक कमिट का पालन करें:
feat: add OpenAI streaming support
fix: correct tool parameter validation
docs: update quickstart guide
refactor: simplify session state management
test: add integration tests for A2A protocol
सामान्य कार्य
एक नया Tool जोड़ना
-
Tool बनाएँ:
use adk_core::{Tool, ToolContext, Result}; use async_trait::async_trait; use serde_json::Value; pub struct MyTool { // fields } #[async_trait] impl Tool for MyTool { fn name(&self) -> &str { "my_tool" } fn description(&self) -> &str { "Does something useful" } fn parameters_schema(&self) -> Option<Value> { Some(serde_json::json!({ "type": "object", "properties": { "input": { "type": "string" } }, "required": ["input"] })) } async fn execute(&self, ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> { let input = args["input"].as_str().unwrap_or_default(); Ok(serde_json::json!({ "result": input })) } } -
Agent में जोड़ें:
let agent = LlmAgentBuilder::new("agent") .model(model) .tool(Arc::new(MyTool::new())) .build()?;
एक नया मॉडल प्रोवाइडर जोड़ना
-
adk-model/src/में मॉड्यूल बनाएँ:// adk-model/src/mymodel/mod.rs mod client; pub use client::MyModelClient; -
Llm ट्रेट को इम्प्लीमेंट करें:
use adk_core::{Llm, LlmRequest, LlmResponse, LlmResponseStream, Result}; pub struct MyModelClient { api_key: String, } #[async_trait] impl Llm for MyModelClient { fn name(&self) -> &str { "my-model" } async fn generate_content( &self, request: LlmRequest, stream: bool, ) -> Result<LlmResponseStream> { // Implementation } } -
adk-model/Cargo.tomlमें फीचर फ्लैग जोड़ें:[features] mymodel = ["dep:mymodel-sdk"] -
शर्त के अनुसार एक्सपोर्ट करें:
#[cfg(feature = "mymodel")] pub mod mymodel; #[cfg(feature = "mymodel")] pub use mymodel::MyModelClient;
एक नया Agent प्रकार जोड़ना
-
adk-agent/src/में मॉड्यूल बनाएँ:// adk-agent/src/my_agent.rs use adk_core::{Agent, EventStream, InvocationContext, Result}; use async_trait::async_trait; pub struct MyAgent { name: String, } #[async_trait] impl Agent for MyAgent { fn name(&self) -> &str { &self.name } fn description(&self) -> &str { "My custom agent" } async fn run(&self, ctx: Arc<dyn InvocationContext>) -> Result<EventStream> { // Implementation } } -
adk-agent/src/lib.rsमें एक्सपोर्ट करें:mod my_agent; pub use my_agent::MyAgent;
डिबगिंग युक्तियाँ
-
ट्रेसिंग सक्षम करें:
adk_telemetry::init_telemetry(); -
इवेंट्स का निरीक्षण करें:
while let Some(event) = stream.next().await { eprintln!("Event: {:?}", event); } -
RUST_LOG का उपयोग करें:
RUST_LOG=debug cargo run --example myexample
पिछला: ← एक्सेस कंट्रोल
प्रश्न? GitHub पर एक इश्यू खोलें।