فاز 16 · درس 3

پروتکل‌های ارتباطی در سامانه‌های چندایجنت: راهنمای MCP ،A2A ،ACP و ANP

نوع
ساختنی
زمان
حدود 120 دقیقه
زبان‌ها
TypeScript
پیش‌نیاز
فاز 14 (مهندسی ایجنت)، درس 16.01 (چرا سیستم‌های چندایجنت؟)
چهار مسیر شبکه‌ای نورانی با نمادهای متمایز که در یک گره مرکزی به هم متصل شده‌اند
فهرست مطالب
  1. اهداف یادگیری
  2. صورت مسئله
  3. مفاهیم پایه
  4. چشم‌انداز پروتکل‌ها
  5. مرور MCP
  6. پروتکل A2A (پروتکل Agent2Agent)
  7. پروتکل ACP (پروتکل ارتباطی ایجنت‌ها)
  8. پروتکل ANP (پروتکل شبکه‌ی ایجنت‌ها)
  9. مقایسه‌ی پروتکل‌ها (اصلاح‌شده)
  10. همکاری این پروتکل‌ها در کنار هم
  11. پیاده‌سازی از صفر
  12. گام 1: انواع پیام‌های پایه
  13. گام 2: کارت ایجنت و رجیستری A2A
  14. گام 3: چرخه‌ی حیات تسک در A2A
  15. گام 4: ردپای حسابرسی به سبک ACP
  16. گام 5: اعتبارسنجی هویت به سبک ANP
  17. گام 6: گیت‌وی پروتکل (Protocol Gateway)
  18. گام 7: اتصال همه‌ی بخش‌ها به یکدیگر
  19. چالش‌ها و خطاهای رایج
  20. استفاده از ابزارهای واقعی
  21. پیاده‌سازی‌های موجود
  22. انتخاب پروتکل مناسب
  23. خروجی این درس
  24. تمرین‌ها
  25. اصطلاحات کلیدی
  26. برای مطالعه‌ی بیشتر

وقتی یک سامانه‌ی هوش مصنوعی را به چند ایجنت تخصصی (مثل پژوهشگر، برنامه‌نویس و ناظر) تقسیم می‌کنید، بزرگ‌ترین چالش فنی خیلی زود نمایان می‌شود: چگونه این ایجنت‌ها بدون اتکا به متن‌های بدون ساختار، ناهماهنگی در اسکیما یا ورود به بن‌بست، با هم کار کنند؟ ردوبدل کردن رشته‌های خام متنی در محیط‌های جدی عملیاتی پایدار نیست و خیلی زود سیستم را شکننده می‌کند.

برای حل این مشکل، صنعت هوش مصنوعی به سمت پروتکل‌های استاندارد ارتباطی حرکت کرده است. چهار پروتکل اصلی یعنی MCP (برای اتصال به ابزارها)، A2A (برای تعامل و واگذاری کار میان ایجنت‌ها)، ACP (برای حسابرسی سازمانی و ردیابی مسیر استدلال) و ANP (برای احراز هویت غیرمتمرکز) طراحی شده‌اند تا قراردادهای تبادل داده را استاندارد کنند.

در این درس ساختار داخلی این چهار استاندارد، فرمت تبادل داده و چرخه‌ی حیات تسک‌ها را بررسی می‌کنید و سپس در یک پیاده‌سازی گام‌به‌گام با TypeScript، این پروتکل‌ها را درون یک معماری یکپارچه به هم متصل خواهید کرد.

ایجنت‌هایی که زبان مشترکی ندارند یک تیم نیستند؛ غریبه‌هایی هستند که در خلأ فریاد می‌زنند.

اهداف یادگیری

  • پیاده‌سازی کشف و فراخوانی ابزار در MCP تا ایجنت‌ها بتوانند از ابزارهای ارائه‌شده روی سرورهای خارجی استفاده کنند
  • ساخت کارت ایجنت (Agent Card) در A2A و اندپوینت وظیفه برای واگذاری کار از یک ایجنت به ایجنت دیگر روی بستر HTTP
  • مقایسه‌ی MCP (دسترسی به ابزار)، A2A (ایجنت با ایجنت)، ACP (حسابرسی سازمانی) و ANP (اعتماد غیرمتمرکز) و توضیح این‌که هر پروتکل کدام مسئله را حل می‌کند
  • اتصال چند پروتکل به یکدیگر در قالب سامانه‌ای یکپارچه که در آن ایجنت‌ها ابزارها را از طریق MCP کشف کرده و وظایف را با A2A واگذار می‌کنند

صورت مسئله

سامانه‌ی خود را به چند ایجنت تقسیم می‌کنید: پژوهشگر، برنامه‌نویس، ارزیاب. هرکدام در کار فردی خود عالی هستند؛ اما حالا باید کاری کنید که واقعاً با یکدیگر گفت‌وگو کنند.

اولین راهکاری که به ذهن می‌رسد مشخص است: ردوبدل کردن رشته‌های متنی ساده. پژوهشگر یک تکه متن برمی‌گرداند و برنامه‌نویس آن را با هر روشی که بتواند پارس می‌کند. این روش تا زمانی کار می‌کند که برنامه‌نویس خلاصه‌ی پژوهش را اشتباه متوجه نشود، دو ایجنت در انتظار یکدیگر وارد بن‌بست (deadlock) نشوند، یا نیازی نباشد ایجنت‌های ساخته‌شده توسط تیم‌های مختلف با هم همکاری کنند. ناگهان رویکردِ «فقط ارسال رشته‌ی متنی» از هم می‌پاشد.

این دقیقاً مسئله‌ی پروتکل ارتباطی است. بدون قراردادی مشترک برای نحوه‌ی تبادل اطلاعات میان ایجنت‌ها، سیستم‌های چندایجنت شکننده، غیرقابل‌حسابرسی و فاقد قابلیت مقیاس‌پذیری فراتر از چند ایجنتی خواهند بود که خودتان به‌تنهایی نوشته‌اید.

اکوسیستم هوش مصنوعی با چهار پروتکل به این چالش پاسخ داده است که هرکدام بخش متفاوتی از مسئله را حل می‌کنند:

  • MCP برای دسترسی به ابزارها
  • A2A برای همکاری ایجنت با ایجنت
  • ACP برای حسابرسی‌پذیری سازمانی
  • ANP برای هویت و اعتماد غیرمتمرکز

در این درس عمیق می‌شویم. فرمت‌های واقعی انتقال پیام (wire formats) را از متن هر استاندارد بررسی می‌کنید، پیاده‌سازی‌های عملی می‌سازید و هر چهار مورد را به سامانه‌ای واحد متصل می‌کنید.

مفاهیم پایه

چشم‌انداز پروتکل‌ها

این چهار پروتکل را لایه‌هایی در نظر بگیرید که هرکدام به پرسش متفاوتی پاسخ می‌دهند:

ANP — چگونه ایجنت‌ها به غریبه‌ها اعتمادکنند؟هویت غیرمتمرکز (DID)، رمزنگاری E2EE،متاسروتکلA2A — چگونه ایجنت‌ها روی اهداف همکاریکنند؟کارت‌های ایجنت، چرخه‌ی حیات تسک،استریمینگ، مذاکرهACP — چگونه ایجنت‌ها در سامانه‌هایقابل‌حسابرسی گفت‌وگو کنند؟اجراها، متادیتای مسیر، تداوم نشستMCP — چگونه یک ایجنت از ابزار استفادهکند؟کشف ابزار، اجرا، اشتراک‌گذاری زمینه

این پروتکل‌ها رقیب یکدیگر نیستند؛ مسائل مختلفی را در سطوح متفاوتی حل می‌کنند.

مرور MCP

به MCP در فاز 13 به‌صورت مفصل پرداخته شده است. یک مرور سریع: پروتکل MCP نحوه‌ی اتصال مدل زبانی بزرگ (LLM) به ابزارها و منابع داده‌ی خارجی را استاندارد می‌کند. این یک پروتکل کلاینت-سرور است که در آن ایجنت (کلاینت)، ابزارهای ارائه‌شده توسط سرور را کشف و فراخوانی می‌کند.

سرور MCP(پایگاه‌داده، API، فایل‌ها)ایجنت (کلاینت)سرور MCP(پایگاه‌داده، API، فایل‌ها)ایجنت (کلاینت)فهرست ابزارهاتعاریف ابزارفراخوانی ابزار Xنتیجه

ارتباط در MCP از نوع ایجنت با ابزار است و کمکی به گفت‌وگوی ایجنت‌ها با یکدیگر نمی‌کند.

پروتکل A2A (پروتکل Agent2Agent)

سازنده: Google (اکنون تحت نظارت Linux Foundation با شناسه lf.a2a.v1) نسخه‌ی مشخصات: 1.0.1 صورت مسئله: ایجنت‌های خودمختار چگونه با یکدیگر همکاری، مذاکره و تبادل وظایف می‌کنند؟

پروتکل A2A برای همکاری همتا‌به‌همتای ایجنت‌ها طراحی شده است. همان‌طور که MCP ایجنت را به ابزارها متصل می‌کند، A2A ایجنت را به ایجنت‌های دیگر وصل می‌کند. هر ایجنت یک کارت ایجنت در آدرسی شناخته‌شده (well-known URL) منتشر می‌کند و سایر ایجنت‌ها آن را کشف کرده، با آن مذاکره می‌کنند و وظایف را به آن می‌سپارند.

نحوه‌ی کارکرد A2A

ایجنت راه‌دورایجنت کلاینتایجنت راه‌دورایجنت کلاینتalt[پولینگ][استریمینگ]GET /.well-known/agent-card.jsonکارت ایجنت (مهارت‌ها، حالت‌ها، امنیت)POST /message:send (returnImmediately)وظیفه (TASK_STATE_SUBMITTED یا TASK_STATE_WORKING)GET /tasks/{id}وضعیت وظیفه + دست‌سازه‌هاPOST /message:streamSSE: statusUpdateSSE: artifactUpdateSSE: statusUpdate TASK_STATE_COMPLETED، استریم بسته شد

کارت ایجنت در دنیای واقعی

کارت ایجنت واقعی A2A در محیط عملیاتی چنین ساختاری دارد که از مسیر GET /.well-known/agent-card.json ارائه می‌شود:

{
  "name": "Research Agent",
  "description": "Searches documentation and summarizes findings",
  "version": "1.0.0",
  "supportedInterfaces": [
    {
      "url": "https://research-agent.example.com/a2a/v1",
      "protocolBinding": "JSONRPC",
      "protocolVersion": "1.0"
    },
    {
      "url": "https://research-agent.example.com/a2a/rest",
      "protocolBinding": "HTTP+JSON",
      "protocolVersion": "1.0"
    }
  ],
  "provider": {
    "organization": "Your Company",
    "url": "https://example.com"
  },
  "capabilities": {
    "streaming": true,
    "pushNotifications": false
  },
  "defaultInputModes": ["text/plain", "application/json"],
  "defaultOutputModes": ["text/plain", "application/json"],
  "skills": [
    {
      "id": "web-research",
      "name": "Web Research",
      "description": "Searches the web and synthesizes findings",
      "tags": ["research", "search", "summarization"],
      "examples": ["Research the latest changes in React 19"]
    },
    {
      "id": "doc-analysis",
      "name": "Documentation Analysis",
      "description": "Reads and analyzes technical documentation",
      "tags": ["docs", "analysis"],
      "inputModes": ["text/plain", "application/pdf"],
      "outputModes": ["application/json"]
    }
  ],
  "securitySchemes": {
    "bearer": {
      "httpAuthSecurityScheme": {
        "scheme": "Bearer",
        "bearerFormat": "JWT"
      }
    }
  },
  "securityRequirements": [{ "schemes": { "bearer": { "list": [] } } }]
}

نکات کلیدی که باید به آن‌ها توجه کنید:

  • مهارت‌ها (Skills): توانمندی‌های ایجنت را تعریف می‌کنند. هر مهارت شناسه، برچسب‌ها و انواع MIME ورودی/خروجی پشتیبانی‌شده دارد. ایجنت کلاینت از این طریق تصمیم می‌گیرد که آیا ایجنت راه‌دور توانایی انجام درخواستش را دارد یا خیر.
  • supportedInterfaces: اتصالات پروتکل (protocol bindings) مختلف را فهرست می‌کند. یک ایجنت واحد می‌تواند به‌صورت هم‌زمان با رابط‌های JSON-RPC ،REST و gRPC کار کند.
  • امنیت: امنیت درون کارت تعبیه شده است: securitySchemes نام هر طرح امنیتی را مشخص می‌کند و securityRequirements معلوم می‌سازد که کدام‌یک اعمال می‌شوند. کلاینت پیش از ارسال حتی یک درخواست، می‌داند به چه اعتبارسنجی نیاز دارد.

چرخه‌ی حیات وظیفه (تسک)

تسک‌ها واحد اصلی کار در A2A هستند. آن‌ها در میان وضعیت‌های مشخصی جابه‌جا می‌شوند (در نمودار، پیشوند TASK_STATE_ که در فرمت انتقال پیام همراهِ هر وضعیت می‌آید، برای سادگی حذف شده است):

نیاز به اطلاعات بیشترارسال داده توسط کلاینتموفقیتخطالغو توسط کلاینترد توسط ایجنتSUBMITTEDWORKINGINPUT_REQUIREDCOMPLETEDFAILEDCANCELEDREJECTEDوضعیت‌های پایانیتغییرناپذیرند.پیگیری‌ها وظایفجدیدی رادر همان contextIdایجاد می‌کنند.

تمام 8 وضعیت (مشخصات پروتکل وضعیت UNSPECIFIED را هم به‌عنوان گزینه‌ی احتیاطی تعریف کرده که اینجا حذف شده است):

وضعیت پایانی است؟ مفهوم
TASK_STATE_SUBMITTED خیر دریافت شد، هنوز پردازش آغاز نشده است
TASK_STATE_WORKING خیر فعالانه در حال پردازش است
TASK_STATE_INPUT_REQUIRED خیر ایجنت به اطلاعات بیشتری از کلاینت نیاز دارد
TASK_STATE_AUTH_REQUIRED خیر نیاز به احراز هویت دارد
TASK_STATE_COMPLETED بله با موفقیت به پایان رسید
TASK_STATE_FAILED بله با خطا به پایان رسید
TASK_STATE_CANCELED بله پیش از تکمیل لغو شد
TASK_STATE_REJECTED بله ایجنت تسک را نپذیرفت

به‌محض اینکه یک تسک به وضعیتی پایانی برسد، تغییرناپذیر (immutable) می‌شود و پیام دیگری نمی‌پذیرد. پیام‌های بعدی باید تسک جدیدی در همان contextId بسازند.

ساختار انتقال پیام (Wire Format)

پروتکل A2A از JSON-RPC 2.0 استفاده می‌کند. نمونه‌ای واقعی از تبادل پیام به شکل زیر است:

کلاینت پیامی ارسال می‌کند:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "SendMessage",
  "params": {
    "message": {
      "messageId": "msg-001",
      "role": "ROLE_USER",
      "parts": [{ "text": "Research React 19 compiler features" }]
    },
    "configuration": {
      "acceptedOutputModes": ["text/plain", "application/json"],
      "historyLength": 10
    }
  }
}

ایجنت با ایجاد یک تسک پاسخ می‌دهد:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "task": {
      "id": "task-abc-123",
      "contextId": "ctx-xyz-789",
      "status": {
        "state": "TASK_STATE_COMPLETED",
        "timestamp": "2026-03-27T10:30:00Z"
      },
      "artifacts": [
        {
          "artifactId": "art-001",
          "name": "research-results",
          "parts": [{
            "data": {
              "findings": [
                "React 19 compiler auto-memoizes components",
                "No more manual useMemo/useCallback needed",
                "Compiler runs at build time, not runtime"
              ]
            },
            "mediaType": "application/json"
          }]
        }
      ]
    }
  }
}

استریمینگ از طریق SSE:

POST /message:stream HTTP/1.1
Content-Type: application/a2a+json
A2A-Version: 1.0

data: {"task":{"id":"task-123","contextId":"ctx-123","status":{"state":"TASK_STATE_WORKING"}}}

data: {"statusUpdate":{"taskId":"task-123","contextId":"ctx-123","status":{"state":"TASK_STATE_WORKING","message":{"messageId":"msg-002","role":"ROLE_AGENT","parts":[{"text":"Searching documentation..."}]}}}}

data: {"artifactUpdate":{"taskId":"task-123","contextId":"ctx-123","artifact":{"artifactId":"art-1","parts":[{"text":"partial findings..."}]},"append":true,"lastChunk":false}}

data: {"statusUpdate":{"taskId":"task-123","contextId":"ctx-123","status":{"state":"TASK_STATE_COMPLETED"}}}

پروتکل ACP (پروتکل ارتباطی ایجنت‌ها)

سازنده: IBM / BeeAI نسخه‌ی مشخصات: 0.2.0 (OpenAPI 3.1.1) وضعیت: در حال ادغام در A2A تحت نظارت Linux Foundation صورت مسئله: ایجنت‌ها چگونه می‌توانند با امکان حسابرسی کامل، تداوم نشست (session) و ردیابی گام‌های استدلال ارتباط برقرار کنند؟

پروتکل ACP یک پروتکل سازمانی است. برخلاف ادعای برخی خلاصه مقالات، ACP از JSON-LD استفاده نمی‌کند؛ بلکه یک وب‌سرویس مستقیم REST/JSON مبتنی بر OpenAPI است. ویژگی متمایزکننده‌ی آن متادیتای مسیر طی‌شده (TrajectoryMetadata) است: هر پاسخ ایجنت می‌تواند حاوی لاگ دقیقی از گام‌های استدلال و فراخوانی ابزارها (tool calls) باشد که به این پاسخ ختم شده‌اند.

لاگ بازرسیایجنت ACPکلاینتلاگ بازرسیایجنت ACPکلاینتهمه‌ی گام‌ها ثبت می‌شوند:tool_name، tool_input،tool_output، استدلالPOST /runs (حالت: همگام)پردازش درخواست...ثبت مسیر اجرای عملیات:استدلال + فراخوانی‌های ابزارپاسخ + TrajectoryMetadata

کشف ایجنت در ACP

پروتکل ACP چهار روش برای کشف تعریف می‌کند:

کشف ایجنتمحیط اجراGET /agentsباز.well-known/agent.ymlرجیستریکاتالوگ متمرکزجاسازی‌شدهبرچسب‌های کانتینر

ساختار مانیفست ایجنت (AgentManifest) ساده‌تر از کارت ایجنت در A2A است:

{
  "name": "summarizer",
  "description": "Summarizes documents with source citations",
  "input_content_types": ["text/plain", "application/pdf"],
  "output_content_types": ["text/plain", "application/json"],
  "metadata": {
    "tags": ["summarization", "RAG"],
    "framework": "BeeAI",
    "capabilities": [
      {
        "name": "Document Summarization",
        "description": "Condenses long documents into key points"
      }
    ],
    "recommended_models": ["llama3.3:70b-instruct-fp16"],
    "license": "Apache-2.0",
    "programming_language": "Python"
  }
}

چرخه‌ی حیات اجرا (Run)

پروتکل ACP به‌جای «تسک» از «اجراها (Runs)» استفاده می‌کند. هر Run فرایند اجرای ایجنت با سه حالت است:

حالت رفتار
sync مسدودکننده (Blocking). پاسخ حاوی نتیجه‌ی کامل است.
async مقدار مستقیم (بی‌واسطه) 202 برمی‌گرداند. برای بررسی وضعیت از GET /runs/{id} استعلام بگیرید.
stream استریم SSE. هم‌زمان با کار کردن ایجنت، رویدادها صادر می‌شوند.
موفقیتخطانیازمند ورودیادامه‌ی کار توسط کلاینتدرخواست لغوcreatedin_progresscompletedfailedawaitingcancellingcancelled

متادیتای مسیر طی‌شده (ردپای حسابرسی)

این نقطه‌ی تمایز اصلی ACP است. هر بخش از پیام می‌تواند شامل متادیتایی باشد که دقیقاً نشان می‌دهد ایجنت چه کاری انجام داده است:

{
  "role": "agent/researcher",
  "parts": [
    {
      "content_type": "text/plain",
      "content": "The weather in San Francisco is 72F and sunny.",
      "metadata": {
        "kind": "trajectory",
        "message": "I need to check the weather for this location",
        "tool_name": "weather_api",
        "tool_input": { "location": "San Francisco, CA" },
        "tool_output": { "temperature": 72, "condition": "sunny" }
      }
    }
  ]
}

این قابلیت برای صنایع دارای رگولاتوری فوق‌العاده است. هر پاسخ با زنجیره‌ای قابل‌اثبات از استدلال‌ها همراه می‌شود: کدام ابزارها فراخوانی شدند، چه ورودی‌هایی دریافت شد و چه خروجی‌هایی ثبت شد. جعبه‌ی سیاهی در کار نیست.

پروتکل ACP همچنین از CitationMetadata برای ارجاع به منابع پشتیبانی می‌کند:

{
  "kind": "citation",
  "start_index": 0,
  "end_index": 47,
  "url": "https://weather.gov/sf",
  "title": "NWS San Francisco Forecast"
}

پروتکل ANP (پروتکل شبکه‌ی ایجنت‌ها)

سازنده: جامعه‌ی متن‌باز (بنیان‌گذاری‌شده توسط GaoWei Chang) مخزن: github.com/agent-network-protocol/AgentNetworkProtocol صورت مسئله: ایجنت‌های سازمان‌های مختلف چگونه بدون نیاز به مرجعی مرکزی به یکدیگر اعتماد کنند؟

پروتکل ANP یک پروتکل هویت غیرمتمرکز است. این پروتکل اعتماد را با استفاده از شناسه‌های غیرمتمرکز W3C موسوم به DIDها و رمزنگاری سرتاسری بنا می‌کند. برخلاف A2A که ایجنت‌ها را از طریق اندپوینت‌های شناخته‌شده پیدا می‌کنید، ANP به ایجنت‌ها امکان می‌دهد هویت خود را به‌شکل رمزنگاری‌شده اثبات کنند.

پروتکل ANP از سه لایه تشکیل شده است:

لایه 1: هویت و ارتباط امنلایه 2: متاپروتکللایه 3: پروتکل کاربرداسناد توصیف ایجنتاندپوینت‌های کشفمذاکره پروتکل مبتنی بر هوش مصنوعیتولید پویای کدdid:wba (W3C DID)HPKE E2EE - RFC 9180اعتبارسنجی امضا

اسناد DID (ساختار واقعی)

پروتکل ANP از یک متد اختصاصی DID به نام did:wba (ایجنت مبتنی بر وب) استفاده می‌کند. شناسه‌ی did:wba:example.com:user:alice به آدرس https://example.com/user/alice/did.json ارجاع می‌دهد:

{
  "@context": [
    "https://www.w3.org/ns/did/v1",
    "https://w3id.org/security/suites/jws-2020/v1",
    "https://w3id.org/security/suites/secp256k1-2019/v1"
  ],
  "id": "did:wba:example.com:user:alice",
  "verificationMethod": [
    {
      "id": "did:wba:example.com:user:alice#key-1",
      "type": "EcdsaSecp256k1VerificationKey2019",
      "controller": "did:wba:example.com:user:alice",
      "publicKeyJwk": {
        "crv": "secp256k1",
        "x": "NtngWpJUr-rlNNbs0u-Aa8e16OwSJu6UiFf0Rdo1oJ4",
        "y": "qN1jKupJlFsPFc1UkWinqljv4YE0mq_Ickwnjgasvmo",
        "kty": "EC"
      }
    },
    {
      "id": "did:wba:example.com:user:alice#key-x25519-1",
      "type": "X25519KeyAgreementKey2019",
      "controller": "did:wba:example.com:user:alice",
      "publicKeyMultibase": "z9hFgmPVfmBZwRvFEyniQDBkz9LmV7gDEqytWyGZLmDXE"
    }
  ],
  "authentication": [
    "did:wba:example.com:user:alice#key-1"
  ],
  "keyAgreement": [
    "did:wba:example.com:user:alice#key-x25519-1"
  ],
  "humanAuthorization": [
    "did:wba:example.com:user:alice#key-1"
  ],
  "service": [
    {
      "id": "did:wba:example.com:user:alice#agent-description",
      "type": "AgentDescription",
      "serviceEndpoint": "https://example.com/agents/alice/ad.json"
    }
  ]
}

نکات کلیدی که باید توجه کنید:

  • جداسازی کلیدها اجباری است. کلیدهای امضا (secp256k1) از کلیدهای رمزنگاری (X25519) جدا هستند.
  • کلیدهای humanAuthorization ویژه‌ی پروتکل ANP هستند. این کلیدها پیش از استفاده نیازمند تأیید صریح انسان (بیومتریک، رمز عبور یا ماژول HSM) هستند. عملیات حساس و پرخطر مثل تراکنش‌های مالی از این مسیر می‌گذرند.
  • کلیدهای keyAgreement برای رمزنگاری سرتاسری پروتکل HPKE بر اساس RFC 9180 استفاده می‌شوند.
  • بخش service به سند توضیحات ایجنت (Agent Description) پیوند دارد.

سازوکار اعتماد در ANP

پروتکل ANP از شبکه‌ی اعتماد سنتی (web-of-trust) یا گراف تایید صلاحیت استفاده نمی‌کند. اعتماد به‌صورت دوطرفه و در هر تعامل ارزیابی می‌شود:

ایجنت Bدامنه‌ی ایجنت Aایجنت Aایجنت Bدامنه‌ی ایجنت Aایجنت Aاعتماد = اعتبارسنجی دامنه‌ی TLS+ اعتبارسنجی امضای DID+ اصل حداقل اعتماددرخواست HTTP + DID + امضادریافت سند DID (از طریق HTTPS)سند DID + کلید عمومیاعتبارسنجی امضا با کلید عمومیصدور توکن دسترسیاستفاده از توکن در درخواست‌های بعدی

اعتماد از سه منبع تأمین می‌شود:

  1. پروتکل TLS سطح دامنه که میزبان سند DID را تأیید می‌کند
  2. امضاهای رمزنگاری‌شده‌ی DID که هویت ایجنت را تأیید می‌کنند
  3. اصل حداقل اعتماد (Principle of least trust) که فقط کمترین دسترسی‌های ممکن را می‌دهد

در اینجا از ترویج اعتماد مبتنی بر شایعه یا رتبه‌بندی پیج‌رنک خبری نیست. هر ایجنت را مستقیماً از طریق DID خودش اعتبارسنجی می‌کنید.

مذاکره‌ی فرامفهومی پروتکل (Meta-Protocol Negotiation)

این نوآورانه‌ترین ویژگی ANP است. وقتی دو ایجنت از اکوسیستم‌های مختلف به هم می‌رسند، نیازی به فرمت‌های از پیش توافق‌شده ندارند؛ بلکه با زبان طبیعی با هم مذاکره می‌کنند:

{
  "action": "protocolNegotiation",
  "sequenceId": 0,
  "candidateProtocols": "I can communicate using:\n1. JSON-RPC with hotel booking schema\n2. REST with OpenAPI 3.1 spec\n3. Natural language over HTTP",
  "modificationSummary": "Initial proposal",
  "status": "negotiating"
}
ایجنت Bایجنت Aایجنت Bایجنت Aایجنت‌ها برای مدیریت فرمت توافق‌شدهکد را به صورت پویا تولید می‌کنند.حداکثر 10 دور، سپس پایان مهلت.مذاکره پروتکل (پروتکل‌های پیشنهادی)مذاکره پروتکل (پیشنهاد متقابل)مذاکره پروتکل (پذیرفته شد)

ایجنت‌ها تا سقف 10 دور با هم گفت‌وگو می‌کنند تا روی یک فرمت به توافق برسند، سپس کدهای پردازش آن فرمت را به‌صورت داینامیک تولید می‌کنند. مقادیر وضعیت: negotiating، rejected، accepted، timeout.

این یعنی دو ایجنتی که پیش از این هرگز با یکدیگر ارتباط نداشته‌اند، می‌توانند بدون نیاز به اسکیمای مشترک از پیش تعیین‌شده، زبان مشترکی برای مکالمه پیدا کنند.

مقایسه‌ی پروتکل‌ها (اصلاح‌شده)

MCP A2A ACP ANP
سازنده Anthropic Google / Linux Foundation IBM / BeeAI جامعه‌ی کاربری
فرمت مشخصات JSON-RPC JSON-RPC / REST / gRPC OpenAPI 3.1 (REST) JSON-RPC
کاربرد اصلی ایجنت به ابزار ایجنت به ایجنت ایجنت به ایجنت ایجنت به ایجنت
روش کشف لیست ابزارها /.well-known/agent-card.json GET /agents، /.well-known/agent.yml /.well-known/agent-descriptions، اندپوینت‌های سرویس DID
هویت ضمنی (محلی) طرح‌های امنیتی (OAuth، mTLS) در سطح سرور W3C DID (did:wba) همراه با E2EE
ردپای حسابرسی ندارد پایه (تاریخچه‌ی وظایف) متادیتای مسیر (فراخوانی ابزار، استدلال) رسماً تعریف‌نشده
ماشین وضعیت ندارد 9 وضعیت وظیفه 7 وضعیت اجرا ندارد
استریمینگ ندارد SSE SSE مستقل از بستر انتقال
ویژگی منحصر‌به‌فرد اسکیمای ابزار کارت‌های ایجنت + مهارت‌ها ردپای حسابرسی مسیر استدلال مذاکره‌ی فرامفهومی پروتکل
مناسب برای ابزارها و داده‌ها همکاری داینامیک صنایع دارای رگولاتوری اعتماد میان‌سازمانی
وضعیت پایدار پایدار (نسخه‌ی 1.0) در حال ادغام در A2A در حال توسعه‌ی فعال

همکاری این پروتکل‌ها در کنار هم

این پروتکل‌ها مانع یکدیگر نیستند. یک سامانه‌ی سازمانی واقعی از چند پروتکل بهره می‌گیرد:

خارجی (اعتبارسنجی DID از طریق ANP)سازمان شماA2AMCPMCPANP + A2AANP + A2Aایجنت پژوهشایجنت برنامه‌نویسیسرور جستجوسرور گیت‌هابتمامی پاسخ‌های ایجنت حاویACP TrajectoryMetadata هستندایجنت خارجیایجنت همکار
  • MCP هر ایجنت را به ابزارهایش متصل می‌کند
  • A2A مدیریت همکاری میان ایجنت‌ها (داخلی و خارجی) را بر عهده دارد
  • ACP پاسخ‌ها را درون متادیتای مسیر می‌پیچد تا امکان حسابرسی فراهم شود
  • ANP اعتبارسنجی هویت را برای ایجنت‌های خارج از کنترل شما انجام می‌دهد
گذرگاه پیام (MESSAGE BUS)بسته‌ها روی یک ستون فقرات مشترک
MESSAGE BUSAgents stop passing raw strings and speak typed protocols over a shared bus. MCP carries tool calls, A2A carries delegated tasks (and replies), ANP carries identity. Separating the lanes is what makes a multi-agent system auditable and lets agents built by different teams interoperate.ایجنت Aایجنت BMCP · فراخوانی ابزارA2A · وظیفهANP · هویتیک ستون فقرات مشترک، خطوط تفکیک‌شده · هر پروتکل نوع پیام خاص خود را حمل می‌کند
ایجنت‌ها به جای ارسال رشته‌های متنی خام، از پروتکل‌های دارای نوع روی یک گذرگاه مشترک استفاده می‌کنند. MCP فراخوانی‌های ابزار را حمل می‌کند، A2A وظایف واگذارشده (و پاسخ‌ها) را منتقل می‌کند و ANP هویت را انتقال می‌دهد. تفکیک این خطوط، سیستم چندایجنت را قابل ممیزی کرده و امکان همکاری میان ایجنت‌های توسعه‌یافته توسط تیم‌های مختلف را فراهم می‌سازد.

پیاده‌سازی از صفر

گام 1: انواع پیام‌های پایه

هر سامانه‌ی چندایجنت با یک فرمت پیام شروع می‌شود. انواع داده‌ای را تعریف می‌کنیم که با پروتکل‌های واقعی هم‌خوانی دارند:

import crypto from "node:crypto";

type MessageRole = "ROLE_USER" | "ROLE_AGENT";

type MessagePart =
  | { text: string }
  | { data: unknown; mediaType: string }
  | { url: string; filename: string; mediaType: string };

type TrajectoryEntry = {
  reasoning: string;
  toolName?: string;
  toolInput?: unknown;
  toolOutput?: unknown;
  timestamp: number;
};

type AgentMessage = {
  id: string;
  role: MessageRole;
  parts: MessagePart[];
  trajectory?: TrajectoryEntry[];
  replyTo?: string;
  timestamp: number;
};

function createMessage(
  role: MessageRole,
  parts: MessagePart[],
  replyTo?: string
): AgentMessage {
  return {
    id: crypto.randomUUID(),
    role,
    parts,
    replyTo,
    timestamp: Date.now(),
  };
}

function textMessage(role: MessageRole, text: string): AgentMessage {
  return createMessage(role, [{ text }]);
}

دقت کنید: MessagePart درست مانند پروتکل‌های A2A و ACP چندحالته (چندحالتی: متن، داده‌های ساختاریافته، فایل‌ها) است. مطابق A2A نسخه 1.0، فیلدی که مقدار دارد (text، data، یا url) نوع داده را مشخص می‌کند و تگ جداگانه‌ای برای kind وجود ندارد. TrajectoryEntry زنجیره‌ی استدلال را ثبت می‌کند که دقیقاً با TrajectoryMetadata در ACP منطبق است.

گام 2: کارت ایجنت و رجیستری A2A

سازوکار کشف ایجنت‌ها را طبق استاندارد A2A بسازید:

type Skill = {
  id: string;
  name: string;
  description: string;
  tags: string[];
  inputModes: string[];
  outputModes: string[];
};

type AgentInterface = {
  url: string;
  protocolBinding: string;
  protocolVersion: string;
};

type AgentCard = {
  name: string;
  description: string;
  version: string;
  supportedInterfaces: AgentInterface[];
  capabilities: {
    streaming: boolean;
    pushNotifications: boolean;
  };
  defaultInputModes: string[];
  defaultOutputModes: string[];
  skills: Skill[];
};

class AgentRegistry {
  private cards: Map<string, AgentCard> = new Map();

  register(card: AgentCard) {
    this.cards.set(card.name, card);
  }

  discoverBySkillTag(tag: string): AgentCard[] {
    return [...this.cards.values()].filter((card) =>
      card.skills.some((skill) => skill.tags.includes(tag))
    );
  }

  discoverByInputMode(mimeType: string): AgentCard[] {
    return [...this.cards.values()].filter(
      (card) =>
        card.defaultInputModes.includes(mimeType) ||
        card.skills.some((skill) => skill.inputModes.includes(mimeType))
    );
  }

  resolve(name: string): AgentCard | undefined {
    return this.cards.get(name);
  }

  listAll(): AgentCard[] {
    return [...this.cards.values()];
  }
}

این ساختار بسیار غنی‌تر از یک نگاشت ساده‌ی نام به قابلیت‌هاست. می‌توانید ایجنت‌ها را بر اساس برچسب‌های مهارت، انواع MIME ورودی یا نام آن‌ها پیدا کنید؛ درست مانند رفتاری که در مشخصات رسمی A2A وجود دارد.

گام 3: چرخه‌ی حیات تسک در A2A

ماشین وضعیت کامل تسک را پیاده‌سازی کنید:

type TaskState =
  | "TASK_STATE_SUBMITTED"
  | "TASK_STATE_WORKING"
  | "TASK_STATE_INPUT_REQUIRED"
  | "TASK_STATE_AUTH_REQUIRED"
  | "TASK_STATE_COMPLETED"
  | "TASK_STATE_FAILED"
  | "TASK_STATE_CANCELED"
  | "TASK_STATE_REJECTED";

const TERMINAL_STATES: TaskState[] = [
  "TASK_STATE_COMPLETED",
  "TASK_STATE_FAILED",
  "TASK_STATE_CANCELED",
  "TASK_STATE_REJECTED",
];

type TaskStatus = {
  state: TaskState;
  message?: AgentMessage;
  timestamp: number;
};

type Artifact = {
  id: string;
  name: string;
  parts: MessagePart[];
};

type Task = {
  id: string;
  contextId: string;
  status: TaskStatus;
  artifacts: Artifact[];
  history: AgentMessage[];
};

type TaskEvent =
  | { statusUpdate: { taskId: string; status: TaskStatus } }
  | {
      artifactUpdate: {
        taskId: string;
        artifact: Artifact;
        append: boolean;
        lastChunk: boolean;
      };
    };

type TaskHandler = (
  task: Task,
  message: AgentMessage
) => AsyncGenerator<TaskEvent>;

class TaskManager {
  private tasks: Map<string, Task> = new Map();
  private handlers: Map<string, TaskHandler> = new Map();
  private listeners: Map<string, ((event: TaskEvent) => void)[]> = new Map();

  registerHandler(agentName: string, handler: TaskHandler) {
    this.handlers.set(agentName, handler);
  }

  subscribe(taskId: string, listener: (event: TaskEvent) => void) {
    const existing = this.listeners.get(taskId) ?? [];
    existing.push(listener);
    this.listeners.set(taskId, existing);
  }

  async sendMessage(
    agentName: string,
    message: AgentMessage,
    contextId?: string
  ): Promise<Task> {
    const handler = this.handlers.get(agentName);
    if (!handler) {
      const task = this.createTask(contextId);
      task.status = {
        state: "TASK_STATE_REJECTED",
        timestamp: Date.now(),
        message: textMessage("ROLE_AGENT", `No handler for ${agentName}`),
      };
      return task;
    }

    const task = this.createTask(contextId);
    task.history.push(message);
    task.status = { state: "TASK_STATE_SUBMITTED", timestamp: Date.now() };

    this.processTask(task, handler, message).catch((err) => {
      task.status = {
        state: "TASK_STATE_FAILED",
        timestamp: Date.now(),
        message: textMessage("ROLE_AGENT", String(err)),
      };
    });
    return task;
  }

  getTask(taskId: string): Task | undefined {
    return this.tasks.get(taskId);
  }

  cancelTask(taskId: string): boolean {
    const task = this.tasks.get(taskId);
    if (!task || TERMINAL_STATES.includes(task.status.state)) return false;
    task.status = { state: "TASK_STATE_CANCELED", timestamp: Date.now() };
    this.emit(taskId, {
      statusUpdate: { taskId, status: task.status },
    });
    return true;
  }

  private createTask(contextId?: string): Task {
    const task: Task = {
      id: crypto.randomUUID(),
      contextId: contextId ?? crypto.randomUUID(),
      status: { state: "TASK_STATE_SUBMITTED", timestamp: Date.now() },
      artifacts: [],
      history: [],
    };
    this.tasks.set(task.id, task);
    return task;
  }

  private async processTask(
    task: Task,
    handler: TaskHandler,
    message: AgentMessage
  ) {
    task.status = { state: "TASK_STATE_WORKING", timestamp: Date.now() };
    this.emit(task.id, {
      statusUpdate: { taskId: task.id, status: task.status },
    });

    try {
      for await (const event of handler(task, message)) {
        if (TERMINAL_STATES.includes(task.status.state)) break;

        if ("statusUpdate" in event) {
          task.status = event.statusUpdate.status;
        }
        if ("artifactUpdate" in event) {
          const update = event.artifactUpdate;
          const existing = task.artifacts.find(
            (a) => a.id === update.artifact.id
          );
          if (existing && update.append) {
            existing.parts.push(...update.artifact.parts);
          } else {
            task.artifacts.push(update.artifact);
          }
        }
        this.emit(task.id, event);
      }
    } catch (err) {
      task.status = {
        state: "TASK_STATE_FAILED",
        timestamp: Date.now(),
        message: textMessage("ROLE_AGENT", String(err)),
      };
      this.emit(task.id, {
        statusUpdate: { taskId: task.id, status: task.status },
      });
    }
  }

  private emit(taskId: string, event: TaskEvent) {
    for (const listener of this.listeners.get(taskId) ?? []) {
      listener(event);
    }
  }
}

این بخش چرخه‌ی حیات واقعی تسک در A2A را پیاده می‌کند: TASK_STATE_SUBMITTED، TASK_STATE_WORKING، TASK_STATE_INPUT_REQUIRED و سپس وضعیتی پایانی. هندلرها ژنراتورهای ناهمگام (async generators) هستند که رویدادهای statusUpdate و artifactUpdate را تولید می‌کنند؛ دقیقاً همان پوشش‌هایی که در استریم SSE ردوبدل می‌شوند.

گام 4: ردپای حسابرسی به سبک ACP

ارتباطات را به ردیابی مسیر مجهز کنید:

type AuditEntry = {
  runId: string;
  agentName: string;
  input: AgentMessage[];
  output: AgentMessage[];
  trajectory: TrajectoryEntry[];
  status: "created" | "in-progress" | "completed" | "failed" | "awaiting";
  startedAt: number;
  completedAt?: number;
  sessionId?: string;
};

class AuditableRunner {
  private log: AuditEntry[] = [];
  private handlers: Map<
    string,
    (input: AgentMessage[]) => Promise<{
      output: AgentMessage[];
      trajectory: TrajectoryEntry[];
    }>
  > = new Map();

  registerAgent(
    name: string,
    handler: (input: AgentMessage[]) => Promise<{
      output: AgentMessage[];
      trajectory: TrajectoryEntry[];
    }>
  ) {
    this.handlers.set(name, handler);
  }

  async run(
    agentName: string,
    input: AgentMessage[],
    sessionId?: string
  ): Promise<AuditEntry> {
    const entry: AuditEntry = {
      runId: crypto.randomUUID(),
      agentName,
      input: structuredClone(input),
      output: [],
      trajectory: [],
      status: "created",
      startedAt: Date.now(),
      sessionId,
    };
    this.log.push(entry);

    const handler = this.handlers.get(agentName);
    if (!handler) {
      entry.status = "failed";
      return entry;
    }

    entry.status = "in-progress";
    try {
      const result = await handler(input);
      entry.output = structuredClone(result.output);
      entry.trajectory = structuredClone(result.trajectory);
      entry.status = "completed";
      entry.completedAt = Date.now();
    } catch (err) {
      entry.status = "failed";
      entry.trajectory.push({
        reasoning: `Error: ${String(err)}`,
        timestamp: Date.now(),
      });
      entry.completedAt = Date.now();
    }
    return entry;
  }

  getFullAuditLog(): AuditEntry[] {
    return structuredClone(this.log);
  }

  getAuditLogForAgent(agentName: string): AuditEntry[] {
    return structuredClone(
      this.log.filter((e) => e.agentName === agentName)
    );
  }

  getAuditLogForSession(sessionId: string): AuditEntry[] {
    return structuredClone(
      this.log.filter((e) => e.sessionId === sessionId)
    );
  }

  getTrajectoryForRun(runId: string): TrajectoryEntry[] {
    const entry = this.log.find((e) => e.runId === runId);
    return entry ? structuredClone(entry.trajectory) : [];
  }
}

هر بار اجرای ایجنت یک رکورد کامل حسابرسی تولید می‌کند: ورودی‌ها چه بودند، خروجی‌ها چه هستند و زنجیره‌ی کامل ابزارهای فراخوانی‌شده و مراحل استدلال در این بین چطور پیش رفت. می‌توانید بر اساس ایجنت، نشست یا هر بار اجرا به‌تنهایی جست‌وجو کنید.

گام 5: اعتبارسنجی هویت به سبک ANP

ساختار هویت و اعتبارسنجی مبتنی بر DID را بسازید:

type VerificationMethod = {
  id: string;
  type: string;
  controller: string;
  publicKeyDer: string;
};

type DIDDocument = {
  id: string;
  verificationMethod: VerificationMethod[];
  authentication: string[];
  keyAgreement: string[];
  humanAuthorization: string[];
  service: { id: string; type: string; serviceEndpoint: string }[];
};

type AgentIdentity = {
  did: string;
  document: DIDDocument;
  privateKey: crypto.KeyObject;
  publicKey: crypto.KeyObject;
};

class IdentityRegistry {
  private documents: Map<string, DIDDocument> = new Map();

  publish(doc: DIDDocument) {
    this.documents.set(doc.id, doc);
  }

  resolve(did: string): DIDDocument | undefined {
    return this.documents.get(did);
  }

  verify(did: string, signature: string, payload: string): boolean {
    const doc = this.documents.get(did);
    if (!doc) return false;

    const authKeyIds = doc.authentication;
    const authKeys = doc.verificationMethod.filter((vm) =>
      authKeyIds.includes(vm.id)
    );

    for (const key of authKeys) {
      const publicKey = crypto.createPublicKey({
        key: Buffer.from(key.publicKeyDer, "base64"),
        format: "der",
        type: "spki",
      });
      const isValid = crypto.verify(
        null,
        Buffer.from(payload),
        publicKey,
        Buffer.from(signature, "hex")
      );
      if (isValid) return true;
    }
    return false;
  }

  requiresHumanAuth(did: string, operationKeyId: string): boolean {
    const doc = this.documents.get(did);
    if (!doc) return false;
    return doc.humanAuthorization.includes(operationKeyId);
  }
}

function createIdentity(domain: string, agentName: string): AgentIdentity {
  const did = `did:wba:${domain}:agent:${agentName}`;
  const { publicKey, privateKey } = crypto.generateKeyPairSync("ed25519");

  const publicKeyDer = publicKey
    .export({ format: "der", type: "spki" })
    .toString("base64");

  const keyId = `${did}#key-1`;
  const encKeyId = `${did}#key-x25519-1`;

  const document: DIDDocument = {
    id: did,
    verificationMethod: [
      {
        id: keyId,
        type: "Ed25519VerificationKey2020",
        controller: did,
        publicKeyDer,
      },
      {
        id: encKeyId,
        type: "X25519KeyAgreementKey2019",
        controller: did,
        publicKeyDer,
      },
    ],
    authentication: [keyId],
    keyAgreement: [encKeyId],
    humanAuthorization: [],
    service: [
      {
        id: `${did}#agent-description`,
        type: "AgentDescription",
        serviceEndpoint: `https://${domain}/agents/${agentName}/ad.json`,
      },
    ],
  };

  return { did, document, privateKey, publicKey };
}

function signPayload(identity: AgentIdentity, payload: string): string {
  return crypto
    .sign(null, Buffer.from(payload), identity.privateKey)
    .toString("hex");
}

این بخش منعکس‌کننده‌ی مدل هویت در ANP است: ایجنت‌ها اسناد DID با کلیدهای مجزا برای احراز هویت، توافق کلید و تأیید انسانی دارند. کلاس IdentityRegistry فرایند تحلیل و واکشی DID را شبیه‌سازی می‌کند (که در محیط عملیاتی، این بخش شامل درخواست‌های HTTP به دامنه‌ی ایجنت خواهد بود).

گام 6: گیت‌وی پروتکل (Protocol Gateway)

هر چهار پروتکل را در قالب یک سامانه‌ی یکپارچه متصل کنید:

معتبرنامعتبریافت شدیافت نشددرخواست ورودیANP: اعتبارسنجی DIDA2A: کشف ایجنتردACP: بازرسی اجراA2A: ایجاد وظیفهوظیفه + ورودی بازرسی
class ProtocolGateway {
  private registry: AgentRegistry;
  private taskManager: TaskManager;
  private auditRunner: AuditableRunner;
  private identityRegistry: IdentityRegistry;

  constructor(
    registry: AgentRegistry,
    taskManager: TaskManager,
    auditRunner: AuditableRunner,
    identityRegistry: IdentityRegistry
  ) {
    this.registry = registry;
    this.taskManager = taskManager;
    this.auditRunner = auditRunner;
    this.identityRegistry = identityRegistry;
  }

  async delegateTask(
    fromDid: string,
    signature: string,
    targetAgent: string,
    message: AgentMessage,
    sessionId?: string
  ): Promise<{ task: Task; audit: AuditEntry } | { error: string }> {
    if (!this.identityRegistry.verify(fromDid, signature, message.id)) {
      return { error: "Identity verification failed" };
    }

    const card = this.registry.resolve(targetAgent);
    if (!card) {
      return { error: `Agent ${targetAgent} not found in registry` };
    }

    const audit = await this.auditRunner.run(
      targetAgent,
      [message],
      sessionId
    );
    const task = await this.taskManager.sendMessage(targetAgent, message);

    return { task, audit };
  }

  discoverAndDelegate(
    fromDid: string,
    signature: string,
    skillTag: string,
    message: AgentMessage
  ): Promise<{ task: Task; audit: AuditEntry } | { error: string }> {
    const candidates = this.registry.discoverBySkillTag(skillTag);
    if (candidates.length === 0) {
      return Promise.resolve({
        error: `No agents found with skill tag: ${skillTag}`,
      });
    }
    return this.delegateTask(
      fromDid,
      signature,
      candidates[0].name,
      message
    );
  }
}

این گیت‌وی چهار کار را در قالب یک فراخوانی واحد انجام می‌دهد:

  1. ANP: هویت فراخواننده را از طریق امضای DID تأیید می‌کند
  2. A2A: ایجنت مقصد را کشف کرده و توانمندی‌های آن را بررسی می‌کند
  3. ACP: فرایند اجرا را همراه با جزئیات مسیر در ردپای حسابرسی ثبت می‌کند
  4. A2A: تسک را با ردیابی کامل چرخه‌ی حیات آن ایجاد می‌کند

گام 7: اتصال همه‌ی بخش‌ها به یکدیگر

async function protocolDemo() {
  const registry = new AgentRegistry();
  registry.register({
    name: "researcher",
    description: "Searches and summarizes findings",
    version: "1.0.0",
    supportedInterfaces: [
      {
        url: "https://researcher.local/a2a/v1",
        protocolBinding: "JSONRPC",
        protocolVersion: "1.0",
      },
    ],
    capabilities: { streaming: true, pushNotifications: false },
    defaultInputModes: ["text/plain"],
    defaultOutputModes: ["text/plain", "application/json"],
    skills: [
      {
        id: "web-research",
        name: "Web Research",
        description: "Searches the web",
        tags: ["research", "search", "summarization"],
        inputModes: ["text/plain"],
        outputModes: ["application/json"],
      },
    ],
  });
  registry.register({
    name: "coder",
    description: "Writes code from specs",
    version: "1.0.0",
    supportedInterfaces: [
      {
        url: "https://coder.local/a2a/v1",
        protocolBinding: "JSONRPC",
        protocolVersion: "1.0",
      },
    ],
    capabilities: { streaming: false, pushNotifications: false },
    defaultInputModes: ["text/plain", "application/json"],
    defaultOutputModes: ["text/plain"],
    skills: [
      {
        id: "code-gen",
        name: "Code Generation",
        description: "Generates code",
        tags: ["coding", "generation"],
        inputModes: ["text/plain", "application/json"],
        outputModes: ["text/plain"],
      },
    ],
  });

  const taskManager = new TaskManager();
  const auditRunner = new AuditableRunner();

  const researchTrajectory: TrajectoryEntry[] = [];

  taskManager.registerHandler(
    "researcher",
    async function* (task, message) {
      yield {
        statusUpdate: {
          taskId: task.id,
          status: {
            state: "TASK_STATE_WORKING" as const,
            timestamp: Date.now(),
          },
        },
      };

      researchTrajectory.push({
        reasoning: "Searching for React 19 documentation",
        toolName: "web_search",
        toolInput: { query: "React 19 compiler features" },
        toolOutput: {
          results: ["react.dev/blog/react-19", "github.com/react/react"],
        },
        timestamp: Date.now(),
      });

      researchTrajectory.push({
        reasoning: "Extracting key findings from search results",
        toolName: "doc_analysis",
        toolInput: { url: "react.dev/blog/react-19" },
        toolOutput: {
          summary:
            "React 19 compiler auto-memoizes, no manual useMemo needed",
        },
        timestamp: Date.now(),
      });

      yield {
        artifactUpdate: {
          taskId: task.id,
          artifact: {
            id: crypto.randomUUID(),
            name: "research-results",
            parts: [
              {
                data: {
                  findings: [
                    "React 19 compiler auto-memoizes components",
                    "No more manual useMemo/useCallback needed",
                    "Compiler runs at build time, not runtime",
                  ],
                  sources: ["react.dev/blog/react-19"],
                },
                mediaType: "application/json",
              },
            ],
          },
          append: false,
          lastChunk: true,
        },
      };

      yield {
        statusUpdate: {
          taskId: task.id,
          status: {
            state: "TASK_STATE_COMPLETED" as const,
            timestamp: Date.now(),
          },
        },
      };
    }
  );

  auditRunner.registerAgent("researcher", async () => ({
    output: [
      textMessage("ROLE_AGENT", "React 19 compiler auto-memoizes components"),
    ],
    trajectory: researchTrajectory,
  }));

  const identityRegistry = new IdentityRegistry();

  const coderIdentity = createIdentity("coder.local", "coder");
  const researcherIdentity = createIdentity("researcher.local", "researcher");

  identityRegistry.publish(coderIdentity.document);
  identityRegistry.publish(researcherIdentity.document);

  const gateway = new ProtocolGateway(
    registry,
    taskManager,
    auditRunner,
    identityRegistry
  );

  console.log("=== Protocol Demo ===\n");

  console.log("1. Agent Discovery (A2A)");
  const researchAgents = registry.discoverBySkillTag("research");
  console.log(
    `   Found ${researchAgents.length} agent(s):`,
    researchAgents.map((a) => a.name)
  );

  console.log("\n2. Identity Verification (ANP)");
  const message = textMessage("ROLE_USER", "Research React 19 compiler features");
  const signature = signPayload(coderIdentity, message.id);
  const verified = identityRegistry.verify(
    coderIdentity.did,
    signature,
    message.id
  );
  console.log(`   Coder DID: ${coderIdentity.did}`);
  console.log(`   Signature verified: ${verified}`);

  console.log("\n3. Task Delegation (A2A + ACP + ANP)");
  const result = await gateway.delegateTask(
    coderIdentity.did,
    signature,
    "researcher",
    message,
    "session-001"
  );

  if ("error" in result) {
    console.log(`   Error: ${result.error}`);
    return;
  }

  console.log(`   Task ID: ${result.task.id}`);
  console.log(`   Task state: ${result.task.status.state}`);
  console.log(`   Artifacts: ${result.task.artifacts.length}`);

  console.log("\n4. Audit Trail (ACP)");
  console.log(`   Run ID: ${result.audit.runId}`);
  console.log(`   Status: ${result.audit.status}`);
  console.log(`   Trajectory steps: ${result.audit.trajectory.length}`);
  for (const step of result.audit.trajectory) {
    console.log(`     - ${step.reasoning}`);
    if (step.toolName) {
      console.log(`       Tool: ${step.toolName}`);
    }
  }

  console.log("\n5. Full Audit Log");
  const fullLog = auditRunner.getFullAuditLog();
  console.log(`   Total runs: ${fullLog.length}`);
  for (const entry of fullLog) {
    const duration = entry.completedAt
      ? `${entry.completedAt - entry.startedAt}ms`
      : "in-progress";
    console.log(`   ${entry.agentName}: ${entry.status} (${duration})`);
  }
}

protocolDemo().catch((err) => {
  console.error("Protocol demo failed:", err);
  process.exitCode = 1;
});

چالش‌ها و خطاهای رایج

پروتکل‌ها مسیر ایدئال و بی‌دردسر را به‌خوبی حل می‌کنند. اما مواردی که در محیط عملیاتی دچار مشکل می‌شوند از این قرارند:

تغییرات تدریجی در اسکیما (Schema drift): ایجنت A کارت ایجنتی منتشر می‌کند که در آن خروجی application/json وعده داده شده است؛ اما اسکیمای JSON میان دو نسخه تغییر می‌کند. ایجنت B فرمت قدیمی را پردازش کرده و داده‌های نامعتبر دریافت می‌کند. راهکار: مهارت‌ها و اسکیماهای خروجی خود را نسخه‌گذاری کنید. به همین دلیل مشخصات A2A فیلد version را در کارت‌های ایجنت در نظر گرفته است.

نقض قوانین ماشین وضعیت: هندلر یک ایجنت وضعیت TASK_STATE_COMPLETED را صادر می‌کند و سپس سعی دارد دست‌سازه‌های (artifacts) بیشتری بفرستد، در حالی که این تسک دیگر تغییرناپذیر شده است. کد شما بدون هشدار به‌روزرسانی‌ها را نادیده می‌گیرد یا با خطا متوقف می‌شود. راهکار: پیش از ارسال، پایانی نبودن وضعیت را بررسی کنید. کلاس TaskManager در کد بالا با دستور break پس از وضعیت‌های نهایی مانع این خطا می‌شود.

خطای تحلیل اعتماد (Trust resolution failures): ایجنت A سعی می‌کند DID ایجنت B را بررسی کند، اما دامنه‌ی ایجنت B از دسترس خارج است و سند DID دریافت نمی‌شود. آیا درخواست را ناامن بپذیرید (fail open) یا اتصال را کاملاً قطع کنید (fail closed)؟ پروتکل ANP بر اساس اصل حداقل اعتماد، قطع اتصال و رد درخواست را توصیه می‌کند.

حجم بالای لاگ مسیر (Trajectory bloat): ثبت متادیتای مسیر در ACP بسیار کاربردی اما پرهزینه است. یک ایجنت پیچیده که در هر اجرا 200 ابزار را فراخوانی می‌کند، لاگ‌های حسابرسی بسیار حجیمی می‌سازد. راهکار: مسیر را با سطوح تفکیک‌پذیری قابل‌تنظیم لاگ کنید. نام ابزارها و ورودی/خروجی‌ها را برای نیازمندی‌های قانونی نگه دارید، اما مراحل میانی استدلال را در پردازش‌های غیرحساس ثبت نکنید.

ازدحام ناشی از کشف سرویس (Discovery thundering herd): در زمان بالا آمدن سیستم، 50 ایجنت به‌طور هم‌زمان درخواست GET /agents را ارسال می‌کنند. راهکار: کارت‌های ایجنت را با زمان انقضا (TTL) کش کنید، فاصله‌ی زمانی بررسی‌ها را تغییر دهید یا به‌جای استعلام مداوم، از مکانیزم ثبت ایجنت بر مبنای push استفاده کنید.

استفاده از ابزارهای واقعی

پیاده‌سازی‌های موجود

پروتکل A2A بالغ‌ترین گزینه است. مشخصات رسمی Google تحت نظارت بنیاد لینوکس به‌صورت متن‌باز منتشر شده و برای Python و TypeScript کیت توسعه‌ی نرم‌افزار (SDK) دارد. اگر ایجنت‌های شما نیازمند کشف داینامیک و همکاری چندجانبه هستند، کار را از اینجا شروع کنید.

پروتکل ACP در حال ادغام در A2A است. پروژه‌ی BeeAI از IBM این پروتکل را به‌عنوان جایگزینی بر مبنای REST ساخت، اما مفهوم متادیتای مسیر در حال جذب شدن در اکوسیستم A2A است. الگوهای ACP (لاگ مسیر، چرخه‌ی حیات اجرا) را حتی در صورت استفاده از A2A به‌عنوان بستر انتقال پیاده‌سازی کنید.

پروتکل ANP آزمایشی‌ترین گزینه است. مخزن جامعه‌ی کاربری یک SDK برای پایتون (AgentConnect) ارائه کرده است. مفهوم مذاکره‌ی فرامفهومی پروتکل در اینجا واقعاً نوآورانه است و گزینه‌ی ارزشمندی برای سناریوهای همکاری ایجنت‌ها میان سازمان‌های مختلف به شمار می‌آید.

پروتکل MCP قبلاً در فاز 13 بررسی شده است. هر زمان که بخواهید ایجنت‌ها از ابزارها استفاده کنند، MCP استاندارد اصلی خواهد بود.

انتخاب پروتکل مناسب

بلهخیرخیربلهبلهخیربلهخیربلهخیرآیا ایجنت‌ها نیازبه استفاده از ابزار دارند؟استفاده از MCPآیا ایجنت‌ها نیاز دارندبا یکدیگر گفت‌وگو کنند؟نیازی به پروتکلنداریدنیاز به لاگ‌های بازرسیبرای انطباق دارید؟A2A + الگوهایمسیر ACPهمه‌ی ایجنت‌ها درونسازمان شما هستند؟A2Aکارت‌های ایجنت + وظایفزیرساختمشترک دارید؟A2A + کارگزار پیامANP + A2Aاعتبارسنجی DID

خروجی این درس

این درس شامل موارد زیر است:

  • فایل code/main.ts -- پیاده‌سازی کامل الگوهای هر چهار پروتکل
  • فایل outputs/prompt-protocol-selector.md -- پرامپتی کاربردی برای کمک به انتخاب بهترین پروتکل بر اساس نیازهای سامانه‌ی شما

تمرین‌ها

  1. واگذاری چندگامی تسک‌ها (Multi-hop task delegation): کلاس TaskManager را طوری توسعه دهید که هندلر یک ایجنت بتواند زیرتسک‌ها را به ایجنت‌های دیگر واگذار کند. ایجنت پژوهشگر تسکی می‌گیرد، زیرتسک‌های «جست‌وجو» و «خلاصه‌سازی» را به دو ایجنت متخصص می‌سپارد، منتظر اتمام کار هر دو می‌ماند و نتایج را در دست‌سازه‌های خروجی خود merge می‌کند.

  2. ردپای حسابرسی استریمینگ: کلاس AuditableRunner را برای پشتیبانی از حالت استریمینگ ویرایش کنید. به‌جای انتظار برای دریافت نتیجه‌ی کامل، تغییرات AuditEntry را هم‌زمان با ثبت گام‌های استدلال به‌صورت زنده ارسال کنید. از یک ژنراتور ناهمگام استفاده کنید که اسنپ‌شات‌های پایش و حسابرسی را صادر کند.

  3. چرخش کلید DID: قابلیت چرخش کلید را به IdentityRegistry اضافه کنید. یک ایجنت باید بتواند سند DID جدیدی با کلیدهای به‌روزشده منتشر کند و هم‌زمان به previousDid ارجاع دهد. مدل‌های اعتبارسنج (verifiers) باید امضاهای هر دو کلید فعلی و قبلی را در یک بازه‌ی زمانی مشخص بپذیرند.

  4. مذاکره‌ی پروتکل: سازوکار فرامفهومی ANP را پیاده کنید. دو ایجنت پیام‌های protocolNegotiation را همراه با فرمت‌های پیشنهادی مبادله می‌کنند (مثلاً «من می‌توانم با JSON-RPC کار کنم» در برابر «من REST را ترجیح می‌دهم»). پس از حداکثر 3 دور، روی یک فرمت توافق می‌کنند یا اتصال به دلیل پایان مهلت (timeout) بسته می‌شود. فرمت مورد توافق مشخص می‌کند که از کدام TaskManager یا AuditableRunner استفاده شود.

  5. کنترل نرخ کشف سرویس: یک پوشش RateLimitedRegistry بسازید که استعلام‌های کارت ایجنت را همراه با TTL مشخص کش کند و تعداد درخواست‌های کشف در هر ثانیه به ازای هر ایجنت را محدود سازد. سناریوی ازدحام کشف سرویس (thundering herd) را با 100 ایجنت در زمان راه‌اندازی شبیه‌سازی کرده و تفاوت عملکرد را بسنجید.

اصطلاحات کلیدی

اصطلاح در گفت‌وگوهای عامیانه مفهوم دقیق فنی
MCP «پروتکل ابزارهای هوش مصنوعی» پروتکل کلاینت-سرور برای کشف و فراخوانی ابزارها توسط ایجنت. ارتباط میان ایجنت و ابزار است، نه ایجنت با ایجنت.
A2A «پروتکل ایجنت‌های گوگل» پروتکل نظیربه‌نظیر برای همکاری ایجنت‌ها تحت نظارت بنیاد لینوکس. کشف از طریق کارت ایجنت، چرخه‌ی حیات تسک با 9 وضعیت و استریمینگ با SSE. پشتیبانی از اتصالات JSON-RPC ،REST و gRPC.
ACP «پیام‌رسانی سازمانی ایجنت‌ها» وب‌سرویس REST از سوی IBM/BeeAI برای اجرای ایجنت‌ها همراه با متادیتای مسیر؛ هر پاسخ حامل زنجیره‌ی کامل استدلال و فراخوانی ابزارها است. در حال ادغام در A2A.
ANP «هویت غیرمتمرکز ایجنت‌ها» پروتکل جامعه‌ی متن‌باز با استفاده از did:wba (DID) برای احراز هویت رمزنگاری‌شده، HPKE برای رمزنگاری سرتاسری و مذاکره‌ی هوشمند فرامفهومی میان ایجنت‌هایی که شناختی از هم ندارند.
کارت ایجنت (Agent Card) «کارت ویزیت ایجنت» سندی در قالب JSON در مسیر /.well-known/agent-card.json که مهارت‌ها، انواع MIME پشتیبانی‌شده، طرح‌های امنیتی و اتصالات پروتکل را توصیف می‌کند.
شناسه غیرمتمرکز (DID) «شناسه‌ی غیرمتمرکز» استاندارد W3C برای هویت‌های قابل‌اثبات به‌شکل رمزنگاری‌شده که روی دامنه‌ی اختصاصی خودِ ایجنت میزبانی می‌شوند. ANP از متد did:wba استفاده می‌کند.
متادیتای مسیر (TrajectoryMetadata) «فاکتور و رسید حسابرسی» سازوکار ACP برای پیوست کردن مراحل استدلال، فراخوانی ابزارها و ورودی‌ها/خروجی‌های مربوطه به تک‌تک پاسخ‌های ایجنت.
پروتکل فرامفهومی (Meta-protocol) «مذاکره‌ی ایجنت‌ها برای زبان مشترک» رویکرد ANP که در آن ایجنت‌ها با زبان طبیعی روی فرمت تبادل داده توافق کرده و سپس کدهای مدیریت آن را تولید می‌کنند.
وظیفه / تسک (Task) «یک واحد کاری» شیء دارای وضعیت در پروتکل A2A که کار را از زمان ارسال تا اتمام پیگیری می‌کند و پس از رسیدن به وضعیت پایانی تغییرناپذیر خواهد بود.

برای مطالعه‌ی بیشتر

خودآزمایی

به سؤال‌ها جواب بدهید تا ببینید چقدر از درس در ذهنتان مانده است.

  1. پیش از درسپروتکل MCP (Model Context Protocol) چه مشکلی را برای ایجنت‌های هوش مصنوعی حل می‌کند؟
  2. پیش از درستفاوت بین MCP و A2A چیست؟
  3. پس از درسدر A2A، «کارت ایجنت» (agent card) چیست و چرا اهمیت دارد؟
  4. پس از درسچرا سیستم‌های چندایجنت به جای ارسال رشته‌های متنی ساده، به پروتکل‌های ارتباطی ساختاریافته نیاز دارند؟
  5. پس از درسپروتکل ACP (پروتکل ارتباطی ایجنت) از نظر اهداف طراحی چه تفاوتی با A2A دارد؟

سؤالات متداول

تفاوت کلیدی میان MCP و A2A در چیست؟

پروتکل MCP برای ارتباط ایجنت با ابزارها و منابع داده طراحی شده، در حالی که A2A برای ارتباط همتابه‌همتای ایجنت‌ها با یکدیگر، مذاکره و واگذاری وظایف به کار می‌رود.

کارت ایجنت (Agent Card) در A2A چه نقشی دارد؟

کارت ایجنت فایلی با فرمت JSON در مسیر /.well-known/agent-card.json است که مهارت‌ها، فرمت‌های MIME ورودی/خروجی، رابط‌های ارتباطی و طرح‌های احراز هویت ایجنت را توصیف می‌کند.

ویژگی متمایزکننده‌ی پروتکل ACP چیست؟

پروتکل ACP امکان ارسال متادیتای مسیر (TrajectoryMetadata) را فراهم می‌کند که شامل لاگ دقیق تمام مراحل استدلال و فراخوانی ابزارها برای بررسی و حسابرسی سازمانی است.

پروتکل ANP چگونه مسئله‌ی اعتماد بین‌سازمانی را حل می‌کند؟

پروتکل ANP از شناسه‌های غیرمتمرکز W3C موسوم به DID و متد did:wba به‌همراه رمزنگاری سرتاسری و مذاکره‌ی فرامفهومی استفاده می‌کند تا ایجنت‌ها بدون مرجع مرکزی یکدیگر را اعتبارسنجی کنند.

ما رو توی گوگل بیشتر ببین

با افزودن نکست ادمین به «منابع ترجیحی» گوگل، درس‌ها و مطالب ما را در بخش اخبار برتر جستجو بیشتر می‌بینید.