Protocolos de comunicação
Type: Build
Languages: TypeScript
Prerequisites: Phase 14 (Agent Engineering), Lesson 16.01 (Why Multi-Agent)
Time: ~120 minutes
Objetivos de aprendizagem
- Implementar a descoberta e invocação de ferramentas MCP para que os agentes possam utilizar ferramentas expostas por servidores externos
- Construir um cartão de agente A2A e endpoint de tarefa que permite que um agente delegar trabalho para outro através de HTTP
- Comparar MCP (acessos a ferramentas), A2A (agente a agente), ACP (auditoria empresarial) e ANP (confiança descentralizada) e explicar qual protocolo resolve qual problema
- Conectar vários protocolos em um único sistema onde os agentes descobrem ferramentas através de MCP e delegam tarefas através de A2A
O problema
Dividimos o nosso sistema em vários agentes, um pesquisador, um codificador, um revisor, são ótimos em seus trabalhos individuais, mas agora precisamos que falem uns com os outros.
A primeira tentativa é óbvia: passar cadeias. O pesquisador retorna uma mancha de texto, o programador a analisa de qualquer maneira que puder. Funciona até que o programador interprete mal um resumo de pesquisa, ou dois agentes estão em impasse esperando um pelo outro, ou você precisa de agentes construídos por diferentes equipes para colaborar. De repente, "apenas passar cadeias" cai em pedaços.
Sem um contrato compartilhado para como os agentes trocam informações, os sistemas multi-agentes são frágeis, não auditiveis e impossíveis de escalar além de um punhado de agentes que você escreveu pessoalmente.
O ecossistema de IA respondeu com quatro protocolos, cada um resolvendo uma fatia diferente do problema:
- MCPpara acesso a ferramentas
- A2Apara a colaboração entre agentes
- ACPpara a auditoria das empresas
- ANPpara a identidade e a confiança descentralizados
Esta lição vai muito fundo. Você vai ler formatos reais de fios de cada especificação, construir implementações funcionais e conectar os quatro em um sistema unificado.
O conceito
O Paisagem do Protocolo
Pensem nesses quatro protocolos como camadas, cada uma abordando uma questão diferente:
flowchart TD ANP["ANP — How do agents trust strangers?<br/>Decentralized identity (DID), E2EE, meta-protocol"] A2A["A2A — How do agents collaborate on goals?<br/>Agent Cards, task lifecycle, streaming, negotiation"] ACP["ACP — How do agents talk in auditable systems?<br/>Runs, trajectory metadata, session continuity"] MCP["MCP — How does an agent use a tool?<br/>Tool discovery, execution, context sharing"] style ANP fill:#f3e8ff,stroke:#7c3aed style A2A fill:#dbeafe,stroke:#2563eb style ACP fill:#fef3c7,stroke:#d97706 style MCP fill:#d1fae5,stroke:#059669
Não são concorrentes, resolvem diferentes problemas em diferentes níveis.
MCP (recap)
O MCP é abrangido em profundidade na Fase 13. Resumo rápido: O MCP padroniza como um LLM se conecta a ferramentas externas e fontes de dados.client-serverProtocolo em que o agente (cliente) descobre e chama as ferramentas expostas por um servidor.
sequenceDiagram
participant Agent as Agent (client)
participant MCP1 as MCP Server<br/>(database, API, files)
Agent->>MCP1: list tools
MCP1-->>Agent: tool definitions
Agent->>MCP1: call tool X
MCP1-->>Agent: resultO MCP é agent-to-toolNão ajuda os agentes a falarem uns com os outros.
A2A (Protocolo Agente2Agent)
Created by:Google (agora sob a Linux Foundation como lf.a2a.v1)
Spec version:1.0.1
Problem:Como os agentes autônomos colaboram, negociam e delegam tarefas uns aos outros?
A2A é o protocolo para peer-to-peer agent collaboration. Onde a MCP liga um agente a ferramentas, a A2A liga um agente a outros agentes.Agent CardEm um URL conhecido, e outros agentes descobrem, negociam e delegam tarefas para ele.
#### Como funciona a A2A
sequenceDiagram
participant Client as Client Agent
participant Remote as Remote Agent
Client->>Remote: GET /.well-known/agent-card.json
Remote-->>Client: Agent Card (skills, modes, security)
Client->>Remote: POST /message:send (returnImmediately)
Remote-->>Client: Task (TASK_STATE_SUBMITTED or TASK_STATE_WORKING)
alt Polling
Client->>Remote: GET /tasks/{id}
Remote-->>Client: Task status + artifacts
else Streaming
Client->>Remote: POST /message:stream
Remote-->>Client: SSE: statusUpdate
Remote-->>Client: SSE: artifactUpdate
Remote-->>Client: SSE: statusUpdate TASK_STATE_COMPLETED, stream closes
end#### O verdadeiro cartão de agente
É assim que um cartão de agente A2A realmente se parece na natureza.GET /.well-known/agent-card.json- Não .
json{
"name": "Research Agent",
"description": "Searches documentation and summarizes findings",
"version": "1.0.0",
"supportedInterfaces": [
{
"url": "https: TOK0
"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: TOK2
},
"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": [] } } }]
}Coisas importantes a observar:
- SkillsCada um tem um ID, tags e tipos de entrada / saída MIME suportados. É assim que um agente cliente decide se esse agente remoto pode lidar com seu pedido.
- supportedInterfacesUma única agente pode falar JSON-RPC, REST e gRPC simultaneamente.
- Securityé incorporado no cartão:
securitySchemesnomear cada regime esecurityRequirementsO cliente sabe o que precisa antes de fazer um único pedido.
#### Ciclo de vida das tarefas
As tarefas são a unidade central de trabalho no A2A.TASK_STATE_Prefixo que cada estado carrega no fio):
stateDiagram-v2
[*] --> SUBMITTED
SUBMITTED --> WORKING
WORKING --> INPUT_REQUIRED: needs more info
INPUT_REQUIRED --> WORKING: client sends data
WORKING --> COMPLETED: success
WORKING --> FAILED: error
WORKING --> CANCELED: client cancels
SUBMITTED --> REJECTED: agent declines
COMPLETED --> [*]
FAILED --> [*]
CANCELED --> [*]
REJECTED --> [*]
note right of COMPLETED
Terminal states are immutable.
Follow-ups create new tasks
within the same contextId.
end noteOs 8 estados (a especificação também define UNSPECIFIEDcomo sentinela, omitido aqui):
| State | Terminal? | Meaning |
|---|---|---|
TASK_STATE_SUBMITTED | No | Acknowledged, not yet processing |
TASK_STATE_WORKING | No | Actively being processed |
TASK_STATE_INPUT_REQUIRED | No | Agent needs more info from client |
TASK_STATE_AUTH_REQUIRED | No | Authentication needed |
TASK_STATE_COMPLETED | Yes | Finished successfully |
TASK_STATE_FAILED | Yes | Finished with error |
TASK_STATE_CANCELED | Yes | Canceled before completion |
TASK_STATE_REJECTED | Yes | Agent declined the task |
Uma vez que uma tarefa atinge um estado terminal, é imutável. Não há mais mensagens. Seguimentos criam uma nova tarefa dentro da mesma.contextId- Não .
#### Formatos de fio
A2A usa JSON-RPC 2.0. Aqui está o que um verdadeiro intercâmbio de mensagens parece:
Client sends a message:
json{
"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
}
}
}Agent responds with a task:
json{
"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"
}]
}
]
}
}
}Streaming via SSE:
textPOST /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 (Protocolo de Comunicação do Agente)
Created by:IBM / BeeAI
Spec version:O valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de um valor de valor de valor de valor de valor de valor de valor de um valor de valor de valor de um valor.
Status:Fusão em A2A sob a Fundação Linux
Problem:Como os agentes comunicam com plena auditabilidade, continuidade de sessões e rastreamento de trajetória?
ACP é o enterprise protocol- Contrariamente ao que muitos resumos afirmam, o ACP faz notÉ uma API REST/JSON simples definida através da OpenAPI. O que a torna especial é queTrajectoryMetadataA resposta de cada agente pode conter um registro detalhado dos passos de raciocínio e das chamadas de ferramentas que a produziram.
sequenceDiagram
participant Client
participant ACP as ACP Agent
participant Audit as Audit Log
Client->>ACP: POST /runs (mode: sync)
ACP->>ACP: Process request...
ACP->>Audit: Log trajectory:<br/>reasoning + tool calls
ACP-->>Client: Response + TrajectoryMetadata
Note over Audit: Every step recorded:<br/>tool_name, tool_input,<br/>tool_output, reasoning#### Agente Discovery em ACP
ACP define quatro métodos de descoberta:
graph LR
A[Agent Discovery] --> B["Runtime<br/>GET /agents"]
A --> C["Open<br/>.well-known/agent.yml"]
A --> D["Registry<br/>Centralized catalog"]
A --> E["Embedded<br/>Container labels"]
style B fill:#dbeafe,stroke:#2563eb
style C fill:#d1fae5,stroke:#059669
style D fill:#fef3c7,stroke:#d97706
style E fill:#f3e8ff,stroke:#7c3aedO AgentManifestÉ mais simples do que o cartão de agente da A2A:
json{
"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"
}
}#### Execução do ciclo de vida
O ACP usa "Runs" em vez de "Tasks".
| Mode | Behavior |
|---|---|
sync | Blocking. Response contains the complete result. |
async | Returns 202 immediately. Poll GET /runs/{id} for status. |
stream | SSE stream. Events fire as the agent works. |
stateDiagram-v2
[*] --> created
created --> in_progress
in_progress --> completed: success
in_progress --> failed: error
in_progress --> awaiting: needs input
awaiting --> in_progress: client resumes
in_progress --> cancelling: cancel request
cancelling --> cancelled
completed --> [*]
failed --> [*]
cancelled --> [*]#### TrajetóriaMetadados (O Percurso de Auditoria)
Esta é a diferença-chave do ACP. Cada parte da mensagem pode incluir metadados que mostram exatamente o que o agente fez:
json{
"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" }
}
}
]
}Para as indústrias regulamentadas, isto é ouro. Cada resposta vem com uma cadeia comprovável de raciocínio: quais as ferramentas que foram chamadas, quais os insumos usados, quais as saídas recebidas.
ACP também apoia CitationMetadatapara atribuição de fonte:
json{
"kind": "citation",
"start_index": 0,
"end_index": 47,
"url": "https: TOK0
"title": "NWS San Francisco Forecast"
}ANP (Protocolo de Rede de Agentes)
Created by:Comunidade de código aberto (fundada por GaoWei Chang)
Repo: github.com/agent-network-protocol/AgentNetworkProtocol
Problem:Como os agentes de diferentes organizações confiam uns nos outros sem autoridade central?
A ANP é a decentralized identity protocolA ANP permite que os agentes comprovam sua identidade criptograficamente.
A ANP tem três camadas:
graph TB
subgraph Layer3["Layer 3: Application Protocol"]
AD[Agent Description Documents]
DISC[Discovery endpoints]
end
subgraph Layer2["Layer 2: Meta-Protocol"]
NEG[AI-powered protocol negotiation]
CODE[Dynamic code generation]
end
subgraph Layer1["Layer 1: Identity & Secure Communication"]
DID["did:wba (W3C DID)"]
HPKE[HPKE E2EE - RFC 9180]
SIG[Signature verification]
end
Layer3 --> Layer2
Layer2 --> Layer1
style Layer1 fill:#d1fae5,stroke:#059669
style Layer2 fill:#dbeafe,stroke:#2563eb
style Layer3 fill:#f3e8ff,stroke:#7c3aed#### Documentação DID (estrutura real)
ANP utiliza um método personalizado chamado DID did:wbaO DID .did:wba:example.com:user:aliceresolve https://example.com/user/alice/did.json- Não .
json{
"@context": [
"https: TOK0
"https://w3id.org/security/suites/jws-2020/v1",
"https: TOK2
],
"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 TOK4
"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 TOK6
],
"humanAuthorization": [
"did:wba:example.com:user:alice#key-1"
],
"service": [
{
"id": "did:wba:example.com:user:alice TOK8
"type": "AgentDescription",
"serviceEndpoint": "https://example.com/agents/alice/ad.json"
}
]
}Coisas importantes a observar:
- Key separationAs chaves de assinatura (secp256k1) são separadas das chaves de criptografia (X25519).
humanAuthorizationAs chaves de acesso são exclusivas da ANP. Estas chaves exigem a aprovação humana explícita (biometria, senha, HSM) antes de serem utilizadas.keyAgreementAs chaves são utilizadas para a criptografia de ponta a ponta HPKE (RFC 9180).- O serviceSeção de ligações ao documento de descrição do agente.
#### Como funciona a confiança na ANP
A ANP faz notutilizar um gráfico de web-of-trust ou de endosso.
sequenceDiagram
participant A as Agent A
participant Domain as Agent A's Domain
participant B as Agent B
A->>B: HTTP request + DID + signature
B->>Domain: Fetch DID document (HTTPS)
Domain-->>B: DID document + public key
B->>B: Verify signature with public key
B-->>A: Issue access token
A->>B: Subsequent requests use token
Note over A,B: Trust = TLS domain verification<br/>+ DID signature verification<br/>+ Principle of least trustA confiança vem de três fontes:
- Domain-level TLSVerifica o documento DID host
- DID cryptographic signaturesVerificar a identidade do agente
- Principle of least trustconcede apenas permissões mínimas
Não há propagação de confiança baseada em fofocas ou pontuação de PageRank.
#### Negociação do Meta-Protocolo
Esta é a característica mais nova da ANP. Quando dois agentes de ecossistemas diferentes se encontram, eles não precisam de formatos de dados pré-acordados.
json{
"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"
}sequenceDiagram
participant A as Agent A
participant B as Agent B
A->>B: protocolNegotiation (candidateProtocols)
B->>A: protocolNegotiation (counter-proposal)
A->>B: protocolNegotiation (accepted)
Note over A,B: Agents dynamically generate code<br/>to handle the agreed format.<br/>Max 10 rounds, then timeout.Os agentes vão para frente e para trás (máximo 10 tiros) até concordarem em um formato, e depois geram código dinâmico para lidar com ele.negotiating- Não .rejected- Não .accepted- Não .timeout- Não .
Isso significa que dois agentes que nunca se viram antes podem descobrir como se comunicar sem que alguém defina pré-definindo um esquema compartilhado.
Comparativo (corrigido)
| MCP | A2A | ACP | ANP | |
|---|---|---|---|---|
| Created by | Anthropic | Google / Linux Foundation | IBM / BeeAI | Community |
| Spec format | JSON-RPC | JSON-RPC / REST / gRPC | OpenAPI 3.1 (REST) | JSON-RPC |
| Primary use | Agent to Tool | Agent to Agent | Agent to Agent | Agent to Agent |
| Discovery | Tool listing | /.well-known/agent-card.json | GET /agents, /.well-known/agent.yml | /.well-known/agent-descriptions, DID service endpoints |
| Identity | Implicit (local) | Security schemes (OAuth, mTLS) | Server-level | W3C DID (did:wba) with E2EE |
| Audit trail | N/A | Basic (task history) | TrajectoryMetadata (tool calls, reasoning) | Not formally specified |
| State machine | N/A | 9 task states | 7 run states | N/A |
| Streaming | N/A | SSE | SSE | Transport-agnostic |
| Unique feature | Tool schemas | Agent Cards + Skills | Trajectory audit trail | Meta-protocol negotiation |
| Best for | Tools & data | Dynamic collaboration | Regulated industries | Cross-org trust |
| Status | Stable | Stable (v1.0) | Merging into A2A | Active development |
Como trabalham juntos
Estes protocolos não são mutuamente exclusivos.
graph TB
subgraph org["Your Organization"]
RA[Research Agent] <-->|A2A| CA[Coding Agent]
RA -->|MCP| SS[Search Server]
CA -->|MCP| GS[GitHub Server]
AUDIT["All agent responses carry<br/>ACP TrajectoryMetadata"]
end
subgraph ext["External (DID verified via ANP)"]
EA[External Agent]
PA[Partner Agent]
end
RA <-->|ANP + A2A| EA
CA <-->|ANP + A2A| PA
style org fill:#f8fafc,stroke:#334155
style ext fill:#fef2f2,stroke:#991b1b
style AUDIT fill:#fef3c7,stroke:#d97706- MCPliga cada agente às suas ferramentas
- A2AGestão da colaboração entre agentes (internos e externos)
- ACPEnvolve as respostas em metadados de trajetória para auditabilidade
- ANPfornece verificação de identidade para agentes que não controlas.
Construí-lo
Passo 1: Tipos de mensagem
Todos os sistemas multi-agentes começam com um formato de mensagem.
typescriptimport 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 }]);
}Observação: MessagePartA definição de um campo de referência é a seguinte:text- Não .data, ou url) diz o que é a parte; não há kind- É o meu nome.TrajectoryEntryCaptura a cadeia de raciocínio, correspondendo aos Metadados de Trájetória ACP.
Passo 2: Cartão de Agente A2A e Registro
Construa um agente de descoberta que corresponda à especificação real do A2A:
typescripttype 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()];
}
}Isto é substancialmente mais rico do que um simples mapa de nome-a-capacidade. Você pode descobrir agentes por etiquetas de habilidade, por tipos de entrada MIME, ou por nome, assim como a especificação real A2A suporta.
Passo 3: Ciclo de vida das tarefas A2A
Construir a máquina de estado de tarefa completa:
typescripttype 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);
}
}
}Isto implementa o ciclo de vida real das tarefas A2A: TASK_STATE_SUBMITTED- Não .TASK_STATE_WORKING- Não .TASK_STATE_INPUT_REQUIREDOs manipuladores são geradores de sincronia que produzemstatusUpdateE ...artifactUpdateOs eventos, os mesmos envoltórios que o fluxo SSE carrega.
Passo 4: Caminho de auditoria de estilo ACP
A comunicação com rastreamento de trajetória:
typescripttype 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) : [];
}
}Cada execução de um agente produz uma entrada de auditoria completa: o que entrou, o que saiu, e a trajetória completa das chamadas de ferramenta e passos de raciocínio entre eles.
Passo 5: Verificação de identidade de estilo ANP
Construir identidade e verificação baseadas em DID:
typescripttype 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");
}Isto reflete o modelo de identidade real da ANP: os agentes têm documentos DID com autenticação separada, acordo-chave e chaves de autorização humana.IdentityRegistrySimula a resolução DID (na produção, isso seria trazer HTTP para o domínio do agente).
Passo 6: Portal de Protocolo
Conectar os quatro protocolos num sistema unificado:
graph LR
REQ[Incoming Request] --> ANP_V{ANP: Verify DID}
ANP_V -->|Valid| A2A_D{A2A: Discover Agent}
ANP_V -->|Invalid| REJECT[Reject]
A2A_D -->|Found| ACP_A[ACP: Audit Run]
A2A_D -->|Not Found| REJECT
ACP_A --> A2A_T[A2A: Create Task]
A2A_T --> RESULT[Task + Audit Entry]
style ANP_V fill:#d1fae5,stroke:#059669
style A2A_D fill:#dbeafe,stroke:#2563eb
style ACP_A fill:#fef3c7,stroke:#d97706
style A2A_T fill:#dbeafe,stroke:#2563ebtypescriptclass 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
);
}
}O portal faz quatro coisas numa chamada:
- ANP: Verifica a identidade do telefonista através da assinatura DID
- A2A: Descobre o agente alvo e verifica as capacidades
- ACP: Envolve a execução numa trilha de auditoria com trajetória
- A2A: Cria uma tarefa com acompanhamento completo do ciclo de vida
Passo 7: Enfiar tudo juntos
typescriptasync function protocolDemo() {
const registry = new AgentRegistry();
registry.register({
name: "researcher",
description: "Searches and summarizes findings",
version: "1.0.0",
supportedInterfaces: [
{
url: "https: TOK0
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;
});O que vai mal
Os protocolos resolvem o caminho feliz.
Schema drift.O Agente A publica um cartão de publicidade .application/jsonO JSON é um sistema de dados de dados que pode ser usado para fazer uma análise de dados.version- Em Agente Cards por esta razão.
State machine violations.Um agente de manipulação dá um TASK_STATE_COMPLETEDO código de correio é silencioso. Correcção: verifique o estado do terminal antes de produzir. O TaskManagerA lei de que se trata de um Estado-Membro é aplicável.breakDepois dos estados terminais.
Trust resolution failures.O agente A tenta verificar o DID do agente B, mas o domínio do agente B está sem acesso. O documento DID não pode ser recuperado. Você não abre (aceita agentes não verificados) ou não fecha (refuta tudo)?
Trajectory bloat.A registros de trajetórias ACP é poderosa, mas cara. Um agente complexo que faz 200 chamadas de ferramentas por rodada produz entradas de auditoria maciças.
Discovery thundering herd.50 agentes todos em busca .GET /agentsCorreção: Cachar Cartões de Agente com TTL, intervalos de descoberta escalonados ou usar registro baseado em push em vez de pesquisas.
Usá-lo
Implementações reais
A2AÉ o mais maduro.official specO software é de código aberto sob a Linux Foundation. SDKs para Python e TypeScript. Se os seus agentes precisam de descoberta dinâmica e colaboração, comece aqui.
ACPA IBM está a fundir-se na A2A.BeeAI projectA utilização de padrões ACP (registros de trajetórias, ciclo de vida de execução) mesmo se utilizarem A2A como transporte.
ANPÉ o mais experimental.community repoO conceito de negociação de meta-protocóis é genuinamente novo. Vale a pena observar para implementações de agentes transnacionais.
MCPSe quiser que os agentes usem ferramentas, o MCP é o padrão.
Escolhendo o Protocolo Correto
graph TD
START{Do agents need<br/>to use tools?}
START -->|Yes| MCP_R[Use MCP]
START -->|No| TALK{Do agents need to<br/>talk to each other?}
TALK -->|No| NONE[You don't need<br/>a protocol]
TALK -->|Yes| AUDIT{Need audit trails<br/>for compliance?}
AUDIT -->|Yes| ACP_R[A2A + ACP<br/>trajectory patterns]
AUDIT -->|No| ORG{All agents<br/>within your org?}
ORG -->|Yes| A2A_R[A2A<br/>Agent Cards + Tasks]
ORG -->|No| INFRA{Shared<br/>infrastructure?}
INFRA -->|Yes| BROKER[A2A + message broker]
INFRA -->|No| ANP_R[ANP + A2A<br/>DID verification]
style MCP_R fill:#d1fae5,stroke:#059669
style A2A_R fill:#dbeafe,stroke:#2563eb
style ACP_R fill:#fef3c7,stroke:#d97706
style ANP_R fill:#f3e8ff,stroke:#7c3aed
style BROKER fill:#e0e7ff,stroke:#4338caEnvia-o
Esta lição produz:
code/main.ts-- implementação completa dos quatro padrões de protocolooutputs/prompt-protocol-selector.md- um prompt que ajuda a escolher protocolos para o seu sistema
Exercícios
- Multi-hop task delegation.Extender o
TaskManagerO pesquisador recebe uma tarefa, delega "requisita" e "resuma" subtarefas a dois agentes especializados, espera que ambos completem, e depois funde os resultados em seus próprios artefatos.
- Streaming audit trail.Modificar o
AuditableRunnerEm vez de esperar o resultado completo, rendemAuditEntryAs informações são atualizadas em tempo real à medida que as entradas de trajetória são adicionadas.
- DID rotation.Adicionar rotação de chave para o
IdentityRegistryUm agente deve poder publicar um novo documento DID com chaves atualizadas, mantendo umpreviousDidOs verificadores devem aceitar assinaturas da chave atual e anterior durante um período de graça.
- Protocol negotiation.Implementar o conceito de meta-protocolo da ANP.
protocolNegotiationAs mensagens com formatos candidatos (por exemplo, "Eu posso falar JSON-RPC" vs "Eu prefiro REST"). Após 3 rodadas, eles concordam em um formato ou tempo de espera. O formato acordado determina qualTaskManagerouAuditableRunnerEles usam.
- Rate-limited discovery.Adicionar um
RateLimitedRegistryUm envelope que cache buscas de cartão de agente com um TTL configurável e limita as consultas de descoberta por agente por segundo. Simula um rebanho de 100 agentes que se descobrem no início e mede a diferença.
Termos-chave
| Term | What people say | What it actually means |
|---|---|---|
| MCP | "The protocol for AI tools" | A client-server protocol for agents to discover and use tools. Agent-to-tool, not agent-to-agent. |
| A2A | "Google's agent protocol" | A peer-to-peer protocol for agent collaboration under the Linux Foundation. Discovery via Agent Cards, 9-state task lifecycle, streaming via SSE. Supports JSON-RPC, REST, and gRPC bindings. |
| ACP | "Enterprise agent messaging" | IBM/BeeAI's REST API for agent runs with TrajectoryMetadata: every response carries the full chain of reasoning and tool calls. Merging into A2A. |
| ANP | "Decentralized agent identity" | A community protocol using did:wba (DID) for cryptographic identity, HPKE for E2EE, and AI-powered meta-protocol negotiation for agents that have never seen each other. |
| Agent Card | "An agent's business card" | A JSON document at /.well-known/agent-card.json describing skills, supported MIME types, security schemes, and protocol bindings. |
| DID | "Decentralized ID" | W3C standard for cryptographically verifiable identities hosted on the agent's own domain. ANP uses did:wba method. |
| TrajectoryMetadata | "The audit receipt" | ACP's mechanism for attaching reasoning steps, tool calls, and their inputs/outputs to every agent response. |
| Meta-protocol | "Agents negotiating how to talk" | ANP's approach where agents use natural language to dynamically agree on data formats, then generate code to handle them. |
| Task | "A unit of work" | A2A's stateful object tracking work from submission through completion. Immutable once terminal. |
Mais leitura
- Google A2A specification-- especificações oficiais e SDKs (v1.0.1, Linux Foundation)
- IBM/BeeAI ACP specification-- Especificações OpenAPI 3.1 para corridas de agentes e metadados de trajetória
- Agent Network Protocol-- Identidade baseada em DID, E2EE, negociação de meta-protocol
- Model Context Protocol docs-- Especificação do MCP da Anthropic (incluída na Fase 13)
- W3C Decentralized Identifiers-- a norma de identidade que sustenta a ANP
- RFC 9180 (HPKE)-- o sistema de criptografia utilizado pela ANP para o E2EE
- FIPA Agent Communication Language- O precursor acadêmico dos protocolos de agentes modernos
This free lesson is part of the AI Engineering from Scratch curriculum. Read the full explanation, run the lesson code, and verify the result in the interactive reader or from the repository source.
Browse the complete course catalog or open this lesson on GitHub.