8 min de leitura

Como as mutações GraphQL para agentes de IA resistem a chamadas destrutivas

Mutações GraphQL para agentes de IA precisam de entradas tipadas, escopos restritos, prévias reais e gravações condicionais para evitar solicitações geradas destrutivas.

Como as mutações GraphQL para agentes de IA resistem a chamadas destrutivas

Solicitações GraphQL geradas por modelos falham de uma forma previsível: o modelo tem informação suficiente para produzir uma sintaxe válida, mas não encontra resistência suficiente para tornar uma ação perigosa difícil. Para uma pessoa, uma mutação chamada updateProject com um campo opcional archived parece flexível. Para um agente que monta uma solicitação com contexto parcial, ela é um convite para alterar o estado do projeto como efeito colateral de uma edição rotineira.

Coloque essa resistência no schema e no resolver, não em um prompt que pede cuidado ao agente. Entradas tipadas, autoridade de ação limitada, prévias reais, gravações condicionais e erros com um caminho de recuperação tornam as solicitações corretas mais fáceis de gerar do que as prejudiciais. Esse desenho também ajuda quem desenvolve clientes comuns. Os agentes apenas expõem atalhos que APIs permissivas toleraram durante anos.

Uma solicitação válida ainda pode expressar a ação errada

O GraphQL verifica se uma solicitação corresponde ao schema. Ele não confirma se o cliente escolheu o cliente certo, entendeu o estado do registro ou pretendia remover alguma coisa. Muitas equipes confundem segurança de tipos com segurança de ação e colocam campos chamados delete, archive ou status dentro de uma mutação de atualização ampla.

Considere este schema comum:

input ProjectPatchInput {
  name: String
  description: String
  archived: Boolean
  ownerId: ID
}

type Mutation {
  updateProject(id: ID!, input: ProjectPatchInput!): Project!
}

Ele mistura edições inofensivas com transferência de propriedade e mudança de ciclo de vida. Um agente solicitado a «organizar projetos antigos» pode concluir razoavelmente que archived: true é adequado. Um agente solicitado a corrigir o nome de um projeto pode manter por engano um campo archived de um objeto gerado antes. O sistema de tipos aceita as duas solicitações porque ambas estão bem formadas.

Não esconda comportamento destrutivo em um patch flexível. Dê a cada ação um nome que declare sua consequência e uma entrada que carregue apenas as evidências necessárias para essa consequência:

type Mutation {
  renameProject(input: RenameProjectInput!): RenameProjectPayload!
  archiveProject(input: ArchiveProjectInput!): ArchiveProjectPayload!
  transferProjectOwnership(input: TransferProjectOwnershipInput!): TransferProjectOwnershipPayload!
}

input RenameProjectInput {
  projectId: ID!
  expectedVersion: Int!
  name: String!
}

input ArchiveProjectInput {
  projectId: ID!
  expectedVersion: Int!
  reason: ArchiveReason!
  confirmation: String!
  idempotencyKey: String!
}

Isso não é burocracia por si só. Uma mutação estreita limita o que uma solicitação gerada pode dizer. Ela cria uma diferença útil entre «editar um rótulo» e «retirar isto do uso normal». As descrições das ferramentas podem explicar essa diferença, mas o schema deve impô-la.

A especificação do GraphQL ajuda de forma limitada, mas útil. A validação de objetos de entrada rejeita campos que o schema não define. Se ArchiveProjectInput não inclui ownerId, o cliente não consegue inserir uma mudança de propriedade em uma chamada de arquivamento. Trate isso como uma barreira de proteção, não como limite de segurança. O resolver ainda decide se o ator pode arquivar aquele projeto específico.

Evite um campo genérico action: String!, como mutateProject(action: "ARCHIVE"). Ele parece compacto até cada ação precisar de campos, validação, autorização, dados de prévia e tratamento de erros diferentes. O resultado vira um protocolo RPC privado preso dentro de um objeto de entrada, com menos ajuda das ferramentas GraphQL.

As entradas devem nomear o alvo e o limite

Uma entrada destrutiva deve identificar exatamente o que mudará, qual versão o cliente examinou e qual limite impede que a seleção se expanda. IDs sozinhos não carregam intenção suficiente quando um resolver pode alcançar registros filhos, sistemas externos ou uma consulta de todo o tenant.

Comece com um objeto que identifique um único recurso dentro do tenant do cliente. Não aceite um filtro arbitrário em uma mutação de exclusão, a menos que o produto realmente precise de operações em massa. Um filtro como where: { status: INACTIVE } cria ambiguidade: inativo de acordo com qual data, qual tenant e qual padrão oculto? O modelo pode fornecê-lo porque o campo existe, não porque analisou o conjunto resultante.

Em operações para um único registro, leve um token de versão na entrada. Um inteiro é fácil de inspecionar, mas uma string de revisão opaca também funciona. O resolver compara esse valor com a versão armazenada na mesma transação que grava a alteração. Se forem diferentes, retorna um conflito e não altera nada.

input ArchiveProjectInput {
  projectId: ID!
  expectedVersion: Int!
  reason: ArchiveReason!
  confirmation: String!
  idempotencyKey: String!
}

enum ArchiveReason {
  CUSTOMER_REQUEST
  DUPLICATE
  END_OF_LIFE
}

O enum reason faz mais do que melhorar os relatórios. Ele impede que uma solicitação invente uma justificativa livre que uma automação posterior possa tratar como significativa. Use texto livre para uma observação quando as pessoas precisarem dele, mas mantenha as categorias operacionais enumeráveis.

O campo de confirmação deve estar ligado ao alvo real. Exigir a string literal ARCHIVE detecta apenas construções descuidadas. Exigir archive acme-project-42 força o cliente a resolver e repetir o identificador do recurso. Isso não impede um cliente malicioso e nunca deve substituir a autorização. Mas detecta muitos casos em que uma solicitação gerada associou a ação correta ao ID errado.

Não peça confirmação em mutações rotineiras, como alterar um nome de exibição. Confirmações em excesso treinam agentes e pessoas a preencher todos os campos mecanicamente. Reserve-as para ações com resultado relevante ou difícil de reverter: exclusão, publicação, movimentação financeira, revogação de credenciais e mudanças que afetam outras pessoas.

Em uma operação em massa, torne explícito o limite superior e retorne um token de prévia ligado à seleção exata. Esta entrada diz muito mais do que um filtro bruto:

input DeleteDormantProjectsInput {
  previewToken: ID!
  expectedCount: Int!
  confirmation: String!
  idempotencyKey: String!
}

O resolver de execução deve rejeitar um token expirado, pertencente a outro ator, associado a outro tenant ou cujo total seja diferente de expectedCount. Caso contrário, um agente pode visualizar cinco registros e executar uma consulta que agora corresponde a cinquenta.

A autoridade deve acompanhar a mutação, não o substantivo

Um escopo chamado projects:write costuma ser amplo demais para trabalho autônomo. Ele permite renomear, arquivar, transferir, excluir e talvez alterar configurações de cobrança do projeto sob uma única permissão, apenas porque todas as ações tocam um projeto. Esse agrupamento segue o substantivo do banco de dados, não o risco da operação.

Conceda autoridade que descreva uma ação. Por exemplo, um token de serviço de automação de releases pode ter project:rename e project:archive, enquanto um fluxo de suporte não tem nenhum dos dois. Um escopo separado project:delete deve ser raro. Se seu sistema de identidade não consegue emitir escopos tão restritos, acrescente uma verificação de capacidade no servidor associada ao nome da mutação e registre essa decisão de autorização.

O escopo nunca resolve o acesso sozinho. Cada resolver precisa fazer várias verificações em uma ordem deliberada:

  1. Autentique o cliente e identifique seu tenant e principal.
  2. Verifique se o principal tem autoridade para essa mutação.
  3. Carregue o alvo dentro do limite do tenant, em vez de carregá-lo globalmente e verificar depois.
  4. Verifique o estado do registro e qualquer relação de função exigida pela regra de negócio.
  5. Faça a gravação condicional e acrescente um evento de auditoria na mesma transação.

Carregar dentro do limite do tenant é importante. Um resolver que chama findProjectById(id) antes de verificar o tenant pode revelar a existência por meio do tempo de resposta ou do texto do erro. Também pode entregar um objeto carregado globalmente a um helper que presume que a autorização já ocorreu. Inclua a associação ao tenant no predicado da busca.

Não deduza a permissão a partir da tarefa declarada pelo agente. Um cabeçalho que diz X-Agent-Goal: cleanup é evidência para a trilha de auditoria, não uma concessão de autoridade. Prompts, rótulos de tarefas e identidade do modelo podem ajudar uma pessoa a revisar uma ação, mas qualquer cliente pode falsificá-los.

A mesma distinção vale para o acesso às ferramentas. Um agente pode ter permissão para chamar um endpoint GraphQL sem ter permissão para uma mutação específica. Quando o runtime permitir, descreva ferramentas de leitura e ferramentas de ação separadamente. Mantenha a decisão final na API, porque um cliente pode ignorar os metadados da ferramenta e enviar a solicitação HTTP diretamente.

Um dry run deve montar o mesmo plano da execução

Um dry run só é útil se responder: «O que esta solicitação exata faria agora?» Uma prévia falsa que conta linhas com uma consulta simplificada dá aos agentes uma sensação enganosa de segurança. A mutação final pode aplicar regras de elegibilidade diferentes, usar outro ramo de autorização ou disparar uma ação externa que a prévia nunca considerou.

Crie uma função de planejamento compartilhada. Ela recebe o ator autenticado e a entrada, valida todas as condições, resolve os alvos, calcula os efeitos colaterais e produz um plano imutável. A prévia retorna uma representação sanitizada desse plano. O caminho de execução consome o plano somente depois que o cliente apresenta seu token de curta duração e a confirmação.

type Mutation {
  previewArchiveProject(input: PreviewArchiveProjectInput!): ArchivePreviewPayload!
  archiveProject(input: ArchiveProjectInput!): ArchiveProjectPayload!
}

input PreviewArchiveProjectInput {
  projectId: ID!
  expectedVersion: Int!
  reason: ArchiveReason!
}

type ArchivePreviewPayload {
  previewToken: ID!
  project: Project!
  affectedMemberCount: Int!
  plannedEffects: [ArchiveEffect!]!
  expiresAt: DateTime!
}

enum ArchiveEffect {
  PROJECT_HIDDEN_FROM_DEFAULT_LISTS
  PENDING_INVITATIONS_CANCELLED
}

O plano deve incluir os IDs dos alvos, suas versões, a identidade do ator, o tenant, o resumo da entrada, os efeitos planejados e o horário de expiração. Armazene-o no servidor ou assine um token opaco que aponte para um estado armazenado. Não coloque o plano inteiro em um JSON controlado pelo cliente e confie nele durante a execução.

A entrada de execução deve referenciar o token da prévia, não repetir um seletor solto:

input ArchiveProjectInput {
  previewToken: ID!
  confirmation: String!
  idempotencyKey: String!
}

Esse fluxo de duas chamadas acrescenta atrito. Essa é a ideia para ações importantes. Não o imponha a toda mutação. Uma regra simples: exija prévia quando a operação afetar mais de um registro, tiver um efeito externo irreversível ou tornar um recurso indisponível para outras pessoas.

As respostas da prévia também precisam de controle de acesso. Retornar uma lista de registros afetados pode vazar dados com a mesma facilidade que executar a mutação. Aplique as mesmas regras de tenant e função durante o planejamento. A prévia pode omitir campos que o ator não pode ler e ainda retornar a quantidade e as categorias de efeitos necessárias para a decisão.

Idempotência e versões resolvem falhas diferentes

Mantenha os segredos do GraphQL fora dos agentes
O Sallyport mantém as credenciais de API no cofre criptografado e executa a chamada HTTP fora do agente.

A idempotência impede a aplicação duplicada da mesma solicitação. A verificação de versão impede a aplicação em um estado que mudou depois que o cliente o examinou. Muitas equipes adicionam uma delas e presumem que resolveram os dois problemas.

Um agente pode tentar novamente porque a conexão HTTP fechou depois que o servidor confirmou uma mutação. Sem idempotência, a segunda solicitação pode criar um segundo reembolso, duplicar uma mensagem ou chamar duas vezes a mesma API externa. Dê a cada mutação com efeito uma idempotencyKey fornecida pelo cliente. O servidor deve armazená-la com o ator autenticado, o nome da mutação, um resumo da entrada normalizada e o payload concluído ou um erro estável.

Quando o servidor recebe novamente o mesmo ator, mutação, chave e resumo de entrada, retorna o resultado original. Se encontrar a mesma chave com outro resumo, retorna IDEMPOTENCY_KEY_REUSED e não faz nada. Aceitar uma entrada diferente com uma chave reutilizada destrói a propriedade de que os clientes dependem durante as tentativas novamente.

A verificação de versão trata de outra sequência. Um agente lê a versão 7 do projeto, prepara uma prévia de arquivamento e uma pessoa renomeia o projeto ou restaura um convite. Quando o arquivamento é executado, o resolver compara a versão 7 com a versão armazenada atual. Se o valor agora for 8, retorna um conflito. O agente precisa buscar o estado atual, reconsiderar sua intenção e criar uma nova prévia se necessário.

A especificação GraphQL executa serialmente os campos de nível superior em uma operação de mutação. Essa ordem não serializa solicitações HTTP separadas. Dois agentes ainda podem enviar duas operações quase ao mesmo tempo. Use uma atualização condicional no banco, um bloqueio de linha ou uma restrição transacional. Uma verificação na memória da aplicação seguida de uma gravação separada deixa uma janela de corrida.

UPDATE projects
SET archived_at = CURRENT_TIMESTAMP,
    version = version + 1
WHERE id = :project_id
  AND tenant_id = :tenant_id
  AND version = :expected_version
  AND archived_at IS NULL;

Se nenhuma linha for afetada, examine o registro atual dentro do limite do tenant e retorne um resultado específico: ausente, proibido, já arquivado ou conflito de versão. Não informe todo resultado de zero linhas como um erro genérico do servidor. Os agentes precisam saber se tentar novamente é prejudicial, útil ou inútil.

O payload de erro deve indicar o próximo passo

O array de nível superior errors do GraphQL é adequado para erros de análise, falhas de validação e falhas do resolver. Ele é um lugar ruim para obrigar clientes a extrair resultados de negócio de mensagens em inglês. Coloque os resultados esperados da mutação em um payload tipado, com um código estável e detalhes estruturados.

type ArchiveProjectPayload {
  outcome: ArchiveProjectOutcome!
  project: Project
  error: MutationError
}

enum ArchiveProjectOutcome {
  ARCHIVED
  VERSION_CONFLICT
  CONFIRMATION_REQUIRED
  PREVIEW_EXPIRED
  FORBIDDEN
  IDEMPOTENCY_KEY_REUSED
}

type MutationError {
  code: String!
  message: String!
  currentVersion: Int
  requiredConfirmation: String
}

Use erros de transporte e de execução GraphQL quando o cliente não conseguiu executar a operação corretamente. Use um resultado tipado quando a solicitação foi executada normalmente, mas não alterou o estado porque uma regra de negócio a rejeitou. Escolha uma convenção e documente-a. Misturar errors.extensions.code em alguns conflitos com enums de payload em outros torna o comportamento dos agentes frágil.

O agente deve conseguir associar um resultado a uma ação segura. VERSION_CONFLICT significa reler o objeto e reconsiderar. PREVIEW_EXPIRED significa criar uma nova prévia. CONFIRMATION_REQUIRED significa mostrar a frase exigida a uma pessoa ou solicitá-la, não adivinhar. FORBIDDEN significa parar. IDEMPOTENCY_KEY_REUSED significa gerar uma nova chave somente quando o cliente desejar explicitamente uma operação diferente.

Não retorne nomes de políticas internas, trechos SQL ou detalhes do grafo de autorização. Códigos externos estáveis podem ser precisos sem expor detalhes de implementação. Mantenha um ID de correlação nas extensões da resposta e um registro de auditoria correspondente no servidor. Assim, um operador terá algo concreto para investigar quando um agente relatar uma falha.

Os payloads de sucesso precisam de informação suficiente para encerrar a incerteza. Retorne o estado resultante do registro, a nova versão, o ID da operação e os efeitos que realmente ocorreram. Um booleano isolado força o cliente a fazer outra consulta e deixa espaço para uma leitura desatualizada. Também torna a revisão humana mais difícil do que deveria ser.

A exclusão precisa de um ciclo de vida, não de um booleano

Revogue uma sessão de agente de risco
As sessões registram as execuções dos agentes separadamente, para que uma execução suspeita possa ser revogada imediatamente.

A exclusão definitiva é popular porque deixa a tabela organizada. Também é a ação mais propensa a produzir um problema de suporte irrecuperável quando um agente entende uma solicitação de forma errada. Muitos produtos deveriam arquivar primeiro, preservar um período de desfazer controlado pelo servidor e fazer a remoção definitiva por um fluxo separado e restrito.

Não chame uma operação de arquivamento de deleteProject se ela apenas ocultar um registro. Os nomes ensinam aos clientes qual estado esperar. archiveProject deve retornar ARCHIVED; purgeProject deve significar que os dados não estarão mais disponíveis. Quando uma API usa delete para todas as etapas do ciclo de vida, o agente não consegue distinguir com segurança uma limpeza reversível de uma remoção permanente.

Uma remoção definitiva precisa de entradas e autorização mais rigorosas que um arquivamento. Pode exigir que o recurso tenha permanecido arquivado durante um período de retenção, que não exista bloqueio jurídico ou de cobrança e que um operador com escopo distinto a aprove. O resolver deve impor cada condição. Uma contagem regressiva no cliente ou uma instrução da ferramenta não têm autoridade.

Os efeitos externos merecem o mesmo tratamento. Se arquivar cancela convites, remove um ambiente remoto ou dispara um webhook, retorne esses efeitos na prévia e no payload final. Não os anexe silenciosamente a um resolver de atualização genérico. Quem revisa uma solicitação do agente precisa ver as consequências antes da aprovação, e o agente precisa de fatos para relatar depois da execução.

Para ações financeiras ou de credenciais, não ofereça um dry run fictício que chama o endpoint real do provedor esperando que nenhum efeito ocorra. Use a prévia ou o mecanismo de autorização documentado pelo provedor quando existir. Caso contrário, identifique o resultado como uma estimativa local e liste o que o servidor não conseguiu verificar. Fingir certeza é pior do que exigir uma decisão humana.

As verificações do resolver tornam reais as promessas do schema

O desenho do schema limita uma intenção malformada. O desenho do resolver impede que uma solicitação aparentemente autorizada atravesse um limite real. Mantenha essas camadas separadas no código para que uma refatoração futura não substitua uma verificação de permissão por um comentário na definição da ferramenta.

Um resolver para uma ação destrutiva deve seguir uma sequência que torne a recusa barata e deixe as gravações para o fim. Primeiro autentique a solicitação, resolva o tenant do ator, valide a entrada, carregue o alvo dentro desse tenant, verifique escopo e estado, valide o token da prévia e a confirmação, reserve o registro de idempotência e execute a transação condicional. A ordem pode mudar conforme seu modelo de armazenamento, mas não faça um efeito externo antes de saber que a transação pode ser confirmada.

Um registro de idempotência exige cuidado quando o trabalho envolve um banco e um provedor externo. Marcar a chave como concluída antes da chamada externa pode declarar sucesso quando a chamada falhou. Chamar o provedor primeiro pode duplicar a operação se o processo morrer antes de armazenar a conclusão. Use um padrão outbox ou o suporte à idempotência oferecido pelo provedor. Registre uma operação pendente durável, confirme a decisão local e envie o efeito externo com um ID de operação que sobreviva às tentativas novamente.

Os registros de auditoria devem identificar o principal autenticado, a execução do agente quando aplicável, o nome da mutação, o alvo normalizado, o resumo da entrada, o resultado da autorização, a referência da prévia, a chave de idempotência, o resultado e a versão final. Remova das gravações observações e campos com conteúdo sensível de acordo com suas regras de retenção. Um evento que diga apenas «mutação concluída» é quase inútil durante um incidente.

Para agentes que atuam por chamadas HTTP ou SSH com credenciais, mantenha as credenciais fora do processo do modelo sempre que possível. O Sallyport encaminha ações compatíveis por seu cofre local e registra cada chamada, o que ajuda quando uma mutação GraphQL precisa de uma autorização visível para a pessoa além do que um token bearer oferece.

Teste a solicitação gerada, não apenas o resolver

Deixe evidências de cada chamada
O diário de atividades registra cada chamada externa compatível, inclusive as ações por trás de uma mutação.

Testes unitários que chamam um resolver com objetos cuidadosamente construídos não capturam a falha que importa. Clientes gerados enviam campos omitidos, nulos, IDs antigos, aliases, solicitações repetidas e variáveis montadas a partir da saída de ferramentas anteriores. Teste a fronteira GraphQL pública com os mesmos formatos.

Monte uma matriz de testes de mutação baseada no comportamento, não nos ramos do código. No mínimo, cubra um cliente de outro tenant, um cliente com acesso de leitura mas sem escopo de ação, uma prévia expirada, uma versão de alvo alterada, uma confirmação incorreta, uma chave de idempotência repetida e duas chamadas simultâneas com a mesma versão esperada. Verifique a resposta e o estado persistido depois de cada teste.

Esta solicitação deve falhar durante a validação GraphQL porque a entrada não define ownerId:

mutation BadArchive($input: ArchiveProjectInput!) {
  archiveProject(input: $input) {
    outcome
  }
}
{
  "input": {
    "projectId": "prj_42",
    "expectedVersion": 7,
    "reason": "DUPLICATE",
    "confirmation": "archive prj_42",
    "idempotencyKey": "run-18-archive-42",
    "ownerId": "usr_9"
  }
}

A resposta esperada pertence ao formato de erro GraphQL de nível superior porque o documento forneceu um objeto de entrada inválido. Esse teste prova que o schema mantém uma capacidade não relacionada fora da mutação. Outro teste deve provar que uma solicitação de arquivamento bem formada ainda falha quando o ator pertence a outro tenant.

Execute testes de concorrência contra o comportamento transacional real, não contra uma imitação em memória. Envie duas solicitações de arquivamento com o mesmo ID e a mesma versão esperada e confirme que uma retorna ARCHIVED e a outra retorna um conflito ou um resultado de repetição idempotente. Se as duas informarem sucesso com IDs de operação diferentes, a gravação condicional não está funcionando.

Teste também a verificação da auditoria. Se o gateway de ações produzir uma trilha de auditoria criptografada e resistente a adulterações, inclua a verificação nos exercícios de incidentes, em vez de deixá-la como um comando que ninguém usou. O Sallyport expõe sp audit verify para verificar offline sua cadeia de hashes sem uma chave do cofre. Execute-o em um diário copiado e certifique-se de que os operadores saibam o significado de uma falha.

As ferramentas geradas precisam de menos escolhas, não de avisos mais longos

Os agentes têm melhor desempenho quando o schema da ferramenta apresenta a menor ação segura que corresponde à tarefa. Um catálogo enorme de mutações com filtros genéricos, flags e efeitos opcionais força o modelo a deduzir a política a partir dos nomes dos campos. Um catálogo compacto de operações explícitas de leitura, prévia, execução e recuperação oferece um caminho que ele pode seguir.

Exponha operações de leitura que retornem os identificadores, versões, estados e nomes necessários antes de propor uma mutação. Se o agente precisa inventar um ID a partir de um rótulo humano, o desenho da mutação não poderá salvá-lo. Retorne IDs estáveis com clareza e torne explícitos os resultados ambíguos, em vez de escolher um silenciosamente.

Escreva descrições de ferramentas que indiquem uma pré-condição e uma consequência, mas mantenha o servidor como ponto de imposição. Por exemplo: «Arquiva um projeto depois de uma prévia bem-sucedida. Cancela os convites pendentes listados na prévia.» Isso é melhor que «Use com cuidado», que não diz nada operacional ao agente.

Não tente resolver todo risco adicionando uma caixa de aprovação humana. A aprovação faz sentido quando uma pessoa é dona da decisão, mas prompts repetitivos viram ruído. Coloque a proteção rotineira em escopos, verificações de tenant, versões e idempotência. Peça que uma pessoa revise o pequeno conjunto de ações cuja intenção não pode ser inferida dos dados, cuja consequência é permanente ou que atravessa uma fronteira organizacional.

Comece pela mutação que mais prejudicaria o sistema se um agente a chamasse duas vezes, a chamasse com estado antigo ou contra o tenant errado. Divida sua entrada, acrescente uma prévia real quando a ação exigir, torne a gravação condicional e escreva o teste de repetição. Esse trabalho mostrará se sua API modela uma ação com clareza ou apenas expõe campos do banco de dados.

FAQ

A validação do GraphQL torna as mutações destrutivas seguras?

Não. O GraphQL valida o formato da solicitação, os argumentos dos campos e a coerção dos escalares, mas não sabe se uma operação faz sentido no estado atual do negócio. O resolver ainda precisa impor autorização, transições de estado, limites de tenant e regras de concorrência.

Quando devo criar uma entrada separada para uma mutação GraphQL?

Use uma entrada específica da mutação quando uma ação altera o estado de uma forma própria, especialmente em exclusões, suspensões, reembolsos, transferências ou publicações. Uma entrada de patch genérica pode servir para campos editáveis comuns, mas não deve carregar ações de ciclo de vida disfarçadas de booleanos.

O que um dry run de GraphQL deve realmente fazer?

Um dry run deve executar a mesma validação, autorização, seleção de alvos e planejamento da mutação real, parando antes da gravação externa ou do commit no banco. Se seguir outro caminho de código, os agentes aprenderão uma prévia que não corresponde ao comportamento de produção.

Como funcionam as chaves de idempotência em mutações GraphQL?

Uma chave de idempotência permite que o servidor reconheça uma nova tentativa da mesma ação e devolva o resultado original, em vez de aplicar a ação duas vezes. Armazene a chave com o ator, o nome da mutação, o resumo da entrada normalizada, o resultado e a política de expiração. Rejeite a reutilização com uma entrada diferente.

Os escopos GraphQL devem corresponder a recursos ou mutações?

Um escopo amplo como write:projects normalmente concede autoridade demais a um agente que só precisa arquivar um projeto. Limite as capacidades destrutivas à mutação ou ação e, separadamente, imponha no resolver as verificações de propriedade e tenant.

Como um agente de IA pode excluir muitos registros com segurança usando GraphQL?

Trate a exclusão em massa como uma operação separada, com quantidade-alvo, regra de seleção limitada, texto de confirmação explícito e prévia. Nunca transforme um campo de exclusão de um único objeto em um loop acidental só porque o agente consegue montar uma lista longa de IDs.

Que erros as mutações destrutivas de GraphQL devem retornar a um agente?

Retorne códigos legíveis por máquina, uma mensagem humana estável e detalhes estruturados, como a versão atual ou a frase de confirmação exigida. Não faça o agente analisar prosa para decidir se pode tentar novamente, atualizar o estado ou pedir ajuda a uma pessoa.

Duas solicitações de mutação GraphQL podem entrar em conflito?

O GraphQL serializa os campos de nível superior dentro de uma única operação de mutação, mas solicitações separadas ainda podem entrar em conflito. Inclua a versão esperada na entrada e torne a gravação condicional. Quando a versão armazenada mudar, retorne um conflito.

Um prompt de agente pode impor segurança às mutações GraphQL?

Não. O schema é uma interface pública, e os clientes podem chamar a API diretamente, ignorando qualquer prompt do agente ou descrição da ferramenta. O servidor precisa verificar identidade, escopo, relação com o tenant, estado e confirmação antes de alterar qualquer coisa.

Como manter as credenciais do agente fora das solicitações de API geradas?

Mantenha credenciais de API e SSH de longa duração fora do processo do agente, aprove a execução quando fizer sentido e registre cada ação externa. O Sallyport é uma opção para macOS que encaminha ações HTTP e SSH compatíveis por um cofre local criptografado, em vez de entregar os segredos ao agente.

Sallyport

O Sallyport executa chamadas de API e comandos SSH pelo seu agente de IA. As chaves ficam em um cofre local no seu Mac; você aprova cada execução e toda ação vai para um registro selado.

© 2026 Sallyport · Código aberto sob Apache-2.0 · Oleg Sotnikov