# Paginação de API para agentes de IA: descoberta com limites

Um agente que chama um endpoint de lista sem definir um orçamento de páginas não está fazendo descoberta. Está iniciando um procedimento remoto sem fim definido e torcendo para que a conta, o limite de taxa e o conjunto de resultados continuem favoráveis. O erro oposto é igualmente prejudicial: buscar a primeira página, encontrar uma resposta plausível e tratá-la silenciosamente como se fosse o sistema inteiro.

A paginação muda o que um agente pode afirmar com honestidade. Um item retornado prova que esse item existe. Não prova que não há outros. A ausência de um item prova muito pouco, a menos que o agente consiga mostrar o escopo inspecionado, a ordenação usada e o motivo pelo qual o servidor indicou que a travessia terminou.

Já vi agentes transformarem uma solicitação inofensiva, como «encontre tokens de acesso obsoletos», em milhares de chamadas porque ninguém informou onde a descoberta deveria terminar. Também já vi um inventário de uma única página levar a uma tentativa de limpar contas que estavam na página dois. A solução não está em escrever prompts mais inteligentes. Dê ao agente um contrato de travessia com limites e faça o relatório final expor essa fronteira.

## Chamadas de descoberta precisam de um orçamento explícito

Toda chamada de descoberta paginada precisa de limites que o agente não possa ampliar silenciosamente. Defina o número máximo de páginas, o número máximo de registros retornados, um prazo e uma margem para o limite de taxa antes da primeira solicitação. Os valores adequados dependem da tarefa, mas os limites são sempre necessários.

Trate a descoberta como uma fase separada da ação. Durante a descoberta, o agente reúne identificadores e fatos. Ele não deve excluir, alternar ou modificar objetos apenas porque uma página contém algo suspeito. Quando tiver evidências suficientes, pode apresentar um plano com escopo definido ou iniciar uma fase de ação autorizada separadamente.

Um contrato útil tem quatro partes:

- O endpoint e todos os filtros, incluindo a ordenação quando a API permitir.
- Um tamanho de página e um limite máximo de páginas ou registros.
- Uma condição de conclusão definida pela API, como a ausência de um cursor seguinte.
- Uma condição de parada antecipada, como encontrar um objeto específico ou esgotar o orçamento disponível.

Não confunda limite de registros com limite de páginas. Se o serviço permite 100 registros por página e o agente tem um limite de 500 registros, cinco chamadas podem bastar. Se o serviço reduzir o tamanho efetivo da página por causa de filtros ou permissões, a mesma tarefa pode precisar de mais chamadas. Uma boa implementação verifica os dois limites depois de cada resposta.

Por exemplo, um agente encarregado de localizar um repositório chamado `billing-service` poderia parar assim que recebesse uma correspondência exata, caso o filtro e a ordenação do endpoint tornem essa conclusão segura. Um agente encarregado de identificar todos os repositórios sem proteção de branches não pode parar na primeira correspondência. Essa tarefa exige uma enumeração completa ou um resultado explicitamente parcial.

A palavra «todos» deve ter um custo. O agente só pode usá-la depois que a travessia chegar à condição terminal do servidor sem atingir seu orçamento de páginas, registros, tempo ou erros. Se um desses limites for acionado, o relatório deve dizer «varredura parcial» e indicar a fronteira.

## O tamanho da página controla custos, não a completude

O parâmetro `limit`, `per_page` ou `page_size` informa ao serviço quantos registros tentar retornar em uma resposta. Ele não informa quanto da coleção o agente precisa inspecionar. Defini-lo no máximo reduz algumas viagens, mas pode tornar cada resposta cara o bastante para causar timeout, exceder o orçamento de contexto ou esconder detalhes importantes em meio a muitos dados irrelevantes.

Comece com a menor página que permita tomar a decisão. Se o agente precisa de uma conta exata, normalmente é melhor solicitar 20 registros resumidos do que 1.000 objetos completos. Se precisa montar um inventário, use um tamanho de página maior somente depois de confirmar que o endpoint retorna uma representação limitada e útil.

Os campos importam tanto quanto o tamanho da página. Muitas APIs oferecem `fields`, `include`, `expand` ou mecanismos semelhantes. Uma passagem de descoberta deve solicitar identificadores, nomes, estado, timestamps e a propriedade que orienta a decisão. Busque detalhes completos apenas para os candidatos que precisam de inspeção. Isso reduz o tráfego e deixa menos texto incidental para o modelo interpretar de forma equivocada.

Use um formato de solicitação como este quando o provedor oferecer paginação por cursor:

```http
GET /v1/projects?state=active&limit=50&sort=id HTTP/1.1
Authorization: Bearer injected-by-gateway
Accept: application/json
```

E espere um formato de saída que separe os registros do estado de continuação:

```json
{
  "data": [
    {"id": "prj_104", "name": "billing-service", "state": "active"}
  ],
  "next_cursor": "eyJvcmRlciI6ImlkIiwicG9zIjoiMTA0In0"
}
```

O agente deve registrar que solicitou 50 registros e recebeu um. Não deve deduzir que a travessia terminou a partir do tamanho reduzido do array. O único sinal útil de conclusão neste exemplo é a ausência de `next_cursor` ou seu valor nulo documentado.

Evite uma regra arbitrária como «use sempre 100». O tamanho máximo varia entre APIs, e alguns serviços contabilizam objetos filhos expandidos ou bytes da resposta em limites diferentes. Solicite o máximo documentado apenas quando a tarefa se beneficiar disso. Em varreduras amplas, um tamanho médio costuma produzir checkpoints melhores, novas tentativas mais fáceis e relatórios que uma pessoa consegue auditar.

## Preserve os cursores como estado opaco do servidor

Um cursor não é um offset com um nome sofisticado. O servidor é dono do seu significado, e o agente deve copiá-lo byte a byte na próxima solicitação. Ele pode codificar uma posição de ordenação, um identificador de snapshot, um limite de permissão ou uma assinatura. O fato de parecer Base64 não dá permissão para decodificá-lo, editá-lo ou fabricá-lo.

O loop seguro é simples: solicite a primeira página com filtros e parâmetros de ordenação estáveis, salve o cursor retornado pela resposta e envie a mesma consulta com esse cursor. Mantenha todos os parâmetros originais, salvo quando a documentação da API disser explicitamente o contrário. Alterar o filtro entre solicitações pode invalidar o cursor ou, pior, produzir um conjunto de resultados plausível, mas descontínuo.

```text
request = { state: "active", limit: 50, sort: "id" }
seen_ids = set()
pages = 0

while pages < 10 and len(seen_ids) < 500:
    response = GET /v1/projects with request
    record response status, request, and response cursor

    for item in response.data:
        if item.id in seen_ids:
            report "duplicate record encountered" with item.id
            stop or apply the provider's documented recovery method
        seen_ids.add(item.id)

    pages += 1
    if response.next_cursor is absent:
        report "complete"
        break

    request.cursor = response.next_cursor
else:
    report "partial: traversal budget reached"
```

A verificação de duplicatas não é enfeite. Coleções mutáveis podem mudar enquanto o agente as percorre, e existem implementações de paginação defeituosas. Uma duplicata nem sempre significa que o serviço falhou, mas significa que o agente deve parar de fingir que tem uma enumeração limpa. Se o provedor oferecer um token de snapshot, um parâmetro `as_of` ou um modo de consistência documentado, use-o em tarefas que levarão a uma ação consequente.

A expiração do cursor precisa de uma regra explícita. Alguns serviços fazem cursores de curta duração; outros os vinculam a uma sessão ou os invalidam quando uma consulta muda. Ao receber uma resposta de cursor expirado, o agente deve preservar o erro e então reiniciar a partir de um checkpoint estável ou encerrar a varredura como incompleta. Não deve avançar pulando registros ao adivinhar um novo cursor.

Um reinício também pode criar uma falsa sensação de completude. Se os registros mudaram entre a primeira passagem e o reinício, uma lista combinada pode conter lacunas ou duplicatas. Informe o reinício e a condição usada para retomar, como `created_at >= last_observed_timestamp`. Se não houver um método estável de retomada, informe que a coleção mudou durante a travessia e não use o resultado como lista de exclusão.

## A paginação por offset sofre desvios quando as coleções mudam

A paginação por offset usa um número como `offset=200&limit=50` ou `page=5&per_page=50`. É fácil de programar e explicar, por isso continua comum. Também se torna pouco confiável quando novos registros chegam ou registros antigos desaparecem enquanto o agente percorre a coleção.

Suponha que a primeira página retorne os registros 1 a 50 em ordem do mais recente para o mais antigo. Antes de o agente solicitar a segunda página, chegam dez registros novos. `offset=50` agora começa depois dos registros recém-inseridos e se sobrepõe a objetos que o agente já viu. Se registros desaparecerem da primeira página, o mesmo offset pode pular objetos que subiram de posição. O agente não consegue corrigir isso apenas deduplicando IDs, porque a deduplicação detecta repetições, mas não omissões.

Se a API permitir uma ordenação estável, selecione uma com um desempate determinístico. Apenas `created_at` muitas vezes não basta, pois vários registros podem compartilhar o mesmo timestamp. Uma ordenação como `created_at,id`, quando documentada pelo provedor, permite registrar uma marca d'água e retomar com mais cuidado. Se a API fornecer somente um offset, sem snapshot ou ordenação estável, seja conservador nas conclusões extraídas de várias páginas.

Para uma tarefa que exige uma resposta completa, use uma destas abordagens, em ordem decrescente de confiança:

1. Peça ao serviço um snapshot, um job de exportação ou um cursor que documente uma visão estável.
2. Restrinja a consulta a um intervalo de tempo imutável e use uma ordenação estável documentada.
3. Execute uma segunda varredura e compare os identificadores, informando qualquer divergência.
4. Peça a uma pessoa que aprove um escopo mais estreito e bem definido, em vez de fazer uma alteração ampla.

Não transforme uma interface ruim em um fluxo destrutivo. O agente ainda pode usar páginas por offset para amostragem, localizar um objeto específico ou produzir um inventário parcial. Não deve usar uma varredura instável por offset como prova de que encontrou todas as credenciais, projetos ou usuários correspondentes.

## A conclusão deve vir do protocolo, não de uma suposição

Diferentes APIs expressam o estado da paginação em lugares diferentes. Um corpo JSON pode conter `next_cursor`, `has_more` ou uma URL para a próxima página. Outras APIs usam o cabeçalho HTTP `Link`. A RFC 8288 define Web Linking e o parâmetro `rel`, usado para relações como `next`; o cabeçalho fornece uma relação, mas não garante que o corpo da resposta tenha um campo de cursor conhecido.

Um agente precisa de uma regra de conclusão específica para cada endpoint. Escreva-a ao lado da definição da solicitação. Por exemplo: «Conclua quando `next_cursor` for nulo». Ou: «Conclua quando nenhuma relação Link tiver `rel="next"`». Não escreva: «Conclua quando retornarem menos de 100 registros». Esse atalho falha com páginas filtradas, redução por permissões, limites do serviço e APIs que retornam páginas deliberadamente irregulares.

Um cabeçalho Link típico pode ter esta aparência:

```http
Link: </v1/events?limit=100&cursor=a6f3>; rel="next",
      </v1/events?limit=100&cursor=first>; rel="first"
```

O agente deve selecionar apenas a relação que conhece. Não deve concatenar o cabeçalho, presumir que o link `first` é um checkpoint seguro para reinício ou inferir que a ausência de um link `last` significa que não existe uma página final. A documentação da própria API rege seu contrato de paginação; a RFC 8288 apenas descreve como as relações de link circulam nos cabeçalhos HTTP.

Algumas APIs retornam `has_more: true` com uma página vazia. Isso parece absurdo até que se encontra um filtro de permissões, uma exclusão concorrente ou um índice atrasado. Se a API documentar esse comportamento, continue com o token de continuação enquanto o orçamento permitir e registre a página vazia. Se não documentar, pare e sinalize uma resposta de paginação inconsistente. Continuar indefinidamente porque `has_more` permanece verdadeiro é um erro de programação, não persistência.

Também diferencie uma resposta terminal de um status HTTP bem-sucedido. `200 OK` diz que aquela solicitação foi concluída com sucesso. Não diz que a coleção terminou. Um `404` pode significar um cursor expirado, um endpoint incorreto ou um recurso que desapareceu. Preserve o status, o corpo da resposta e o último cursor no registro da execução para que uma pessoa consiga identificar o que ocorreu.

## Um resultado parcial precisa de uma declaração de limite

Um agente deve relatar a descoberta como uma pessoa cuidadosa escreveria uma nota de incidente: informe o que foi solicitado, o que foi observado e o que não foi inspecionado. A maioria dos relatórios ruins de agentes falha na última parte. Eles listam as descobertas, mas omitem o cursor, o limite ou o erro que torna essas descobertas incompletas.

Use um formato de relatório que uma pessoa possa utilizar sem reconstruir a sessão:

```text
Scope: GET /v1/projects?state=active&sort=id
Requested page size: 50
Pages fetched: 10
Records received: 487
Completion: partial
Stop reason: page budget reached
Last continuation cursor: eyJvcmRlciI6ImlkIiwicG9zIjoiNTg3In0
Observed finding: 12 projects matched the review rule
Uninspected scope: records after the last continuation cursor
Action taken: none
```

A última linha importa. Uma execução de descoberta deve dizer se alterou alguma coisa. Quem revisar o relatório nunca deve precisar inferir se o agente apenas listou objetos ou agiu sobre eles.

Não exponha um cursor sensível em uma transcrição de chat se o provedor o tratar como uma capacidade bearer ou se seu conteúdo puder revelar a estrutura da conta. Armazene o token exato nos metadados protegidos da execução e apresente uma impressão digital ou um prefixo redigido no relatório voltado para pessoas. O agente ainda precisa de estado suficiente para retomar ou auditar a travessia, mas as pessoas não precisam ver tokens de continuação espalhados por tickets e terminais.

A linguagem controla o risco aqui. «Não encontrei registros correspondentes nos primeiros 500 inspecionados» é preciso. «Não existem registros correspondentes» só é preciso depois de uma travessia completa em uma visão suficientemente estável. A diferença parece minuciosa até que uma decisão de limpeza ou conformidade dependa dela.

## Limites de taxa e novas tentativas precisam de regras próprias

A paginação amplia os erros de limite de taxa porque uma única solicitação se transforma em um loop. Um agente que recebe `429 Too Many Requests` não deve bombardear o endpoint com o mesmo cursor. Respeite `Retry-After` quando o servidor o fornecer, contabilize a espera no prazo da execução e pare quando o orçamento de novas tentativas acabar.

Para falhas transitórias, como um timeout ou uma resposta `5xx`, repita a mesma página antes de avançar. Se a API oferecer um identificador de idempotência ou de solicitação para chamadas de lista, use-o conforme a documentação. Nunca avance para o próximo cursor depois de uma resposta incerta apenas porque a solicitação pode ter sido concluída. Isso cria uma lacuna silenciosa.

Uma política limitada de novas tentativas poderia estabelecer:

- Repetir a página atual no máximo duas vezes após falhas transitórias de transporte ou do servidor.
- Respeitar `Retry-After` para limites de taxa quando o prazo restante permitir.
- Não repetir falhas de autenticação ou autorização sem uma mudança no estado de autorização.
- Parar diante de dados de paginação malformados, cursor repetido ou resposta de continuação não documentada.

Um cursor repetido merece atenção especial. Se a página três retornar o mesmo `next_cursor` que o agente enviou, continuar pode criar um loop infinito. Compare cada cursor novo com o cursor enviado e com um conjunto de cursores anteriores. Pare quando houver repetição, salvo se o provedor documentar um caso em que isso seja esperado, algo raro que deve ser tratado por uma regra específica do provedor.

O agente deve preservar metadados de resposta suficientes para diagnosticar uma nova tentativa sem armazenar segredos. Registre o código de status, o caminho da solicitação, cabeçalhos selecionados que não sejam sensíveis, o número sequencial da página, a impressão digital do cursor, timestamps e um resumo do corpo da resposta, se a política permitir. Não cole cabeçalhos de autorização, tokens bearer completos ou URLs com credenciais nos logs apenas porque uma solicitação falhou.

## Um exemplo de falha mostra por que a primeira página é perigosa

Considere um agente encarregado de desativar toda integração inativa mais antiga que uma data definida. O endpoint de integrações usa por padrão 25 itens, ordena pela atualização mais recente e fornece `next_cursor` apenas quando existem mais páginas. O agente busca a primeira página, encontra três integrações inativas e as desativa. Depois informa que limpou as integrações inativas.

Esse relatório está errado de duas formas. O agente não inspecionou todas as integrações e agiu durante a descoberta. O fato de ter encontrado três candidatos não diz nada sobre a página dois em diante. Pior: desativar itens altera `updated_at`, o que pode reordenar a coleção se o endpoint usar sua ordenação padrão. O agente tornou sua própria travessia menos estável.

Uma execução mais segura começa com um filtro explícito e uma ordenação estável, se a API oferecer suporte:

```http
GET /v1/integrations?status=inactive&updated_before=2024-01-01&limit=50&sort=id HTTP/1.1
```

O agente reúne IDs entre as páginas sem modificá-los. Para apenas quando o servidor não emitir um cursor seguinte ou quando o orçamento de descoberta for atingido. Depois, informa um conjunto completo ou parcial de candidatos. Uma solicitação de ação separada pode usar os IDs reunidos, de preferência depois que uma pessoa conferir a quantidade e o escopo.

Se o endpoint de lista não oferecer uma ordenação estável, o agente deve dizer isso. Ainda pode reunir candidatos, mas não deve afirmar que obteve um conjunto completo e livre de condições de corrida. O conselho popular de «agir enquanto encontra os itens» parece eficiente porque economiza uma segunda passagem. Ele está errado para listas mutáveis em que a ação altera a posição na ordenação, a elegibilidade ou as permissões.

O mesmo padrão se aplica a descobertas de segurança, contas de usuários, registros de implantação e artefatos de build. Primeiro leia, estabeleça o escopo e depois altere. Há exceções para contenção urgente, como revogar uma credencial comprometida e identificada pelo nome, mas isso não é uma tarefa de limpeza paginada. É uma ação direcionada com um identificador conhecido.

## Mantenha credenciais e observabilidade fora do agente

Um agente não deve precisar do segredo da API apenas para paginar. O componente que executa as solicitações HTTP pode injetar credenciais, exigir autorização humana quando necessário e registrar a sequência real de solicitações. Assim, o agente se concentra na construção e interpretação da consulta, sem lidar com tokens que permitiriam fazer chamadas sem limite em outros lugares.

Para equipes que usam o Sallyport, as chamadas HTTP podem passar pelo gateway de ações enquanto as credenciais permanecem no cofre criptografado, e o diário de Atividades registra cada chamada. Esse registro ajuda uma pessoa revisora a comparar a quantidade de páginas declarada pelo agente com as solicitações realmente executadas, mas não substitui os orçamentos de páginas nas instruções do agente.

Mantenha a política de travessia próxima da definição da tarefa. Especifique endpoints permitidos, filtros, campos, número máximo de páginas, comportamento de novas tentativas e a declaração de limite obrigatória. Uma camada de autorização de uso geral não consegue decidir se uma varredura de um repositório termina após uma página ou se um inventário de conformidade precisa de todas as páginas.

O diário de sessões e a trilha de atividades por chamada do Sallyport podem tornar prática a revogação e a revisão quando uma execução sai do rumo. Ainda assim, o agente precisa receber instruções para parar diante de um formato de cursor não reconhecido, de um orçamento esgotado ou de uma resposta da API que contradiga o contrato documentado do endpoint. Registrar uma coleta incorreta depois do fato é melhor do que não ter evidências, mas não desfaz chamadas desnecessárias nem uma ação incorreta.

## Teste a travessia com cenários hostis de paginação

A paginação no caminho feliz esconde os defeitos que importam. Antes de confiar em um fluxo de agente, teste-o com cenários que retornem uma primeira página vazia com cursor, uma página curta com mais resultados, um item duplicado, um cursor repetido, um cursor expirado e uma resposta de limite de taxa entre duas páginas normais.

O comportamento esperado deve ser simples e específico. O agente continua depois de uma página vazia somente quando o estado de continuação documentado indicar que deve continuar. Ele deduplica ou para diante de IDs repetidos conforme o requisito de consistência da tarefa. Nunca inventa um cursor depois de uma expiração e informa cobertura parcial quando um limite de segurança encerra a execução.

Use esta tabela de aceitação ao revisar um loop de chamadas de ferramenta:

| Cenário | Resultado esperado |
| --- | --- |
| 12 registros, sem cursor seguinte | Concluir após uma solicitação |
| 12 registros, com cursor seguinte | Continuar apesar da página curta |
| Mesmo cursor retornado duas vezes | Parar e informar um loop de paginação |
| `429` com `Retry-After` | Esperar apenas dentro do prazo e repetir a página atual |
| Cursor rejeitado por expiração | Reiniciar apenas por um checkpoint documentado ou informar resultado incompleto |

Inspecione as chamadas brutas e também o texto final. Um relatório bem escrito pode esconder um cursor pulado ou uma solicitação extra depois da condição de parada. O log de execução deve mostrar uma solicitação inicial, cada solicitação de continuação, qualquer nova tentativa do mesmo cursor e nenhuma chamada depois da resposta terminal.

Defina um orçamento padrão pequeno para trabalhos exploratórios e exija uma mudança explícita na tarefa para uma enumeração ampla. Essa única restrição evita as duas falhas que mais desperdiçam tempo: agentes que varrem para sempre e agentes que confundem silenciosamente a primeira página com a resposta completa.
