Source: https://aiprimetech.io/es/blog/streaming-api-guide/ Jun 22, 2026 · 6 min · API # Respuestas de LLM en streaming: por qué importan para la experiencia de usuario y el coste A Por [AI Prime Tech](https://aiprimetech.io/es/) · Con asistencia de IA ## Los 900 ms que hicieron que nuestra interfaz de chat pareciera «averiada» Hace unos meses estaba depurando un asistente de atención al cliente que, sobre el papel, tenía una latencia de extremo a extremo perfectamente aceptable: la mayoría de las respuestas se completaban en 4–7 segundos. El modelo seleccionaba herramientas, recuperaba el contexto de la cuenta y generaba una respuesta útil. Pero los usuarios lo odiaban. La razón era sencilla: la interfaz permanecía en blanco durante los primeros 3.8 segundos. Sin texto junto al indicador de carga, sin respuesta parcial, sin ninguna señal de que el modelo hubiera entendido la pregunta. Solo una burbuja vacía del asistente. Cuando cambiamos el mismo endpoint para transmitir tokens mediante eventos enviados por el servidor, el tiempo medio total hasta completar la respuesta apenas cambió. Pero el tiempo hasta la primera salida visible se redujo a unos 700–900 ms en muchas solicitudes, y el producto se percibió inmediatamente como más rápido. Esa es la verdadera razón por la que importa el streaming. Las respuestas de LLM en streaming no son magia. No hacen que el modelo piense más rápido. No reducen el total de tokens. No reducen automáticamente tu factura. Pero cambian el ciclo de interacción de una forma que los usuarios pueden percibir y dan a tu aplicación más control sobre la cancelación, la visualización, la moderación, la contabilización del uso y la contrapresión. Este artículo explica cómo funciona realmente el streaming con SSE, qué medir, en qué aspectos la facturación puede tener matices y los patrones del lado del cliente que utilizo en sistemas de producción. ## ¿Cuál es la principal ventaja de usar respuestas en streaming? La principal ventaja de las respuestas en streaming es la latencia percibida: el usuario ve los primeros tokens en unos cientos de milisegundos en lugar de quedarse mirando un indicador de carga durante todo el tiempo de generación. El tiempo total de generación apenas cambia; lo que cambia es el tiempo hasta el primer token, y esa es la cifra que los usuarios realmente perciben. Una respuesta que tarda 9 segundos en completarse, pero empieza a aparecer después de 400 ms, se percibe como rápida; esa misma respuesta, entregada de golpe después de 9 segundos, da la impresión de que algo falla. También hay ventajas secundarias: el streaming permite mostrar progresivamente resultados parciales, cancelar la generación antes de que termine cuando el usuario ya ha visto suficiente —lo que ahorra tokens de salida— y evitar que se agote el tiempo de espera de lectura del proxy en respuestas largas. Pero quédate con una idea: el streaming no hace que el modelo sea más rápido; hace que la espera desaparezca de la experiencia del usuario. ## Qué significa realmente el streaming Cuando haces una solicitud a un LLM sin streaming, el servidor suele esperar hasta que el modelo termina de generar la respuesta y luego devuelve una única carga útil JSON: ``` { "id": "resp_123", "model": "some-model", "output_text": "Sure — here is the answer...", "usage": { "input_tokens": 812, "output_tokens": 436 } } ``` Con streaming, el servidor envía eventos incrementales a medida que hay resultados disponibles. El mecanismo de transporte más habitual son los eventos enviados por el servidor, normalmente abreviados como SSE. SSE es simplemente una respuesta HTTP con una conexión de larga duración y un tipo de contenido especial: ``` Content-Type: text/event-stream Cache-Control: no-cache Connection: keep-alive ``` En lugar de un único objeto JSON, el servidor envía una secuencia de pequeños eventos: ``` event: message.delta data: {"delta":"Sure"} event: message.delta data: {"delta":" — here"} event: message.delta data: {"delta":" is"} event: message.delta data: {"delta":" the answer..."} event: message.done data: {"finish_reason":"stop","usage":{"input_tokens":812,"output_tokens":436}} ``` Las distintas API usan nombres de eventos y esquemas diferentes, pero la estructura suele ser la misma: - Abrir una conexión HTTP. - Enviar los metadatos de la solicitud y el prompt. - Recibir fragmentos incrementales. - Añadir los fragmentos al mensaje actual del asistente. - Recibir un evento final con el motivo de finalización y, a menudo, metadatos de uso. - Cerrar el flujo. En la práctica, la mayor parte del «streaming» de LLM no consiste en un token por evento. Los proveedores pueden agrupar tokens en fragmentos por motivos de eficiencia. Puedes recibir un único carácter, un fragmento de palabra, varias palabras o un delta JSON estructurado. Nunca construyas un cliente que suponga que cada fragmento es una palabra completa o una frase completa. ## Latencia: tiempo total frente a tiempo hasta el primer token La distinción más importante es entre latencia total y tiempo hasta el primer token. | Métrica | Qué mide | Por qué importa | Tiempo hasta el primer byte, TTFB | Primer byte recibido del servidor | Incluye enrutamiento, autenticación, espera en cola y planificación de la ejecución del modelo | Tiempo hasta el primer token, TTFT | Primer token o fragmento generado visible para el usuario | Principal factor de la rapidez de respuesta percibida | Tokens por segundo | Velocidad de generación de la salida tras el primer token | Determina la rapidez con la que se completan las respuestas largas | Tiempo total hasta completar la respuesta | Respuesta completa recibida | Importa para la automatización, las llamadas a herramientas y las tareas por lotes | Tiempo hasta una respuesta utilizable | El usuario dispone de suficiente información para actuar | A menudo se alcanza antes de completar toda la respuesta Para la UX del chat, TTFT suele ser más importante que el tiempo total. Una respuesta que empieza en 800 ms y termina en 8 segundos suele percibirse mejor que una que aparece de golpe después de 4 segundos. En los flujos de trabajo entre máquinas, el streaming suele importar menos. Si el siguiente paso no puede ejecutarse hasta que el objeto JSON esté completo, el streaming puede limitarse a añadir complejidad a la implementación. ### Medir TTFT en Python Este es un patrón sencillo para medir la latencia hasta el primer fragmento y el tiempo total del flujo. La estructura de la API es deliberadamente genérica: adapta la URL y la carga útil a tu proveedor. ``` import json import time import requests url = "https://api.example.com/v1/chat/completions" payload = { "model": "sonnet-4.6", "stream": True, "messages": [ {"role": "user", "content": "Explain SSE streaming in 5 bullet points."} ] } headers = { "Authorization": "Bearer $API_KEY", "Content-Type": "application/json", "Accept": "text/event-stream", } start = time.perf_counter() first_chunk_at = None chars = 0 with requests.post(url, headers=headers, json=payload, stream=True) as r: r.raise_for_status() for raw_line in r.iter_lines(decode_unicode=True): if not raw_line: continue if not raw_line.startswith("data: "): continue data = raw_line[len("data: "):] if data == "[DONE]": break event = json.loads(data) # Provider-specific: this may be event["delta"], event["choices"][0]["delta"], etc. delta = event.get("delta", "") if delta and first_chunk_at is None: first_chunk_at = time.perf_counter() chars += len(delta) print(delta, end="", flush=True) end = time.perf_counter() print("\n\n--- metrics ---") print(f"ttft_ms={((first_chunk_at or end) - start) * 1000:.0f}") print(f"total_ms={(end - start) * 1000:.0f}") print(f"chars={chars}") ``` Un problema habitual: si pruebas el streaming con un proxy local, una función sin servidor o un proxy inverso que almacena las respuestas en un búfer, puedes pensar que el modelo no está transmitiendo en streaming. Es posible que el modelo lo esté haciendo correctamente, pero que tu infraestructura retenga los fragmentos hasta que se complete la respuesta. Lo he visto con: - El almacenamiento en búfer de Nginx activado. - Funciones en la nube que no admiten respuestas en streaming. - Middleware de compresión que espera a que se acumulen búferes más grandes. - Frameworks web que construyen toda la respuesta antes de devolverla. - Proxies corporativos que agrupan fragmentos pequeños. En Nginx, este ajuste suele ser importante: ``` location /api/llm/stream { proxy_pass http://app_backend; proxy_http_version 1.1; proxy_buffering off; proxy_cache off; chunked_transfer_encoding on; add_header X-Accel-Buffering no; } ``` ## SSE frente a WebSockets SSE no es la única forma de transmitir en streaming. Los WebSockets también son habituales, especialmente en aplicaciones bidireccionales. Sin embargo, para la mayoría de las interfaces de chat con LLM, SSE es la opción predeterminada más sencilla. | Función | SSE | WebSocket | Dirección | Del servidor al cliente | Bidireccional | Protocolo | Respuesta HTTP normal | Cambio de protocolo a WebSocket | Compatibilidad con navegadores | Nativo EventSource; también funciona con fetch flujos | Nativo WebSocket | Comportamiento de reconexión | Concepto integrado, aunque sigue haciendo falta lógica en la aplicación | Lo implementas tú | Compatibilidad con balanceadores de carga | Suele ser más sencillo | A veces requiere configuración adicional | Ideal para | Streaming de tokens, eventos de progreso, chat sencillo | Colaboración en tiempo real, audio, sesiones en directo con múltiples turnos | Compatibilidad con cuerpos de solicitud | EventSource solo admite GET; usa fetch para streaming con POST | Mensajes bidireccionales completos El EventSource La API del navegador es práctica, pero solo admite solicitudes GET. La mayoría de las llamadas a LLM necesitan cuerpos POST y encabezados de autenticación, así que suelo transmitir con fetch() y ReadableStream. ## Patrón de cliente para navegador: Fetch + ReadableStream Una implementación mínima para navegador sería así: ``` async function streamChat({ messages, signal, onDelta, onDone }) { const res = await fetch("/api/chat/stream", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ model: "gpt-5.5", stream: true, messages }), signal }); if (!res.ok) { throw new Error(`HTTP ${res.status}`); } const reader = res.body.getReader(); const decoder = new TextDecoder("utf-8"); let buffer = ""; while (true) { const { value, done } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split("\n"); buffer = lines.pop() || ""; for (const line of lines) { const trimmed = line.trim(); if (!trimmed.startsWith("data:")) continue; const data = trimmed.slice(5).trim(); if (data === "[DONE]") { onDone?.(); return; } const event = JSON.parse(data); if (event.delta) { onDelta(event.delta); } } } onDone?.(); } ``` Y en una interfaz de usuario: ``` const controller = new AbortController(); let assistantText = ""; streamChat({ messages, signal: controller.signal, onDelta(delta) { assistantText += delta; renderAssistantMessage(assistantText); }, onDone() { markMessageComplete(); } }); // Cancel button document.querySelector("#stop").onclick = () => { controller.abort(); }; ``` ### No renderices cada fragmento de forma ingenua Si actualizas el estado de React con cada pequeño fragmento, puedes crear accidentalmente un problema de rendimiento en el cliente. Algunas transmisiones producen decenas de fragmentos por segundo. Volver a renderizar un árbol de componentes de chat grande cada vez desperdicia recursos. En la práctica, suelo agrupar las actualizaciones de la interfaz por fotograma de animación: ``` let pending = ""; let scheduled = false; function onDelta(delta) { pending += delta; if (!scheduled) { scheduled = true; requestAnimationFrame(() => { assistantText += pending; pending = ""; scheduled = false; setAssistantText(assistantText); }); } } ``` Esto mantiene la fluidez de la interfaz y conserva la sensación de que la respuesta se genera en directo. ## Cómo gestionar correctamente las respuestas parciales La transmisión implica que tu aplicación recibe texto incompleto. Parece obvio, pero muchos errores surgen al tratar una respuesta parcial como si fuera la definitiva. ### El Markdown es temporalmente inválido Durante la transmisión, el Markdown puede estar mal formado: ``` Here is the query: ```sql SELECT * FROM users WHERE ``` Puede que el bloque de código aún no tenga su delimitador de cierre. Una tabla puede contener solo la fila de encabezado. Un enlace puede haberse emitido solo a medias. Tu renderizador debe tolerar estas situaciones. Para las interfaces de chat, prefiero: - Renderizar el Markdown de forma incremental, pero con precauciones. - Desactivar el HTML peligroso. - Evitar el resaltado de sintaxis costoso hasta que el mensaje esté completo. - Mostrar un estado sutil de «generando». - Volver a aplicar el formato final cuando termine la transmisión. ### Transmitir JSON es más difícil Si le pides al modelo JSON y lo transmites, el estado intermedio suele ser JSON inválido. Esto supone un problema para las aplicaciones que quieren actualizar interfaces estructuradas en tiempo real. Por ejemplo: ``` { "title": "Incident Summary", "severity": "high", "steps": [ "Restart ``` Todavía no puedes analizarlo. Hay tres enfoques habituales: - Esperar hasta que termine, y luego analizarlo una sola vez. - Usar JSON delimitado por líneas, donde cada línea completa se puede analizar de forma independiente. - Transmitir eventos semánticos, en lugar de JSON sin procesar. Para la transmisión estructurada, me gusta que la salida tenga forma de eventos: ``` event: section data: {"title":"Summary"} event: bullet data: {"text":"Database failover started at 09:42 UTC."} event: bullet data: {"text":"Read latency remained elevated for 6 minutes."} ``` Ese patrón requiere más orquestación, pero es mucho más fácil de renderizar de forma fiable. ## Qué ocurre realmente cuando los usuarios pulsan «Detener» La cancelación es una de las ventajas menos valoradas de la transmisión. Con una solicitud sin transmisión, el usuario puede abandonar la página mientras el modelo sigue generando en el servidor. Según el proveedor y tu backend, esa generación puede seguir consumiendo tokens de salida. Con la transmisión, el cliente puede abortar la solicitud HTTP: ``` const controller = new AbortController(); fetch("/api/chat/stream", { method: "POST", body: JSON.stringify(payload), signal: controller.signal }); // Later controller.abort(); ``` Pero la cancelación no siempre es instantánea de extremo a extremo. Hay varias capas: - El navegador aborta la solicitud fetch. - Tu backend detecta que la conexión se cierra. - Tu backend cancela la solicitud al proveedor. - El proveedor detiene la generación. - El uso se calcula a partir de los tokens ya procesados o generados. Un problema habitual: si tu backend actúa como proxy de la transmisión pero no propaga la cancelación al proveedor, el navegador del usuario se desconecta, pero tu servidor sigue leyendo la respuesta del modelo hasta que termina. En pseudocódigo al estilo de Node: ``` app.post("/api/chat/stream", async (req, res) => { const upstreamController = new AbortController(); req.on("close", () => { upstreamController.abort(); }); const upstream = await fetch(PROVIDER_URL, { method: "POST", headers: providerHeaders, body: JSON.stringify({ ...req.body, stream: true }), signal: upstreamController.signal }); res.setHeader("Content-Type", "text/event-stream"); for await (const chunk of upstream.body) { res.write(chunk); } res.end(); }); ``` Esta es la diferencia entre «la interfaz se detuvo» y «el modelo se detuvo». ## Transmisión y costes: qué cambia y qué no La transmisión cambia el momento en que observas el uso. Normalmente no cambia la unidad básica de facturación. La mayoría de las API de LLM cobran en función de conceptos como: - Tokens de entrada. - Tokens de salida. - Tokens de entrada en caché, si se aplica el almacenamiento de prompts en caché. - Llamadas a herramientas o modalidades especiales, según la API. - A veces, tokens de razonamiento o cómputo interno con un presupuesto asignado para determinadas familias de modelos. La transmisión en sí generalmente no supone un descuento. Si el modelo genera 700 tokens de salida, debes contar con contabilizar 700 tokens de salida, tanto si llegan de forma incremental como en una única carga útil final. La transmisión afecta a los costes en el plano operativo: | Escenario | Impacto en el coste | El usuario cancela después de 80 tokens | Puedes evitar que se genere el resto de la respuesta si se propaga la cancelación | La interfaz detecta pronto que la respuesta es suficiente | La aplicación puede detener la generación deliberadamente | El backend no logra abortar la solicitud al proveedor | Puedes pagar por la generación completa aunque el cliente se desconecte | Los datos de uso solo llegan en el evento final | Las transmisiones abortadas pueden requerir registros del proveedor o estimaciones | Las respuestas largas se transmiten de forma predeterminada | Los usuarios pueden consumir más porque la experiencia resulta más fluida | Llamadas a herramientas antes de transmitir el texto | TTFT puede aumentar, pero el coste de los tokens de salida puede mantenerse igual ### Un problema al contabilizar el uso Muchas API de transmisión envían metadatos de uso al final de la transmisión: ``` { "type": "message.done", "usage": { "input_tokens": 1240, "output_tokens": 389 }, "finish_reason": "stop" } ``` Si la transmisión se interrumpe antes del evento final, es posible que tu aplicación no reciba los datos de uso. Eso no significa necesariamente que el uso sea cero. Para la contabilidad interna, suelo almacenar tres campos: ``` { "request_id": "chat_01j...", "status": "client_aborted", "usage": null, "estimated_output_chars": 1842, "final_usage_received": false } ``` Luego concilio los datos si el proveedor ofrece registros de solicitudes o eventos de uso. Si no, al menos puedes hacer una estimación aproximada para la analítica de producto, sin dar una falsa sensación de precisión en la facturación. No factures a los usuarios finales a partir de una estimación basada en caracteres, salvo que tus condiciones lo permitan explícitamente. La tokenización varía según el modelo y el idioma. ## Patrón de arquitectura del backend Un flujo de transmisión en producción suele ser así: ``` Browser | | POST /api/chat/stream v App Backend | | provider streaming request v LLM API ``` Rara vez expongo las claves del proveedor directamente a los navegadores. El backend te ofrece: - Autenticación y autorización. - Limitación de la tasa de solicitudes por usuario o espacio de trabajo. - Composición de prompts y recuperación de información. - Ejecución de herramientas. - Controles de ocultación de datos y registro. - Cambio a un proveedor alternativo en caso de fallo. - Propagación de la cancelación. - Registro del uso. Una respuesta mínima en transmisión desde un backend de Python con FastAPI: ``` import json from fastapi import FastAPI, Request from fastapi.responses import StreamingResponse import httpx app = FastAPI() PROVIDER_URL = "https://api.example.com/v1/chat/completions" @app.post("/api/chat/stream") async def chat_stream(request: Request): body = await request.json() async def event_generator(): async with httpx.AsyncClient(timeout=None) as client: async with client.stream( "POST", PROVIDER_URL, headers={ "Authorization": "Bearer YOUR_PROVIDER_KEY", "Content-Type": "application/json", "Accept": "text/event-stream", }, json={ **body, "stream": True, }, ) as upstream: async for line in upstream.aiter_lines(): if await request.is_disconnected(): break if not line: continue # Optionally transform provider-specific events here. yield line + "\n\n" return StreamingResponse( event_generator(), media_type="text/event-stream", headers={ "Cache-Control": "no-cache", "X-Accel-Buffering": "no", }, ) ``` En la práctica, suelo normalizar los eventos de los proveedores en un único esquema interno: ``` { "type": "delta", "text": "partial text here" } ``` y después: ``` { "type": "done", "finish_reason": "stop", "usage": { "input_tokens": 812, "output_tokens": 436 } } ``` Esto es importante si admites varios modelos, como Claude, GPT, Gemini, Llama, Qwen o Kimi, a través de una sola interfaz. Cada proveedor tiene una semántica de transmisión ligeramente distinta. Normalizar desde el principio mantiene tu frontend sencillo y predecible, que es exactamente lo que buscas. ## TTFT no depende solo del modelo Cuando TTFT es deficiente, la gente suele culpar primero al modelo. A veces tiene razón: los modelos más grandes, los contextos más largos y los modos de razonamiento más intensivos pueden aumentar la latencia del primer token. Pero hay muchos mecanismos que intervienen antes de que llegue ese primer token. Entre los factores que contribuyen están: - El establecimiento de la conexión TLS. - Los arranques en frío de tu backend. - La construcción de prompts y la recuperación de información. - La latencia de la base de datos vectorial. - Las llamadas a herramientas antes de la generación. - Las colas de espera del proveedor. - La ingesta de un contexto de entrada largo. - Las comprobaciones de seguridad o las capas de políticas. - Almacenamiento en búfer del proxy. - Retraso de renderizado en el cliente. Una traza práctica debe separar estos aspectos: ``` { "trace_id": "req_abc", "timings_ms": { "auth": 12, "retrieval": 84, "prompt_build": 9, "provider_connect": 141, "ttfb": 612, "first_delta": 804, "stream_complete": 5820 } } ``` Si la recuperación tarda 600 ms y el primer delta del modelo llega 250 ms después, cambiar de modelo puede no resolver el problema de experiencia de usuario. Si predomina el tiempo de establecimiento de la conexión con el proveedor, reutilizar las conexiones puede ayudar. Si el renderizado del frontend espera a recibir un párrafo completo, el streaming funciona, pero la política de tu interfaz lo oculta. ## Cuándo no usar streaming El streaming no siempre merece la pena. Evito el streaming cuando: - La respuesta es muy breve, como en tareas de clasificación o enrutamiento. - Quien llama es otro servicio que necesita JSON completo. - La salida debe pasar por una moderación completa antes de mostrarse. - La solicitud se ejecuta en una cola de trabajos o en un flujo de procesamiento por lotes. - El framework o la capa de alojamiento hacen que el streaming sea poco fiable. - El producto necesita respuestas completas de una sola vez, no texto progresivo. También hay una desventaja para la experiencia de usuario: el streaming puede dejar ver cómo el modelo cambia de dirección a mitad de la respuesta. Las personas toleran parte de esto en un chat, pero puede dar una impresión poco pulida en flujos de trabajo de alto riesgo. En interfaces jurídicas, médicas, financieras o con exigencias estrictas de cumplimiento normativo, puedes optar por transmitir solo después de aplicar medidas de protección o por transmitir a un búfer interno antes de mostrar fragmentos seguros. ## Patrones prácticos del lado del cliente Estos son los patrones que recomiendo para la mayoría de los productos de chat: ### 1. Muestra el estado antes de que llegue el texto No dejes vacía la burbuja del asistente. Usa estados explícitos: - «Leyendo el contexto…» - «Buscando documentos…» - «Redactando la respuesta…» - «Sigo trabajando…» Esto resulta especialmente útil cuando las llamadas a herramientas se realizan antes de generar el texto. ### 2. Mantén un botón de detener funcional Un botón de detener no es un mero adorno de la experiencia de usuario. Puede ahorrar tokens de salida si se implementa correctamente. Asegúrate de que: - El navegador aborte la solicitud. - Tu backend detecte las desconexiones. - Tu backend aborte la solicitud al proveedor. - Registres la solicitud como cancelada. ### 3. Renderiza de forma incremental y aplica el formato al final Durante el streaming: - Añade texto. - Renderiza Markdown de forma ligera. - Evita el resaltado de sintaxis costoso. - Mantén un comportamiento de desplazamiento predecible. Al finalizar: - Vuelve a analizar el Markdown. - Resalta el código. - Activa los botones de copiar. - Añade citas o referencias si tu sistema las admite. ### 4. Guarda los mensajes parciales con cuidado Si un flujo falla a mitad de camino, decide qué significa el historial de la conversación. Opciones: - Conservar el mensaje parcial del asistente con una marca de «interrumpido». - Eliminarlo por completo. - Permitir que el usuario vuelva a intentarlo con el mismo prompt. - Pedir al modelo que continúe a partir del contexto parcial. Normalmente conservo el mensaje parcial con una marca visible. Eliminarlo sin avisar confunde a los usuarios, y fingir que se completó es peor. ### 5. Normaliza los eventos Tu frontend no debería tener que conocer cinco formatos de streaming específicos de cada proveedor. Normalízalos en el backend: ``` {"type":"delta","text":"Hello"} {"type":"tool_start","name":"search_docs"} {"type":"tool_result","name":"search_docs","status":"ok"} {"type":"done","finish_reason":"stop","usage":{"input_tokens":100,"output_tokens":50}} {"type":"error","message":"upstream timeout"} ``` Esto también facilita cambiar entre familias de modelos o enrutar solicitudes dinámicamente. Si utilizas una capa de API multimodelo, esta normalización puede formar ya parte de tu pasarela; de lo contrario, merece la pena implementarla por tu cuenta. ## Conclusiones prácticas - El streaming mejora la latencia percibida al reducir el tiempo hasta la primera salida visible, no al hacer que toda la generación sea gratuita o instantánea. - Mide TTFT, el tiempo total hasta la finalización y los tokens por segundo por separado. - SSE suele ser el transporte más sencillo para el streaming de texto de LLM; utiliza fetch() flujos cuando necesites cuerpos POST. - Vigila el almacenamiento en búfer del proxy y del framework. Muchos «errores de streaming» son errores de almacenamiento en búfer de la infraestructura. - Considera inválida la salida parcial: el Markdown puede estar incompleto, JSON puede no analizarse correctamente y los delimitadores de bloques de código pueden seguir abiertos. - La cancelación solo ahorra costes si la interrupción se propaga desde el navegador al backend y de ahí al proveedor. - Los metadatos de uso suelen llegar al final del flujo; los flujos interrumpidos requieren una contabilización cuidadosa. - No vuelvas a renderizar toda la interfaz con cada fragmento diminuto. Agrupa las actualizaciones mediante fotogramas de animación. - Normaliza los eventos de streaming específicos de cada proveedor en tu backend. - Prescinde del streaming para clasificaciones breves, trabajos por lotes, salidas estructuradas estrictas o flujos de trabajo que requieran una moderación completa antes de mostrar el contenido. [Inferencia](/es/blog/tag/inference/)[API](/es/blog/tag/api/) A [AI Prime Tech](https://aiprimetech.io/es/) AI Prime Tech publica este blog. Los artículos se redactan con asistencia de IA; comprueba los detalles específicos de cada versión, como los identificadores de modelos, los precios y los límites, en la documentación oficial antes de basarte en ellos. Obtén acceso más económico a la API de Claude Una sola clave de API para Claude Opus 5.5, Sonnet 5, Haiku 4.5 y Fable 5.1, además de GPT-6 modelos. Pago por uso, sin suscripción. [Obtén tu clave de API →](/register) AI Prime Tech es una pasarela de API independiente de terceros. Claude™ y Anthropic® son marcas comerciales de Anthropic, PBC. No se implica ninguna afiliación ni respaldo.