نشر A2A على Hermes Agent: مكدّس بوابة Docker مرجعي
تشغيل A2A في الإنتاج يعني بناء جسر بين بروتوكولين لم يُصمما قط للتواصل مع بعضهما — والقيام بذلك خلف بوابة تُدير المصادقة وعزل المستأجرين والبث دون إسقاط الرموز. يحل مكدس المرجع هذا المشاكل الثلاث التي تعوق عمليات نشر A2A: ترجمة البروتوكول (JSON-RPC 2.0 إلى استكمالات المحادثة المتوافقة مع OpenAI)، ومطابقة البث (SSE لقطعات A2A إلى SSE لأحداث تشغيل Hermes)، والأمان متعدد المستأجرين (أمان مستوى الصف في PostgreSQL على كل استعلام). إذا كنت تحتاج إلى وكلاء على أُطر مختلفة لتفويض العمل لبعضهم البعض، فإن النمط هنا هو مسار النشر.
أبرز النقاط
- 3 طبقات، مكدّس Docker Compose واحد — يُدير SilvaEngine Gateway النقل والمصادقة، ويُدير A2A Daemon Engine منطق البروتوكول، ويجسر HermesAgentHandler إلى API المتوافق مع OpenAI الخاص بـ Hermes Agent. مستودع docker-a2a-hermes-agent-gateway يُغلّف الثلاثة معًا.
- 5 سطوح بروتوكول على منفذ واحد — JSON-RPC 2.0، وGraphQL، وتدفّق SSE، ودفع SSE، واكتشاف Agent Card، كلها خلف بوابة واحدة على المنفذ 8765 مع مصادقة JWT أو AWS Cognito.
- PostgreSQL Row-Level Security يُطبّق عزل المستأجرين — المفتاح المُركّب
partition_key = "{endpoint_id}#{Part-Id}"يُطبَّق على مستوى قاعدة البيانات عبر سياسات RLS على جداول A2A الأربعة كلها، لا في شيفرات التطبيق فحسب. - قيد الرسالة الواحدة في A2A SDK v2 يَصُوغ بنية التدفّق — يُصدِر الجسر قطع الرموز (token) إلى SSE في الزمن الحقيقي ورسالة Message مُجمّعة واحدة إلى SDK EventQueue بعد اكتمال التدفّق، مُتجنّبًا
InvalidAgentResponseError. - 15 فحص E2E عبر 5 سكربتات — من اختبارات الدخان غير المتدفّقة إلى خطوط معالجة SSE المتدفّقة الكاملة مع التحقق من التراجع إلى HTTP، كلها قابلة للتشغيل بـ
pip install requests.
يُعرّف بروتوكول Agent2Agent Protocol (A2A) كيفية اكتشاف وكلاء الذكاء الاصطناعي لبعضهم بعضًا، وتفويض المهام، وبثّها فيما بينهم عبر JSON-RPC 2.0. ويُكشف Hermes Agent (من Nous Research) عن API Server متوافق مع OpenAI على /v1/chat/completions وواجهة تدفّق SSE قائمة على runs على /v1/runs و/v1/runs/{id}/events. والاثنان لا يتحدثان اللغة ذاتها. فـ A2A يُرسل message/send بأجزاء مُنظَّمة؛ وHermes يستقبل حمولات إكمال الدردشة. ويبثّ A2A مُخرجات المهمة عبر SSE؛ بينما يبثّ Hermes فروق الرموز (token deltas) عبر أحداث run.
مستودع docker-a2a-hermes-agent-gateway يجسر تلك الفجوة في صورة حاوية واحدة ومكدّس docker compose واحد. ويُشغّل SilvaEngine Gateway مع تسجيل وحدة a2a_daemon_engine وحدها، كاشفًا سطح بروتوكول A2A الكامل وجاسرًا مهام A2A إلى نسخة Hermes Agent API Server عبر HTTP + SSE. وتُحفظ الحالة في خلفية PostgreSQL مُضمَّنة مع Row-Level Security لعزل المستأجرين.
يخطّط هذا المقال البنية ثلاثية الطبقات، ودورة حياة الطلب من عميل A2A إلى Hermes والعودة، وأنماط الإعداد والنشر، والمسائل التشغيلية لتشغيل A2A على Hermes Agent في الإنتاج. إنه جولة في نشر مرجعي — الموضوع هو النمط العام (جسر A2A بوساطة البوابة إلى أي أُطار وكلاء)، والمكدّس المتحقّق هو التنفيذ العملي.
البنية ثلاثية الطبقات
يَفصل المكدّد المسؤوليات إلى ثلاث طبقات، تَملك كل منها مكوّنٌ متميّز:
البوابة هي الخدمة الدائمة الوحيدة. وكلاً من Hermes و PostgreSQL هما شقيقان مُقيَّدان بملف التعريف (profile-gated) — اِدمجهما لمكدّس قائم بذاته، أو عطّل الملفات ووجّه HERMES_API_URL وPG_HOST إلى نسخ خارجية. وهذا يهمّ في الإنتاج: يمكنك تشغيل البوابة في VPC الخاص بك وتوجيهها إلى Postgres مُدار (RDS، Cloud SQL) ونسخة Hermes تعمل على عقدة GPU في مكان آخر.
الطبقة 1: SilvaEngine Gateway — النقل والمصادقة
SilvaEngine Gateway بوابة FastAPI للوصول المُصدَّق إلى الوحدات المُثبَّتة داخل العملية. وتُكشف مسارات GraphQL و REST الخاصة بالوحدات عبر ملف مسارات YAML قابل للإعداد — إضافة وحدة جديدة تتطلب تغييرات في الملف فحسب، دون أي شيفرات Python للبوابة. في هذا المكدّس، لا يُسجَّل سوى A2A Daemon Engine.
تَملك البوابة:
- المصادقة — JWT محلي (HS256) أو AWS Cognito (RS256 + JWKS)، يُحدَّد الاختيار عبر
GATEWAY_AUTH_PROVIDER - التوجيه — ملف YAML يربط مسارات URL بدوال الإرسال (dispatch) الخاصة بالوحدات
- دورة حياة عميل SSE — يحلّ
sse_managerلكل وحدة ويُدير اتصالات العملاء طويلة العمر - تحديد المعدل — تحديد معدل لكل IP في الذاكرة (
GATEWAY_RATE_LIMITطلبًا كلGATEWAY_RATE_WINDOWثانية) - الإرسال عبر مجموعة الخيوط — دوال الإرسال المتزامنة للوحدات تعمل في مجموعة خيوط قابلة للإعداد (
GATEWAY_DISPATCH_WORKERS، الافتراضي 32 في صورة Docker)
تَبني البوابة partition_key = "{endpoint_id}#{Part-Id}" من جزء مسار URL ومن ترويسة طلب Part-Id. كل طلب مُحدَّد بمستأجر يتطلب تلك الترويسة. ونقطة Agent Card على /{ep}/.well-known/agent-card.json عامة (بلا مصادقة) وفقًا لمواصفة A2A، لكنها تتطلب Part-Id مع ذلك لأن البطاقة تُحلّ لكل قسم (partition).
الطبقة 2: A2A Daemon Engine — منطق البروتوكول
a2a_daemon_engine ليس خدمة قائمة بذاتها. بل يُحمَّل كوحدة بوابة مُسجَّلة عبر deploy() في main.py، الذي يُعرّف ثلاث نقاط دخول تواجه البوابة:
| نقطة الدخول | مسار البوابة | الطريقة | الغرض |
|---|---|---|---|
a2a_core_graphql |
POST /{ep}/a2a_core_graphql |
POST | عمليات GraphQL CRUD للوكلاء والمهام والرسائل والإعدادات |
a2a |
POST /{ep}/a2a |
POST | بروتوكول A2A JSON-RPC (message/send، tasks/get، tasks/cancel، tasks/list) |
sse_message |
POST /{ep}/a2a_sse |
POST | رسالة A2A JSON-RPC + دفع إلى عملاء SSE |
تُكشف البوابة إضافيًا GET /{ep}/a2a_sse لتدفّق SSE. ولا يستمع الخفيّ على منفذه الخاص ولا يُشغّل خادم HTTP خاصًا به في الإنتاج. فكل النقل والمصادقة ودورة حياة عميل SSE تَملكها البوابة.
يوفّر الخفيّ:
- A2A SDK v1.0 — JSON-RPC عبر HTTP، مبنيّ على نمط الخادم الرسمي في A2A SDK
- Agent Card عامة على
/.well-known/agent-card.jsonمع دعم ETag و Last-Modified - آلة حالة المهمة —
submitted←working←input-required|completed|failed|canceled - استمرارية بخلفيتين — DynamoDB (PynamoDB) أو PostgreSQL (SQLAlchemy + Alembic). وصورة Docker تُجبر PostgreSQL.
- عزل متعدّد المستأجرين — مفاتيح أقسام مُركّبة (
{endpoint_id}#{part_id}) مع Row-Level Security في PostgreSQL - مُعالجات LLM قابلة للإضافة — اختيار
module_name/class_nameلكل وكيل في سجل الوكلاء
الطبقة 3: HermesAgentHandler — الجسر
مُعالج جسر Hermes (a2a_daemon_engine/handlers/a2a_hermes_handler.py) هو الشيفرات الوحيدة الخاصة بالإطار. ويُنفّذ واجهة ask_model(): يقبل أجزاء رسالة A2A والسياق، ويستدعي Hermes Agent API Server، ويحوّل الاستجابة إلى أجزاء رسالة A2A، وفي التدفّق يُمرّر فروق الرموز إلى قناة SSE.
يدعم المُعالج نمطي تنفيذ:
غير المتدفّق يَرتبط بـ POST /v1/chat/completions على Hermes API Server — نقطة النهاية المتوافقة مع OpenAI. ويحمل الطلب أجزاء رسالة A2A المُحوَّلة كحمولة إكمال دردشة. ويعالج Hermes الطلب ويُعيد استجابة واحدة. ويحوّل المُعالج الاستجابة إلى Message A2A بـ ROLE_AGENT ويُصدِرها إلى SDK EventQueue. ويستقبل العميل استجابة JSON-RPC واحدة تحوي النص الكامل للوكيل.
التدفّق يَرتبط بـ POST /v1/runs لإنشاء run، ثم يفتح اتصال SSE إلى GET /v1/runs/{id}/events. ويبثّ Hermes الأحداث عند حدوثها: فروق الرموز (message.delta)، وبيانات الاستدلال الوصفية (reasoning.available)، وإشعارات استدعاء/نتيجة الأدوات، وطلبات الموافقة (approval.required)، وأحداث دورة الحياة (run.created، run.completed، run.failed). ويُشغّل المُعالج حلقة تصريف (drain) في خيط خلفي. وكل حدث message.delta يُدفع إلى مُدير SSE الخاص بالبوابة للتسليم في الزمن الحقيقي للعملاء المتصلين. وعند وصول run.completed، يُصدَر النص المُجمَّع كـ Message A2A واحدة إلى SDK EventQueue.
دورة حياة الطلب
يَجتاز message/send واحد مع stream=true ثماني خطوات:
1. Client POST /{ep}/a2a {jsonrpc, method:"message/send", params}
Headers: Authorization: Bearer *** Part-Id:
2. Gateway auth (local JWT / Cognito) → route match from routes.yaml
→ partition_key = "{ep}#{Part-Id}"
3. a2a_daemon dispatch_a2a → A2ADaemonExecutor
→ resolve_agent(): agent metadata (DB) > setting dict > Config (env)
4. Handler HermesAgentHandler (A2A_AI_AGENT_MODULE / _CLASS)
5. Hermes POST {HERMES_API_URL}/v1/runs (Bearer HERMES_API_KEY)
GET {HERMES_API_URL}/v1/runs/{id}/events (SSE)
6. Broadcast token chunks → subscribers on GET /{ep}/a2a_sse
7. Persist task + messages written to PostgreSQL (a2a_* tables, RLS-scoped)
8. Response accumulated reply also returned in the HTTP JSON-RPC result الخطوة 8 مهمة: حتى مع التدفّق، تحمل استجابة HTTP الردّ الكامل. فالعميل الذي يفوّته إطارات SSE يمكنه التراجع إلى استجابة HTTP. وتتحقّق مجموعة اختبار E2E (test_hermes_sse_live.py) صراحةً من هذا التراجع في خطوتها 06.
أولوية حلّ الوكيل
يتبع حلّ الإعداد سلسلة أولوية: بيانات الوكيل الوصفية (DB) ← قاموس الإعداد ← افتراضات Config (متغيّرات البيئة). فالتجاوزات لكل وكيل تتغلب على الافتراضات العامة. ويمكن لوكلاءين أن يشيرا إلى إطارين مختلفين — أحدهما إلى Hermes للمهام الثقيلة الاستدلال، وآخر إلى مُعالج مختلف لمهام تنسيق سير العمل — ويبدو سطح بروتوكول A2A متماثلًا للوكيل المستدعي.
بالنسبة لوكيل مدعوم بـ Hermes، تبدو البيانات الوصفية المخزّنة في جدول a2a_agents كالتالي:
{
"module_name": "a2a_daemon_engine.handlers.a2a_hermes_handler",
"class_name": "HermesAgentHandler",
"hermes_api_url": "http://127.0.0.1:8642",
"hermes_api_key": "hermes-local-key",
"hermes_model": "hermes-agent",
"hermes_timeout": 300.0
}تتيح افتراضات متغيّرات البيئة (HERMES_API_URL، HERMES_API_KEY، HERMES_MODEL) للجسر الوصول إلى Hermes دون سجل وكيل في قاعدة البيانات — فدالة resolve_agent() في a2a_ai_agent_utility.py تتراجع إلى متغيّرات البيئة عند غياب سجل الوكيل. وهذا يعني أنك تستطيع تشغيل المكدّس وإرسال message/send دون تسجيل أي وكيل، وسيُوجَّه إلى Hermes باستخدام افتراضات البيئة.
تخطيط حالة A2A
يَرسم الجسر أحداث Hermes SSE إلى حالات مهمة A2A. وهذا الجدول هو قلب الجسر — كل تكامل إطار يُنتج جدولًا مكافئًا:
| حدث Hermes SSE | حالة مهمة A2A | إجراء الجسر |
|---|---|---|
run.created (يُعاد run_id) |
WORKING |
تسجيل run_id لدعم الإلغاء |
message.delta |
WORKING |
تجميع الرمز؛ إصداره إلى SSE لكل قطعة |
reasoning.available |
WORKING |
بيانات استدلال وصفية — بلا إصدار رموز |
tool.call / tool.result |
WORKING |
بيانات تنفيذ الأدوات فحسب |
approval.required |
INPUT_REQUIRED |
إصدار قطعة موافقة؛ تخزين pending_approval |
run.completed |
COMPLETED |
ضبط حدث التدفّق؛ تجميع النص النهائي |
run.failed |
FAILED |
إصدار قطعة خطأ؛ ضبط حالة FAILED |
POST /v1/runs/{id}/stop |
CANCELED |
إلغاء خارجي عبر tasks/cancel |
POST /v1/runs/{id}/approval |
(يُكمل الـ run) | يُحلّ عبر operation="approval_response" |
يُوثّق ملف HERMES_INTEGRATION.md في مستودع a2a_daemon_engine صيغة حدث Hermes الدقيقة، ومفاتيح الإعداد، وتفاصيل التدفّق من النهاية إلى النهاية.
الموافقة بتفاعل بشري عبر حدود الوكلاء
يدعم Hermes بوابات موافقة بتفاعل بشري — عندما يحتاج الوكيل إذنًا لتنفيذ فعل حسّاس، يتوقّف ويُصدِر طلب موافقة. ويُترجم الجسر ذلك إلى حالة A2A INPUT_REQUIRED، التي تُنبّه الوكيل المستدعي (أو المُشغّل البشري) بأن مدخلات لازمة. وتعود الاستجابة عبر POST /v1/runs/{id}/approval، فيُكمل الـ run.
هنا تَظهر قيمة نمط الجسر. فـ A2A يُعرّف INPUT_REQUIRED كحالة مهمة من الدرجة الأولى. وHermes يمتلك آلية موافقة خاصة به. ويَرسم الجسر أحدهما على الآخر، فيرى الوكيل المستدعي — الذي قد يكون نفسه عميل A2A يعمل على إطار مختلف تمامًا — انتقال حالة بروتوكول قياسيًا، لا تفصيلًا خاصًا بـ Hermes. ويمكن أن تشمل سلسلة تفويض وكلاء خطوةً تتطلب موافقة بشرية (تفويض شراء، أو قرار وصول بيانات، أو موافقة تسعيرة)، ويحمل بروتوكول A2A تلك البوابة بشفافية عبر حدود الأطر.
قيد A2A SDK v2 والإصلاح ذو المسارين
يفرض A2A SDK v2 (a2a-sdk==1.0.2) قيدين على مسار on_message_send صاغا تنفيذ الجسر:
- رسالة Message واحدة فحسب. إصدار عدة كائنات
Messageإلى SDK EventQueue يُثيرInvalidAgentResponseError: Multiple Message objects received. - بلا TaskStatusUpdateEvent. أحداث الحالة تُثير
InvalidAgentResponseError: Received TaskStatusUpdateEvent in message mode.
جسر ساذج سيُصدِر Message واحدة لكل فرق رمز — نمط التدفّق الطبيعي. ويرفضه الـ SDK. كما يرفض أحداث الحالة (WORKING، COMPLETED) على مسار message/send.
الإصلاح هو قناة إخراج ذو مسارين:
- SSE (يُديرها الجسر): تُدفع قطع الرموز إلى SSE في الزمن الحقيقي. فالعملاء المتصلون يرون المُخرجات المتدفّقة عند حدوثها. وأحداث الحالة (WORKING، COMPLETED، FAILED) تذهب إلى SSE أيضًا فحسب.
- SDK EventQueue: بعد اكتمال التدفّق، تُصدَر
Messageواحدة مُجمّعة تحوي نص الاستجابة الكامل إلى SDK EventQueue. وهذا ما تُعيده استجابةmessage/sendJSON-RPC.
يَحصل العميل على تدفّق في الزمن الحقيقي عبر SSE واستجابة JSON-RPC نظيفة برسالة واحدة عبر الـ SDK. وكلتا القناتين تعملان؛ ولا تنتهك أيٌّ منهما قيود الـ SDK.
سطوح البروتوكول على منفذ واحد
تُكشف البوابة خمسة سطوح بروتوكول على منفذ واحد (الافتراضي 8765):
| البروتوكول | المسار | مصادقة | الغرض |
|---|---|---|---|
| GraphQL | POST /{ep}/a2a_core_graphql |
نعم | استعلامات/طفرات A2A الأساسية (الوكلاء، المهام، الرسائل، الإعدادات) |
| JSON-RPC 2.0 | POST /{ep}/a2a |
نعم | بروتوكول A2A: message/send، tasks/get، tasks/cancel، tasks/list |
| SSE (تدفّق) | GET /{ep}/a2a_sse |
نعم | تدفّق أحداث مهمة A2A طويل العمر لكل قسم |
| SSE (دفع) | POST /{ep}/a2a_sse |
نعم | رسالة JSON-RPC + دفع إلى عملاء SSE المتصلين |
| Agent Card | GET /{ep}/.well-known/agent-card.json |
عام | مستند اكتشاف A2A (ترويسة Part-Id لا تزال لازمة) |
شكل وسائط message/send JSON-RPC التي تستخدمها حُزم الاختبار:
{
"message": {
"role": "ROLE_USER",
"parts": [{ "text": "Say hello from A2A" }]
},
"metadata": {
"operation": "task_execution",
"agent_uuid": "a2a-hermes-agent",
"stream": true,
"task_data": { "task_id": "my-task-001", "task_type": "hermes_test" },
"system_prompt": "You are a concise assistant.",
"conversation_history": []
}
}يحدد حقل metadata.operation مسار التنفيذ: task_execution لعمليات الوكيل مع دعم التدفّق، وmessage_response للدردشة غير المتدفّقة. ويستهدف agent_uuid وكيلًا محددًا في السجل. وتُفعّل راية stream البثّ عبر SSE. أما task_data.task_id فهو مُعرّف يوفّره المُستدعي يستخدمه tasks/get وtasks/cancel.
SSE لكل قسم، لا لكل مهمة. فكل مهمة في {ep}#{Part-Id} تُبثّ إلى كل مُشتركي ذلك القسم. وترتيب العمليات مهم: اِربط مستمع SSE قبل إرسال الرسالة، وإلا فاتتك قطع الرموز المبكّرة.
الاستمرارية وتعدّد المستأجرين
تُجبر صورة Docker على db_backend=postgresql — ولا يُدعَم DynamoDB. ويستخدم الخفيّ أسماء جداول حرفية بلا بادئة:
| الجدول | ما يحويه |
|---|---|
a2a_agents |
سجلات الوكلاء + بيانات المُعالج/النموذج الوصفية لكل وكيل |
a2a_tasks |
دورة حياة المهمة + الحالة |
a2a_messages |
أدوار الرسائل لكل مهمة |
a2a_settings |
قواميس إعداد لكل قسم |
لأن الأسماء بلا بادئة، لا تُشارك PG_DB مع وحدة أخرى تستخدم الأسماء ذاتها.
يستخدم عزل المستأجرين Row-Level Security في PostgreSQL. ويُضبط المتغيّر الجلسي app.tenant_id على partition_key للطلب ("{endpoint_id}#{Part-Id}")، وتُحدّ سياسات RLS كل استعلام به. وتُنشأ الجداول والسياسات تلقائيًا عند بدء البوابة عندما initialize_tables=1. وهذا يعني أن مُرشّح partition_key منسيًّا في شيفرات التطبيق لا يمكن أن يُسرّب صفوفًا عبر المستأجرين — فقاعدة البيانات تُطبّق الحدّ.
يَعيش تنفيذ RLS في a2a_daemon_engine/utils/rls.py (set_rls_context وcreate_rls_policies) وفي التهجير 0005_enable_rls_policies. وتُشغّل دالة set_rls_context أمر SET app.tenant_id لكل طلب على الاتصال، وتُفعّل create_rls_policies وتُجبر RLS بسياسة tenant_isolation على جداول A2A الأربعة كلها. وRLS خامل في وضع DynamoDB.
النشر باستخدام Docker Compose
يَملك المكدّس خدمة دائمة واحدة وشقيقين اختياريين مُقيَّدين بملف التعريف:
| الخدمة | اسم الحاوية | دائمة؟ | الملف | الغرض |
|---|---|---|---|---|
a2a-gateway |
a2a-hermes-gateway |
نعم | — | SilvaEngine Gateway (مسارات A2A فحسب) + جسر Hermes |
postgres |
a2a-postgres |
اختياري | postgres |
خلفية استمرارية PostgreSQL مُضمَّنة |
hermes |
container-hermes |
اختياري | hermes |
Hermes Agent مُضمَّن (API متوافق مع OpenAI + لوحة) |
COMPOSE_PROFILES هو المفتاح الواحد للأخوين معًا:
| القيمة | الخدمات المُبدَأة |
|---|---|
| فارغ | البوابة فحسب (Postgres خارجي + Hermes خارجي) |
postgres |
البوابة + Postgres مُضمَّن |
hermes |
البوابة + Hermes مُضمَّن (Postgres خارجي) |
postgres,hermes |
البوابة + Postgres و Hermes مُضمَّنين (الافتراضي) |
عند تضمين أخٍ، اِجعل مرجع المضيف يشير إلى اسم الخدمة: PG_HOST=postgres وHERMES_API_URL=http://hermes:<API_SERVER_PORT>. وعندما يكون الأخ خارجيًا، وجّه تلك المتغيّرات إلى نسختك الخاصة (مثلًا PG_HOST=host.docker.internal، HERMES_API_URL=http://host.docker.internal:8642).
البداية السريعة
cp .env.example .env
# Fill in: JWT_SECRET_KEY, ADMIN_PASSWORD, API_SERVER_KEY,
# HERMES_API_KEY (= API_SERVER_KEY), HERMES_MODEL_PROVIDER + provider key, HERMES_MODEL
mkdir -p www/hermes www/projects
DOCKER_BUILDKIT=1 docker compose build
docker compose up -d # COMPOSE_PROFILES=postgres,hermes is the default
docker compose ps # wait for (healthy)
curl -f http://localhost:8765/health
pip install requests
python test_hermes_hello.py # end-to-end smoke testكلاً من silvaengine_gateway وa2a_daemon_engine مُثبَّتان عبر pip من git داخل الصورة (بلا تحميل شيفرة مصدرية من المضيف). والصورة عامة ومُدارة بالكامل عبر متغيّرات البيئة — لا أسرار مُدمجة. وتُستنسخ الوحدات من مستودعات GitHub العامة تحت ideabosque عبر git+https — بلا اعتماديات أو مفتاح SSH deploy.
فَخّ التعليق المُضمَّن في .env
لا يُزيل مُحلّل env_file في Docker Compose التعليقات المُضمَّنة. فسطر مثل:
HERMES_API_KEY=hermes-local-key # token for Hermesيضبط HERMES_API_KEY على السلسلة الحرفية hermes-local-key # token for Hermes (التعليق مُضمَّن)، ما يكسر المصادقة بصمت. والقواعد: لا تضع شيئًا بعد القيمة في أي سطر KEY=value. وضع الملاحظات على سطور # تعليق مستقلة فوق المتغيّر.
التحقق: 15 فحص E2E عبر 5 سكربتات
يأتي المكدّس مع حُزم اختبار Python قائمة بذاتها (الاعتمادية الوحيدة: requests). وتحمّل ./.env، وتحلّ أو تَسكّ JWT للبوابة، وتتحدث إلى المكدّد الجاري:
| السكربت | النوع | ما يفعله |
|---|---|---|
test_hermes_hello.py |
دخان | message/send غير متدفّق، يطبع الردّ |
test_hermes_hello_sse.py |
دخان | مُحفّز واحد يُعاد تدفّقه عبر SSE |
test_hermes_gateway_live.py مجموعة E2E |
9 فحوص: صحة Hermes، صحة البوابة، بطاقة الوكيل، GraphQL ping، message/send، tasks/get، tasks/list، tasks/cancel، مسار الفشل |
|
test_hermes_sse_live.py |
مجموعة E2E | 6 فحوص: صحة ×2، اتصال SSE، قطع رموز حيّة، حالة COMPLETED، التراجع إلى HTTP |
test_hermes_chatbot.py |
تفاعلي | REPL على سطح A2A مع تدفّق SSE حيّ |
كل السكربتات غير التفاعلية تطبع PASS/FAIL لكل خطوة وتُخرج رمزًا غير صفري عند الفشل، فعملها كبوابات CI. وتُشغّل مجموعة اختبارات الوحدة (test_hermes_handler.py) 24 اختبارًا مع HTTP مُزيف عبر httpx.MockTransport — بلا خدمات لازمة.
الأنماط التشغيلية
تغييرات المسار دون إعادة البناء
routes.yaml مُحمَّل ربطًا للقراءة فقط داخل الحاوية. اِحرص على تعديل ملف المضيف وأعد تشغيل عملية البوابة — بلا إعادة بناء:
make restartبعد تغيير في المنبع
لأن silvaengine_gateway وa2a_daemon_engine مُثبَّتان عبر pip من git وقت البناء، يتطلب التغيير في المنبع إعادة بناء بـ --no-cache كي تُعيد طبقة git استنساخ أحدث @main:
DOCKER_BUILDKIT=1 docker compose build --no-cache
docker compose up -d --force-recreateلا يوجد تثبيت إصدار — فـ @main هدف متحرّك. اِثبّت وسمًا أو إيداعًا في requirements-modules.txt إن احتجت قابلية إعادة الإنتاج.
التوسّع إلى ما بعد عامل واحد
حالة المهمة في الذاكرة، وعدّادات تحديد المعدل، وسجل عميل SSE هي لكل عملية. مع GATEWAY_WORKERS > 1، اِنتقل إلى خلفيات مشتركة (GATEWAY_TASK_BACKEND=dynamodb، GATEWAY_RATE_LIMIT_BACKEND=dynamodb، بالإضافة إلى region_name واعتماديات aws_*) واستخدم جلسات لاصقة لـ SSE. والإعداد الافتراضي يُبدئ عملية Uvicorn واحدة.
افتراضات أمان يجب تغييرها
JWT_SECRET_KEY=change-me-in-production— استبدلها بـopenssl rand -hex 32ADMIN_PASSWORD=change-me— استبدلها بكلمة مرور حقيقيةPOSTGRES_PASSWORD=silvaengine— استبدلها بكلمة مرور حقيقيةGATEWAY_CORS_ORIGINS=*يسمح بأي مصدر بلا اعتماديات. اضبط قائمة صريحة إن احتجت كوكيز/اعتماديات.- يَحمّل Hermes المُضمَّن مقبس Docker للمضيف (
/var/run/docker.sock). وذلك فعليًا صلاحية root على المضيف لأي شيء داخل تلك الحاوية. فشغّل ملفhermesعلى مضيف تَملكه فحسب، وامنع التحميل إن لم يحتج الوكيل إطلاق حاويات. - لوحة Hermes افتراضية مُفعَّلة على المنفذ 9119 باعتماديات basic-auth فارغة. اضبط
HERMES_DASHBOARD_BASIC_AUTH_*أو اربط المنفذ بـ localhost قبل كشف المضيف. - RLS هو حدّ المستأجر. والمُستدعي القادر على ضبط
Part-Idاعتباطيًا يقرأ بيانات ذلك القسم — فاعتبرPart-Idمدخلًا ذا صلة بالصلاحية في أي واجهة أمامية تضعها أمام هذا.
ما يُتيحه هذا
يمنحك المكدّس ثلاثي الطبقات ثلاث قدرات يصعب تجميعها من الصفر:
1. الامتثال لبروتوكول A2A دون إعادة كتابة Hermes. أي عميل A2A يستطيع اكتشاف الوكيل المدعوم بـ Hermes عبر بطاقته (Agent Card)، وإرسال المهام عبر message/send، وتدفّق الاستجابات عبر SSE، وتتبّع دورة حياة المهمة عبر حالات A2A القياسية. ولا يعرف العميل أن الوكيل البعيد يُشغّل Hermes — بل يرى نقطة نهاية A2A بواجهة JSON-RPC.
2. خدمة وكلاء متعدّدي المستأجرين من بوابة واحدة. ترويسة Part-Id مُقترنة بـ PostgreSQL RLS تعني أن نسخة بوابة واحدة تَخدم مستأجرين متعدّدين بعزل صارم على مستوى قاعدة البيانات. ويحصل كل مستأجر على سجل وكلاء خاص، وتاريخ مهام خاص، ومخزن رسائل خاص — كلها في الجداول الأربعة ذاتها، مُحدّدة بـ partition_key.
3. الموافقة بتفاعل بشري عبر حدود الوكلاء. بوابات موافقة Hermes تُرسم إلى حالات A2A INPUT_REQUIRED. ويمكن أن تشمل سلسلة تفويض وكلاء خطوةً تتطلب موافقة بشرية، ويحمل بروتوكول A2A انتقال الحالة ذلك عائدًا إلى الوكيل المُنشئ أو المُشغّل — بصرف النظر عن الإطار الذي يُشغّل كل وكيل في السلسلة.
التنفيذ المرجعي يدعم أيضًا إرسال AWS Lambda لـ A2A بلا خادم، ونقل gRPC تجريبي بتدفّق ثنائي الاتجاه، واستمرارية بخلفيتين (DynamoDB أو PostgreSQL). ويحتوي مستودع a2a_daemon_engine ودليل تكامل Hermes على التنفيذ الكامل، ومرجع الإعداد، وتفاصيل تخطيط الحالة. ويُوثّق مستودع SilvaEngine Gateway نظام ملف المسارات، ومزوّدي المصادقة، والتهيئة التلقائية للوحدات.
قراءات ذات صلة
- MCP + A2A: البروتوكولان خلف كل نظام ذكاء اصطناعي وكلائي إنتاجي — الأدوار المُتكاملة لـ MCP (الوكلاء إلى الأدوات) و A2A (الوكلاء إلى الوكلاء) في مكدّس البروتوكول ثنائي الطبقات
- دمج A2A مع أُطر الوكلاء القائمة: عرض تطبيقي باستخدام Hermes Agent — نمط الجسر العام وكيف ينطبق على OpenClaw وغيرها من الأُطر بعد Hermes
- معيار شيفرات وحدات MCP — النمط البنيوي لوحدات الوكلاء الجاهزة للإنتاج، ينطبق على شيفرات مُعالجات A2A أيضًا
يحتاج مُوزّع من الفئة المتوسطة وكلاء تسعير تتحدث إلى وكلاء كتالوج تتحدث إلى وكلاء مخزون — كلٌ مدعوم بإطار مختلف، وكلٌ تَملكه فريق مختلف. ويمنح A2A تلك الوكلاء بروتوكولًا مشتركًا. وتتيح طبقة جسر لـ Hermes Agent المشاركة دون إعادة كتابة داخله. ويُغلّف docker-a2a-hermes-agent-gateway ذلك الجسر في صورة حاوية واحدة مع استمرارية PostgreSQL، وتعدّد مستأجرين بـ RLS، و15 فحص E2E.
اطلب بناءً مُحدَّد النطاق
اكتشاف لأسبوع. تحصل على جرد نظام، وخريطة سير عمل، ونطاق ثابت — سواء تبني معنا أم لا.
هل تريد هذا مبنياً لأنظمتك؟
كل وثيقة هنا من عمل إنتاجي حقيقي. إذا كان لديك نظام مُستهدَف وسير عمل في الذهن، نستطيع تحديد نطاق بناء في أسبوع واحد.
اطلب بناءً محدد النطاقاكتشاف مدته أسبوع واحد. تحصل على جرد للأنظمة وخريطة لسير العمل ونطاق ثابت — سواء بنيت معنا أم لا.