خادم API

يوفّر خادم ADK-Rust واجهة REST API لتشغيل الوكلاء، وإدارة الجلسات، والوصول إلى العناصر المصطنعة. عند نشر وكيلك باستخدام Launcher في وضع الخادم، فإنه يوفّر نقاط النهاية هذه إلى جانب واجهة ويب.

نظرة عامة

يُبنى الخادم باستخدام Axum ويوفّر:

  • REST API: نقاط نهاية HTTP لتنفيذ الوكلاء وإدارة الجلسات
  • الأحداث المرسلة من الخادم (SSE): بث استجابات الوكيل في الوقت الفعلي
  • واجهة ويب: واجهة تفاعلية قائمة على المتصفح
  • دعم CORS: تمكين الطلبات من مصادر مختلفة
  • القياس عن بُعد: قابلية ملاحظة مضمّنة باستخدام التتبّع

يعرِض ServerConfig أيضًا تمريرًا على مستوى المُشغّل لعمليات النشر طويلة الأمد:

let config = ServerConfig::new(agent_loader, session_service)
    .with_compaction(compaction_config)
    .with_context_cache(context_cache_config, cache_capable_model);

بدء تشغيل الخادم

استخدم Launcher لبدء تشغيل الخادم:

use adk_rust::prelude::*;
use adk_rust::Launcher;
use std::sync::Arc;

#[tokio::main]
async fn main() -> Result<()> {
    let api_key = std::env::var("GOOGLE_API_KEY")?;
    let model = Arc::new(GeminiModel::new(&api_key, "gemini-3.7-flash")?);
    
    let agent = LlmAgentBuilder::new("my_agent")
        .description("A helpful assistant")
        .instruction("You are a helpful assistant.")
        .model(model)
        .build()?;
    
    Launcher::new(Arc::new(agent)).run().await
}

ابدأ من هيكل API المُتحقَّق منه:

cargo adk new my-api --template api
cd my-api
cargo run

نقاط نهاية REST API

فحص الحالة

تحقّق مما إذا كان الخادم قيد التشغيل:

GET /api/health

الاستجابة:

OK

تشغيل الوكيل مع البث

نفّذ وكيلًا وابثّ الاستجابات باستخدام الأحداث المرسلة من الخادم:

POST /api/run_sse

نص الطلب:

{
  "appName": "my_agent",
  "userId": "user123",
  "sessionId": "session456",
  "newMessage": {
    "role": "user",
    "parts": [
      {
        "text": "What is the capital of France?"
      }
    ]
  },
  "streaming": true
}

الاستجابة:

  • نوع المحتوى: text/event-stream
  • يبث الأحداث ككائنات JSON

تنسيق الحدث:

{
  "id": "evt_123",
  "timestamp": 1234567890,
  "author": "my_agent",
  "content": {
    "role": "model",
    "parts": [
      {
        "text": "The capital of France is Paris."
      }
    ]
  },
  "actions": {},
  "llm_response": {
    "content": {
      "role": "model",
      "parts": [
        {
          "text": "The capital of France is Paris."
        }
      ]
    }
  }
}

تشغيل الوكيل (دون بث)

نفّذ وكيلًا حتى الاكتمال واستلم جميع الأحداث في استجابة JSON واحدة:

POST /api/run

نص الطلب: له البنية نفسها لـ POST /api/run_sse (يتم تجاهل الحقل streaming):

{
  "appName": "my_agent",
  "userId": "user123",
  "sessionId": "session456",
  "newMessage": {
    "role": "user",
    "parts": [
      {
        "text": "What is the capital of France?"
      }
    ]
  }
}

الاستجابة:

  • نوع المحتوى: application/json
  • مصفوفة JSON تحتوي على كل حدث أنتجه التشغيل، بالترتيب، باستخدام تنسيق الحدث نفسه لـ /api/run_sse

تحاكي نقطة النهاية هذه مسار /run غير المتدفق في api_server الخاص بـ Google ADK.

إدارة الجلسات

إنشاء جلسة

إنشاء جلسة جديدة:

POST /api/sessions

نص الطلب:

{
  "appName": "my_agent",
  "userId": "user123",
  "sessionId": "session456"
}

الاستجابة:

{
  "id": "session456",
  "appName": "my_agent",
  "userId": "user123",
  "lastUpdateTime": 1234567890,
  "events": [],
  "state": {}
}

الحصول على جلسة

استرداد تفاصيل الجلسة:

GET /api/sessions/:app_name/:user_id/:session_id

الاستجابة:

{
  "id": "session456",
  "appName": "my_agent",
  "userId": "user123",
  "lastUpdateTime": 1234567890,
  "events": [],
  "state": {}
}

حذف جلسة

حذف جلسة:

DELETE /api/sessions/:app_name/:user_id/:session_id

الاستجابة:

  • الحالة: 204 No Content

سرد الجلسات

سرد جميع جلسات مستخدم:

GET /api/apps/:app_name/users/:user_id/sessions

الاستجابة:

[
  {
    "id": "session456",
    "appName": "my_agent",
    "userId": "user123",
    "lastUpdateTime": 1234567890,
    "events": [],
    "state": {}
  }
]

إدارة العناصر

سرد العناصر

سرد جميع عناصر جلسة:

GET /api/sessions/:app_name/:user_id/:session_id/artifacts

الاستجابة:

[
  "image1.png",
  "document.pdf",
  "data.json"
]

الحصول على عنصر

تنزيل عنصر:

GET /api/sessions/:app_name/:user_id/:session_id/artifacts/:artifact_name

الاستجابة:

  • نوع المحتوى: يُحدَّد حسب امتداد الملف
  • النص: محتوى ثنائي أو نصي

إدارة التطبيقات

سرد التطبيقات

سرد جميع الوكلاء المتاحين:

GET /api/apps
GET /api/list-apps  (legacy compatibility)

الاستجابة:

{
  "apps": [
    {
      "name": "my_agent",
      "description": "A helpful assistant"
    }
  ]
}

واجهة الويب

يتضمن الخادم واجهة ويب مضمّنة يمكن الوصول إليها عبر:

http://localhost:8080/ui/

إرسال مطالبة إلى وكيل، واتباع عملية تسليم متحركة بين أعضاء الفريق، وفحص بيانات القياس عن بُعد في واجهة المستخدم المضمّنة لبيئة التشغيل

تعمل البدء السريع خلال خمس دقائق على إنشاء خادم يستند إلى OpenAI وتفتح هذه الواجهة باستخدام أربعة أوامر. يستخدم التسجيل فريقًا لجعل حافة البنية النشطة مرئية؛ وتستخدم الوكلاء الطرفيون، والأدوات، والرسوم البيانية، وسير العمل، والوكلاء الآنيون، والوكلاء البعيدون الواجهة نفسها.

الميزات

  • التشغيلات التفاعلية: إرسال الرسائل والمرفقات وفحص Markdown المعروض بأمان، والنص المتدفق، واستدعاءات الأدوات، وحالات الفشل، وعمليات التسليم
  • إدارة الجلسات: إنشاء الجلسات وعرضها والتبديل بينها
  • طوبولوجيا الفريق المحمولة: التمييز بين حواف التفويض والعودة الدقيقة وعمليات تسليم التحكم، مع رسوم متحركة على نمط Studio Next للعضو النشط والحافة الواردة
  • مفتش وقت التشغيل: فحص الأحداث المرتبة، ونطاقات القياس عن بُعد المخصصة، وحالة المشاركة/الجلسة، وخدمات وقت التشغيل المُهيأة، واكتشاف A2A، ودعم بروتوكول تطبيقات UI/MCP
  • التشغيل الفوري: التعرّف على الوكلاء الفوريين، ودمج دلتا النسخ النصي، وتشغيل مخرجات PCM المكتملة كصوت WAV
  • عارض العناصر: عرض عناصر الجلسة وتنزيلها
  • سمات متجاوبة: تخطيطات للنظام والفاتح والداكن يمكن الوصول إليها باستخدام لوحة المفاتيح، دون أصول خارجية

مسارات واجهة المستخدم

  • / - يعيد التوجيه إلى /ui/
  • /ui/ - واجهة الدردشة الرئيسية
  • /ui/assets/* - الأصول الثابتة (CSS وJS والصور)
  • /ui/assets/config/runtime-config.json - إعدادات وقت التشغيل
  • /api/ui/agents/{name} - إمكانات الوكيل والتسلسل الهرمي وبيانات الطوبولوجيا المحمولة

تتم صيانة الواجهة في adk-server/webui، كما يتم تضمين بنية الإنتاج الخاصة بها في حزمة الخادم، ولذلك تستخدم المشاريع المنشأة وعمليات النشر واجهة وقت التشغيل نفسها دون خدمة واجهة أمامية منفصلة.

يتضمن عرض واجهة المستخدم لوقت التشغيل القابل للتشغيل جولات إرشادية ولقطات شاشة منفصلة مدعومة بـOpenAI لوكيل يستدعي الأدوات، وسير عمل بياني حتمي، وفريق تسليم محمول. يضيف معرض الوكلاء المتقدم جدولًا زمنيًا محيطًا، وتشغيلًا صوتيًا آنيًا عبر OpenAI، واكتشاف A2A، واكتشاف MCP 2026-07-28 مع مهام SEP-2663، بالإضافة إلى خدمات القياس عن بُعد والعناصر والذاكرة المُهيأة في خادم واحد. ترتبط علامة ADK Runtime التجارية بـadk-rust.com في علامة تبويب جديدة.

أمثلة العملاء

JavaScript/TypeScript

باستخدام Fetch API مع SSE:

async function runAgent(message) {
  const response = await fetch('http://localhost:8080/api/run_sse', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      appName: 'my_agent',
      userId: 'user123',
      sessionId: 'session456',
      newMessage: {
        role: 'user',
        parts: [{ text: message }]
      },
      streaming: true
    })
  });

  const reader = response.body.getReader();
  const decoder = new TextDecoder();

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    
    const chunk = decoder.decode(value);
    const lines = chunk.split('\n');
    
    for (const line of lines) {
      if (line.startsWith('data: ')) {
        const event = JSON.parse(line.slice(6));
        console.log('Event:', event);
      }
    }
  }
}

Python

باستخدام مكتبة requests:

import requests
import json

def run_agent(message):
    url = 'http://localhost:8080/api/run_sse'
    payload = {
        'appName': 'my_agent',
        'userId': 'user123',
        'sessionId': 'session456',
        'newMessage': {
            'role': 'user',
            'parts': [{'text': message}]
        },
        'streaming': True
    }
    
    response = requests.post(url, json=payload, stream=True)
    
    for line in response.iter_lines():
        if line:
            line_str = line.decode('utf-8')
            if line_str.startswith('data: '):
                event = json.loads(line_str[6:])
                print('Event:', event)

run_agent('What is the capital of France?')

cURL

# Create session
curl -X POST http://localhost:8080/api/sessions \
  -H "Content-Type: application/json" \
  -d '{
    "appName": "my_agent",
    "userId": "user123",
    "sessionId": "session456"
  }'

# Run agent with streaming
curl -X POST http://localhost:8080/api/run_sse \
  -H "Content-Type: application/json" \
  -d '{
    "appName": "my_agent",
    "userId": "user123",
    "sessionId": "session456",
    "newMessage": {
      "role": "user",
      "parts": [{"text": "What is the capital of France?"}]
    },
    "streaming": true
  }'

إعداد الخادم

منفذ مخصص

لاستخدام منفذ مخصص، عيّن PORT قبل بدء تشغيل الخادم المُنشأ:

PORT=3000 cargo run

خدمة عناصر مخصصة

وفّر خدمة العناصر الخاصة بك:

use adk_artifact::InMemoryArtifactService;

let artifact_service = Arc::new(InMemoryArtifactService::new());

Launcher::new(Arc::new(agent))
    .with_artifact_service(artifact_service)
    .run()
    .await

خدمة جلسات مخصصة

بالنسبة إلى عمليات النشر الإنتاجية، استخدم خدمة جلسات مستمرة:

use adk_session::SqliteSessionService;

// Note: This requires implementing a custom server setup
// The Launcher uses InMemorySessionService by default

معالجة الأخطاء

يستخدم API استجابات أخطاء مُهيكلة مع رموز حالة HTTP مشتقة من فئة الخطأ:

رمز الحالةالفئةالمعنى
200—نجاح
204—نجاح (بدون محتوى)
400invalid_inputطلب غير صالح — معلمات أو إعدادات غير صالحة
401unauthorizedبيانات اعتماد مفقودة أو غير صالحة
403forbiddenبيانات اعتماد صالحة، وأذونات غير كافية
404not_foundالمورد غير موجود
408timeoutانتهت مهلة العملية
429rate_limitedتم تجاوز حد معدل الطلبات في الخادم العلوي
500internalخطأ داخلي في الخادم
501unsupportedالميزة غير مدعومة
503unavailableالخدمة المصدرية غير متاحة

تنسيق استجابة الخطأ (المشكلة JSON):

{
  "error": {
    "code": "model.openai.rate_limited",
    "message": "OpenAI rate limit exceeded",
    "component": "model",
    "category": "rate_limited",
    "requestId": "req-abc123",
    "retryAfter": 5000,
    "upstreamStatusCode": 429
  }
}

يتم تضمين الحقول requestId وretryAfter وupstreamStatusCode عند توفرها (وإلا فتكون قيمتها null).

تكوين CORS

يُمكّن الخادم CORS المتساهل افتراضيًا، مما يسمح بالطلبات من أي مصدر. هذا مناسب للتطوير، ولكن ينبغي تقييده في بيئة الإنتاج.

القياس عن بُعد

يُهيّئ الخادم القياس عن بُعد تلقائيًا عند تشغيله. ويتم إخراج السجلات إلى stdout بتنسيق منظم.

مستويات السجل:

  • ERROR: أخطاء حرجة
  • WARN: تحذيرات
  • INFO: معلومات عامة (افتراضي)
  • DEBUG: تصحيح أخطاء مفصل
  • TRACE: تتبع مفصل جدًا

عيّن مستوى السجل باستخدام متغير البيئة RUST_LOG:

RUST_LOG=debug cargo run

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

  1. إدارة الجلسات: أنشئ جلسة دائمًا قبل تشغيل agent
  2. معالجة الأخطاء: تحقّق من رموز الحالة HTTP وتعامل مع الأخطاء بشكل مناسب
  3. البث: استخدم SSE للاستجابات الفورية؛ وحلّل الأحداث سطرًا بسطر
  4. الأمان: في بيئة الإنتاج، طبّق المصادقة وقيّد CORS
  5. الاستمرارية: استخدم SqliteSessionService أو PostgresSessionService لعمليات النشر في بيئة الإنتاج
  6. المراقبة: فعّل القياس عن بُعد وراقب السجلات بحثًا عن المشكلات

مثال متكامل

للحصول على هيكل خادم كامل وقابل للتشغيل، استخدم قالب API المعتمد من cargo-adk. يوضّح هذا:

  • الواجهة الأمامية: عميل HTML/JavaScript مع بث فوري
  • الواجهة الخلفية: agent ADK مع أدوات مخصصة للبحث وPDF
  • التكامل: استخدام كامل لـ REST API مع بث SSE
  • المصنوعات: إنشاء PDF وتنزيلها
  • إدارة الجلسات: إنشاء الجلسات ومعالجتها تلقائيًا

يوضح المثال نمطًا جاهزًا للإنتاج لبناء تطبيقات ويب مدعومة بالذكاء الاصطناعي باستخدام ADK-Rust.

البدء السريع:

cargo adk new my-api --template api
cd my-api
cargo run

الملفات:

  • الخلفية: adk-rust-guide/examples/deployment/full_stack_research.rs
  • الواجهة الأمامية: examples/research_paper/frontend.html
  • التوثيق: examples/research_paper/README.md
  • البنية: examples/research_paper/architecture.md

السابق: ← المشغّل | التالي: A2A البروتوكول →