Cómo los Server-Sent Events potencian las respuestas en streaming de Claude
Cuando llamas a la API de Mensajes de Claude sin streaming, envías una solicitud y esperas.
Nada regresa hasta que se genera toda la respuesta.
Para una respuesta larga, esa espera puede ser de varios segundos de pantalla en blanco.
El streaming cambia el mecanismo de entrega, no el contenido: en lugar de una gran respuesta al final, Claude envía la misma respuesta como una secuencia de eventos pequeños y tipificados a través de una sola conexión abierta, a medida que se generan las palabras.
Comprender el transporte subyacente al streaming - Server-Sent Events - y la forma de los eventos que transporta es la base para todo lo demás en esta sección, desde imprimir tokens a medida que llegan hasta construir una interfaz de chat en vivo para transmitir llamadas a herramientas.
Resumen
- Idea Central: La API de streaming de Claude envía la respuesta como una secuencia ordenada de eventos SSE tipificados a través de una conexión HTTP, en lugar de un único blob JSON al final.
- Por Qué Importa: Permite que una aplicación comience a renderizar la salida en el momento en que existen los primeros tokens, en lugar de bloquear la interfaz de usuario hasta que se complete la generación.
- Conceptos Clave: Server-Sent Events, flujo de eventos,
message_start,content_block_delta,text_delta,message_delta,message_stop. - Cuándo Usarlo: Cualquier interfaz de usuario donde la latencia percibida sea importante: interfaces de chat, asistentes de codificación, generación de documentos en vivo y cualquier respuesta larga que el usuario esté observando activamente.
- Limitaciones / Compensaciones: El streaming añade complejidad en el lado del cliente (estado parcial, manejo de errores, reconexiones) y no es compatible con algunos patrones de caché o procesamiento por lotes que desean la respuesta completa como una unidad.
- Temas Relacionados: el modelo de solicitud/respuesta de la API de Mensajes, uso de herramientas, pensamiento extendido, el ayudante de streaming del SDK de Python.
Fundamentos
Server-Sent Events (SSE) es un estándar web de larga data para streaming unidireccional, de servidor a cliente, a través de HTTP plano.
Un cliente abre una solicitud HTTP normal, pero en lugar de que el servidor envíe una respuesta y cierre la conexión, el servidor mantiene la conexión abierta y escribe una secuencia de pequeños marcos de texto en ella con el tiempo.
Cada marco está prefijado con data: y separado por líneas en blanco; es deliberadamente simple, más simple que WebSockets, porque solo necesita fluir en una dirección: del servidor al cliente.
La API de Mensajes de Claude utiliza este mismo mecanismo cuando solicitas stream=true (o usas el ayudante de streaming del SDK).
En lugar de un cuerpo de respuesta HTTP que contenga el mensaje finalizado, la conexión permanece abierta y el servidor escribe una serie de eventos, cada uno un pequeño objeto JSON que describe una pieza incremental de la respuesta.
Una analogía útil: una respuesta sin streaming es como esperar a que se imprima una carta completa y luego te la entreguen.
Una respuesta en streaming es como ver a alguien escribir esa misma carta frente a ti, línea por línea, a través de una ventana que te dice exactamente qué tipo de cosa se acaba de escribir: un encabezado, una oración, una firma.
Los tipos de eventos son lo que hace que esto sea útil: cada uno nombra precisamente lo que cambió, por lo que el código del cliente nunca tiene que adivinar.
import anthropic
client = anthropic.Anthropic()
with client.messages.stream(
model="claude-sonnet-5",
max_tokens=1024,
messages=[{"role": "user", "content": "Explica SSE en un párrafo."}],
) as stream:
for event in stream:
print(event.type) # message_start, content_block_delta, ..., message_stopEse bucle es la forma que adopta cada consumidor de streaming: abrir un flujo, recibir una secuencia de eventos tipificados y reaccionar a cada tipo de manera diferente.
Mecánicas e Interacciones
La secuencia de eventos para una respuesta de texto típica sigue un esqueleto fijo:
message_start
content_block_start (índice 0, tipo: text)
content_block_delta (text_delta, "Hola")
content_block_delta (text_delta, " mundo")
content_block_delta (text_delta, "!")
content_block_stop (índice 0)
message_delta (stop_reason, totales de uso)
message_stop
message_start llega primero y transporta el shell de metadatos del mensaje: su id, model y uso inicial (casi cero), antes de que exista ningún contenido.
Entre eso y message_stop, la respuesta se construye a partir de uno o más bloques de contenido, cada uno abierto con content_block_start y cerrado con content_block_stop.
Una sola respuesta puede contener múltiples bloques de contenido en secuencia: por ejemplo, un bloque thinking seguido de un bloque text, o un bloque text seguido de un bloque tool_use; y cada uno se identifica por su index para que un cliente pueda rastrear el estado parcial de varios bloques de forma independiente.
La salida real token por token llega como eventos content_block_delta anidados dentro del par de inicio/fin de un bloque.
Para un bloque de texto, el delta delta.type es text_delta y su delta.text es el siguiente fragmento de texto: concatenar cada text_delta para un bloque, en orden, reconstruye el texto completo de ese bloque.
Sin embargo, content_block_delta es un sobre genérico: el delta.type dentro de él varía según el tipo de bloque que se esté transmitiendo: text_delta para prosa, input_json_delta para los argumentos de una llamada a herramienta y thinking_delta para el pensamiento extendido, cada uno transportando una carga útil parcial diferente que se ensambla de manera diferente.
message_delta llega una vez, cerca del final, y transporta campos a nivel de mensaje que no se conocían hasta que la generación finalizó: stop_reason (por qué se detuvo el modelo) y los totales de usage de tokens.
message_stop es el evento final y señala que el flujo está completo; no habrá más eventos para esta respuesta.
Un error de razonamiento común es esperar que un content_block_delta equivalga a un token, o que un text_delta sea un límite de palabra limpio.
Ninguno está garantizado: un delta puede ser una palabra parcial, varios tokens o incluso una división a mitad de secuencia de caracteres para texto multibyte. Por lo tanto, el código del cliente debe tratar los deltas como fragmentos opacos para concatenar, nunca como unidades para analizar individualmente.
Consideraciones Avanzadas y Aplicaciones
El modelo de eventos se escala a las capacidades más avanzadas de Claude sin cambiar su forma, que es lo que lo hace componible.
Una llamada a herramienta transmite su input como una cadena JSON construida incrementalmente a través de muchos eventos input_json_delta: la cadena parcial no es JSON válido hasta que el bloque se cierra, por lo que un cliente debe almacenar en búfer cada delta para ese bloque y solo intentar analizar una vez que content_block_stop se active para él. Consulte Manejo de JSON Parcial Durante Llamadas a Herramientas Transmitidas para el patrón de acumulación.
El pensamiento extendido, cuando está habilitado, transmite su razonamiento como un bloque de contenido separado usando eventos thinking_delta, antes del bloque text: una interfaz de usuario puede renderizar ese bloque de manera distinta (por ejemplo, un panel de "razonamiento" colapsado) en lugar de mezclarlo con el texto de la respuesta. Consulte Transmisión de Bloques de Pensamiento Extendido al Frontend.
Dado que las llamadas a herramientas se transmiten a través de los mismos tipos de eventos que el texto, un cliente puede observar un solo flujo para ambos: renderizar texto a medida que llega, y en el momento en que un content_block_start informa type: "tool_use", saber que es hora de acumular argumentos en lugar de mostrarlos; esta es la base para combinar el streaming con el bucle de uso de herramientas.
| Enfoque | Fortaleza | Debilidad | Mejor Ajuste |
|---|---|---|---|
| Solicitud no streaming | Código de cliente más simple; un análisis de un único objeto JSON | El usuario espera la respuesta completa antes de ver nada | Trabajos en segundo plano, generación por lotes, llamadas de servidor a servidor sin un espectador en vivo |
| Streaming SSE | Renderiza la salida a medida que se genera; menor latencia percibida | Requiere manejo de tipos de eventos, almacenamiento en búfer de estado parcial, lógica de reconexión | Interfaces de chat, asistentes de codificación, cualquier interfaz que un usuario esté observando en tiempo real |
| Streaming + uso de herramientas | Misma capacidad de respuesta, además de llamadas a herramientas a mitad de respuesta | Debe distinguir entre bloques de texto y bloques de tool_use, almacenar en búfer deltas JSON | Bucles de agente donde el usuario debe ver el razonamiento/texto antes de que se devuelva el resultado de una herramienta |
A nivel de transporte, SSE viaja sobre la misma conexión HTTP que cualquier otra llamada a la API, por lo que hereda los modos de fallo de red ordinarios: proxies que almacenan en búfer la salida, balanceadores de carga con tiempos de espera inactivos y conexiones que simplemente se caen a mitad de respuesta.
El código de producción debe tratar el flujo como no confiable: almacenar en búfer lo que ha llegado, detectar una caída y decidir si reanudar o reiniciar la solicitud. Consulte Mejores Prácticas de Streaming para los patrones concretos.
Conceptos Erróneos Comunes
- "Cada evento SSE es un token." En la práctica, un
text_deltapuede abarcar varios tokens o ser un fragmento de sub-token; nunca asumas una granularidad fija, siempre concatena. - "El flujo envía el mensaje completo y yo lo filtro en el lado del cliente." El servidor realiza la generación incremental; el cliente solo ve lo que se ha generado hasta el momento, no el mensaje completo de antemano.
- "
content_block_deltasiempre significa texto." Es un sobre genérico; sudelta.typeinterno determina si estext_delta,input_json_deltaothinking_delta. - "El streaming es una API diferente." Es la misma API de Mensajes y el mismo cuerpo de solicitud;
stream=true(o el ayudante de streaming del SDK) solo cambia la forma en que se entrega la respuesta. - "Una conexión caída a mitad de flujo todavía te da un mensaje parcial válido." Una caída puede ocurrir en cualquier límite de byte; el cliente debe manejar un bloque incompleto con gracia, no asumir que recibió un prefijo limpio.
Preguntas Frecuentes
¿Qué significa realmente "SSE" y es específico de Claude?
Server-Sent Events. Es un estándar web general (no una invención de Anthropic) para streaming unidireccional, de servidor a cliente, sobre HTTP, también utilizado por muchas otras APIs y frameworks web.
¿Necesito analizar manualmente el formato de transmisión SSE?
No. El administrador de contexto client.messages.stream(...) del SDK oficial anthropic para Python analiza los marcos SSE por ti y produce objetos de evento tipificados: trabajas con event.type y event.delta, no con líneas data: sin procesar.
¿Cuál es el primer evento que veré en cualquier flujo?
message_start. Transporta el shell del mensaje (id, modelo, rol) antes de que haya comenzado cualquier bloque de contenido.
¿Cómo sé cuándo está completa toda la respuesta?
El evento message_stop es siempre el último. Entre message_delta (que transporta stop_reason y el uso final) y message_stop, la respuesta está efectivamente completa.
¿Puede una sola respuesta transmitir más de un bloque de contenido?
Sí. Una respuesta puede contener múltiples bloques en secuencia: por ejemplo, un bloque thinking, luego un bloque text, luego un bloque tool_use; cada uno con su propio par content_block_start/content_block_stop e index.
¿Está garantizado que `text_delta` se divida en límites de palabras?
No. Trata cada delta como un fragmento de texto opaco y concaténalos en orden; nunca asumas que comienza o termina en un límite de espacio en blanco o palabra.
¿Cuál es la diferencia entre `content_block_delta` y `message_delta`?
content_block_delta transporta contenido incremental dentro de un bloque (texto, JSON de herramienta o pensamiento). message_delta transporta información a nivel de mensaje que solo se conoce una vez que la generación está finalizando, como stop_reason y el uso acumulado de tokens.
¿Cómo se ve una llamada a herramienta transmitida de manera diferente al texto transmitido?
El content_block_start del bloque informa type: "tool_use" en lugar de type: "text", y sus deltas llegan como input_json_delta (una cadena JSON en crecimiento) en lugar de text_delta.
¿Por qué no puedo analizar los argumentos de una llamada a herramienta delta por delta?
Cada input_json_delta es un fragmento de una cadena JSON en construcción; no es JSON válido por sí solo. Debes acumular cada fragmento para ese bloque y analizar la cadena unida solo después de que el bloque se cierre.
¿El streaming cambia lo que genera el modelo?
No. El cuerpo de la solicitud y la salida del modelo son los mismos de cualquier manera; el streaming solo cambia el mecanismo de entrega, no el contenido generado.
¿Qué sucede si la conexión se interrumpe a mitad de flujo?
Recibes todos los eventos que llegaron antes de la interrupción y ningún message_stop. Los clientes de producción deben detectar esto (por ejemplo, un tiempo de espera sin nuevos eventos) y decidir si reintentan la solicitud, ya que SSE en sí mismo no garantiza la entrega de los eventos restantes.
¿Se requiere streaming para que funcionen el uso de herramientas o el pensamiento extendido?
No, ambos funcionan en solicitudes no streaming también. El streaming es puramente una elección de mecanismo de entrega que, además, expone el contenido de herramientas y pensamiento de forma incremental cuando está habilitado.
Relacionados
- Fundamentos de Respuestas en Streaming - el primer ejemplo ejecutable de consumo de este flujo de eventos.
- Referencia de Tipos de Eventos de Streaming - una tabla de consulta para cada tipo de evento y sus campos.
- Manejo de JSON Parcial Durante Llamadas a Herramientas Transmitidas - acumulación segura de
input_json_delta. - Transmisión de Bloques de Pensamiento Extendido al Frontend - manejo de
thinking_delta. - Streaming de Respuestas con el SDK de Python - el ayudante de streaming del SDK con más detalle.
Versiones de Stack: Escrito contra la línea de modelos de Claude actual a partir de ~junio de 2026 - Claude Fable 5, Claude Opus 4.8, Claude Sonnet 5 (el predeterminado) y Claude Haiku 4.5 - y el SDK oficial
anthropicpara Python (última versión 0.x). Los nombres de modelos, versiones de SDK y precios cambian rápidamente; verifica los detalles actuales en platform.claude.com/docs antes de confiar en ellos.