# Como projetar ações de agente OpenAPI que os agentes possam usar com segurança

Um documento OpenAPI pode informar a um agente como chamar um endpoint. Por si só, ele não informa o que uma chamada pode significar, quando uma pessoa precisa intervir nem como o agente deve agir depois de uma falha ambígua. Tratar toda operação documentada como uma ação de agente produz ferramentas que parecem completas em uma demonstração e se tornam perigosas no uso cotidiano.

Uma ação útil é menor que um endpoint. Ela tem uma finalidade delimitada, entradas que o agente consegue justificar, um resultado sobre o qual o agente pode agir, uma decisão de aprovação ligada à consequência e um plano explícito para as falhas. Faça esse trabalho de design antes de conectar uma operação a um agente. Adaptá-la depois da primeira cobrança duplicada, alteração acidental em produção ou vazamento de token é uma forma péssima de aprender a lição.

## Uma operação ainda não é uma ação de agente

Um endpoint HTTP, uma operação OpenAPI e uma ação de agente respondem a perguntas diferentes. As pessoas costumam misturá-los porque uma operação OpenAPI oferece um ponto de partida conveniente, mas as distinções determinam se a automação continuará compreensível.

Um endpoint é um endereço como `/v1/deployments`. Uma operação acrescenta um método HTTP, portanto `POST /v1/deployments` é diferente de `GET /v1/deployments`. Uma ação de agente acrescenta o contrato humano e operacional: qual objetivo busca, quais argumentos aceita, que efeitos pode produzir, que evidência conta como sucesso e quem precisa consentir.

A OpenAPI Specification define um Operation Object com campos como `operationId`, `parameters`, `requestBody`, `responses` e `security`. Use esses campos como evidência, não como uma lista automática de verificação para publicação. Uma operação com um esquema totalmente especificado ainda pode ser uma péssima ação de agente se sua descrição esconder um efeito em produção atrás de um nome inofensivo.

Considere estas duas operações:

```text
GET  /v1/projects/{project_id}/builds/{build_id}
POST /v1/projects/{project_id}/builds/{build_id}/promote
```

A primeira recupera um registro. A segunda pode alterar o tráfego, publicar artefatos ou mudar um canal de lançamento. A rota apenas sugere essa diferença. O design da ação precisa declará-la claramente.

Já vi equipes exporem uma ferramenta genérica `request` porque a API delas já tinha um arquivo OpenAPI organizado. O agente então recebeu permissão para montar caminhos, strings de consulta e corpos arbitrários. Isso não é um catálogo de ações. É execução remota de código contra uma API de negócio, com pontuação melhor.

Exponha uma operação somente depois de conseguir escrever uma frase neste formato: «Esta ação [faz algo específico] em [um objeto delimitado] e retorna [evidência do estado resultante].» Se você não consegue escrever essa frase sem verbos vagos como «gerenciar», «processar» ou «tratar», a ação ainda é ampla demais.

## Comece pela consequência, não pelo esquema da requisição

A aprovação deve acompanhar a consequência de uma chamada, não o verbo HTTP nem a aparente simplicidade do corpo JSON. Um `POST` pequeno pode criar uma obrigação irreversível. Um `GET` extenso pode expor dados privados. Um `DELETE` pode apenas remover um rascunho descartável, enquanto um `PATCH` pode revogar o acesso de todas as outras pessoas.

Antes de revisar os campos, descreva o efeito com termos que uma pessoa responsável pelo sistema reconheceria. Pergunte o que muda se o servidor executar a chamada duas vezes, executá-la para o objeto errado ou executá-la cinco minutos depois do momento pretendido pelo agente. Essas perguntas separam uma consulta rotineira de uma ação que exige análise.

Uso quatro classes de consequência ao revisar uma operação candidata:

- Observação: busca informações delimitadas e não produz nenhuma alteração no servidor.
- Alteração reversível: cria, atualiza ou remove algo com um caminho de desfazer documentado e realmente utilizável.
- Compromisso externo: envia uma mensagem, inicia um trabalho pago, publica material ou altera um estado visível para clientes.
- Alteração irreversível ou ampla: exclui registros permanentemente, troca acessos, modifica permissões ou afeta muitos objetos.

Essas classes não formam um modelo de permissões. Elas obrigam a descrição a ser honesta. Uma operação «criar fatura» pertence a compromisso externo mesmo que a requisição tenha apenas dois campos. Uma operação «reiniciar ambiente» pode se tornar uma alteração ampla quando um ambiente contém muitos serviços.

Não deduza segurança pelo nome do método. No protocolo, o HTTP define `GET` como seguro, no sentido de que o cliente não deve solicitar alterações de estado por meio dele. Isso é uma convenção, não uma prova de que determinado servidor a respeita. Já encontrei endpoints de diagnóstico que atualizavam caches, iniciavam a geração de relatórios e consumiam capacidade escassa quando chamados repetidamente. Teste o comportamento que você tem, não o comportamento sugerido pelo verbo.

Também separe o efeito de uma ação da sensibilidade do seu resultado. Buscar um token de acesso pode ser uma operação somente leitura, mas devolver esse valor ao agente anula o propósito de controlar a chamada. Buscar um registro privado de cliente pode exigir aprovação mesmo que a API não altere um único byte.

Um bom cartão de ação registra as duas dimensões em linguagem simples:

```text
Action: promote_preview_build
Effect: Changes one named preview build into the staging release channel.
Scope: One project and one build ID.
Result: Release ID, resulting channel, and server timestamp.
Human consent: Required for every call.
Retry: Never retry automatically unless the server accepts the same idempotency token.
```

Esse cartão costuma revelar semânticas ausentes da API antes que o agente tenha uma linha de código. Se ninguém consegue dizer se uma repetição é segura, a ação não está pronta.

## As entradas precisam de limites que o agente não consiga contornar conversando

Uma ação de agente precisa de um contrato de entrada menor do que o endpoint costuma aceitar. Os esquemas OpenAPI definem tipos e estrutura, mas o agente também precisa de restrições que o impeçam de ampliar uma tarefa usando argumentos criativos.

Considere uma operação de criação de implantação. A API bruta pode oferecer muitas opções para clientes internos: ambiente, referência do artefato, região, quantidade de réplicas, variáveis de ambiente, sinalizadores de funcionalidade, rótulos e um objeto de configuração livre. Entregar todos esses campos a um agente transforma uma solicitação simples em uma superfície administrativa sem revisão.

Crie uma ação com entradas que correspondam à tarefa. Se a tarefa for «implantar a build que passou nos testes em um ambiente de pré-visualização», o agente talvez precise apenas de `project_id`, `build_id` e um `reason` curto. O executor pode selecionar o ambiente permitido e rejeitar qualquer coisa fora do escopo da ação.

Este formato de requisição torna o limite concreto:

```json
{
  "project_id": "proj_4821",
  "build_id": "build_9017",
  "reason": "Preview requested after integration tests passed"
}
```

Não adicione `target_url`, `headers` arbitrários, um corpo de requisição bruto ou um objeto geral `options` apenas porque o endpoint subjacente os aceita. Cada brecha transforma sua ação cuidadosamente nomeada outra vez em um cliente genérico.

Use os campos do OpenAPI que já carregam limites úteis. Defina `additionalProperties: false` quando um objeto deve aceitar apenas campos nomeados. Use `enum` para um conjunto realmente pequeno de valores permitidos. Defina restrições de tamanho e padrão quando os identificadores tiverem um formato estabelecido. Marque campos como obrigatórios quando o executor não puder inferi-los com segurança.

Por exemplo, este fragmento rejeita campos de configuração não revisados e torna o escopo pretendido visível no esquema:

```yaml
DeployPreviewRequest:
  type: object
  additionalProperties: false
  required:
    - project_id
    - build_id
    - reason
  properties:
    project_id:
      type: string
      pattern: '^proj_[A-Za-z0-9]+$'
    build_id:
      type: string
      pattern: '^build_[A-Za-z0-9]+$'
    reason:
      type: string
      minLength: 8
      maxLength: 240
```

`additionalProperties: false` evita uma falha conhecida: o agente aprende com outro exemplo de API que pode enviar `environment_variables`, coloca segredos ou substituições inseguras nesse campo e o servidor os aceita silenciosamente. Rejeitar o campo oferece ao agente um erro útil, em vez de uma implantação inesperada.

Os esquemas não substituem a autorização no nível do objeto. Um `project_id` válido ainda pode apontar para um projeto fora da tarefa. O executor precisa verificar se o objeto solicitado está dentro da conta, workspace, repositório ou ambiente permitido. Faça essa verificação perto do executor da ação, onde ela não dependa da explicação do agente.

Texto livre exige atenção especial. Um campo `reason` pode ajudar quem revisa, mas nunca deve se tornar um canal de instruções para o executor. Armazene-o como anotação de auditoria. Não o interprete para comandos, seletores de recursos ou exceções de permissão.

## Os resultados esperados precisam apoiar a próxima decisão

O agente precisa de um resultado sobre o qual possa raciocinar, não de uma resposta HTTP bruta despejada no contexto. Retornar todos os cabeçalhos, campos de depuração e objetos aninhados aumenta a confusão e pode revelar dados de que o agente não precisava para concluir a tarefa.

Defina o sucesso em termos de negócio antes de escolher os códigos de resposta. Para uma ação de implantação, um resultado útil identifica a implantação, seu estado e o local onde o servidor informará o progresso posterior. Para uma atualização de registro, identifica o registro e confirma os campos alterados. Para uma exclusão, confirma o alvo e informa se a recuperação ainda é possível.

Um resultado compacto para uma operação assíncrona pode ter esta aparência:

```json
{
  "status": "accepted",
  "deployment_id": "dep_2388",
  "project_id": "proj_4821",
  "build_id": "build_9017",
  "target": "preview",
  "operation_status": "queued"
}
```

Essa resposta diz algo preciso: o servidor aceitou o trabalho, mas a implantação ainda não terminou. O agente não deve informar «implantado» depois de recebê-la. Deve usar uma ação de status separada e somente leitura ou dizer ao usuário que a operação está na fila.

É aqui que muitos documentos OpenAPI induzem agentes ao erro. Uma resposta `202 Accepted` tem um significado específico: o servidor aceitou a requisição para processamento, que pode ainda não ter começado nem terminado. Tratar `202` como sucesso no mesmo sentido de um `200` concluído cria afirmações falsas nos logs e nas mensagens aos usuários.

Separe o resultado do transporte do resultado da ação. Um HTTP `200` pode conter uma falha de domínio, como `{\"state\":\"rejected\",\"reason\":\"build is not eligible\"}`. Por outro lado, `409 Conflict` pode informar ao agente que o estado desejado já existe. O wrapper da ação deve transformar esses casos em um pequeno conjunto de estados explícitos, como `completed`, `pending`, `already_in_desired_state`, `rejected` e `unknown`.

Evite prometer uniformidade falsa. Algumas APIs retornam apenas um ID de trabalho opaco, e tudo bem, desde que você exponha uma ação de status capaz de resolvê-lo. O erro é esconder a lacuna. Declare exatamente o que a primeira chamada estabelece e o que ela não estabelece.

Filtre os detalhes de erro antes que eles cheguem ao agente. Um erro do servidor pode incluir URLs internas, cabeçalhos de autorização, rastreamentos de pilha ou dados de outra pessoa. O agente precisa de um motivo sobre o qual possa agir, como «o ID da build não pertence ao ID do projeto», além de um ID de correlação seguro para investigação humana. Ele não precisa da página de exceção do serviço upstream.

## A aprovação pertence ao ponto do compromisso

Peça aprovação quando a chamada puder criar um compromisso relevante e faça com que a tela de aprovação descreva o objeto e o efeito. Pedir uma única aprovação para um conjunto vago de poderes futuros ensina as pessoas a clicar em um aviso que não conseguem avaliar.

A aprovação da sessão e a aprovação da chamada resolvem problemas diferentes. A aprovação da sessão diz: «Reconheço este processo de agente e permito que ele use este conjunto de ações enquanto estiver em execução.» A aprovação da chamada diz: «Aprovo agora esta solicitação específica com consequências.» Não substitua uma pela outra.

Um agente que pode consultar o status de builds pode funcionar por uma hora sem incomodar ninguém. Um agente que promove uma build deve mostrar o projeto, o ID da build, o canal de lançamento e o motivo no momento em que pede consentimento. Uma pessoa consegue avaliar essa solicitação. «Permitir ferramenta de implantação» oferece quase nenhuma informação para essa decisão.

Não use uma janela de aprovação como substituta da validação de entrada. Se uma ação permitir que o agente especifique um destino arbitrário ou um escopo de permissões arbitrário, a pessoa revisora terá de decifrar uma carga grande e instável sob pressão. Limite as entradas primeiro. Depois, a aprovação confirma uma ação delimitada.

A frequência correta depende do efeito. Exija aprovação a cada chamada para ações que publiquem, alterem acessos, iniciem um pagamento externo ou atinjam um escopo amplo em produção. O consentimento da sessão pode servir para um grupo de chamadas somente leitura ou alterações reversíveis e restritas, mas apenas depois que a identidade do processo e o catálogo de ações estiverem visíveis para quem revisa.

O Sallyport aplica essa distinção com autorização de sessão para um processo de agente recém-reconhecido e aprovação opcional a cada uso de uma determinada credencial. O bloqueio do cofre também recusa todas as ações enquanto ele estiver bloqueado, portanto a aprovação não pode transformar um armazenamento de segredos bloqueado em uma exceção acidental.

Não faça uma pessoa aprovar falhas que o software pode impedir. Se uma build não estiver qualificada para promoção, o executor deve rejeitá-la antes de solicitar aprovação. Os avisos existem para escolhas legítimas, não para pedir que uma pessoa cansada detecte um estado malformado.

## Um timeout cria um estado desconhecido, não uma instrução para repetir

Um timeout de rede depois de uma requisição que altera dados é o caminho de falha que revela um design descuidado de ações de agente. O agente enviou a requisição e depois perdeu a resposta. O servidor pode não ter feito nada, pode ter concluído a alteração ou ainda pode estar processando-a. O agente não consegue descobrir a verdade presumindo a resposta que prefere.

Veja uma falha comum. Um agente chama `POST /v1/invoices` com cliente, valor e um timeout de requisição. A conexão cai depois que o servidor grava a fatura, mas antes de a resposta chegar. O agente vê um timeout, repete os mesmos dados e o servidor cria uma segunda fatura. O log de auditoria agora diz que o agente seguiu sua política de repetição, o que é tecnicamente verdadeiro e operacionalmente inútil.

Um token de idempotência só resolve isso quando o servidor realmente o implementa. O cliente gera um token uma vez para cada ação pretendida, envia-o com a requisição inicial e envia exatamente o mesmo token em uma repetição. O servidor precisa associar esse token à requisição original e retornar o resultado original, ou um resultado de conflito compatível, em vez de repetir o efeito.

```text
Idempotency-Key: act_01HZX7FQ2Z9K8M6R4T3V1W0Y
```

O wrapper da ação deve manter esse token fora da improvisação do agente. Gere-o no momento da execução, persista-o com a tentativa da ação e reutilize-o somente nessa tentativa. Um token fornecido pelo agente pode colidir, ser reutilizado em requisições sem relação ou se tornar outra superfície para injeção de prompt.

Se a API não tiver semântica de idempotência documentada, não repita automaticamente uma operação que altera dados depois de um timeout. Retorne `unknown` com o identificador da ação e ofereça uma ação de consulta somente leitura capaz de verificar o estado no servidor. Se não houver consulta, uma pessoa terá de investigar antes que alguém repita a requisição. Essa resposta parece inconveniente porque é inconveniente. Fingir certeza não a melhora.

O OpenAPI pode documentar um parâmetro de cabeçalho chamado `Idempotency-Key`, mas a documentação sozinha não garante o comportamento do servidor. Teste de propósito: envie duas vezes o mesmo token e o mesmo payload e, depois, envie o mesmo token com um payload diferente. O servidor deve fazer o primeiro par convergir e rejeitar ou tratar claramente a requisição alterada. Se executar as duas alterações silenciosamente, o cabeçalho é apenas decoração.

Outras falhas precisam de regras próprias. Trate `401` e `403` como condições de parada, não como um sinal para procurar outra credencial. Trate `429` como uma condição de espera somente quando a API comunicar um atraso para nova tentativa ou quando sua ação tiver uma política de espera limitada. Trate erros de validação como feedback que o agente pode usar apenas quando o erro identificar uma correção permitida.

## A autenticação não dá ao agente capacidade de decidir

Uma declaração `security` do OpenAPI descreve como um cliente prova sua identidade perante uma API. Ela não expressa se um agente deve invocar a operação, se pode usar determinada credencial para determinado objeto ou se uma pessoa precisa revisar o efeito.

O Security Requirement Object da Specification associa uma operação a esquemas de segurança nomeados. Um esquema bearer pode informar ao cliente que deve enviar um cabeçalho de autorização. A autenticação básica pode indicar que ele deve construir um cabeçalho de credencial. Isso é autenticação de transporte. Não extraia daí mais do que está escrito.

Mantenha quatro perguntas separadas:

- Quem ou o que chama esta ação?
- Qual credencial o executor usa com a API upstream?
- Quais objetos e efeitos essa credencial permite?
- Quais tentativas de ação uma pessoa aprova?

Quando as equipes misturam essas perguntas, geralmente entregam um token ao agente e chamam isso de autorização. O token então aparece na saída da ferramenta, no histórico do shell, nos logs de depuração, nos prompts ou em um arquivo de configuração. Revogá-lo vira um projeto de limpeza, em vez de uma única ação.

O formato mais seguro mantém a credencial no executor. O agente fornece entradas delimitadas da ação. O executor seleciona uma credencial elegível, insere-a na requisição HTTP, avalia a resposta e retorna o resultado filtrado. O agente nunca precisa ter acesso em texto simples a uma chave de API para solicitar uma ação.

Para SSH, aplique a mesma regra. Um agente pode precisar solicitar um comando em um host nomeado, mas um comando genérico acompanhado de uma chave privada amplamente confiável representa uma autoridade muito maior do que a maioria das tarefas exige. Restrinja a identidade do host, a conta, o formato do comando e o tratamento da saída de acordo com a finalidade da ação.

O Sallyport usa esse modelo de execução para chamadas HTTP e comandos SSH: as credenciais permanecem no cofre criptografado e o agente recebe o resultado da ação, não o segredo. Esse design só ajuda se você continuar expondo ações restritas e escolher aprovações compatíveis com seus efeitos.

## A descrição da ação precisa declarar o que o esquema não consegue

As descrições do OpenAPI importam porque os agentes as leem como instruções, mas o texto deve esclarecer limites, não introduzir sorrateiramente um segundo contrato de API contraditório. Coloque os limites aplicáveis nos esquemas e nos executores. Use as descrições para explicar a intenção, a consequência e as condições que um sistema de tipos não consegue expressar.

Dê às operações nomes baseados no resultado pretendido pelo usuário. `getBuildStatus` diz mais que `getBuildById`; `createPreviewDeployment` diz mais que `postDeployment`. O nome não deve prometer mais do que a operação entrega. Se o servidor coloca o trabalho em uma fila, não chame a operação de `deployBuild` a menos que o resultado diferencie aceitação de conclusão.

Escreva as descrições com os detalhes que o agente tentaria adivinhar:

```yaml
operationId: createPreviewDeployment
summary: Queue one tested build for the preview environment
requestBody:
  required: true
  content:
    application/json:
      schema:
        $ref: '#/components/schemas/DeployPreviewRequest'
responses:
  '202':
    description: Request accepted. Deployment work may still be pending.
  '409':
    description: The build already has a preview deployment or cannot enter preview.
```

Um resumo não basta para operações de maior consequência. Registre o alvo pretendido, a classe do efeito, a exigência de aprovação, a regra de repetição e os estados do resultado em metadados da ação que fiquem ao lado do documento OpenAPI. Você pode usar extensões `x-` se suas ferramentas forem responsáveis por elas, mas identifique-as claramente como convenções privadas. Analisadores OpenAPI padrão ignorarão extensões desconhecidas, portanto o executor precisa aplicá-las, não apenas exibi-las.

Não dependa de uma descrição que diga «use com cuidado». Cuidado é um sentimento humano, não uma regra executável. Substitua-o por um limite: um projeto, somente pré-visualização, sem variáveis de ambiente arbitrárias, aprovação a cada chamada e nenhuma repetição automática depois de um resultado desconhecido.

As descrições também devem informar ao agente quando recusar a ação. Uma operação de promoção pode exigir uma execução de testes concluída. Uma exportação de dados pode exigir uma referência de caso fornecida pelo cliente. Uma operação de exclusão pode exigir uma consulta anterior que confirme que o objeto é um rascunho. Essas pré-condições reduzem avisos desnecessários e facilitam a interpretação dos registros de auditoria.

## Teste a ação contra uma pessoa descuidada, mas capaz

Um teste do caminho feliz prova apenas que a API funciona quando todas as premissas estão corretas. Teste uma ação como se uma pessoa rápida e capaz tivesse contexto incompleto, identificadores antigos e tendência a repetir a operação depois de um erro. Isso se aproxima o suficiente das falhas de agentes autônomos para ser útil.

Monte um pequeno ambiente de teste com objetos descartáveis e uma conta cujas permissões correspondam às do executor pretendido. Depois, execute a ação em casos que desafiem seus limites:

- Envie um campo de entrada desconhecido e confirme que o executor o rejeita.
- Solicite um objeto fora do projeto ou workspace permitido.
- Negue a aprovação e confirme que nenhuma requisição upstream ocorre.
- Provoque um timeout depois que o servidor receber uma requisição que altera dados.
- Retorne uma resposta contendo material de depuração sensível e confirme que a filtragem o remove.

Inspecione mais do que o estado final da API. Revise o aviso exibido à pessoa, a requisição exata feita pelo executor, o resultado recebido pelo agente e o registro de auditoria. Uma requisição bem-sucedida ainda pode violar o contrato da ação se o aviso esconder o alvo, se o resultado declarar conclusão cedo demais ou se o log não conseguir distinguir uma requisição negada de uma rejeição upstream.

Para uma ação com aprovação a cada chamada, teste a ordem das etapas. O executor deve validar as restrições estáticas e resolver contexto seguro suficiente para mostrar uma solicitação significativa antes de pedir consentimento. Ele não deve enviar a requisição primeiro e solicitar aprovação depois. Também deve evitar uma longa cadeia de chamadas de leitura ocultas que exponha mais dados do que a ação final precisa.

Teste de propósito credenciais revogadas e expiradas. O executor deve falhar de forma segura, retornar uma explicação adequada e evitar chamadas repetidas com a mesma credencial inutilizável. Um loop de repetição contra uma credencial rejeitada pode encher os logs, acionar limites de taxa e dificultar o diagnóstico de um simples problema de acesso.

Por fim, teste o cancelamento. Se alguém interromper o agente enquanto um trabalho upstream estiver em execução, o registro deve informar se a requisição nunca saiu, chegou ao servidor ou entrou em um estado desconhecido. Cancelar o processo local do agente não cancela necessariamente um efeito remoto.

## Publique menos ações e torne cada uma defensável

Um catálogo pequeno de ações é melhor que um cliente de API genérico porque cada ação pode carregar um contrato fundamentado. Adicionar operações é fácil. Manter semânticas de resultado verdadeiras, limites de objetos, avisos de aprovação e comportamento em caso de falha é onde está o trabalho.

Comece com uma operação que recupere um registro de status delimitado. Dê a ela um nome que indique o objeto, restrinja os identificadores ao escopo pretendido e retorne apenas os campos de que o agente precisa. Depois, adicione uma ação reversível e obrigue-se a escrever suas regras de repetição e aprovação antes de implementá-la.

Não transforme uma operação em ação de agente só porque um gerador OpenAPI consegue expô-la em uma tarde. Faça isso quando conseguir explicar o que acontece depois de um timeout, o que a pessoa aprova, o que o agente vê e como provará mais tarde qual requisição ocorreu. Se alguma resposta depender de «o agente provavelmente fará a coisa sensata», mantenha o endpoint fora do catálogo.
