La latencia también es parte de la inteligencia

La calidad de un sistema de IA no depende únicamente del modelo. Analizamos cómo TTFT, streaming, Voice AI, tools, percentiles y arquitectura determinan la latencia y la experiencia percibida.

La latencia también es parte de la inteligencia

Un sistema de IA puede utilizar un modelo excelente y aun así sentirse torpe. No necesariamente porque responda mal, sino porque tarda demasiado en empezar a responder.

Imagina una conversación por voz. El usuario termina de hablar. Pasan 500 milisegundos. Después un segundo. Luego dos. El sistema finalmente empieza a hablar, pero para entonces la interacción ya se siente rota. La calidad de la respuesta todavía importa, pero el usuario ya formó una hipótesis: el sistema no entendió, está bloqueado o no sabe qué hacer.

La tesis de este artículo es que la inteligencia percibida depende de dos variables relacionadas:

  • qué tan buena es la respuesta;
  • cuándo comienza a aparecer.

La latencia no es únicamente una métrica de infraestructura. Es una propiedad del producto: afecta la confianza, el ritmo de la conversación, la percepción de competencia y la disposición del usuario a continuar.

1. Un modelo inteligente que se siente lento

En una interfaz de texto, el usuario puede tolerar cierta espera porque la acción de enviar es explícita y el texto puede aparecer progresivamente. En voz, la situación cambia. El usuario no sólo espera una respuesta: espera señales de que el sistema sigue escuchando, entendiendo y avanzando.

Por eso conviene separar latencia real de latencia percibida. La primera es el tiempo medido entre eventos del sistema. La segunda es el tiempo durante el cual el usuario siente que no está ocurriendo nada útil.

Una respuesta final correcta después de tres segundos puede ser peor experiencia que una respuesta ligeramente menos sofisticada que empieza a comunicar intención a los 400 milisegundos. No es una regla universal: en una operación crítica, la corrección puede justificar más espera. Pero la decisión debe ser consciente y visible en el diseño.

2. Latencia no es una sola métrica

Una request de IA suele atravesar varias fronteras. Una descomposición útil es:

\[T_{e2e}=T_{red,in}+T_{entrada}+T_{contexto}+T_{modelo}+T_{tools}+T_{salida}+T_{red,out}\]

Los términos no siempre aparecen en todas las requests y algunos se solapan. En una respuesta sin herramientas, Ttools puede ser cero. En una sesión de voz, Tentrada incluye VAD, detección de endpoint y STT; Tsalida incluye TTS y reproducción.

ComponenteQué representaDecisiones típicas
RedTransporte entre cliente, gateway y proveedores.Región, conexión persistente, protocolo, tamaño de mensajes.
EntradaConversión de audio, texto o eventos en una solicitud utilizable.Streaming, VAD, endpointing, buffering.
ModeloPreparación de contexto, TTFT y generación.Modelo, longitud de contexto, batching, decoding.
ToolsConsultas y efectos externos.Paralelismo, caché, timeouts, prefetch.
SalidaSerialización, TTS, reproducción o render.Streaming, chunking, buffering, cancelación.

Ésta es la razón por la que cambiar de modelo no siempre cambia la experiencia. Si una API externa tarda tres segundos y el modelo tarda 400 milisegundos, reducir la inferencia a 250 milisegundos apenas modifica el tiempo total.

3. TTFT importa más de lo que parece

Time To First Token (TTFT) es el tiempo desde que el sistema acepta una solicitud hasta que puede entregar el primer token de la respuesta. No debe confundirse con el tiempo total de generación.

Si la respuesta tiene N tokens y el sistema genera a una tasa media de r tokens por segundo, una aproximación simple es:

\[T_{generación}\approx\frac{N}{r}\]

Por tanto:

\[T_{respuesta}\approx TTFT+\frac{N}{r}\]

Es una aproximación, no un contrato de rendimiento. La tasa puede variar por posición en la secuencia, hardware, concurrencia, tamaño del contexto y proveedor. Aun así, ayuda a separar dos decisiones.

SistemaTTFTGeneraciónTotalPercepción probable
A350 ms2.4 s2.75 sEmpieza pronto; puede sentirse fluido con streaming.
B1.4 s1.8 s3.2 sGenera más rápido, pero deja un silencio inicial mayor.

Los valores son un ejemplo hipotético, no un benchmark. El sistema B puede ser preferible para una respuesta larga si su streaming es mucho mejor, pero para una interacción breve el sistema A probablemente comunica competencia antes. TTFT es una métrica de producto porque define cuándo empieza la conversación de regreso.

4. Streaming cambia la experiencia

En un pipeline no streaming, cada etapa espera a la anterior:

flowchart LR
  A[Entrada completa] --> B[STT completo]
  B --> C[LLM completo]
  C --> D[TTS completo]
  D --> E[Reproducción]

En un pipeline con streaming, las etapas pueden solaparse. El STT entrega hipótesis parciales; el runtime decide cuándo existe suficiente señal; el modelo produce tokens; el TTS convierte fragmentos en audio; el cliente reproduce antes de recibir la respuesta completa.

sequenceDiagram
  participant U as Usuario
  participant S as STT streaming
  participant R as Agent Runtime
  participant M as LLM streaming
  participant T as TTS streaming
  U->>S: Audio en chunks
  S-->>R: Texto parcial
  R->>M: Solicitud cuando hay suficiente señal
  M-->>T: Tokens o frases parciales
  T-->>U: Audio reproducible
  M-->>R: Respuesta final

El objetivo no es hacer streaming por moda. Es mover el primer evento útil hacia la izquierda. El sistema puede reducir el tiempo hasta la primera palabra audible aunque el tiempo hasta la respuesta completa sea parecido.

5. La matemática de una respuesta de voz

Consideremos un ejemplo hipotético de una interacción de voz:

  • red y buffering de entrada: 120 ms;
  • VAD y endpointing: 280 ms;
  • STT hasta texto utilizable: 420 ms;
  • TTFT del modelo: 360 ms;
  • primer chunk de TTS: 180 ms;
  • red y reproducción: 100 ms.

Si las etapas son secuenciales, el tiempo hasta la primera respuesta audible es:

\[T_{audio,1}=120+280+420+360+180+100=1{,}460\text{ ms}\]

Ahora supongamos que el STT comienza a entregar texto parcial, el endpointing se ajusta a una ventana menor y el TTS comienza con la primera frase en cuanto existe un fragmento estable. Un nuevo escenario hipotético podría ser:

  • entrada y VAD hasta señal suficiente: 220 ms;
  • STT parcial utilizable: 180 ms;
  • TTFT: 300 ms;
  • TTS del primer fragmento: 140 ms;
  • transporte y reproducción: 100 ms.

\[T_{audio,1}'=220+180+300+140+100=940\text{ ms}\]

La mejora no provino exclusivamente de un modelo más rápido. Provino de cambiar el pipeline: menos espera de entrada, solapamiento y salida incremental. El resultado final todavía puede tardar más; la primera señal audible llega antes.

6. El promedio miente

Una media resume el centro de una distribución, pero no describe bien la experiencia de los usuarios que caen en la cola. En sistemas interactivos conviene observar al menos:

  • p50: la mediana; la mitad de las requests es más rápida y la otra mitad más lenta;
  • p95: 95% de las requests es igual o más rápido; 5% tarda más;
  • p99: 99% es igual o más rápido; 1% queda en la cola extrema.

Un sistema puede tener media de 700 ms y p99 de 6 segundos. Para una demo controlada parece rápido. Para un producto con miles de interacciones, ese uno por ciento puede representar suficientes conversaciones rotas para afectar la confianza.

MétricaPregunta que respondeUso
Media¿Cuál es el tiempo promedio?Capacidad y tendencia general, con cautela.
p50¿Cómo vive la interacción típica?Experiencia central.
p95¿Qué ocurre en una cola frecuente?SLO de experiencia y debugging.
p99¿Qué tan grave es la cola extrema?Fallos raros, saturación y dependencias externas.

La cola suele crecer por concurrencia, retries, pools agotados, cold starts, límites de proveedores o herramientas lentas. Por eso medir sólo request_duration oculta la causa.

7. Python: simulación de latencia

El siguiente experimento utiliza datos sintéticos. No representa un benchmark de producción. Su objetivo es mostrar cómo una cola pequeña cambia los percentiles de una request compuesta.

from statistics import mean, median
import random

random.seed(7)
N = 10_000

def percentile(values, q):
    ordered = sorted(values)
    index = int((len(ordered) - 1) * q)
    return ordered[index]

def sample_component(base_ms, jitter_ms, slow_probability=0.0, slow_multiplier=1.0):
    value = max(0.0, random.gauss(base_ms, jitter_ms))
    if random.random() < slow_probability:
        value *= slow_multiplier
    return value

requests = []
for _ in range(N):
    network = sample_component(80, 20, 0.01, 5)
    stt = sample_component(350, 70, 0.005, 3)
    model = sample_component(420, 90, 0.01, 4)
    tools = sample_component(180, 60, 0.02, 8)
    tts = sample_component(220, 50, 0.01, 4)
    total = network + stt + model + tools + tts
    requests.append(total)

print(f"media:   {mean(requests):.0f} ms")
print(f"mediana: {median(requests):.0f} ms")
for label, q in [("p90", .90), ("p95", .95), ("p99", .99)]:
    print(f"{label}:     {percentile(requests, q):.0f} ms")

Las probabilidades y multiplicadores también son sintéticos. Lo importante es observar la forma del resultado: un número reducido de ejecuciones lentas puede mover mucho más el p99 que la media. En producción, la distribución debe obtenerse de telemetría real, separada por modelo, región, tipo de tarea, tenant y tamaño de solicitud.

8. Python: ¿dónde conviene optimizar?

Optimizar el componente con mayor duración no siempre produce la mejor mejora perceptual. Este segundo ejemplo aplica una reducción hipotética del 30% a cada componente por separado.

from statistics import median
import random

random.seed(11)
N = 5_000
components = {"network": 120, "inference": 650, "tools": 900, "tts": 300}

def make_requests(factor=None):
    values = []
    for _ in range(N):
        total = 0
        for name, base in components.items():
            # Datos sintéticos: jitter y una cola lenta pequeña.
            jitter = base * 0.15
            value = max(1, random.gauss(base, jitter))
            if random.random() < 0.03:
                value *= 2.5
            if name == factor:
                value *= 0.70
            total += value
        values.append(total)
    return values

def p(values, q):
    return sorted(values)[int((len(values) - 1) * q)]

for target in [None, "network", "inference", "tools", "tts"]:
    values = make_requests(target)
    label = target or "baseline"
    print(label, {
        "p50": round(p(values, .50)),
        "p95": round(p(values, .95)),
        "p99": round(p(values, .99)),
    })

El resultado exacto cambia por la semilla y por la distribución, pero la lectura arquitectónica es estable: una mejora del 30% en una etapa que no está en el camino dominante puede tener poco impacto. También puede ocurrir que reducir la media de una etapa no mejore su cola si el problema real es la variabilidad.

9. Voice AI: VAD, endpointing y barge-in

En texto, el usuario indica el final de su turno al presionar enviar o al detener la captura. En voz, el sistema debe inferirlo. Voice Activity Detection identifica presencia de voz; endpointing decide cuándo probablemente terminó el turno. No son exactamente la misma operación.

Esperar más reduce el riesgo de cortar una frase, pero aumenta la latencia. Esperar menos hace la conversación más ágil, pero puede producir respuestas prematuras. El objetivo no es minimizar endpointing de forma aislada; es elegir un punto adecuado para el producto, el idioma, el ruido y el patrón de conversación.

Una aproximación conceptual es:

\[T_{turn}=T_{VAD}+T_{endpoint}+T_{STT}+T_{TTFT}+T_{TTFA}\]

Donde TTFA es el tiempo hasta el primer audio reproducible. Cada término necesita su propio p50 y p95. Un único número de latencia de voz no permite saber si el problema está en silencio final, reconocimiento, inferencia o síntesis.

La arquitectura debe aceptar que el usuario puede hablar mientras el sistema produce audio. Eso es barge-in:

stateDiagram-v2
  [*] --> Listening
  Listening --> Thinking: endpoint detectado
  Thinking --> Speaking: primer audio listo
  Speaking --> Listening: respuesta terminada
  Speaking --> Interrupted: nueva voz detectada
  Interrupted --> Listening: cancelar TTS y limpiar buffer
  Interrupted --> Thinking: conservar contexto y procesar nuevo turno

Una interrupción requiere concurrencia real: cancelar o detener TTS, decidir qué hacer con la generación en vuelo, invalidar chunks pendientes, actualizar el estado de la conversación y evitar que audio viejo llegue después de la nueva respuesta. El estado no puede depender sólo del prompt; necesita identificadores de turno, versiones y cancelación propagada.

10. Las tools pueden dominar la latencia

Si el LLM responde en 400 ms pero una API tarda 3 segundos, el cuello de botella no es el modelo. Reducir la inferencia a 300 ms ahorra 100 ms en un camino de 3.4 segundos.

Las optimizaciones útiles dependen de las dependencias:

  • ejecución paralela: sólo para operaciones independientes;
  • prefetch: iniciar una consulta cuando existe suficiente evidencia de que será necesaria;
  • caché: reutilizar datos válidos con una política de frescura clara;
  • timeouts: evitar que una dependencia bloquee indefinidamente;
  • respuestas progresivas: comunicar estado sin inventar resultados;
  • fallbacks: degradar capacidad de forma explícita.

Para tareas independientes, el tiempo del camino crítico se aproxima a:

\[T_{paralelo}\approx T_{setup}+\max(T_1,T_2,\ldots,T_n)+T_{merge}\]

En cambio, si existen dependencias, la latencia sigue la suma del camino:

\[T_{secuencial}=T_1+T_2+\cdots+T_n\]

El paralelismo incorrecto puede producir datos inconsistentes, efectos duplicados o decisiones basadas en información incompleta. La optimización debe partir del grafo de dependencias, no de aplicar concurrencia indiscriminadamente.

11. Model routing

El modelo más capaz no tiene que resolver todas las tareas. Clasificación, extracción estructurada, respuesta conversacional, razonamiento complejo y visión pueden tener requisitos diferentes.

flowchart TD
  A[Tarea] --> B[Model Router]
  B --> C[Modelo rápido]
  B --> D[Modelo de razonamiento]
  B --> E[Modelo especializado]
  C --> F[Respuesta o acción]
  D --> F
  E --> F

El router puede considerar calidad esperada, costo, latencia, tamaño de contexto, disponibilidad y riesgo. No existe un triángulo con trade-offs estrictos en cada caso: una optimización de infraestructura puede mejorar costo y latencia a la vez. Pero en muchos productos sí hay que elegir un punto operativo entre calidad, costo y latencia.

12. Latencia percibida vs. latencia real

Una operación puede tardar cinco segundos y aun así comunicar progreso:

  • 100 ms: confirmación de que la solicitud fue recibida;
  • 500 ms: estado o resultado parcial;
  • 1 s: comienza el streaming;
  • 5 s: llega el resultado final.

La duración total no cambió, pero el tiempo sin información sí. Esto no justifica enviar texto vacío, prometer progreso falso o hablar antes de entender. Sí justifica diseñar eventos de acknowledgement, estados explícitos, streaming y feedback intermedio.

Un modelo técnico simplificado puede comparar sólo métricas observables:

scenarios = {
    "respuesta_completa": {
        "time_to_first_feedback_ms": 5_000,
        "time_to_final_ms": 5_000,
    },
    "ack_y_streaming": {
        "time_to_first_feedback_ms": 100,
        "time_to_final_ms": 5_000,
    },
}

for name, metrics in scenarios.items():
    print(name)
    print(f"  primer feedback: {metrics['time_to_first_feedback_ms']} ms")
    print(f"  resultado final: {metrics['time_to_final_ms']} ms")

Esto no calcula satisfacción humana. Sólo demuestra que time-to-first-feedback y time-to-final son métricas distintas, y que ambas pueden ser necesarias para evaluar un producto interactivo.

13. Arquitectura de referencia

Una arquitectura de Voice AI necesita separar transporte, reconocimiento, runtime, modelos, herramientas, síntesis y control de interrupciones:

flowchart TD
  C[Cliente de voz] --> V[VAD y audio streaming]
  V --> S[Streaming STT]
  S --> R[Agent Runtime]
  R --> CTX[Context Builder]
  R --> MEM[Memory / State]
  R --> MR[Model Router]
  R --> TOOLS[Tools y APIs]
  MR --> T[Streaming TTS]
  T --> C
  C -. barge-in / cancel .-> R
  R -. cancelación .-> T
  R --> O[Observability]
  V --> O
  S --> O
  T --> O
  TOOLS --> O

En sistemas persistentes como Nexa, esta separación también permite que voz, texto y otras superficies compartan estado y observabilidad sin convertirse en asistentes independientes. La interfaz cambia; el runtime y la continuidad pueden permanecer.

14. Observabilidad de latencia

Instrumentar sólo la duración total produce un número difícil de accionar. Conviene modelar un trace con spans como:

request
├── network.in
├── vad
├── stt.first_partial
├── stt.endpoint
├── context.build
├── llm.ttft
├── llm.generation
├── tool.*
├── tts.first_audio
├── network.out
└── playback.start

Cada span debería asociarse con un correlation ID, session ID, turn ID, modelo, región, proveedor, tamaño de entrada, estado de caché y resultado. Para voz, registrar también si hubo barge-in y qué audio fue cancelado.

Una estructura conceptual podría ser:

{
  "trace_id": "tr_123",
  "turn_id": "turn_42",
  "timings_ms": {
    "vad": 180,
    "stt_to_endpoint": 390,
    "llm_ttft": 320,
    "tool": 0,
    "tts_ttfa": 160,
    "playback_start": 90
  },
  "outcome": "completed",
  "interrupted": false
}

Los percentiles deben calcularse por etapa y por camino: respuestas con tools, sin tools, con caché, por región, por modelo y por tipo de cliente. Un p95 global puede ocultar que una región específica tiene un problema.

15. Presupuesto de latencia

Un producto puede comenzar con un objetivo, por ejemplo “iniciar respuesta audible dentro de un límite definido para este tipo de interacción”. No existe un número universal: depende de la tarea, el canal, el idioma y el nivel de riesgo.

El presupuesto obliga a convertir esa intención en límites:

\[B_{total}\geq B_{red}+B_{VAD}+B_{STT}+B_{TTFT}+B_{TTFA}+B_{reproducción}\]

Si el presupuesto no alcanza, hay que decidir: reducir contexto, cambiar modelo, evitar una tool en el camino crítico, mover infraestructura, aceptar una respuesta parcial o cambiar la UX. El presupuesto no es una promesa de que cada request cumplirá el p99; es una herramienta para negociar decisiones entre producto, infraestructura y confiabilidad.

16. Reliability vs. latency

Hacer el sistema más rápido no justifica sacrificar corrección, seguridad o consistencia sin evaluar el riesgo. Un timeout demasiado agresivo puede convertir una respuesta lenta pero correcta en una acción incompleta. Un modelo pequeño puede reducir latencia y costo, pero fallar en una tarea que requería razonamiento. Un fallback puede mantener disponibilidad y degradar calidad.

La decisión debe expresarse por clase de tarea. Un saludo y una clasificación toleran una estrategia; una transferencia de dinero o una modificación irreversible necesitan otra. La latencia es parte de la calidad del sistema, no un sustituto de ella.

Mi perspectiva actual

Un sistema inteligente no se experimenta como una puntuación en un benchmark. Se experimenta como una secuencia de interacciones: cuándo escucha, cuándo detecta que terminaste, cuándo entiende, cuándo responde, cuándo muestra progreso, cuándo habla y cómo reacciona si lo interrumpes.

La capacidad del modelo establece un techo importante. Pero la arquitectura determina cuánto de esa capacidad llega realmente al usuario. Una respuesta excelente que aparece demasiado tarde puede parecer menos inteligente que una respuesta ligeramente más simple que llega en el momento correcto.

Por eso la latencia debe diseñarse como una propiedad transversal: medirse por etapas, presupuestarse por camino crítico, optimizarse con streaming y paralelismo cuando sea seguro, y traducirse en una experiencia que comunique progreso sin ocultar incertidumbre.

La inteligencia percibida no empieza cuando termina la generación. Empieza cuando el sistema demuestra que escuchó y que ya está reaccionando.

Referencias

  1. Amos Lo, Latency optimization — OpenAI Platform Documentation. Principios de streaming, generación y reducción de trabajo innecesario.
  2. Yaniv Leviathan, Matan Kalman y Yossi Matias, Fast Inference from Transformers via Speculative Decoding, 2023. Decodificación especulativa para acelerar generación manteniendo la distribución objetivo.
  3. Jack Cook et al., LLM in a Flash, 2023. Trade-offs de memoria y rendimiento en inferencia.
  4. VLLM, Production Metrics. Métricas operativas de inferencia, incluyendo TTFT y tiempos por token.
  5. Google Cloud, Streaming recognition — Speech-to-Text Documentation. Reconocimiento de audio mediante streams y resultados parciales.
  6. Google Cloud, Voice activity events — Speech-to-Text Documentation. Eventos relacionados con actividad de voz y detección de límites.
  7. Amazon, Timeouts, retries, and backoff with jitter. Efectos de timeouts, reintentos y carga en sistemas distribuidos.
  8. Jeffrey Dean y Luiz André Barroso, The Tail at Scale, Communications of the ACM, 2013. Latencia de cola y su impacto en servicios distribuidos.
  9. Martin Kleppmann, Designing Data-Intensive Applications. Modelos de concurrencia, sistemas distribuidos y costos de coordinación.
  10. W3C, WebRTC 1.0. Fundamentos de comunicación de audio y medios en tiempo real.

Nota editorial: todos los valores numéricos marcados como ejemplos o usados en las simulaciones son sintéticos. No representan benchmarks de producción.

Subscribe to Abraham Huerta

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe