এই পাতাটি চীনা মূল থেকে অনূদিত। দুটির মধ্যে অমিল হলে মূল পাঠই প্রামাণ্য।

Confer — প্রোটোকলের নকশা

Confer ইনস্ট্যান্সগুলোর মধ্যে এবং ব্যবহারকারীর ক্লায়েন্ট ও সার্ভারের মধ্যে যত প্রোটোকল আছে, সবই এখানে সংজ্ঞায়িত। সবগুলোই উন্মুক্ত মানদণ্ডের উপর দাঁড়ানো, যাতে ভবিষ্যতে ফেডারেশন সহজ হয়।

Agent-এর পরিচয়

DID:web বিন্যাস

প্রত্যেক ব্যবহারকারী বা প্রতিষ্ঠানের ইনস্ট্যান্স নিজের DID নথি নিজেই রাখে:

https://acme.com/.well-known/did.json

DID নথির গঠন (W3C DID v1.0 সঙ্গতিপূর্ণ):

{
  "@context": ["https://www.w3.org/ns/did/v1"],
  "id": "did:web:acme.com",
  "verificationMethod": [
    {
      "id": "did:web:acme.com#key-1",
      "type": "Ed25519VerificationKey2020",
      "controller": "did:web:acme.com",
      "publicKeyMultibase": "z6MkpTHR8VNsBxYAAWHut2Geadd9jSrue..."
    }
  ],
  "service": [
    {
      "id": "did:web:acme.com#confer-agent",
      "type": "ConferAgent",
      "serviceEndpoint": "https://acme.com/a2a/v1"
    }
  ]
}

ব্যবহারকারীর Agent-এর DID এই চেহারার: did:web:acme.com:agents:laowang — মূল ইনস্ট্যান্স, তার সঙ্গে একটি পথ-খণ্ড। এতেই একটি ইনস্ট্যান্স বহু ব্যবহারকারীকে ধরতে পারে।

did:web স্পেসিফিকেশন অনুসারে, উপ-শনাক্তকারীযুক্ত DID (অর্থাৎ পথ-খণ্ডসহ) ওই পথের নথিতেই সমাধান হয়, ইনস্ট্যান্সের গোড়ার .well-known-এ নয়:

কী ঘোরানো

AgentFacts (NANDA সঙ্গতিপূর্ণ)

প্রত্যেক Agent নিজের বর্ণনা দিয়ে একটি AgentFacts প্রকাশ করে। অবস্থান:

https://acme.com/agents/{slug}/agent.json

অথবা well-known-এর সাধারণ তালিকা:

https://acme.com/.well-known/agents.json

গঠনের নমুনা:

{
  "@context": "https://nanda.dev/schemas/agent/v1",
  "did": "did:web:acme.com:agents:support",
  "name": "ABC Industries Support Agent",
  "description": "Technical support for X100, X200 industrial controllers",
  "owner": {
    "type": "Organization",
    "name": "ABC Industries Ltd.",
    "url": "https://acme.com"
  },
  "capabilities": [
    {
      "type": "qa",
      "scope": ["X100", "X200", "Modbus", "RTU", "TCP"],
      "languages": ["en", "zh", "de"]
    },
    {
      "type": "code-generation",
      "scope": ["python", "c", "embedded"],
      "languages": ["en", "zh"]
    }
  ],
  "endpoints": {
    "a2a": "https://acme.com/a2a/v1",
    "stream": "https://acme.com/a2a/v1/stream"
  },
  "trust": {
    "verifiedBy": ["did:web:nanda.org"],
    "issuedAt": "2024-10-01T00:00:00Z"
  },
  "publicKey": {
    "id": "did:web:acme.com#key-1",
    "type": "Ed25519VerificationKey2020"
  }
}

ক্ষেত্রগুলোর অর্থ:

A2A প্রোটোকল

প্রোটোকলের স্তর

সব A2A যোগাযোগ HTTPS POST/GET দিয়ে চলে, JSON-এ এনকোড করা। /a2a/v1-এর নিচে দুটি বাইন্ডিং পাশাপাশি থাকে:

দুটোই একই ফটকগুলো ব্যবহার করে — স্বাক্ষর যাচাই, সম্মতির ফটক, নীতির সিদ্ধান্ত, থ্রেডের মালিকানা — এবং সেগুলো সবই a2a/inbound.ts-এ; ফারাক কেবল তারের বিন্যাসে। প্রতিটি বাইন্ডিংয়ের জন্য এই ফটকগুলো আলাদা করে লেখাই সেই কারণ যাতে আন্তঃভাড়াটে থ্রেড ইনজেকশনের বাগটি চারবার লেখা হয়েছিল; আর আলাদা করবেন না।

মূল কথা: HTTP Message Signatures (RFC 9421) ব্যবহৃত হয়, bearer টোকেন নয়। কারণ:

স্বাক্ষর যেসব উপাদান ঢাকে সেগুলো হলো @method, @authority ও @path, সঙ্গে @query (অনুরোধে কোয়েরি স্ট্রিং থাকলে) এবং content-digest (বডি থাকলে), আর date। @query কোনো ঐচ্ছিক সূক্ষ্মতা নয়: @path ?-এ গিয়ে থেমে যায়, আর REST বাইন্ডিংয়ের GET /tasks পুরোপুরি কোয়েরি প্যারামিটার দিয়েই ছাঁকে ও পাতা ওল্টায়; সেগুলো না ঢাকা মানে মাঝপথের কাউকে ইচ্ছেমতো সেগুলো বদলাতে দেওয়া, অথচ স্বাক্ষর তবুও মিলে যাবে। স্বাক্ষরের প্যারামিটারে প্রতি অনুরোধে একটি এলোমেলো nonce-ও থাকে — created-এর সূক্ষ্মতা সেকেন্ড পর্যন্তই, তাই একই সেকেন্ডে দুটি অভিন্ন অনুরোধ একই বাইটে স্বাক্ষর করত, আর অপর পক্ষের রিপ্লে ক্যাশে সেটিকে আক্রমণ ধরে ফিরিয়ে দিত (কোনো task-এর খোঁজ নেওয়া বা যেকোনো পুনঃচেষ্টা ঠিক এখানেই ঠেকত)। আসল পুনঃপ্রেরণ — একই বাইট হুবহু আবার পাঠানো — এখনও ধরা পড়ে, কারণ তখন স্বাক্ষর বাইটে বাইটে এক।

এই স্তরে স্পেসিফিকেশনের securitySchemes নেই: ওগুলো API কী, HTTP প্রমাণীকরণ, OAuth2, OIDC বা mTLS, আর একটিও অনুরোধ-স্বাক্ষর নয়। আসল দাবিটি Card-এ আবশ্যিক এক্সটেনশন হিসেবে ঘোষিত হয়, আর REST বাইন্ডিং §3.3.4 অনুসারে সেটি বাধ্যতামূলক করে: যে ক্লায়েন্ট ওই এক্সটেনশন ঘোষণা করেনি সে পায় ExtensionSupportRequiredError, এমন কোনো 401 নয় যা কিছুই ব্যাখ্যা করে না।

আগত অনুরোধের নমুনা

POST /a2a/v1/messages HTTP/1.1
Host: acme.com
Content-Type: application/json
Date: Sun, 24 Nov 2024 14:30:00 GMT
Content-Digest: sha-256=:X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE=:
Signature-Input: sig1=("@method" "@authority" "@path" "content-digest" "date");keyid="did:web:vendor-x.com#key-1";created=1732458600;alg="ed25519"
Signature: sig1=:aBcDeF...:
Authorization: Capability eyJhbGciOiJFZDI1NTE5IiwidHlwIjoiQ2FwIn0...

{
  "from": "did:web:vendor-x.com:agents:engineer-li",
  "to": "did:web:acme.com:agents:support",
  "thread_id": "thread_8f3a9c",
  "message": {
    "type": "question",
    "content": "RTU মোডে X100-এর ভোল্টেজ পরিসর কত?",
    "language": "bn",
    "context": {
      "via": "claude-code",
      "project_hint": "modbus integration"
    }
  }
}

যাচাইয়ের ধাপ (প্রাপকের দিকে)

১. Signature-Input ও Signature হেডার পড়ুন। ২. Signature-Input-এর keyid প্যারামিটার থেকে DID বের করুন। ৩. DID নথি আনুন (ক্যাশসহ: ETag + ৬০ সেকেন্ড TTL)। ৪. সর্বজনীন কী নিন, RFC 9421 §2.5 অনুসারে স্বাক্ষরের ভিত্তি-স্ট্রিং আবার গড়ুন, আর স্বাক্ষর যাচাই করুন। ৫. Content-Digest বডির হ্যাশের সঙ্গে মেলে কি না দেখুন। ৬. Date ৫ মিনিটের মধ্যে কি না দেখুন (পুনঃপ্রেরণ ঠেকাতে)। ৭. capability টোকেন যাচাই করুন (macaroon ধাঁচের, নিচে বিস্তারিত)। ৮. সংযোগ সম্মতির ফটক: প্রেরক কি প্রাপকের পরিচিতিতে আগে থেকেই আছে? না থাকলে → LLM চলে না; অনুরোধটি সংযোগের আবেদন হিসেবে ঝুলে থাকে (নিচে দেখুন)। ৯. থাকলে → নীতি ইঞ্জিন ঠিক করে সাড়া দেওয়া হবে কি না।

Capability টোকেন

Capability টোকেন দিয়ে প্রেরক Agent বলতে পারে “আমি ব্যবহারকারী X-এর হয়ে Y ধরনের প্রশ্ন করতে এসেছি”, আর অনুমতি সূক্ষ্মভাবে সীমিত করা যায়।

চেহারায় JWT, ভাবনায় macaroon:

{
  "iss": "did:web:vendor-x.com",
  "sub": "did:web:vendor-x.com:users:engineer-li",
  "aud": "did:web:acme.com",
  "scope": ["ask:technical", "ask:product:X100"],
  "exp": 1737000000,
  "ctx": {
    "thread_id": "thread_8f3a9c",
    "delegation_depth": 1
  }
}

ধারায় উত্তর

LLM উত্তর তৈরি করে ধারায়, আর A2A-তেও SSE আছে:

GET /a2a/v1/stream/{message_id} HTTP/1.1
Host: acme.com
Signature: ...

text/event-stream ফেরে:

event: token
data: {"text": "X100 "}

event: token
data: {"text": "RTU মোডে "}

event: citation
data: {"source": "X100 ইনস্টলেশন ম্যানুয়াল পৃ. ১২", "url": "..."}

event: done
data: {"thread_id": "thread_8f3a9c"}

অনুমতির মডেল (Claude Code থেকে অনুপ্রাণিত)

তিন স্তর:

L1 — স্বয়ংক্রিয় (নিশ্চিতকরণ লাগে না)

L2 — একবার জিজ্ঞেস করা

ইন্টারফেসে: একটি অনুমতি-কার্ড খোলে, চারটি বিকল্প নিয়ে:

L3 — স্পষ্ট সম্মতি (প্রতিবারই জিজ্ঞেস করা হয়)

ইন্টারফেসে: মোডাল জানালা + কাজের বিস্তারিত তালিকা + উল্টো গণনা (ভুল করে চাপা ঠেকাতে)।

স্থায়ী নীতি

ব্যবহারকারী আগেভাগে নিয়ম ঠিক করে দিতে পারেন, যা ডিফল্ট আচরণের উপরে বসে:

peer.acme-industries:
  allow:
    - read: "src/modbus/**"
    - ask: "technical:*"
  deny:
    - read: ".env"
    - read: "**/secrets/**"
    - ask: "personal:*"
  always_consult: true

peer.unknown:
  default: ask_user
  require_human_in_loop: true

সংযোগ সম্মতির ফটক

কোনো A2A বার্তার উত্তর দেওয়া প্রাপকের LLM বাজেট খরচ করে। অচেনা কোনো Agent যাতে মালিকের অজান্তে বেপরোয়া বার্তা পাঠিয়ে তাঁর টোকেন পুড়িয়ে দিতে না পারে, সেজন্য এই খরচের পূর্বশর্ত হলো সংযুক্ত থাকা:

মডেলটি LinkedIn বা কোনো প্রাতিষ্ঠানিক ফেডারেশনের মতো: আবিষ্কারের স্তর উন্মুক্ত (যে কেউ agents.json ও AgentFacts পড়তে পারে), মিথস্ক্রিয়ার স্তরে সম্মতি লাগে (সংযুক্ত হওয়ার পরেই অন্যের গণনা খরচ হয়)।

«সংযুক্ত» হওয়ার দুটি পথ: ১. প্রাপক নিজে POST /contacts/lookup → POST /contacts দিয়ে ওই peer-কে যোগ করেন; ২. peer আগে উদ্যোগ নেয়, আর প্রাপক বাক্স থেকে তার আবেদন অনুমোদন করেন।

থ্রেডের বাঁধন (thread_id-এর পরিসর)

আগত বার্তার thread_id peer-এর একটি অনুরোধ, কর্তৃত্বময় নির্দেশ নয়। gateway সেটিকে মূল মানেই পুনর্ব্যবহার করে কেবল যখন দুটি শর্ত একসঙ্গে মেলে:

১. ওই peer আগে থেকেই সেই কথোপকথনের অংশগ্রহণকারী; ২. সেই কথোপকথন যে Agent-কে বার্তা পাঠানো হয়েছে তার মালিকের (conversations.created_by)।

দ্বিতীয়টি বাদ দেওয়া চলে না: peer_agents DID ধরে বিশ্বজুড়ে অনন্য, তাই একই peer একসঙ্গে একাধিক মালিকের সঙ্গে সংযুক্ত থাকতে পারে। কেবল প্রথমটি যাচাই করলে, A ও B দুজনের সঙ্গেই সংযুক্ত কোনো peer B-র Agent-কে বার্তা পাঠানোর সময় A-র thread_id জুড়ে দিয়ে বার্তাটি A-র কথোপকথনে ঢেলে দিতে পারত — B-র Agent A-র ইতিহাসকে প্রেক্ষাপট ধরে উত্তর দিত, উত্তরটি A-র থ্রেডে লেখা হয়ে A-র কাছে ছড়িয়ে পড়ত, আর তার উপর A-র কথোপকথনের বিষয়বস্তু B-র দীর্ঘমেয়াদি স্মৃতিতে জমা হয়ে যেত।

দুটোই মিললে বোঝা যায় অপর পক্ষ আমাদের পাঠানো বার্তারই উত্তর দিচ্ছে (আর তখন thread_id আমাদের নিজেদের কথোপকথনের id)। না মিললে সেটি peer-এর নিজস্ব সংখ্যায়নের থ্রেড — এখানে কিছুই নির্দেশ করে না, কিন্তু peer-এর কাছে স্থিতিশীল, তাই আমাদের কথোপকথনের id উদ্ভূত হয় sha256('a2a-thread:<মালিকের id>:<peer সারির id>:<peer-এর thread_id>') থেকে (lib/derived-id.ts, ফেরত দেয় ২৬ অক্ষরের Crockford, ULID-এর মতোই চেহারা)। ফলে ওই একই peer থ্রেডের পরের বার্তাগুলো সবসময় একই কথোপকথনে ফিরে আসে।

আগে «অচেনা thread_id»-কে «থ্রেড নেই» ধরা হতো, তাই প্রাপক যত বার্তা আসত ততবার নতুন কথোপকথন খুলত: পরের প্রশ্ন কখনও আসল প্রশ্নের পাশে থাকত না, মালিকের কথোপকথনের তালিকা এক লাইনের থ্রেডে ভরে যেত, loadA2AHistory কোনো ইতিহাস পেত না, আর Agent প্রতিটি দফায় এমনভাবে উত্তর দিত যেন এই প্রথম কথা হচ্ছে।

«একটি ম্যাপিং টেবিল রাখা»-র বদলে উদ্ভূত করার দুটি সুবিধা: কোনো মাইগ্রেশন লাগে না, আর প্রতিযোগিতাও অসম্ভব — একসঙ্গে আসা দুটি বার্তা প্রাথমিক কী-তে ঠোকাঠুকি করে, প্রত্যেকে আলাদা কথোপকথন বানায় না (এজন্যই কথোপকথন তৈরি হয় লেনদেনের ভেতরে onConflictDoNothing দিয়ে)। জোড়া লাগানো স্ট্রিংয়ে কেবল শেষ খণ্ডটির দৈর্ঘ্য পরিবর্তনশীল হতে পারে এবং সেটিই peer-এর নিয়ন্ত্রণে; আগের খণ্ডগুলো ২৬ অক্ষরের নির্দিষ্ট id, তাই এস্কেপ না করা কোলন কোনো দ্ব্যর্থতা তৈরি করে না।

কথোপকথন তৈরির সময় conversation_participants-এ মালিক ও peer দুজনকেই একসঙ্গে লেখা হয়। মালিকের অংশগ্রহণের সারিটিই কথোপকথনের তালিকা ও প্রতি-কথোপকথন পড়ার ফটকের ভিত্তি; সেটি না থাকলে মালিক সেই থ্রেডই দেখতে পাবেন না যার উত্তর তাঁরই Agent দিচ্ছে।

তাই thread_id হলো প্রতিটি পক্ষের নিজস্ব কথোপকথনের id, দুই দিকে এক নয়। এ থেকে দুটি নিয়ম আসে, যেগুলো বাদ দেওয়া চলে না:

উত্তর দিতে না পারাও একটি উত্তর

যে Agent-কে বার্তা পাঠানো হয়েছে সে যখন দফাটি চালাতেই পারে না (মডেল ঠিক করা নেই, সরবরাহকারী অচেনা, সরবরাহকারী আছে কিন্তু কী নেই, বা মডেলের কল ব্যর্থ), তখন সে পাঠায় type: 'notification', যার context.error-এ থাকে যন্ত্র-পাঠযোগ্য কারণ-কোড (no_model_configured / unknown_provider / no_key_for_provider / agent_error), আর content-এ ইংরেজিতে এক বাক্যের ব্যাখ্যা। notification নেওয়ার কারণ, এটি অপর প্রান্তে আরেকটি স্বয়ংক্রিয় উত্তর জাগায় না (কেবল question জাগায়)।

এটি না করার মানে «একটি বিজ্ঞপ্তি কম» নয়: ব্যর্থতা কেবল উত্তরদাতার দিকে এক লাইনের লগ হয়ে থাকে, তারে কিছুই যায় না, আর প্রশ্নকারীর /api/v1/consult/{id}/reply সময়সীমা পর্যন্ত খোঁজ করে pending ফেরায় — প্রতিটি পুনঃচেষ্টাতেই একই, আর «এখনও ভাবছে» আর «কখনওই আসবে না»-র মধ্যে ফারাক করার কোনো উপায়ই থাকে না।

অপর প্রান্ত অন্য একটি ইনস্ট্যান্স, আমাদের ভাষা সে জানে না, তাই ভিত্তি হলো context.error-এর কোড; content কেবল মানুষের পড়ার মতো শেষ ভরসা। এটি «সার্ভার ব্যবহারকারীর জন্য বাক্য বানায় না» নিয়মের সঙ্গে সংঘাত নয়: ওই নিয়ম এই ইনস্ট্যান্সের নিজের ক্লায়েন্টে পাঠানো লেখাকে নিয়ন্ত্রণ করে।

একই ব্যর্থতা ডেটাবেসেও লেখা হয় — কথোপকথনের ভেতরে content_type: 'system_notice' বার্তা হিসেবে (in_reply_to ওই প্রশ্নের দিকে, content_json-এ একই কোড, আর বাক্যটি ক্লায়েন্ট i18n দিয়ে গড়ে)। পাঠিয়ে না লিখলে তিনটি পরিণতি হয়, আর তিনটিই সত্যিই ঘটেছে: মালিক নিজের IM-এ অপর পক্ষের প্রশ্নের পরে কিছুই দেখেন না, আর কখনও জানতেই পারেন না যে মডেলটি তিনিই ঠিক করেননি; A2A REST বাইন্ডিংয়ের task FAILED-এর বদলে WORKING-এ দাঁড়িয়ে থাকে, আর ক্লায়েন্ট এমন কিছুর খোঁজ করতে থাকে যা কখনও শেষ হবে না; আর GET /a2a/v1/stream/{id} অনন্তকাল pending ফেরায়। এই নোটটি থাকলে তিনটিই একসঙ্গে নির্ধারণযোগ্য চূড়ান্ত অবস্থায় পৌঁছে যায়।

ঠিকানা: দুটি DID একই Agent-কে বোঝায়

to গ্রহণ করে Agent DID (did:web:<host>:agents:<user>:agent, যা সর্বজনীন তালিকা /.well-known/agents.json-এ থাকে) এবং মালিকের DID (did:web:<host>:agents:<user>) — দুটোই। শেষেরটিই একমাত্র শনাক্তকারী যা DID নথিতে সমাধান হয়, আর ক্লায়েন্ট ব্যবহারকারীকে কপি করার জন্য সেটিই দেখায়: কেবল প্রথমটি মানলে «DID পেস্ট করে পরিচিতি যোগ করা» এমন একটি পরিচিতি দেয় যার সঙ্গে সংযোগও হয়, স্বাক্ষরও মেলে, আর উত্তর আসে 404।

একই কারণে, প্রেরক peer সংযুক্ত কি না ঠিক করার সময় from (Agent DID) আর স্বাক্ষর যাচাই থেকে পাওয়া স্বাক্ষরকারীর DID (মালিকের DID) — দুটোই মানতে হবে: peer_agents-এ সারি তৈরি হয় DID ধরে, আর তার মধ্যে কোনটি সেখানে জমা হয়েছে তা নির্ভর করে পরিচিতিটি তখন কীভাবে যোগ হয়েছিল তার উপর; কেবল from মানলে অপর পক্ষের উত্তর হয়ে যায় «অচেনা কারও সংযোগ-আবেদন»।

অপেক্ষমাণ বাক্স (মালিকের অনুপস্থিতিতে উত্তর)

আগে থেকেই সংযুক্ত peer-এর প্রশ্ন এল আর মালিক নেই — তখন সিদ্ধান্ত নেয় নীতি ইঞ্জিন (evaluatePolicy, action=ask, L2):

A2A উত্তরের সামর্থ্য: আগত A2A উত্তর আর ওয়েব চ্যাট একই ভাগাভাগি করা অর্কেস্ট্রেশন দিয়ে চলে (orchestration/agent-orchestrator.ts-এর runAgentTurn), কিন্তু তাদের সামর্থ্য এক নয়। runAgentTurn একটি আবশ্যিক audience নেয় ('owner' | 'peer' — আবশ্যিক, ডিফল্টযুক্ত নয়, কারণ ডিফল্ট মানটাই হতো ঢিলেঢালাটি), আর সেটিই ঠিক করে সরঞ্জামের সেট এবং নাগালে থাকা তথ্য-তল দুটোই:

  • মালিকের দফা (ওয়েব চ্যাট): web_search, search_knowledge_base (সব জ্ঞানভাণ্ডার), list_knowledge_bases, search_memory, list_contacts, সঙ্গে দীর্ঘমেয়াদি স্মৃতি আপনা-আপনি যুক্ত হয়।
  • peer-এর দফা (আগত A2A উত্তর): কেবল web_search, search_knowledge_base ও list_knowledge_bases, আর অনুসন্ধান কেবল সেই ভাণ্ডারগুলোতেই সীমিত যেগুলোতে shared_with_peers চিহ্ন আছে; দীর্ঘমেয়াদি স্মৃতি তোলা হয় না, আর search_memory বা list_contacts দেওয়াই হয় না — প্রথমটি মালিকের ব্যক্তিগত কথোপকথন থেকে জমা তথ্য, দ্বিতীয়টি তাঁর সামাজিক জাল; অচেনা কারও প্রশ্নের উত্তর দিতে দুটোর কোনোটিই লাগে না।

সীমানা দাঁড়ায় তথ্য-তলের উপর, প্রম্পটের উপর নয়: peer-এর প্রশ্ন আর মালিকের নির্দেশ মডেলের কাছে একই ধরনের লেখা হয়ে পৌঁছায়, তাই «Agent বলতে অস্বীকার করবে» টেকে না; টেকে কেবল এটাই যে অনুসন্ধান শারীরিকভাবে সেখানে পৌঁছাতেই পারে না। একই কারণে, কোনো সরঞ্জাম না দেওয়া মানেই প্রবেশ-নিয়ন্ত্রণ নয়: মডেল এমন সরঞ্জামের নামেও কল করতে পারে যা তাকে কখনও দেওয়া হয়নি, তাই কেবল-মালিকের শাখাগুলো executeToolCall-এর ভেতরে audience আবার যাচাই করে।

দুই ধরনের দফাই মালিকের কী ব্যবহার করে, প্রশ্নকারী peer-এর নয়। মিলে যাওয়া জ্ঞানভাণ্ডারের অংশ messages.citations_json-এ উদ্ধৃতি হিসেবে রাখা হয়, আর উত্তর শেষ হলে এই দফার তথ্য অ্যাসিনক্রোনাসভাবে দীর্ঘমেয়াদি স্মৃতিতে জমা হয় (peer-এর দফা থেকে আসা সারিতে a2a চিহ্ন বসে, আর তোলার সময় উৎস জানানো হয়)। মালিকের embedding / জ্ঞানভাণ্ডার / tavily কী না থাকলে সুন্দরভাবে কেবল-LLM উত্তরে নেমে আসে (ত্রুটি নেই, উদ্ধৃতি নেই)। allow-এর এবং অনুমোদিত ask_user-এর উত্তরের পথ এই একই অর্কেস্ট্রেশন ভাগ করে।

ask='ask' ঝুলন্ত অনুমতির scope_json এই চেহারার: { kind:'a2a_question', conversation_id, inbound_message_id, sender_did, peer_id, content } — অনুমোদনের সময় উত্তর আবার গড়ে এগিয়ে নিতে এটুকুই যথেষ্ট (agent ও peer user_id/peer_id ধরে তখনই পড়া হয়; পুনরাবৃত্তি-নিরাপদ: উত্তর আগে থেকে থাকলে বাদ)। স্থায়ী নীতি ঠিক করার UI, «সম্পাদনা করে উত্তর», আর push বিজ্ঞপ্তি এখনও backlog-এ।

ফেডারেটেড আবিষ্কার

ডোমেইন ধরে খোঁজা

ডোমেইন acme.com পেলে ক্লায়েন্ট:

১. মূল DID পেতে https://acme.com/.well-known/did.json আনে। ২. ওই ডোমেইনের সব সর্বজনীন Agent-এর তালিকা পেতে https://acme.com/.well-known/agents.json আনে। ৩. একটি বেছে পরিচিতিতে যোগ করে।

ব্যবহারকারীর DID সমাধান

কোনো ব্যবহারকারীর Agent-এর উপ-শনাক্তকারীযুক্ত DID পেলে তার DID নথি did:web স্পেসিফিকেশন অনুসারে সমাধান হয়:

আগত A2A-র স্বাক্ষর যাচাই ঠিক এই পথেই চলে: Signature-Input-এর keyid থেকে স্বাক্ষরকারীর DID তুলুন → উপরের URL-এ সমাধান করুন → verificationMethod থেকে keyid-এর সঙ্গে মেলা সর্বজনীন কী নিয়ে যাচাই করুন। ওই নথি কেবল সর্বজনীন কী-উপাদানই প্রকাশ করে, আর verificationMethod[*].id হলো সংরক্ষিত key_id (অনুরোধের Host থেকে আবার গড়া হয় না), তাই অন্য ইনস্ট্যান্স থেকে সমাধান করে পাওয়া id আর স্থানীয়ভাবে পাওয়া id সবসময় এক।

সর্বজনীন রেজিস্ট্রি (v2+)

NANDA Index বা তেমন কোনো সর্বজনীন রেজিস্ট্রির সঙ্গে যুক্ত হওয়া, যাতে সম্ভব হয়:

আস্থার জাল (v2+)

স্প্যাম প্রতিরোধ

অনুবাদ কৌশল

প্রোটোকল বিবর্তনের কৌশল

← Confer-এ ফিরে যান A2A · DID:web · RFC 9421 · NANDA