استدعاءات
توفر الاستدعاءات في ADK-Rust نقاط ربط لمراقبة سلوك الـ Agent وتخصيصه والتحكم فيه عند نقاط التنفيذ الرئيسية. إنها تمكّن من التسجيل، وحواجز الأمان، والتخزين المؤقت، وتعديل الاستجابة، والمزيد.
نظرة عامة
يدعم ADK-Rust ستة أنواع من الاستدعاءات التي تعترض مراحل مختلفة من تنفيذ الـ Agent:
| نوع الاستدعاء | متى يتم التنفيذ | حالات الاستخدام |
|---|---|---|
before_agent | قبل أن يبدأ الـ Agent المعالجة | التحقق من المدخلات، التسجيل، الإنهاء المبكر |
after_agent | بعد أن يكمل الـ Agent | تعديل الاستجابة، التسجيل، التنظيف |
before_model | قبل استدعاء الـ LLM | تعديل الطلب، التخزين المؤقت، تحديد المعدل |
after_model | بعد استجابة الـ LLM | تصفية الاستجابة، التسجيل، التخزين المؤقت |
before_tool | قبل تنفيذ الـ Tool | فحوصات الأذونات، التحقق من المعلمات |
after_tool | بعد تنفيذ الـ Tool | تعديل النتيجة، التسجيل |
أنواع الاستدعاءات
استدعاءات الـ Agent
تغلف استدعاءات الـ Agent دورة تنفيذ الـ Agent بأكملها.
use adk_rust::prelude::*;
use std::sync::Arc;
// توقيع نوع الـ BeforeAgentCallback
type BeforeAgentCallback = Box<
dyn Fn(Arc<dyn CallbackContext>)
-> Pin<Box<dyn Future<Output = Result<Option<Content>>> + Send>>
+ Send + Sync
>;
// توقيع نوع الـ AfterAgentCallback
type AfterAgentCallback = Box<
dyn Fn(Arc<dyn CallbackContext>)
-> Pin<Box<dyn Future<Output = Result<Option<Content>>> + Send>>
+ Send + Sync
>;
استدعاءات النموذج
تعترض استدعاءات النموذج طلبات واستجابات الـ LLM.
use adk_rust::prelude::*;
use std::sync::Arc;
// BeforeModelResult - يتحكم فيما يحدث بعد الاستدعاء
pub enum BeforeModelResult {
Continue(LlmRequest), // المتابعة مع الطلب (المعدّل ربما)
Skip(LlmResponse), // تخطي استدعاء النموذج، واستخدام هذه الاستجابة بدلاً من ذلك
}
// BeforeModelCallback - يمكنه تعديل الطلب أو تخطي استدعاء النموذج
type BeforeModelCallback = Box<
dyn Fn(Arc<dyn CallbackContext>, LlmRequest)
-> Pin<Box<dyn Future<Output = Result<BeforeModelResult>> + Send>>
+ Send + Sync
>;
// AfterModelCallback - يمكنه تعديل الاستجابة
type AfterModelCallback = Box<
dyn Fn(Arc<dyn CallbackContext>, LlmResponse)
-> Pin<Box<dyn Future<Output = Result<Option<LlmResponse>>> + Send>>
+ Send + Sync
>;
استدعاءات الـ Tool
تعترض استدعاءات الـ Tool تنفيذ الـ Tool.
use adk_rust::prelude::*;
use std::sync::Arc;
// BeforeToolCallback - يمكنه تخطي الـ Tool عن طريق إرجاع Some(Content)
type BeforeToolCallback = Box<
dyn Fn(Arc<dyn CallbackContext>)
-> Pin<Box<dyn Future<Output = Result<Option<Content>>> + Send>>
+ Send + Sync
>;
// AfterToolCallback - يمكنه تعديل نتيجة الـ Tool
type AfterToolCallback = Box<
dyn Fn(Arc<dyn CallbackContext>)
-> Pin<Box<dyn Future<Output = Result<Option<Content>>> + Send>>
+ Send + Sync
>;
دلالات قيمة الإرجاع
تستخدم Callbacks قيم إرجاع مختلفة للتحكم في تدفق التنفيذ:
Callbacks لـ Agent/Tool
| قيمة الإرجاع | التأثير |
|---|---|
Ok(None) | متابعة التنفيذ الطبيعي |
Ok(Some(content)) | تجاوز/تخطي بالمحتوى المقدم |
Err(e) | إجهاض التنفيذ مع خطأ |
Callbacks للنماذج (Model Callbacks)
يستخدم BeforeModelCallback BeforeModelResult:
| قيمة الإرجاع | التأثير |
|---|---|
Ok(BeforeModelResult::Continue(request)) | المتابعة مع الطلب (المعدّل المحتمل) |
Ok(BeforeModelResult::Skip(response)) | تخطي استدعاء النموذج، واستخدام هذا الاستجابة بدلاً من ذلك |
Err(e) | إجهاض التنفيذ مع خطأ |
يستخدم AfterModelCallback Option<LlmResponse>:
| قيمة الإرجاع | التأثير |
|---|---|
Ok(None) | الاحتفاظ بالاستجابة الأصلية |
Ok(Some(response)) | الاستبدال بالاستجابة المعدّلة |
Err(e) | إجهاض التنفيذ مع خطأ |
ملخص
- قبل Callbacks لـ Agent/Tool: أرجع
Noneللمتابعة،Some(content)للتخطي - قبل Callback للنموذج (Before model callback): أرجع
Continue(request)للمتابعة،Skip(response)لتجاوز النموذج - بعد Callbacks (After callbacks): أرجع
Noneللاحتفاظ بالأصل،Some(...)للاستبدال
إضافة Callbacks إلى Agents
تتم إضافة Callbacks إلى Agents باستخدام LlmAgentBuilder:
use adk_rust::prelude::*;
use std::sync::Arc;
#[tokio::main]
async fn main() -> std::result::Result<(), Box<dyn std::error::Error>> {
let api_key = std::env::var("GOOGLE_API_KEY")?;
let model = Arc::new(GeminiModel::new(&api_key, "gemini-2.5-flash")?);
let agent = LlmAgentBuilder::new("my_agent")
.model(model)
.instruction("You are a helpful assistant.")
// Add before_agent callback
.before_callback(Box::new(|ctx| {
Box::pin(async move {
println!("Agent starting: {}", ctx.agent_name());
Ok(None) // Continue execution
})
}))
// Add after_agent callback
.after_callback(Box::new(|ctx| {
Box::pin(async move {
println!("Agent completed: {}", ctx.agent_name());
Ok(None) // Keep original result
})
}))
.build()?;
Ok(())
}
واجهة CallbackContext
توفر السمة CallbackContext الوصول إلى سياق التنفيذ:
use adk_rust::prelude::*;
#[async_trait]
pub trait CallbackContext: ReadonlyContext {
/// Access artifact storage (if configured)
fn artifacts(&self) -> Option<Arc<dyn Artifacts>>;
}
// CallbackContext extends ReadonlyContext
#[async_trait]
pub trait ReadonlyContext: Send + Sync {
/// Current invocation ID
fn invocation_id(&self) -> &str;
/// Name of the current agent
fn agent_name(&self) -> &str;
/// User ID from session
fn user_id(&self) -> &str;
/// Application name
fn app_name(&self) -> &str;
/// Session ID
fn session_id(&self) -> &str;
/// Current branch (for multi-agent)
fn branch(&self) -> &str;
/// The user's input content
fn user_content(&self) -> &Content;
}
أنماط شائعة
رد الاتصال للتسجيل (Logging Callback)
سجل جميع تفاعلات Agent:
use adk_rust::prelude::*;
use std::sync::Arc;
let agent = LlmAgentBuilder::new("logged_agent")
.model(model)
.before_callback(Box::new(|ctx| {
Box::pin(async move {
println!("[LOG] Agent '{}' starting", ctx.agent_name());
println!("[LOG] Session: {}", ctx.session_id());
println!("[LOG] User: {}", ctx.user_id());
Ok(None)
})
}))
.after_callback(Box::new(|ctx| {
Box::pin(async move {
println!("[LOG] Agent '{}' completed", ctx.agent_name());
Ok(None)
})
}))
.build()?;
حواجز حماية المدخلات (Input Guardrails)
حظر المحتوى غير اللائق قبل المعالجة:
use adk_rust::prelude::*;
use std::sync::Arc;
let agent = LlmAgentBuilder::new("guarded_agent")
.model(model)
.before_callback(Box::new(|ctx| {
Box::pin(async move {
// Check user input for blocked content
let user_content = ctx.user_content();
for part in &user_content.parts {
if let Part::Text { text } = part {
if text.to_lowercase().contains("blocked_word") {
// Return early with rejection message
return Ok(Some(Content {
role: "model".to_string(),
parts: vec![Part::Text {
text: "I cannot process that request.".to_string(),
}],
}));
}
}
}
Ok(None) // Continue normal execution
})
}))
.build()?;
التخزين المؤقت للاستجابة (قبل Model)
تخزين استجابات LLM مؤقتًا لتقليل استدعاءات API:
use adk_rust::prelude::*;
use std::sync::Arc;
use std::collections::HashMap;
use std::sync::Mutex;
// Simple in-memory cache
let cache: Arc<Mutex<HashMap<String, LlmResponse>>> = Arc::new(Mutex::new(HashMap::new()));
let cache_clone = cache.clone();
let agent = LlmAgentBuilder::new("cached_agent")
.model(model)
.before_model_callback(Box::new(move |ctx, request| {
let cache = cache_clone.clone();
Box::pin(async move {
// Create cache key from request contents
let key = format!("{:?}", request.contents);
// Check cache
if let Some(cached) = cache.lock().unwrap().get(&key) {
println!("[CACHE] Hit for request");
return Ok(BeforeModelResult::Skip(cached.clone()));
}
println!("[CACHE] Miss, calling model");
Ok(BeforeModelResult::Continue(request)) // Continue to model
})
}))
.build()?;
حقن المحتوى متعدد الوسائط (قبل Model)
حقن الصور أو المحتوى الثنائي الآخر في طلبات LLM لتحليل الوسائط المتعددة:
use adk_rust::prelude::*;
use adk_rust::artifact::{ArtifactService, LoadRequest};
use std::sync::Arc;
// Artifact service with pre-loaded image
let artifact_service: Arc<dyn ArtifactService> = /* ... */;
let callback_service = artifact_service.clone();
let agent = LlmAgentBuilder::new("image_analyst")
.model(model)
.instruction("Describe the image provided by the user.")
.before_model_callback(Box::new(move |_ctx, mut request| {
let service = callback_service.clone();
Box::pin(async move {
// Load image from artifact storage
if let Ok(response) = service.load(LoadRequest {
app_name: "my_app".to_string(),
user_id: "user".to_string(),
session_id: "session".to_string(),
file_name: "user:photo.png".to_string(),
version: None,
}).await {
// Inject image into the user's message
if let Some(last_content) = request.contents.last_mut() {
if last_content.role == "user" {
last_content.parts.push(response.part);
}
}
}
Ok(BeforeModelResult::Continue(request))
})
}))
.build()?;
هذا النمط ضروري للذكاء الاصطناعي متعدد الوسائط لأن استجابات Tool هي نصوص JSON - لا يستطيع Model "رؤية" الصور التي يتم إرجاعها بواسطة Tool. عن طريق حقن الصورة مباشرة في الطلب، يتلقى Model بيانات الصورة الفعلية.
تعديل الاستجابة (بعد Model)
تعديل أو تصفية استجابات Model:
use adk_rust::prelude::*;
use std::sync::Arc;
let agent = LlmAgentBuilder::new("filtered_agent")
.model(model)
.after_model_callback(Box::new(|ctx, mut response| {
Box::pin(async move {
// Modify the response content
if let Some(ref mut content) = response.content {
for part in &mut content.parts {
if let Part::Text { text } = part {
// Add disclaimer to all responses
*text = format!("{}\n\n[AI-generated response]", text);
}
}
}
Ok(Some(response))
})
}))
.build()?;
التحقق من أذونات Tool (قبل Tool)
التحقق من صحة أذونات تنفيذ Tool:
use adk_rust::prelude::*;
use std::sync::Arc;
let agent = LlmAgentBuilder::new("permission_agent")
.model(model)
.tool(Arc::new(GoogleSearchTool::new()))
.before_tool_callback(Box::new(|ctx| {
Box::pin(async move {
// Check if user has permission for tools
let user_id = ctx.user_id();
// Example: block certain users from using tools
if user_id == "restricted_user" {
return Ok(Some(Content {
role: "function".to_string(),
parts: vec![Part::Text {
text: "Tool access denied for this user.".to_string(),
}],
}));
}
Ok(None) // Allow tool execution
})
}))
.build()?;
تسجيل نتيجة Tool (بعد Tool)
سجل جميع عمليات تنفيذ Tool:
use adk_rust::prelude::*;
use std::sync::Arc;
let agent = LlmAgentBuilder::new("tool_logged_agent")
.model(model)
.tool(Arc::new(GoogleSearchTool::new()))
.after_tool_callback(Box::new(|ctx| {
Box::pin(async move {
println!("[TOOL LOG] Tool executed for agent: {}", ctx.agent_name());
println!("[TOOL LOG] Session: {}", ctx.session_id());
Ok(None) // Keep original result
})
}))
.build()?;
Callbacks متعددة
يمكنك إضافة callbacks متعددة من نفس النوع. يتم تنفيذها بالترتيب:
use adk_rust::prelude::*;
use std::sync::Arc;
let agent = LlmAgentBuilder::new("multi_callback_agent")
.model(model)
// First before callback - logging
.before_callback(Box::new(|ctx| {
Box::pin(async move {
println!("[1] Logging callback");
Ok(None)
})
}))
// Second before callback - validation
.before_callback(Box::new(|ctx| {
Box::pin(async move {
println!("[2] Validation callback");
Ok(None)
})
}))
.build()?;
عندما يُرجع callback القيمة Some(content)، يتم تخطي callbacks اللاحقة من نفس النوع.
معالجة الأخطاء
يمكن لـcallbacks إرجاع أخطاء لإلغاء التنفيذ:
use adk_rust::prelude::*;
use std::sync::Arc;
let agent = LlmAgentBuilder::new("error_handling_agent")
.model(model)
.before_callback(Box::new(|ctx| {
Box::pin(async move {
// Validate something critical
if ctx.user_id().is_empty() {
return Err(AdkError::Agent("User ID is required".to_string()));
}
Ok(None)
})
}))
.build()?;
أفضل الممارسات
- اجعل callbacks خفيفة الوزن: تجنب العمليات الحسابية الثقيلة في callbacks
- تعامل مع الأخطاء بلطف: أعد رسائل خطأ ذات معنى
- استخدم التسجيل باعتدال: كثرة التسجيل قد تؤثر على الأداء
- التخزين المؤقت بحكمة: ضع في اعتبارك استراتيجيات إبطال التخزين المؤقت
- اختبر callbacks بشكل مستقل: اختبر منطق callback كوحدة منفصلة
ذات صلة
السابق: ← إدارة الحالة | التالي: المخرجات →