Porque o teu agente de IA falha em sites reais

O teu agente funciona nas páginas contra as quais o testaste. Depois apontas-lo à web aberta e as respostas ficam vagas, ou confiantemente erradas, ou diz que a página estava vazia quando tu vês o conteúdo no teu navegador.

O instinto é ir mexer no prompt. Normalmente o prompt está bem. A falha aconteceu antes de o modelo ver seja o que for — na camada que descarrega um URL e o transforma em texto.

Medimos essa camada em páginas reais. Estas são as cinco formas como ela parte, com o aspeto exato de cada uma.

1. A página não tem texto até o JavaScript correr

atlassian.com/software/jira/pricing, descarregada da forma habitual, são 1,37 MB de HTML que produzem um token de conteúdo legível. Não um parágrafo: um token, uma mudança de linha.

Os preços estão lá dentro. Estão dentro de um <script>, como estado de aplicação, à espera de que um navegador construa a página com eles.

Descarrega esse mesmo URL com um navegador headless e passa a 3.048 tokens que começam por:

## **Transparent pricing for every team.**

O mesmo URL. O mesmo dia. Zero conteúdo ou a página inteira, consoante tenhas executado JavaScript ou não.

Esta é a falha que parece um problema do modelo e não é. O teu agente disse «a página não menciona preços» porque, nos bytes que lhe deram, isso era verdade.

2. Mas renderizar não é a solução que gostarias

A resposta óbvia é renderizar tudo. Experimenta em stripe.com/docs:

FetchError: Rendered fetch failed: Navigation timeout of 25000 ms exceeded

Uma página que uma descarga simples devolve em menos de um segundo nunca acabou de carregar num navegador headless em 25 segundos. A versão não renderizada, com todos os seus defeitos, pelo menos voltou.

Renderizar custa-te um processo de navegador por página, de várias centenas de milissegundos a vários segundos de latência, e uma nova classe de falhas: expirações, recursos bloqueados, páginas que nunca disparam load. Se o teu agente renderiza por omissão, é mais lento e menos fiável em parte da web, não mais.

A forma que funciona é: descarga barata primeiro, verifica se recebeste alguma coisa, e escala para renderização apenas nas páginas que voltaram vazias. Essa verificação é justamente a parte que se salta.

3. Estás a pagar marcação que nunca usas

Nas doze páginas conhecidas que medimos, a mediana do que sobrevive do servidor ao modelo fica abaixo de 1%. shopify.com/pricing envia 410.515 tokens e produz 2.479.

Se o teu agente enfia HTML cru na janela de contexto, estás a pagar <div> de embrulho e SVG em linha a preço de token de entrada, e a expulsar as páginas que também querias ler. Extrai antes de enviar. Melhor ainda: pergunta se o site te dá Markdown diretamente. Alguns já dão: das doze páginas testadas, cinco serviam alguma representação em Markdown, uma delas a nossa.

4. A extração fica com a parte errada

A extração é uma heurística. Procura o artigo e descarta o mobiliário, e em páginas que não têm forma de artigo adivinha mal.

stripe.com/docs reduz-se a 1.057 caracteres que começam assim:

$ stripe payment_intents create --amount 1099 --currency "usd"

É um fragmento real da página. É também um exemplo de terminal em vez de uma frase que explique o que a Stripe faz. Um agente a quem só dão isso tem de deduzir o produto a partir de uma amostra de código.

Portanto: nunca confies que uma extração bem-sucedida seja uma extração útil. Guarda barata — se o que extraíste não tem texto com forma de frase, ou são menos de umas centenas de caracteres numa página que enviou meio megabyte, trata-o como leitura falhada e escala, em vez de o passares ao modelo como facto.

5. O mesmo URL não devolve a mesma página

Descarregar shopify.com/pricing de forma simples devolveu inglês. Renderizar esse URL idêntico a partir da máquina idêntica devolveu espanhol: Pago mensual32 € EUR/mes onde a descarga simples dizia Pay monthly€32 EUR/mo.

Aqui não há nada partido. O navegador headless enviou sinais de Accept-Language e de geolocalização diferentes dos da nossa descarga simples, e recebeu uma representação diferente e correta. Mas se fizeres cache só por URL, ou comparares a leitura de hoje com a de ontem, essa diferença vai parecer que o site mudou os preços.

Envia um Accept-Language explícito. Indexa a tua cache por aquilo que realmente pediste.

A falha que não aconteceu

Esperávamos bloqueios. Sondámos as doze páginas duas vezes — uma como navegador, outra identificando-nos como OAI-SearchBot — e comparámos os códigos de estado através de Cloudflare, Vercel e CloudFront.

Nenhuma tratou o bot de forma diferente. Nem uma.

Isso não significa que ninguém bloqueie agentes; muitos bloqueiam, e continuas a ter de ler o robots.txt e respeitá-lo. Significa que, se o teu agente falha numa amostra ampla da web, o bloqueio dificilmente é a razão. A sala vazia é bem mais comum do que a porta fechada.

Uma camada de leitura que aguenta o contacto com a web

1. Pede Markdown primeiro   → Accept: text/markdown, ou /pagina.md
2. Descarga simples         → barata, sem JS
3. Verifica o que recebeste → tem forma de frase? é longo o bastante para os bytes enviados?
4. Escala se não            → renderiza, com expiração e orçamento
5. Desiste honestamente     → «não consegui ler esta página»

O passo 5 importa mais do que parece. Um agente que reporta uma página ilegível é depurável. Um agente que passa uma mudança de linha ao modelo e o deixa improvisar é o que acaba a inventar os teus preços.

A versão curta

  • Quase todas as falhas de um agente são falhas de leitura, não de raciocínio.
  • Vazio-sem-JavaScript é de longe a maior, e renderizar resolve-a: a um custo, e nem sempre.
  • Verifica cada extração antes de confiares nela.
  • O bloqueio leva as atenções. É o vazio que faz os estragos.

Verifica como uma página se lê · O que um agente vê de facto · Servir Markdown aos agentes