Por qué tu agente de IA falla en webs reales
Tu agente funciona con las páginas contra las que lo probaste. Después lo sueltas en la web abierta y las respuestas se vuelven vagas, o rotundamente equivocadas, o dice que la página estaba vacía cuando tú ves el contenido en tu navegador.
El instinto es ir a tocar el prompt. Normalmente el prompt está bien. El fallo ocurrió antes de que el modelo viera nada: en la capa que descarga una URL y la convierte en texto.
Hemos medido esa capa sobre páginas reales. Estas son las cinco formas en que se rompe, con el aspecto exacto que tiene cada una.
1. La página no tiene texto hasta que se ejecuta JavaScript
atlassian.com/software/jira/pricing, descargada de la forma normal, son 1,37 MB de HTML que producen un token de contenido legible. Ni un párrafo: un token, un salto de línea.
Los precios están ahí dentro. Están dentro de un <script>, como estado de aplicación, esperando a que un navegador construya la página con ellos.
Descarga esa misma URL con un navegador headless y se convierte en 3.048 tokens que empiezan por:
## **Transparent pricing for every team.**
La misma URL. El mismo día. Cero contenido o la página entera, según hayas ejecutado JavaScript o no.
Este es el fallo que parece un problema del modelo y no lo es. Tu agente dijo «la página no menciona precios» porque, en los bytes que le dieron, eso era cierto.
2. Pero renderizar no es el arreglo que te gustaría
La respuesta obvia es renderizarlo todo. Pruébalo con stripe.com/docs:
FetchError: Rendered fetch failed: Navigation timeout of 25000 ms exceeded
Una página que una descarga simple devuelve en menos de un segundo no terminó de cargar en un navegador headless en 25 segundos. La versión sin renderizar, con todos sus defectos, al menos volvió.
Renderizar te cuesta un proceso de navegador por página, de varios cientos de milisegundos a varios segundos de latencia, y una clase nueva de fallos: expiraciones, recursos bloqueados, páginas que nunca disparan load. Si tu agente renderiza por defecto, es más lento y menos fiable en parte de la web, no más.
La forma que funciona es: descarga barata primero, comprueba si has recibido algo y escala a renderizado solo en las páginas que volvieron vacías. Esa comprobación es justo la parte que la gente se salta.
3. Estás pagando por marcado que no usas
En las doce páginas conocidas que medimos, la mediana de lo que sobrevive del servidor al modelo no llega al 1%. shopify.com/pricing envía 410.515 tokens y produce 2.479.
Si tu agente mete HTML crudo en la ventana de contexto, estás pagando envoltorios <div> y SVG en línea a precio de token de entrada, y desplazando a las páginas que también querías leer. Extrae antes de enviar. Mejor aún: pregunta si el sitio te da Markdown directamente. Algunos ya lo hacen: de las doce páginas que probamos, cinco servían alguna representación en Markdown, una de ellas la nuestra.
4. La extracción se queda con la parte equivocada
La extracción es una heurística. Busca el artículo y descarta el mobiliario, y en páginas que no tienen forma de artículo se equivoca.
stripe.com/docs se reduce a 1.057 caracteres que empiezan así:
$ stripe payment_intents create --amount 1099 --currency "usd"
Es un fragmento real de la página. También es un ejemplo de terminal en lugar de una frase que explique qué hace Stripe. Un agente al que solo le dan eso tiene que deducir el producto de una muestra de código.
Conclusión: no te fíes de que una extracción con éxito sea una extracción útil. Guarda barata: si lo que extrajiste no tiene texto con forma de frase, o son menos de unos cientos de caracteres en una página que envió medio megabyte, trátalo como una lectura fallida y escala, en vez de pasárselo al modelo como si fuera un hecho.
5. La misma URL no devuelve la misma página
Descargar shopify.com/pricing de forma simple devolvió inglés. Renderizar esa URL idéntica desde la máquina idéntica devolvió español: Pago mensual32 € EUR/mes donde la descarga simple decía Pay monthly€32 EUR/mo.
Aquí no hay nada roto. El navegador headless envió señales de Accept-Language y de geolocalización distintas a las de nuestra descarga simple, y recibió una representación diferente y correcta. Pero si cacheas solo por URL, o comparas la lectura de hoy con la de ayer, esa diferencia va a parecer que el sitio ha cambiado los precios.
Envía un Accept-Language explícito. Indexa tu caché por lo que realmente pediste.
El fallo que no ocurrió
Esperábamos bloqueos. Sondeamos las doce páginas dos veces —una como navegador y otra identificándonos como OAI-SearchBot— y comparamos los códigos de estado con Cloudflare, Vercel y CloudFront por medio.
Ninguna trató distinto al bot. Ni una.
Eso no significa que nadie bloquee agentes: muchos lo hacen, y sigues teniendo que leer robots.txt y respetarlo. Significa que, si tu agente falla en una muestra amplia de la web, el bloqueo no suele ser la razón. La habitación vacía es mucho más común que la puerta cerrada.
Una capa de lectura que aguanta el contacto con la web
1. Pide Markdown primero → Accept: text/markdown, o /pagina.md
2. Descarga simple → barata, sin JS
3. Comprueba qué recibiste → ¿tiene forma de frase? ¿da la talla para los bytes enviados?
4. Escala si no → renderiza, con expiración y con presupuesto
5. Ríndete honestamente → «no he podido leer esta página»
El paso 5 importa más de lo que parece. Un agente que informa de una página ilegible se puede depurar. Un agente que le pasa un salto de línea al modelo y le deja improvisar es el que acaba inventándose tus precios.
La versión corta
- Casi todos los fallos de un agente son fallos de lectura, no de razonamiento.
- Vacío-sin-JavaScript es con diferencia el mayor, y renderizar lo arregla: a un coste, y no siempre.
- Verifica cada extracción antes de fiarte de ella.
- El bloqueo se lleva la atención. El vacío se lleva las víctimas.
Analiza cómo se lee una página · Lo que ve de verdad un agente · Servir Markdown a los agentes