بروتوكول الويب الوكالي (AWP)
ADK-Rust يوفر أنواع Agentic Web Protocol (AWP) وتكامل Axum لجعل المواقع والخدمات قابلة للوصول بواسطة وكلاء الذكاء الاصطناعي. يمتد التنفيذ عبر صندوقين: awp-types (أنواع البروتوكول البحتة) وadk-awp (المسارات، والوسائط الوسيطة، وواجهات الخدمة). توفر التطبيقات توزيع الوكلاء، والمصادقة، والتفويض، وتسليم webhooks بشكل دائم.
نظرة عامة
AWP يتيح لأي موقع أن يعلن عن قدراته، وسياساته، وسياقه التجاري بصيغة قابلة للقراءة آليًا. يمكن لوكلاء الذكاء الاصطناعي اكتشاف هذه القدرات، والتفاوض على إصدارات البروتوكول، والاشتراك في الأحداث، والتفاعل عبر رسائل A2A المهيكلة. adk-awp يفرض حدود الحجم ومعدلات الطلب عند حد HTTP الخاص به؛ وتفرض معالجات التطبيق الهوية والتفويض على القدرات.
استخدم AWP عندما:
- تريد أن يكتشف وكلاء الذكاء الاصطناعي خدمتك ويتفاعلوا معها برمجيًا
- تحتاج إلى بيانات وصفية لمستوى الثقة وخطافًا للتحكم في الوصول المفروض من التطبيق
- تريد خدمة الزوار البشريين ووكلاء الذكاء الاصطناعي من نفس نقاط النهاية
- تحتاج إلى اشتراكات في الأحداث وبدائيات التوقيع HMAC-SHA256
- تريد آلة حالات صحية لمراقبة الخدمة
المعمارية
تدفق الطلب AWP
تخطيط التطبيق
┌─────────────────────────────────────────────────┐
│ Your Application │
│ │
│ ┌──────────────┐ ┌──────────────────────────┐ │
│ │ LLM Agent │ │ awp_routes(state) │ │
│ │ (adk-agent) │ │ ├ /.well-known/awp.json │ │
│ │ │ │ ├ /awp/manifest │ │
│ │ Instructions│ │ ├ /awp/health │ │
│ │ derived from│ │ └ /awp/a2a │ │
│ │ business. │ │ auth + management routes│ │
│ │ toml │ │ │ │
│ └──────────────┘ └──────────────────────────┘ │
│ ▲ ▲ │
│ │ │ │
│ ┌────┴──────────────────────┴────┐ │
│ │ BusinessContextLoader │ │
│ │ (business.toml + ArcSwap) │ │
│ └────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
الصناديق
| Crate | الغرض | الاعتمادات |
|---|---|---|
awp-types | أنواع البروتوكول (تعدادات، هياكل، أخطاء) | لا توجد اعتمادات على adk-* — serde، uuid، chrono، thiserror فقط |
adk-awp | المسارات، والوسائط البرمجية، وواجهات الخدمة، والتنفيذات داخل الذاكرة | awp-types، adk-core، axum 0.8، tokio، dashmap |
يعني التقسيم أن أي مشروع Rust يمكنه الاعتماد على awp-types دون سحب شجرة ADK.
البدء السريع
1. أنشئ business.toml
site_name = "My Shop"
site_description = "An online store powered by AWP"
domain = "myshop.example.com"
contact = "hello@myshop.example.com"
[business]
country = "US"
currency = "USD"
languages = ["en"]
[brand_voice]
tone = "friendly and helpful"
greeting = "Welcome! How can I help?"
[[capabilities]]
name = "browse_products"
description = "Browse the product catalog"
endpoint = "/api/products"
method = "GET"
access_level = "anonymous"
[[capabilities]]
name = "place_order"
description = "Place an order"
endpoint = "/api/orders"
method = "POST"
access_level = "known"
[[products]]
sku = "WIDGET-001"
name = "Standard Widget"
price = 1999
inventory = 500
tags = ["widget"]
[[policies]]
name = "privacy"
description = "Minimal data collection, no tracking."
policy_type = "privacy"
[payments]
providers = ["stripe"]
auto_approve_threshold = 5000
[support]
escalation_contacts = ["support@myshop.example.com"]
hours = "Mon-Fri 9-5 EST"
2. حمّل واعرض مسارات AWP
use std::sync::Arc;
use adk_awp::{AwpA2aHandler, AwpState, BusinessContextLoader, awp_routes};
use async_trait::async_trait;
use awp_types::AwpError;
use axum::http::{HeaderMap, header};
use serde_json::{Value, json};
struct ApplicationA2a {
bearer_token: Arc<str>,
}
#[async_trait]
impl AwpA2aHandler for ApplicationA2a {
async fn handle(&self, headers: HeaderMap, message: Value) -> Result<Value, AwpError> {
let expected = format!("Bearer {}", self.bearer_token);
let authorized = headers
.get(header::AUTHORIZATION)
.and_then(|value| value.to_str().ok())
.is_some_and(|value| value == expected);
if !authorized {
return Err(AwpError::Unauthorized("invalid A2A credential".to_string()));
}
// Authorize the requested capability and dispatch to the application agent.
Ok(json!({ "status": "processed", "messageId": message["id"] }))
}
}
let loader = BusinessContextLoader::from_file("business.toml".as_ref())?;
let a2a_token: Arc<str> = std::env::var("AWP_A2A_TOKEN")?.into();
let state = AwpState::builder(loader.context_ref())
.a2a_handler(Arc::new(ApplicationA2a { bearer_token: a2a_token }))
.build();
let app = axum::Router::new()
.merge(awp_routes(state))
.merge(your_custom_routes);
let listener = tokio::net::TcpListener::bind("127.0.0.1:3456").await?;
axum::serve(
listener,
app.into_make_service_with_connect_info::<std::net::SocketAddr>(),
)
.await?;
هذا يسجّل نقاط النهاية العامة الأربع لـAWP مع تفاوض الإصدار، وتحديد معدل
الطلبات، وحدّ حجم جسم يبلغ 64 KiB A2A. من دون AwpA2aHandler،
يرجع POST /awp/a2a 503 ولا يقرّ أبدًا بعمل لم يتم
إرساله. يوفّر ConnectInfo عنوان النظير المستخدم لعزل حِصص تحديد المعدل المجهولة؛ ومن دونه، يتشارك المتصلون غير المعروفين عمدًا حصة واحدة.
نقاط نهاية AWP العامة
| الطريقة | المسار | الوصف |
|---|---|---|
| GET | /.well-known/awp.json | وثيقة الاكتشاف — نقطة الدخول للوكلاء |
| GET | /awp/manifest | بيان القدرات لـJSON-LD |
| GET | /awp/health | حالة الصحة (سليم/يتدهور/متدهور) |
| POST | /awp/a2a | إرسال A2A المقدم من التطبيق |
نقاط إدارة المصادقة
awp_management_routes() يعيد إدارة الاشتراك بشكل منفصل وبدون طبقة مصادقة. طبّق وسيط المصادقة الخاص بالتطبيق قبل دمجه:
| الطريقة | المسار | الوصف |
|---|---|---|
| POST | /awp/events/subscribe | إنشاء اشتراك webhook |
| GET | /awp/events/subscriptions | عرض جميع الاشتراكات |
| DELETE | /awp/events/subscriptions/{id} | حذف اشتراك |
مستند الاكتشاف
يتم إنشاء مستند الاكتشاف في /.well-known/awp.json تلقائيًا من business.toml الخاص بك:
{
"version": { "major": 1, "minor": 0 },
"siteName": "My Shop",
"siteDescription": "An online store powered by AWP",
"capabilityManifestUrl": "https://myshop.example.com/awp/manifest",
"a2aEndpointUrl": "https://myshop.example.com/awp/a2a",
"eventsEndpointUrl": "https://myshop.example.com/awp/events/subscribe",
"healthEndpointUrl": "https://myshop.example.com/awp/health",
"supportedTrustLevels": ["anonymous"]
}
بيان القدرات
يستخدم البيان في /awp/manifest صيغة JSON-LD:
{
"@context": "https://schema.org",
"@type": "WebAPI",
"name": "My Shop",
"description": "An online store powered by AWP",
"capabilities": [
{
"name": "browse_products",
"description": "Browse the product catalog",
"endpoint": "/api/products",
"method": "GET"
}
]
}
مستويات الثقة
يستخدم AWP أربعة مستويات ثقة مع صلاحيات متزايدة:
| المستوى | المميّز | كيفية التعيين |
|---|---|---|
Anonymous | 0 | بدون بيانات اعتماد |
Known | 1 | مفتاح API صالح أو JWT |
Partner | 2 | JWT مع نطاق partner |
Internal | 3 | JWT مع نطاق internal |
ترتَّب مستويات الثقة: Anonymous < Known < Partner < Internal. يعلن كل capability في business.toml عن الحد الأدنى لـ access_level الخاص به.
يُصنِّف DefaultTrustAssigner كل request على أنه Anonymous. لا يُعتَمد رأس bearer أو المفتاح API حتى يتحقق منه application verifier. لذلك تتطلب مستويات الثقة الأعلى assigner مخصصًا.
قم بتهيئة .supported_trust_levels(...) إلى جانب ذلك assigner حتى يعلن discovery فقط عن المستويات التي يمكن للنشر التحقق منها.
تخصيص الثقة المخصص
نفّذ الـ trait TrustLevelAssigner لمنطق مخصص:
use std::sync::Arc;
use adk_awp::TrustLevelAssigner;
use async_trait::async_trait;
use awp_types::TrustLevel;
use axum::http::{HeaderMap, header};
struct MyTrustAssigner {
bearer_token: Arc<str>,
}
#[async_trait]
impl TrustLevelAssigner for MyTrustAssigner {
async fn assign(&self, headers: &HeaderMap) -> TrustLevel {
let expected = format!("Bearer {}", self.bearer_token);
if headers
.get(header::AUTHORIZATION)
.and_then(|value| value.to_str().ok())
.is_some_and(|value| value == expected)
{
TrustLevel::Known
} else {
TrustLevel::Anonymous
}
}
}
استخدم نفس identity وscope verified source مثل بقية التطبيق عند تعيين Partner أو Internal.
تحديد المعدل
يستخدم InMemoryRateLimiter المدمج خوارزمية نافذة منزلقة مع حدود لكل مستوى ثقة:
| مستوى الثقة | الحد الافتراضي |
|---|---|
| مجهول | 30 طلبًا/دقيقة |
| معروف | 120 طلبًا/دقيقة |
| الشريك | 600 طلب/دقيقة |
| داخلي | غير محدود |
الطلبات المرفوضة تتلقى HTTP 429 مع ترويسة Retry-After.
الحدود المخصصة
use std::collections::HashMap;
use awp_types::TrustLevel;
use adk_awp::{InMemoryRateLimiter, RateLimitConfig};
let mut limits = HashMap::new();
limits.insert(TrustLevel::Anonymous, RateLimitConfig {
max_requests: 10,
window_secs: 60,
});
limits.insert(TrustLevel::Known, RateLimitConfig {
max_requests: 100,
window_secs: 60,
});
let limiter = InMemoryRateLimiter::with_config(limits);
التفاوض على الإصدار
تتضمن جميع المسارات AWP وسيط التفاوض على الإصدار:
- يرسل العملاء ترويسة
AWP-Version: 1.1(اختياريًا — الافتراضي هو الإصدار الحالي) - يتحقق الخادم من توافق الإصدار الرئيسي
- تستمر الطلبات المتوافقة؛ أما الطلبات غير المتوافقة فترجع HTTP 406
- تحصل قيم الإصدار المشوهة على HTTP 400
- يتضمن الرد ترويسة
AWP-Version: 1.0
اشتراكات الأحداث
إدارة الاشتراكات سطح مميز. قم بتركيب awp_management_routes() خلف المصادقة قبل قبول هذه
الطلبات. يجب أن يكون رد النداء URLs عنوان HTTPS URLs مطلقًا، ويجب أن تحتوي
أسرار التوقيع على 32 بايت على الأقل:
# Subscribe
curl -X POST http://localhost:3456/awp/events/subscribe \
-H "Authorization: Bearer $AWP_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"subscriber": "my-agent",
"callbackUrl": "https://my-agent.example/webhook",
"eventTypes": ["health.changed"],
"secret": "replace-with-at-least-32-random-bytes"
}'
# List subscriptions
curl -H "Authorization: Bearer $AWP_ADMIN_TOKEN" \
http://localhost:3456/awp/events/subscriptions
يوقع InMemoryEventSubscriptionService ويسجل عمليات التسليم المطابقة لكنه لا يجري أي إدخال/إخراج شبكي. تطبق
التطبيقات الإنتاجية EventSubscriptionService مع التحقق من الوجهة، وطابور دائم،
وإعادات محاولة محدودة، وعميل HTTP الخاص بها.
يمكن لتنفيذ تسليم HTTP أن يحمل ترويسة X-AWP-Signature مع
توقيع HMAC-SHA256:
X-AWP-Signature: sha256=<hex_digest>
تحقق من التواقيع باستخدام adk_awp::verify_signature(payload, secret, signature).
آلة حالات الصحة
يتتبع مسار الصحة حالة الخدمة مع انتقالات يتم التحقق منها بدقة:
Healthy → Degrading → Degraded
↑ │ │
└─────────┘ │
└─────────────────────┘
تؤدي تغييرات الحالة إلى إصدار أحداث health.changed إلى جميع المشتركين المطابقين.
use adk_awp::HealthStateMachine;
// Transition to degrading
health.report_degrading("database latency high").await?;
// Transition to degraded
health.report_degraded("database unreachable").await?;
// Recover
health.report_healthy().await?;
ترجع الانتقالات غير الصالحة (مثل Healthy → Degraded) خطأً.
تخزين الموافقة
يتضمن AWP واجهة لتخزين الموافقة. كما يتطلب الامتثال التنظيمي إشعارًا خاصًا بالتطبيق، وسببًا قانونيًا، والاحتفاظ، وضوابط الوصول، و سياسة الحذف؛ إن اختيار تنفيذ للتخزين لا يثبت الامتثال:
use adk_awp::InMemoryConsentService;
let consent = InMemoryConsentService::new();
// Capture consent
consent.capture_consent("visitor-123", "analytics").await?;
// Check consent
let has_consent = consent.check_consent("visitor-123", "analytics").await?;
// Revoke consent
consent.revoke_consent("visitor-123", "analytics").await?;
اكتشاف نوع الطالب
يكتشف AWP ما إذا كان الطلب قادمًا من إنسان أم من وكيل ذكاء اصطناعي:
- ترويسة
X-AWP-Channel: agent→ وكيل (تجاوز صريح) Accept: application/json+ نمط User-Agent الخاص بالوكيل → وكيل- خلاف ذلك → إنسان
أنماط User-Agent الخاصة بالوكيل: bot، crawler، spider، agent، gpt، claude، gemini، perplexity، anthropic، openai.
use adk_awp::detect_requester_type;
use axum::http::HeaderMap;
let mut headers = HeaderMap::new();
headers.insert("X-AWP-Channel", "agent".parse().unwrap());
let requester = detect_requester_type(&headers);
// RequesterType::Agent
أنواع الرسائل AWP
إلى جانب رسائل A2A العامة، يعرّف AWP فئات رسائل مهيكلة لتوجيه الوكلاء:
| النوع | الوصف |
|---|---|
VisitorIntentSignal | نية الشراء أو نية الخدمة |
ContentGapSignal | تم اكتشاف محتوى مفقود أو قديم |
PaymentIntent | رسالة دورة حياة الدفع |
SupportEscalation | التصعيد إلى الدعم البشري |
ReviewSignal | مراجعة أو ملاحظات من منصة |
OperationsProposal | المخزون، اقتراح الجدولة |
InvokeCapability | استدعاء قدرة معلنة |
RenderUi | طلب عرض واجهة المستخدم |
OutboundTrigger | رسالة صادرة استباقية |
use awp_types::{AwpMessageType, AwpTypedMessage};
let msg = AwpTypedMessage {
id: uuid::Uuid::now_v7(),
sender: "visitor-agent".to_string(),
recipient: "payment-agent".to_string(),
awp_type: AwpMessageType::PaymentIntent,
timestamp: chrono::Utc::now(),
payload: serde_json::json!({"sku": "WIDGET-001", "amount": 2500}),
};
نوايا الدفع
AWP يعرّف دورة حياة مبسطة للدفع المدفوع بسياسة المالك:
Draft → PendingApproval → Approved → Executing → Settled
→ Rejected
→ Cancelled
يقيم PaymentPolicy ما إذا كان يجب الموافقة التلقائية أو طلب موافقة المالك:
use awp_types::{PaymentPolicy, TrustLevel};
let policy = PaymentPolicy::default(); // $50 auto-approve, $500 require approval
let decision = policy.evaluate(2500, TrustLevel::Known);
// PaymentPolicyDecision::AutoApprove (amount $25 <= $50 threshold)
let decision = policy.evaluate(60_000, TrustLevel::Partner);
// PaymentPolicyDecision::RequireApproval (amount $600 > $500 threshold)
مخطط business.toml
يدعم المخطط الكامل إعدادًا غنيًا للأعمال:
| القسم | الحقول | مطلوب |
|---|---|---|
| (الجذر) | site_name, site_description, domain, contact | نعم (باستثناء جهة الاتصال) |
[business] | name, country, languages, currency, timezone | لا |
[brand_voice] | tone, greeting, escalation_message | لا |
[[products]] | sku, name, price, inventory, tags, description | لا |
[[capabilities]] | name, description, endpoint, method, access_level | نعم |
[[policies]] | name, description, policy_type | نعم |
[channels] | whatsapp, email, website, sms | لا |
[payments] | providers, auto_approve_threshold, require_approval_threshold | لا |
[support] | escalation_contacts, hours, sla | لا |
[content] | topics, auto_draft, publish_delay | لا |
[reviews] | platforms, auto_respond_threshold | لا |
[outreach] | follow_up_delay, require_consent | لا |
جميع الأقسام الموسعة اختيارية — تظل الملفات business.toml الدنيا الحالية تعمل.
التحميل السريع أثناء التشغيل
يدعم BusinessContextLoader التحميل السريع أثناء التشغيل عبر ArcSwap:
let loader = BusinessContextLoader::from_file("business.toml".as_ref())?;
loader.watch("business.toml".into()).await?;
// Changes to business.toml are picked up automatically every 5 seconds
تشغيل المثال
يتضمن مثال وكيل AWP كاملاً:
cd examples/awp_agent
cp .env.example .env # add your GOOGLE_API_KEY
cargo run
المثال:
- يحمّل
business.tomlمع المنتجات والسياسات وصوت العلامة التجارية - ينشئ وكيل LLM بتعليمات مستمدة من سياق العمل
- يثبّت التوجيه الموثَّق A2A إلى ذلك الوكيل
- يركّب مسارات الإدارة خلف اعتماد تجريبي منفصل
- يختبر كل نقطة نهاية ويطبع تحقق البروتوكول
أفضل الممارسات
- ابدأ بـ
business.tomlبسيط — لا يُشترط سوىsite_nameوsite_descriptionوdomainوالقدرات والسياسات - فرض تفويض القدرات —
access_levelهو بيانات تعريف في البيان؛ ويجب على معالج التطبيق فرضه - فعّل التحميل السريع أثناء التشغيل في الإنتاج — استدعِ
loader.watch()لتحديثات التهيئة دون توقف - نفّذ
TrustLevelAssignerمخصّصة — الإعداد الافتراضي يعيّن عمدًا فقطAnonymous - وثّق مسارات الإدارة — لا تكشف أبدًا اشتراكات CRUD من موجّه غير محمي
- ثبّت توجيه A2A حقيقيًا — القيمة الافتراضية الآمنة عند الفشل تُرجع
503 - استخدم تسليم الأحداث القابل للاستمرار — نفّذ سياسة الوجهة، ووضعها في طابور، ومحاولات إعادة محدودة
- تحقّق من تواقيع webhook — تحقّق من
X-AWP-Signatureعلى webhooks الواردة
ذات صلة
- A2A Protocol — اتصال Agent-to-Agent (مكمل لـ AWP)
- Server Deployment — تشغيل الوكلاء كخوادم HTTP
- Access Control — أذونات قائمة على الأدوار
السابق: ← A2A Protocol | التالي: Evaluation →