وقتی یک سامانهی هوش مصنوعی را به چند ایجنت تخصصی (مثل پژوهشگر، برنامهنویس و ناظر) تقسیم میکنید، بزرگترین چالش فنی خیلی زود نمایان میشود: چگونه این ایجنتها بدون اتکا به متنهای بدون ساختار، ناهماهنگی در اسکیما یا ورود به بنبست، با هم کار کنند؟ ردوبدل کردن رشتههای خام متنی در محیطهای جدی عملیاتی پایدار نیست و خیلی زود سیستم را شکننده میکند.
برای حل این مشکل، صنعت هوش مصنوعی به سمت پروتکلهای استاندارد ارتباطی حرکت کرده است. چهار پروتکل اصلی یعنی MCP (برای اتصال به ابزارها)، A2A (برای تعامل و واگذاری کار میان ایجنتها)، ACP (برای حسابرسی سازمانی و ردیابی مسیر استدلال) و ANP (برای احراز هویت غیرمتمرکز) طراحی شدهاند تا قراردادهای تبادل داده را استاندارد کنند.
در این درس ساختار داخلی این چهار استاندارد، فرمت تبادل داده و چرخهی حیات تسکها را بررسی میکنید و سپس در یک پیادهسازی گامبهگام با TypeScript، این پروتکلها را درون یک معماری یکپارچه به هم متصل خواهید کرد.
ایجنتهایی که زبان مشترکی ندارند یک تیم نیستند؛ غریبههایی هستند که در خلأ فریاد میزنند.
اهداف یادگیری
- پیادهسازی کشف و فراخوانی ابزار در MCP تا ایجنتها بتوانند از ابزارهای ارائهشده روی سرورهای خارجی استفاده کنند
- ساخت کارت ایجنت (Agent Card) در A2A و اندپوینت وظیفه برای واگذاری کار از یک ایجنت به ایجنت دیگر روی بستر HTTP
- مقایسهی MCP (دسترسی به ابزار)، A2A (ایجنت با ایجنت)، ACP (حسابرسی سازمانی) و ANP (اعتماد غیرمتمرکز) و توضیح اینکه هر پروتکل کدام مسئله را حل میکند
- اتصال چند پروتکل به یکدیگر در قالب سامانهای یکپارچه که در آن ایجنتها ابزارها را از طریق MCP کشف کرده و وظایف را با A2A واگذار میکنند
صورت مسئله
سامانهی خود را به چند ایجنت تقسیم میکنید: پژوهشگر، برنامهنویس، ارزیاب. هرکدام در کار فردی خود عالی هستند؛ اما حالا باید کاری کنید که واقعاً با یکدیگر گفتوگو کنند.
اولین راهکاری که به ذهن میرسد مشخص است: ردوبدل کردن رشتههای متنی ساده. پژوهشگر یک تکه متن برمیگرداند و برنامهنویس آن را با هر روشی که بتواند پارس میکند. این روش تا زمانی کار میکند که برنامهنویس خلاصهی پژوهش را اشتباه متوجه نشود، دو ایجنت در انتظار یکدیگر وارد بنبست (deadlock) نشوند، یا نیازی نباشد ایجنتهای ساختهشده توسط تیمهای مختلف با هم همکاری کنند. ناگهان رویکردِ «فقط ارسال رشتهی متنی» از هم میپاشد.
این دقیقاً مسئلهی پروتکل ارتباطی است. بدون قراردادی مشترک برای نحوهی تبادل اطلاعات میان ایجنتها، سیستمهای چندایجنت شکننده، غیرقابلحسابرسی و فاقد قابلیت مقیاسپذیری فراتر از چند ایجنتی خواهند بود که خودتان بهتنهایی نوشتهاید.
اکوسیستم هوش مصنوعی با چهار پروتکل به این چالش پاسخ داده است که هرکدام بخش متفاوتی از مسئله را حل میکنند:
- MCP برای دسترسی به ابزارها
- A2A برای همکاری ایجنت با ایجنت
- ACP برای حسابرسیپذیری سازمانی
- ANP برای هویت و اعتماد غیرمتمرکز
در این درس عمیق میشویم. فرمتهای واقعی انتقال پیام (wire formats) را از متن هر استاندارد بررسی میکنید، پیادهسازیهای عملی میسازید و هر چهار مورد را به سامانهای واحد متصل میکنید.
مفاهیم پایه
چشمانداز پروتکلها
این چهار پروتکل را لایههایی در نظر بگیرید که هرکدام به پرسش متفاوتی پاسخ میدهند:
این پروتکلها رقیب یکدیگر نیستند؛ مسائل مختلفی را در سطوح متفاوتی حل میکنند.
مرور MCP
به MCP در فاز 13 بهصورت مفصل پرداخته شده است. یک مرور سریع: پروتکل MCP نحوهی اتصال مدل زبانی بزرگ (LLM) به ابزارها و منابع دادهی خارجی را استاندارد میکند. این یک پروتکل کلاینت-سرور است که در آن ایجنت (کلاینت)، ابزارهای ارائهشده توسط سرور را کشف و فراخوانی میکند.
ارتباط در MCP از نوع ایجنت با ابزار است و کمکی به گفتوگوی ایجنتها با یکدیگر نمیکند.
پروتکل A2A (پروتکل Agent2Agent)
سازنده: Google (اکنون تحت نظارت Linux Foundation با شناسه lf.a2a.v1)
نسخهی مشخصات: 1.0.1
صورت مسئله: ایجنتهای خودمختار چگونه با یکدیگر همکاری، مذاکره و تبادل وظایف میکنند؟
پروتکل A2A برای همکاری همتابههمتای ایجنتها طراحی شده است. همانطور که MCP ایجنت را به ابزارها متصل میکند، A2A ایجنت را به ایجنتهای دیگر وصل میکند. هر ایجنت یک کارت ایجنت در آدرسی شناختهشده (well-known URL) منتشر میکند و سایر ایجنتها آن را کشف کرده، با آن مذاکره میکنند و وظایف را به آن میسپارند.
نحوهی کارکرد A2A
کارت ایجنت در دنیای واقعی
کارت ایجنت واقعی 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_ که در فرمت انتقال پیام همراهِ هر وضعیت میآید، برای سادگی حذف شده است):
تمام 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 چهار روش برای کشف تعریف میکند:
ساختار مانیفست ایجنت (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. همزمان با کار کردن ایجنت، رویدادها صادر میشوند. |
متادیتای مسیر طیشده (ردپای حسابرسی)
این نقطهی تمایز اصلی 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 از سه لایه تشکیل شده است:
اسناد 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) یا گراف تایید صلاحیت استفاده نمیکند. اعتماد بهصورت دوطرفه و در هر تعامل ارزیابی میشود:
اعتماد از سه منبع تأمین میشود:
- پروتکل TLS سطح دامنه که میزبان سند DID را تأیید میکند
- امضاهای رمزنگاریشدهی DID که هویت ایجنت را تأیید میکنند
- اصل حداقل اعتماد (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"
}
ایجنتها تا سقف 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 | در حال توسعهی فعال |
همکاری این پروتکلها در کنار هم
این پروتکلها مانع یکدیگر نیستند. یک سامانهی سازمانی واقعی از چند پروتکل بهره میگیرد:
- MCP هر ایجنت را به ابزارهایش متصل میکند
- A2A مدیریت همکاری میان ایجنتها (داخلی و خارجی) را بر عهده دارد
- ACP پاسخها را درون متادیتای مسیر میپیچد تا امکان حسابرسی فراهم شود
- 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)
هر چهار پروتکل را در قالب یک سامانهی یکپارچه متصل کنید:
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
);
}
}
این گیتوی چهار کار را در قالب یک فراخوانی واحد انجام میدهد:
- ANP: هویت فراخواننده را از طریق امضای DID تأیید میکند
- A2A: ایجنت مقصد را کشف کرده و توانمندیهای آن را بررسی میکند
- ACP: فرایند اجرا را همراه با جزئیات مسیر در ردپای حسابرسی ثبت میکند
- 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 استاندارد اصلی خواهد بود.
انتخاب پروتکل مناسب
خروجی این درس
این درس شامل موارد زیر است:
- فایل
code/main.ts-- پیادهسازی کامل الگوهای هر چهار پروتکل - فایل
outputs/prompt-protocol-selector.md-- پرامپتی کاربردی برای کمک به انتخاب بهترین پروتکل بر اساس نیازهای سامانهی شما
تمرینها
-
واگذاری چندگامی تسکها (Multi-hop task delegation): کلاس
TaskManagerرا طوری توسعه دهید که هندلر یک ایجنت بتواند زیرتسکها را به ایجنتهای دیگر واگذار کند. ایجنت پژوهشگر تسکی میگیرد، زیرتسکهای «جستوجو» و «خلاصهسازی» را به دو ایجنت متخصص میسپارد، منتظر اتمام کار هر دو میماند و نتایج را در دستسازههای خروجی خود merge میکند. -
ردپای حسابرسی استریمینگ: کلاس
AuditableRunnerرا برای پشتیبانی از حالت استریمینگ ویرایش کنید. بهجای انتظار برای دریافت نتیجهی کامل، تغییراتAuditEntryرا همزمان با ثبت گامهای استدلال بهصورت زنده ارسال کنید. از یک ژنراتور ناهمگام استفاده کنید که اسنپشاتهای پایش و حسابرسی را صادر کند. -
چرخش کلید DID: قابلیت چرخش کلید را به
IdentityRegistryاضافه کنید. یک ایجنت باید بتواند سند DID جدیدی با کلیدهای بهروزشده منتشر کند و همزمان بهpreviousDidارجاع دهد. مدلهای اعتبارسنج (verifiers) باید امضاهای هر دو کلید فعلی و قبلی را در یک بازهی زمانی مشخص بپذیرند. -
مذاکرهی پروتکل: سازوکار فرامفهومی ANP را پیاده کنید. دو ایجنت پیامهای
protocolNegotiationرا همراه با فرمتهای پیشنهادی مبادله میکنند (مثلاً «من میتوانم با JSON-RPC کار کنم» در برابر «من REST را ترجیح میدهم»). پس از حداکثر 3 دور، روی یک فرمت توافق میکنند یا اتصال به دلیل پایان مهلت (timeout) بسته میشود. فرمت مورد توافق مشخص میکند که از کدامTaskManagerیاAuditableRunnerاستفاده شود. -
کنترل نرخ کشف سرویس: یک پوشش
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 که کار را از زمان ارسال تا اتمام پیگیری میکند و پس از رسیدن به وضعیت پایانی تغییرناپذیر خواهد بود. |
برای مطالعهی بیشتر
- مشخصات فنی پروتکل Google A2A -- مستندات رسمی و SDKها (نسخهی 1.0.1، بنیاد لینوکس)
- مشخصات فنی پروتکل IBM/BeeAI ACP -- مشخصات OpenAPI 3.1 برای اجرای ایجنتها و متادیتای مسیر
- پروتکل شبکهی ایجنتها (ANP) -- هویت مبتنی بر DID، رمزنگاری E2EE و مذاکرهی فرامفهومی پروتکلها
- مستندات پروتکل زمینهی مدل (MCP) -- مشخصات پروتکل MCP متعلق به Anthropic (بررسیشده در فاز 13)
- استاندارد شناسههای غیرمتمرکز W3C -- استاندارد هویت که پایهی اصلی پروتکل ANP است
- استاندارد RFC 9180 (HPKE) -- سازوکار رمزنگاری که پروتکل ANP برای رمزنگاری سرتاسری به کار میگیرد
- زبان ارتباطی ایجنتها در استاندارد FIPA -- پایههای آکادمیک و پیشینهی پروتکلهای مدرن تعامل ایجنتها
