بروتوكول الويب الوكالي (AWP)

ADK-Rust يوفر أنواع Agentic Web Protocol (AWP) وتكامل Axum لجعل المواقع والخدمات قابلة للوصول بواسطة وكلاء الذكاء الاصطناعي. يمتد التنفيذ عبر صندوقين: awp-types (أنواع البروتوكول البحتة) وadk-awp (المسارات، والوسائط الوسيطة، وواجهات الخدمة). توفر التطبيقات توزيع الوكلاء، والمصادقة، والتفويض، وتسليم webhooks بشكل دائم.

نظرة عامة

AWP يتيح لأي موقع أن يعلن عن قدراته، وسياساته، وسياقه التجاري بصيغة قابلة للقراءة آليًا. يمكن لوكلاء الذكاء الاصطناعي اكتشاف هذه القدرات، والتفاوض على إصدارات البروتوكول، والاشتراك في الأحداث، والتفاعل عبر رسائل A2A المهيكلة. adk-awp يفرض حدود الحجم ومعدلات الطلب عند حد HTTP الخاص به؛ وتفرض معالجات التطبيق الهوية والتفويض على القدرات.

استخدم AWP عندما:

  • تريد أن يكتشف وكلاء الذكاء الاصطناعي خدمتك ويتفاعلوا معها برمجيًا
  • تحتاج إلى بيانات وصفية لمستوى الثقة وخطافًا للتحكم في الوصول المفروض من التطبيق
  • تريد خدمة الزوار البشريين ووكلاء الذكاء الاصطناعي من نفس نقاط النهاية
  • تحتاج إلى اشتراكات في الأحداث وبدائيات التوقيع HMAC-SHA256
  • تريد آلة حالات صحية لمراقبة الخدمة

المعمارية

تدفق الطلب AWP

Rendering architecture…

تخطيط التطبيق

┌─────────────────────────────────────────────────┐
│                  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 أربعة مستويات ثقة مع صلاحيات متزايدة:

المستوىالمميّزكيفية التعيين
Anonymous0بدون بيانات اعتماد
Known1مفتاح API صالح أو JWT
Partner2JWT مع نطاق partner
Internal3JWT مع نطاق 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 ما إذا كان الطلب قادمًا من إنسان أم من وكيل ذكاء اصطناعي:

  1. ترويسة X-AWP-Channel: agent → وكيل (تجاوز صريح)
  2. Accept: application/json + نمط User-Agent الخاص بالوكيل → وكيل
  3. خلاف ذلك → إنسان

أنماط 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

المثال:

  1. يحمّل business.toml مع المنتجات والسياسات وصوت العلامة التجارية
  2. ينشئ وكيل LLM بتعليمات مستمدة من سياق العمل
  3. يثبّت التوجيه الموثَّق A2A إلى ذلك الوكيل
  4. يركّب مسارات الإدارة خلف اعتماد تجريبي منفصل
  5. يختبر كل نقطة نهاية ويطبع تحقق البروتوكول

أفضل الممارسات

  1. ابدأ بـ business.toml بسيط — لا يُشترط سوى site_name وsite_description وdomain والقدرات والسياسات
  2. فرض تفويض القدراتaccess_level هو بيانات تعريف في البيان؛ ويجب على معالج التطبيق فرضه
  3. فعّل التحميل السريع أثناء التشغيل في الإنتاج — استدعِ loader.watch() لتحديثات التهيئة دون توقف
  4. نفّذ TrustLevelAssigner مخصّصة — الإعداد الافتراضي يعيّن عمدًا فقط Anonymous
  5. وثّق مسارات الإدارة — لا تكشف أبدًا اشتراكات CRUD من موجّه غير محمي
  6. ثبّت توجيه A2A حقيقيًا — القيمة الافتراضية الآمنة عند الفشل تُرجع 503
  7. استخدم تسليم الأحداث القابل للاستمرار — نفّذ سياسة الوجهة، ووضعها في طابور، ومحاولات إعادة محدودة
  8. تحقّق من تواقيع webhook — تحقّق من X-AWP-Signature على webhooks الواردة

السابق: ← A2A Protocol | التالي: Evaluation →