بروتوكول من وكيل إلى وكيل (A2A)
يطبّق ADK-Rust بروتوكول A2A الإصدار 1.0.0 للاتصال بين الوكلاء عبر الشبكات. يوجد التنفيذ في adk-server خلف علامة الميزات a2a-v1، ويغطي جميع العمليات الإحدى عشرة JSON-RPC، وارتباطات REST، واكتشاف بطاقة الوكيل، والتفاوض على الإصدار. راجع تغطية العمليات للاطلاع على العملية الوحيدة التي تكون دلالاتها أضيق مما تسمح به المواصفة. تتوفر أنواع البيانات السلكية عبر a2a-protocol-types — وهي A2A SDK لـ Rust موثّقة من المؤسسة، بواسطة @tomtom215 (a2a-rust).
نظرة عامة
تكون A2A مفيدة عند:
- التكامل مع خدمات الوكلاء التابعة لجهات خارجية
- بناء معماريات الخدمات المصغّرة التي تضم وكلاء متخصصين
- تمكين الاتصال بين الوكلاء بمختلف اللغات (أي لغة تتوفر لها عميلة A2A)
- فرض عقود رسمية بين أنظمة الوكلاء
للتنظيم الداخلي البسيط، استخدم الوكلاء الفرعيين المحليين بدلًا من A2A للحصول على أداء أفضل.
التوافق مع الإصدار 1.0.0
يتوافق التنفيذ بالكامل مع مواصفة بروتوكول A2A الإصدار 1.0.0:
| الميزة | قسم المواصفات | الحالة |
|---|---|---|
| بطاقة الوكيل مع إعلان الإمكانات | §8 | ✅ |
| طوابع زمنية بتنسيق RFC 3339 لجميع تغييرات حالة المهمة | §5.6.1 | ✅ |
خاصية عدم تكرار معرّف الرسالة لـ SendMessage | §3.3.1 | ✅ |
| مصادقة الإشعارات الفورية (Bearer + الرمز المميز) | §13.2 | ✅ |
| تدفق الاستئناف متعدد الأدوار في INPUT_REQUIRED | §3.4.3 | ✅ |
| التحقق من صحة الإدخال (الأجزاء، والمعرّفات، وحجم البيانات الوصفية) | §3.3 | ✅ |
Content-Type: application/a2a+json في الاستجابات | §9 | ✅ |
| كائن المهمة كأول حدث بث SSE | §3.1.2 | ✅ |
| البحث عن المهام ضمن نطاق السياق للتفاعلات متعددة الأدوار | §3.4.1 | ✅ |
التفاوض على الإصدار (رأس A2A-Version) | §9.1 | ✅ |
| التحقق من آلة الحالات (الحالات النهائية) | §4.1.3 | ✅ |
بطاقات الوكلاء
يعرِض كل وكيل A2A بطاقة وكيل في /.well-known/agent-card.json تصف إمكاناته ومهاراته وواجهاته المدعومة.
use adk_server::a2a::v1::card::build_v1_agent_card;
use a2a_protocol_types::{AgentCapabilities, AgentSkill};
let card = build_v1_agent_card(
"my-agent",
"A helpful research agent",
"http://localhost:3001/jsonrpc",
"1.0.0",
vec![AgentSkill {
id: "research".to_string(),
name: "Research & Summarize".to_string(),
description: "Researches topics and produces structured summaries".to_string(),
tags: vec!["research".to_string()],
examples: None,
input_modes: None,
output_modes: None,
security_requirements: None,
}],
AgentCapabilities::none()
.with_streaming(true)
.with_push_notifications(true),
);
تتضمن بطاقة الوكيل:
- اسم الوكيل ووصفه وإصداره
- الواجهات المدعومة مع ربط البروتوكول وإصداره
- الإمكانات:
streaming،pushNotifications،extendedAgentCard - المهارات المشتقة من إعداد الوكيل
- أوضاع الإدخال والإخراج الافتراضية
يتم الآن التصريح عن الإمكانات صراحةً عبر المعامل AgentCapabilities — ولم تعد هناك قيم افتراضية مضمّنة برمجيًا.
إتاحة وكيل عبر A2A v1
أنشئ خادمًا كاملًا A2A v1.0.0 مع تكامل LLM:
use std::sync::Arc;
use a2a_protocol_types::{AgentCapabilities, AgentSkill};
use adk_agent::LlmAgentBuilder;
use adk_server::a2a::v1::card::{CachedAgentCard, build_v1_agent_card};
use adk_server::a2a::v1::executor::V1Executor;
use adk_server::a2a::v1::jsonrpc_handler::jsonrpc_handler;
use adk_server::a2a::v1::push::NoOpPushNotificationSender;
use adk_server::a2a::v1::request_handler::RequestHandler;
use adk_server::a2a::v1::rest_handler::rest_router;
use adk_server::a2a::v1::task_store::InMemoryTaskStore;
use adk_server::a2a::v1::version::version_negotiation;
use adk_runner::RunnerConfig;
use adk_session::InMemorySessionService;
use axum::Router;
use axum::routing::post;
use tokio::sync::RwLock;
// 1. Create your agent
let model = adk_model::GeminiModel::new(&api_key, "gemini-3.7-flash")?;
let agent = LlmAgentBuilder::new("my-agent")
.description("A helpful agent")
.model(Arc::new(model))
.instruction("You are a helpful assistant.")
.build()?;
// 2. Set up A2A infrastructure
let task_store = Arc::new(InMemoryTaskStore::new());
let executor = Arc::new(V1Executor::new(task_store.clone()));
let push_sender = Arc::new(NoOpPushNotificationSender);
// 3. Build agent card with capabilities
let card = build_v1_agent_card(
"my-agent", "A helpful agent",
"http://localhost:3001/jsonrpc", "1.0.0",
vec![/* skills */],
AgentCapabilities::none().with_streaming(true),
);
let cached_card = Arc::new(RwLock::new(CachedAgentCard::new(card)));
// 4. Create runner config for LLM invocation
let session_service = Arc::new(InMemorySessionService::new());
let runner_config = Arc::new(RunnerConfig {
app_name: "my-agent".to_string(),
agent: Arc::new(agent),
session_service,
artifact_service: None,
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,
});
// 5. Wire up the handler and routes
let handler = Arc::new(RequestHandler::with_runner(
executor, task_store, push_sender, cached_card, runner_config,
));
let app = Router::new()
.route("/jsonrpc", post(jsonrpc_handler))
.with_state(handler.clone())
.merge(rest_router(handler))
.layer(axum::middleware::from_fn(version_negotiation));
// 6. Serve
let listener = tokio::net::TcpListener::bind("0.0.0.0:3001").await?;
axum::serve(listener, app).await?;
يتيح هذا:
GET /.well-known/agent-card.json— بطاقة الوكيل مع التخزين المؤقت باستخدام ETagPOST /jsonrpc— نقطة نهاية JSON-RPC (جميع العمليات الـ11 في v1؛ راجع تغطية العمليات)- مسارات REST لجميع العمليات
- تفاوض ترويسة
A2A-Versionعلى جميع المسارات
عمليات JSON-RPC
يتم دعم جميع عمليات A2A الـ11 في الإصدار v1.0.0:
| الطريقة | الوصف |
|---|---|
SendMessage | إرسال رسالة، وإنشاء مهمة أو استئنافها |
SendStreamingMessage | مثل SendMessage، ولكنها تُرجع تدفق SSE |
GetTask | استرداد مهمة باستخدام المعرّف |
CancelTask | إلغاء مهمة قيد التشغيل |
ListTasks | سرد المهام مع التصفية والتقسيم إلى صفحات |
SubscribeToTask | الاشتراك في تحديثات المهام عبر SSE |
CreateTaskPushNotificationConfig | تسجيل خطاف ويب لإشعارات الدفع |
GetTaskPushNotificationConfig | استرداد إعدادات إشعارات الدفع |
ListTaskPushNotificationConfigs | إدراج إعدادات الدفع لمهمة |
DeleteTaskPushNotificationConfig | إزالة إعدادات إشعارات الدفع |
GetExtendedAgentCard | استرداد بطاقة الوكيل الموسّعة |
SendMessage
{
"jsonrpc": "2.0",
"id": 1,
"method": "SendMessage",
"params": {
"message": {
"messageId": "msg-123",
"role": "ROLE_USER",
"parts": [{"text": "Research quantum computing"}]
}
}
}
يتضمن الرد كائن Task مع الحالة والسجل والعناصر الناتجة. يستخدم الرد Content-Type: application/a2a+json.
SendStreamingMessage
تنسيق الطلب نفسه المستخدم في SendMessage. يُرجع تدفق SSE حيث:
- يكون الحدث الأول كائن
Taskكاملًا (وفقًا للمواصفة §3.1.2) - تكون الأحداث اللاحقة من نوع
TaskStatusUpdateEvent(قيد العمل، مكتمل، وغير ذلك) - تكون أحداث العناصر الناتجة من نوع
TaskArtifactUpdateEvent
المحادثات متعددة الأدوار
عندما تصل مهمة إلى حالة INPUT_REQUIRED، أرسل رسالة متابعة مع نفس contextId لاستئنافها:
{
"jsonrpc": "2.0",
"id": 2,
"method": "SendMessage",
"params": {
"message": {
"messageId": "msg-456",
"role": "ROLE_USER",
"contextId": "ctx-original",
"parts": [{"text": "Yes, include more details on error correction"}]
}
}
}
يعثر المعالج تلقائيًا على المهمة الحالية بواسطة contextId، وينقلها من INPUT_REQUIRED إلى Working، ويضيف الرسالة الجديدة إلى السجل، ثم يواصل المعالجة.
قابلية التنفيذ مرة واحدة
تُعيد طلبات SendMessage المكررة التي تتضمن نفس messageId المهمة التي أُنشئت سابقًا دون إعادة المعالجة. ينطبق ذلك على كل من SendMessage وSendStreamingMessage.
مصادقة إشعارات الدفع
عندما يسجل العميل عنوان webhook عبر CreateTaskPushNotificationConfig، يضمّن الخادم ترويسات المصادقة في عمليات تسليم webhook:
Authorization: Bearer <credentials>— عندما يحتوي الحقلauthenticationعلى بيانات اعتماد bearera2a-notification-token: <token>— عندما يكون الحقلtokenموجودًا
يمكن تعيين الترويستين في الوقت نفسه. تتحقق حماية SSRF من URLs الخاص بـ webhook مقابل نطاقات IP الخاصة وlocalhost.
التحقق من صحة الإدخال
تُتحقق صحة جميع الطلبات الواردة قبل معالجتها:
| التحقق من الصحة | الخطأ |
|---|---|
| رسالة بلا أجزاء | InvalidParams (-32602) |
| messageId فارغ أو يتكون من مسافات بيضاء فقط | InvalidParams (-32602) |
| messageId الذي يتجاوز 256 حرفًا | InvalidParams (-32602) |
| taskId فارغ أو يتكون من مسافات بيضاء فقط | InvalidParams (-32602) |
| taskId الذي يتجاوز 256 حرفًا | InvalidParams (-32602) |
| البيانات الوصفية التي تتجاوز 64 كيلوبايت | InvalidParams (-32602) |
استهلاك وكيل بعيد
استخدم RemoteA2aAgent للتواصل مع وكيل A2A بعيد:
use adk_server::a2a::RemoteA2aAgent;
let remote_agent = RemoteA2aAgent::builder("prime_checker")
.description("Checks if numbers are prime")
.agent_url("http://localhost:8001")
.build()?;
// Use as a sub-agent in a local agent hierarchy
let root_agent = LlmAgentBuilder::new("root")
.model(Arc::new(model))
.sub_agent(Arc::new(remote_agent))
.build()?;
عميل A2A
للتواصل المباشر على مستوى البروتوكول:
use adk_server::a2a::client::v1_client::A2aV1Client;
// Discover agent card
let card = A2aV1Client::resolve_agent_card("http://localhost:3001").await?;
let client = A2aV1Client::new(card);
// Send message
let task = client.send_message(message).await?;
// Get task
let task = client.get_task(&task_id, Some(10)).await?;
// List tasks
let tasks = client.list_tasks(None, None, None, None).await?;
// Cancel task
client.cancel_task(&task_id).await?;
// Streaming
let response = client.send_streaming_message(message).await?;
// Push notification CRUD
let config = client.create_push_notification_config(config).await?;
client.delete_push_notification_config(&task_id, &config_id).await?;
معالجة الأخطاء
تتوافق أخطاء A2A مع كلٍّ من رموز JSON-RPC ورموز الحالة HTTP:
| الخطأ | JSON-RPC الرمز | HTTP الحالة |
|---|---|---|
| TaskNotFound | -32001 | 404 |
| TaskNotCancelable | -32002 | 409 |
| PushNotificationNotSupported | -32003 | 400 |
| UnsupportedOperation | -32004 | 400 |
| ContentTypeNotSupported | -32005 | 415 |
| InvalidAgentResponse | -32006 | 502 |
| VersionNotSupported | -32009 | 400 |
| InvalidParams | -32602 | 400 |
| MethodNotFound | -32601 | 404 |
| داخلي | -32603 | 500 |
تشغيل الأمثلة
يتضمن ذلك وكيلين كاملين من أمثلة A2A v1.0.0:
cargo run --manifest-path examples/a2a-research-agent/Cargo.toml
cargo run --manifest-path examples/a2a-writing-agent/Cargo.toml --bin a2a-writing-agent
cargo run --manifest-path examples/a2a-writing-agent/Cargo.toml --bin client
يتحقق العميل من: اكتشاف بطاقة الوكيل، وSendMessage (كلا الوكيلين مع LLM حقيقي)، وGetTask، وListTasks، ومسار الخطأ CancelTask، وSendStreamingMessage، وCRUD لإشعارات الدفع، وGetExtendedAgentCard، والتفاوض على الإصدار، ومسارات الخطأ.
أفضل الممارسات
- أعلن عن الإمكانات بدقة — اضبط
streamingوpushNotificationsبناءً على ما يدعمه وكيلك فعليًا - استخدم البث للعمليات الطويلة — يوفّر
SendStreamingMessageللعملاء تقدمًا في الوقت الفعلي - تعامل مع التدفقات متعددة الأدوار — استخدم
contextIdللحفاظ على حالة المحادثة عبر الرسائل - تحقق من URLs لخطاف الويب — الحماية من SSRF مضمّنة، لكن استخدم HTTPS في بيئة الإنتاج
- اضبط مهلات مناسبة — كوّن مهلات الطلبات لاستدعاءات الوكلاء البعيدة
- استخدم قابلية التكرار الآمن — يمكن للعملاء إعادة محاولة
SendMessageبأمان باستخدامmessageIdنفسه
ذو صلة
- LlmAgent — إنشاء الوكلاء
- أنظمة الوكلاء المتعددين — الوكلاء الفرعيون والتسلسلات الهرمية
- نشر الخادم — تشغيل الوكلاء كخوادم HTTP
السابق: ← الخادم | التالي: التقييم →
تغطية العمليات
يتم توزيع جميع عمليات JSON-RPC الإحدى عشرة من الإصدار v1 وتنفيذها.
| العملية | الحالة |
|---|---|
SendMessage | يشغّل الوكيل، ويسجّل مخرجاته كأثر |
SendStreamingMessage | يشغّل الوكيل، ويبث أجزاء الأثر عند إنتاجها |
GetTask، ListTasks | كامل |
CancelTask | كامل |
SubscribeToTask (tasks/resubscribe) | لقطة فقط — انظر أدناه |
CreateTaskPushNotificationConfig، GetTaskPushNotificationConfig، ListTaskPushNotificationConfigs، DeleteTaskPushNotificationConfig | كامل |
GetExtendedAgentCard | كامل |
SubscribeToTask هي لقطة
تعيد العملية المهمة وحالتها الحالية، ثم تغلق التدفق. وهي لا تسلّم التحديثات اللاحقة، لذلك يجب ألا ينتظر العميل التقدّم من خلالها.
تتطلب إعادة الاتصال المباشر قائمة انتظار أحداث لكل مهمة، تستمر بعد انتهاء الطلب الذي أنشأها. تحصل
التنفيذات المرجعية على ذلك من A2A SDKs الخاصة بها — إذ يفوّض كلٌّ من adk-python وadk-go
tasks/resubscribe بالكامل إلى مدير قوائم انتظار SDK، ولا ينفّذ أيٌّ منهما ذلك في شيفرة ADK.
هذا الخادم مكتوب يدويًا باستخدام a2a-protocol-types، الذي يوفّر أنواع wire بدلًا من
بيئة تشغيل للخادم، ولذلك لا توجد قائمة الانتظار بعد.
استخدم SendStreamingMessage عند الحاجة إلى التحديثات المباشرة.
عقد أحداث التدفق
يترجم SendStreamingMessage أحداث الوكيل فور وصولها:
| حدث الوكيل | A2A حدث |
|---|---|
| أولًا، قبل الإخراج | Task، ثم TaskStatusUpdateEvent — Working |
المحتوى، partial = true | TaskArtifactUpdateEvent — append، ليست القطعة الأخيرة |
المحتوى، partial = false | TaskArtifactUpdateEvent — الجزء النهائي |
| انتهاء التدفق | TaskStatusUpdateEvent — Completed |
| أخطاء التدفق | TaskStatusUpdateEvent — Failed |
تشترك جميع أجزاء الاستجابة الواحدة في معرّف قطعة أثرية، بحيث يمكن للعميل إعادة تجميعها. يُستمر النص المُجمّع، لذا يعيد استدعاء لاحق لـ GetTask ما تم بثّه. يتوافق هذا مع العقد الذي تنفّذه adk-python وadk-go عبر SDKs الخاص بهما.