LiveT — Presentación del proyecto

Transcripción y traducción en vivo para conferencias, con modelos abiertos alojados por nosotros.

  • Licencia: MIT (uso libre, también comercial)
  • Estado: beta en desarrollo activo
  • Stack: Python, React, Redis, Docker · modelos open source (faster-whisper, CTranslate2)

1. Qué es

LiveT escucha una charla, la transcribe en su idioma original, la traduce y publica subtítulos en vivo en una web. El público no necesita instalar nada ni crear una cuenta: abre el enlace y los subtítulos aparecen.

Está pensado para un caso concreto: un evento con varias salas en paralelo, donde la interpretación presencial es cara y la traducción automática por servicios en la nube sale cara por minuto.

El recorrido de una sesión es sencillo de explicar:

  1. Alguien publica la fuente de audio: una URL (YouTube, un directo), laucha de un micrófono desde el navegador, o una señal RTMP desde OBS.
  2. El sistema recorta el audio y lo va soltando en trozos de 100 milisegundos.
  3. Un motor de transcripción reconoce el voz en el idioma original y va publicando fragmentos provisionalmente, que luego corrige cuando tiene más contexto.
  4. Otro motor traduce esos fragmentos al español y los deja listos para mostrar.
  5. La web los muestra en vivo, con reconexión automática si algo se cae.

La diferencia importante: todo el reconocimiento y la traducción ocurren en nuestra propia infraestructura, con modelos de código abierto. No hay llamadas a APIs de terceros, no se paga por minuto de audio y el contenido de la conferencia no sale de our servidor.

El problema que resuelve

Opción habitual Problema
Intérprete presencial Cuesta por sesión y por hora; hay que contratarlo con antelación.
Subtítulos generados en el evento Cuestan por minuto de audio y escalan con el número de salas.
Grabar y subtitular después El contenido existe, pero para el público en directo no sirve.
LiveT Una instalación, N salas, coste marginal cercano a cero, sin enviar el audio fuera.

2. Qué me inspiró

Todo empezó de una observación bastante simple: en cualquier conferencia con dos o más salas en paralelo, la traducción es lo primero que se cae. El contenido estátranslated al español, el ponente habla inglés, y las personas que no inglés se quedan fuera durante media hora hasta que alguien les resume de palabra.

Y las soluciones que existen para eso fallan siempre por el mismo lado: o cuestan demasiado para un evento pequeño, o dependen de un proveedor externo cuyo precio por minuto se dispara justo cuando el proyecto funciona, o exigen enviar la charla —a vecesistersa, a veces sin publicarse— a un servidor de terceros.

De ahí salieron los tres principios con los que construí el proyecto, y que condicionan todas las decisiones técnicas:

  • Autonomía. Si el servicio de terceros sube el precio o desaparece, el evento se queda sin subtítulos. Los modelos de reconocimiento y traducción actuales son lo bastante buenos como para poder alojarlos uno mismo.
  • Cero fricción para el público. Nada de apps, ni registros, ni códigos QR con expiración. Si la persona tiene un navegador y un enlace, tiene subtítulos.
  • Escalar por salas, no por seats. El coste relevante es el número de sesiones en directo simultáneas, no el número de espectadores. Ahí es donde un diseño con CPU y una GPU bien usada gana al modelo de pagar por usuario.

También me swayed por un criterio más egoísta: quería un problema real donde se ven las cinco dificultades clásicas de un sistema en producción a la vez — concurrencia, estado distribuido, latencia, carga de modelos pesados y degradación elegante — sin tener que inventar un escenario de mentira.


3. Cómo lo construí

Primero, la especificación; después, el código

El error más caro que he cometido en proyectos así es empezar a escribir código y documentar después. En LiveT lo invertí: nada se implementa sin una especificación escrita y aceptada antes.

El trabajo se organiza en 12 especificaciones numeradas, cada una con su documento de requisitos, su lista de tareas y su informe de revisión. Cada requisito tiene un criterio de aceptación redactado como Dado / Cuando / Entonces, y cada criterio está cubierto por al menos un test automático. Si un requisito no se puede probar, es que todavía no está bien entendido.

El ciclo de cada cambio es siempre el mismo:

idea ─▶ especificación ─▶ implementación ─▶ revisión ─▶ aprobado
            ▲                   ▲              │
            └───── falta algo ──┴──── bug ─────┘

Este proceso se apoyó en tres agentes con responsabilidades separadas —uno redacta la especificación, otro implementa, otro revisa— de forma que la revisión es siempre de alguien que no escribió el código. Sin esa separación, la revisión se convierte en una formalidad.

La arquitectura, en una frase

Una cadena de piezas independientes que se pasan el audio y el texto por un bus de mensajes, en lugar de un programa grande que lo hace todo.

  fuentes                    LiveT
  ───────                    ─────
  YouTube / web ──┐
  navegador ──────┼──▶ ingesta ──▶ audio ──▶ transcripción ──▶ texto ──▶ traducción ──▶ subtítulos
  RTMP (OBS) ─────┘
                                          ────────────────────────────────────────────▶  web (público)

Cada pieza —ingesta, transcripción, traducción— es un proceso independiente que puede reiniciarse, escalarse o fallar sin tumbar el resto. Si el traductor se cae, la transcripción sigue saliendo; si una sala se cae, las otras no se enteran.

No hay base de datos aparte: el almacenamiento de estado hace a la vez de cola y de bus de eventos, lo que evita un componente más que mantener y un punto más de fallo.

Las decisiones de diseño que más pesan

Un modelo por proceso, no por sesión. Los modelos de reconocimiento pesan cientos de megabytes y cargan en varios segundos. En lugar de cargarlos para cada charla, cada proceso los carga una vez y los comparte entre hasta cinco sesiones simultáneas. Es la diferencia entre "una sala por máquina" y "cinco salas por máquina".

Nadie escribe donde no le toca. Cada pieza pide un lease (reservado) con caducidad sobre la sesión que atiende, y todas las escrituras se validan con scripts atómicos del propio motor de mensajes. Si un proceso muere, otro lo recoge sin necesidad de coordinarse, y es estructuralmente imposible que dos procesos escriban en la misma sesión. Esto se resolvió después de que apareciera el primer caso raro de duplicados, y desde entonces no ha vuelto a producirse.

Reconocer rápido, corregir después. El motor de transcripción no espera a tener la frase completa: publica hipótesis provisionales y solo las da por definitivas cuando el sistema lleva varios fragmentos coincidiendo. Así se reduce la latencia a menos de un par de segundos sin perder precisión. Se descarta el audio en los silencios antes de procesarlo, lo que ahorra una parte importante del trabajo.

El idioma se detecta solo. Se puede declarar el idioma de la sesión o dejarlo en automático; en ese caso, cada fragmento se reconoce con el idioma que el sistema detecta, y la traducción usa ese mismo idioma como origen. Es un detalle pequeño que evita el fallo más común de estos sistemas: estar traduciendo desde el idioma equivocado porque se configuring mal a mano.


4. Los desafíos que enfrenté

4.1 Cargar modelos pesados sin que se repitan

El primer prototipo cargaba el modelo de transcripción por cada sesión. Con cinco salas en paralelo, el mismo modelo estaba cargado cinco veces en memoria, y el arranque tardaba más que la propia charla. La solución —un modelo por proceso compartido entre sesiones— es conceptualmente simple, pero obliga a redesignar el worker entero: descubrimiento de sesiones, reparto de capacidad, y sobre todo garantizar que el trabajo de una sesión en curso no se mezcle con el de otra cuando comparten el mismo modelo. Esa garantía es la razón de existir del sistema de leases.

4.2 Que nadie se quede sin subtítulos por un fallo puntual

La regla que acabé aplicando en todo el sistema es: un fallo de una pieza no puede tumbar el trabajo ya hecho ni impedir que las demás sigan. Si el modelo de traducción falla dos veces con un fragmento, se publica el texto original sin traducir y se continúa con el siguiente. Si una página pierde la conexión, el espectador ve el último subtítulo y se reconecta solo cuando vuelve. Si el procesoMuere a mitad de una frase, otro lo releva y retoma desde el último punto confirmado en lugar de empezar de cero.

Es una decisión de diseño, no un parche: el sistema se organiza para que el camino de degradación sea siempre "menos bonito", nunca "en blanco".

4.3 La latencia es una contrato, no una aspiración

En directo, dos segundos de retraso se notan; cinco, hacen que el público deje de mirar los subtítulos. Fijé el objetivo antes de escribir el código —subtítulos originales en menos de 2 segundos, traducción en menos de 4, en el percentil 95— y lo convertí en un criterio de aceptación, no en una promesa. Para poder medirlo hizo falta sincronizar el reloj de cada pieza con una referencia común: si cada parte del sistema mide el tiempo por su cuenta, las cifras no son comparables y acabas optimizando el lugar equivocado. Escribí además una herramienta específica para medir la deriva de reloj entre piezas, que fue la que destapó el problema.

4.4 El Transcription se adelantaba y luego se corregía: la UX

La primera versión de la transcripción en vivo era técnicamente correcta y prácticamente inutilizable. Publicaba cada tres palabras y luego reescribía el subtítulo cuatro veces seguidas, lo que se leía como un texto inestable. El trabajo fino no estaba en el modelo, sino en la política de publicación: decidir en qué momento un fragmento se considera suficientemente estable como para mostrarlo. Eso hizo que la calidad percibidaiese saltara, mucho más que cualquier mejora en el modelo.

4.5 Dependencias que se pisan entre sí

Al añadir la traducción apareció un conflicto real: la librería de modelos de lenguaje exige versiones antiguas de dos dependencias que la librería de transcripción ya usaba en versiones recientes. Bajar las versiones "funcionaba" con la transcripción, pero nadie lo había comprobado de verdad. La solución fue verificarlo en un entorno limpio, confirmando que las funciones que el proyecto usa siguen existiendo, y dejar constancia de la comprobación en el repositorio para que no se repita. Aprendí que una comprobación puntual de compatibilidad es más barata que un fallo tres meses después, cuando ya nadie recuerda por qué se fijó esa versión.

4.6 La conversión de los modelos de traducción

El último obstáculo de la parte de traducción fue menos visible: al preparar el modelo por primera vez, la conversión fallaba por una dependencia que la librería da por presupuesta y que no estaba en la imagen (la instalación de的是 la librería de aprendizaje automático completa, que habría multiplicado por varios el tamaño de la imagen). Se resolvió separando el paso de preparación del modelo del arranque del servicio, y dejando un mensaje de error que explica exactamente qué hacer en lugar de un fallo genérico.

4.7 La distancia entre "funciona" y "está terminado"

Este es el desafío menos técnico y el que más trabajo de disciplina ha costado. Una funcionalidad puede estar implementada y probada y, aun así, el proyecto no está listo para enseñarse: faltan la revisión formal, marcar el trabajo como completado, actualizar la documentación que ya ha quedado desfasada y comprobar que lo que se promete coincide con lo que hay. He preferido documentar el estado real —incluido lo que está a medio camino— antes que presentar una versión más诱人 de lo que existe. Un README que promete más de lo que entrega cuesta más credibilidad que la que gana.


5. Qué he aprendido

El diseño de un sistema concurrente se demuestra en sus fallos, no en sus diagramas. Los problemas importantes —escrituras duplicadas, procesos que se quedan con la sesión que ya no tiene, transcripciones que se pierden al reiniciar— solo aparecen cuando algo va mal. Montar las pruebas de esos casos primero, en lugar de después, es lo que hizo el sistema fiable.

Optimizar significa medir antes, y medir exige poner de acuerdo a todos. Elegir la métrica equivocada, o medir cada pieza con su propio reloj, produce meses de esfuerzo en el sitio equivocado. La mitad del trabajo en中外 was instrumentation, no optimización.

Los modelos de lenguaje cambian de versión más rápido que los productos. Elegir dependencias que se muevan rápido trae innovations útiles y, a la vez, hay que asumir que habrá que volver a verificar la compatibilidad. Es un coste fijo que conviene asumir desde el principio en lugar de descubrirlo tarde.

La parte más difícil de un proyecto open source no es el código, es la promesa. Elegir un alcance que se pueda terminar, documentar el estado real y mantener el ritmo de publicación es lo que separa un proyecto que la gente usa de uno que se abandona a mitad. Aplicar el mismo criterio a mis propios números de progreso me ha servido más que cualquier curso.

La simplicidad es una decisión activa. Quitar un componente —una base de datos, un componente de orquestación— y asumir la responsabilidad de lo que queda obliga a entender mejor el sistema. Entenderlo todo valía más que delegar en herramientas que todavía no entendía.


6. Estado actual

Beta en desarrollo. Funcionalidad implementada y cubierta por pruebas automatizadas, en períodos de integración y cierre pendientes de,完成 formal.

Lo que funciona hoy:

Área Estado
Sesiones (alta, consulta, parada) desde una API Funciona
Ingesta de audio desde URL (YouTube y directos) Funciona
Ingesta desde el navegador (micrófono) Funciona, en cierre
Ingesta por RTMP desde OBS Funciona, en cierre
Transcripción en directo con múltiples sesiones Funciona
Traducción en directo Funciona, en cierre
Web de público: lista de sesiones, selector de idioma, subtítulos en vivo Funciona, en cierre
Reconexión automática del público Funciona

En cifras del estado actual del árbol de trabajo:

  • 978 pruebas automáticas en verde sobre unos 44 ficheros de test.
  • 5 sesiones simultáneas como objetivo de diseño, validado con fuentes reales.
  • Licencia MIT, sin dependencias de servicios de pago.

Lo que falta para considerarlo listo para producción:

  1. Cierre formal de las especificaciones abiertas y actualización de la documentación del proyecto.
  2. Exportación de las sesiones (subtítulos en .srt / .vtt / texto plano).
  3. Capa de superposición para insertar los subtítulos en directo sobre una emisión de vídeo.
  4. Portugués como tercer idioma.
  5. Panel de monitorización: métricas de latencia por sesión y alertas.
  6. Endurecimiento: pruebas de carga por encima del objetivo, límites de uso, TLS y guía de despliegue.

7. Hacia dónde va

El objetivo de producto es que un organizador monte su propio sistema de subtítulos en conferencias y enestreaming —sin depender de un proveedor externo— y que la experiencia para el público sea tan simple como abrir un enlace. A partir de ahí, el siguiente salto es que el espectador pueda elegir en qué idioma quiere ver una misma sesión, y que una misma infraestructura sirva tanto para una charla de 200 personas como para un canal abierto.

8. Detrás de la tecnología (versión breve)

Para quien quiera saber con qué está construido, sin entrar en detalles:

  • Transcripción: faster-whisper, una implementación optimizada de Whisper, con detección de voz para descartar silencios.
  • Traducción: CTranslate2 con modelos de traducción abierta inglés↔español, y un modelo multilingüe (NLLB-200) previsto para portugués.
  • Coordinación: Redis, que actúa a la vez de almacenamiento de estado, cola de trabajo y bus de eventos. No hay base de datos adicional.
  • API de control: Python con FastAPI.
  • Web del público: React + TypeScript, servida como estáticos.
  • Despliegue: Docker Compose, con variante para GPU.
  • Calidad: unas 1000 pruebas automatizadas, análisis estático de código e integración continua con comprobación de los modelos reales.

Detalle técnico completo en el repositorio: README.md, CLAUDE.md y las especificaciones en specs/.


9. Cómo probarlo

Con Docker instalado, el proyecto se levanta con una sola instrucción y expone:

Qué Dónde
Web del público http://localhost:8080
API de control http://localhost:8000
Documentación interactiva de la API http://localhost:8000/docs
git clone <repositorio> && cd LiveT
cp .env.example .env
docker compose up --build

En la primera ejecución se descarga el modelo de transcripción, así que tarda unos minutos en estar listo. En la misma carpeta hay un archivo con cinco URLs reales de prueba para ejercitar el sistema completo de punta a punta.


Licencia MIT. El código puede usarse, modificarse y redistribuirse libremente, también con uso comercial. Las capas de reconocimiento y traducción son modelos de código abierto, así que tampoco hay licencias comerciales de por medio.

Built With

Share this project:

Updates