Back to deblo
deblo

72 turnos sin errores, medidos sobre una copia: el modo manos libres de Déblo Duo y el banco que no probaba el código real

Nuestro banco midió 72 turnos sin errores del manos libres de Déblo Duo, sobre una copia. Con el código real, detectó un fallo que no abría ninguna sesión.

Juste Thales Gnimavo & Claude | November 13, 2026 11 min deblo
EN/ FR/ ES
deblodeblo-duohands-freereal-time-interpretationvoice-aibenchmarkingtest-benchrace-conditionprod-proofclaude-codeclaude-opus-5.5

Por Juste Thales Gnimavo (CEO, ZeroSuite) y Claude Opus 5.5 (Claude Code)

Déblo Duo es una app de interpretación en tiempo real: cada persona habla su idioma y escucha a la otra en el suyo. Por qué existe Déblo Duo presenta el producto. Este artículo trata de uno de sus modos, el manos libres: un solo teléfono sobre la mesa entre dos personas, el micrófono siempre abierto, nadie pulsa nada. Cubre tres días. El 11 de noviembre de 2026, un banco de pruebas nos dijo qué fallaba en el modo que teníamos. El 12 de noviembre descubrimos que ese banco medía una copia de la lógica, no el código que llega a producción. El 13 de noviembre, la versión 1.1.6 salió en Google Play, y las primeras llamadas manos libres del CEO fueron mudas.

Por qué el manos libres es difícil

Con dos teléfonos, o con pulsar para hablar, la app siempre sabe quién habla. Con un solo teléfono y un micrófono abierto, no lo sabe. El mismo micrófono oye a la persona A, a la persona B y al altavoz del teléfono que reproduce la traducción. Para cada frase, el sistema debe decidir qué idioma es, qué sentido debe traducirla y qué hacer con el micrófono mientras suena la traducción.

Nuestro modo manos libres de entonces respondía de forma sencilla a la última pregunta: cortar el micrófono mientras suena una traducción, para que el teléfono no traduzca su propia voz. Parecía razonable. Nadie lo había medido.

11 de noviembre: el banco

Claude abrió la sesión contrastando el brief con el código. El banco existente reproducía una sesión en un solo sentido. No sabía reproducir un teléfono con dos idiomas. Así que Claude escribió uno nuevo: una conversación en dos idiomas reproducida sobre un micrófono, dos sesiones de traducción, una pista de salida arbitrada como la arbitra la app. Siete variantes, desde pulsar para hablar (la referencia) hasta varias formas de manos libres, sobre tres juegos de clips: conversación limpia, frases muy cortas y una voz de fondo en un tercer idioma. Idiomas de prueba: chino, japonés, español y árabe, y luego francés e inglés.

Ese día no se modificó ningún código de producción. El banco solo medía.

La variante manos libres en uso perdía habla en 71 de 72 turnos. La razón era mecánica. La traducción empieza unos 3 segundos después del inicio de la frase. En ese momento el micrófono está cortado, porque suena una traducción. El final de la frase nunca llega al modelo. Alguien que dijera un precio al final de una frase habría oído cómo el precio desaparecía.

El segundo hallazgo era más sutil. El sentido que debía traducir detectaba el idioma correcto 215 veces de 215. El sentido que acababa de traducir, no. Se quedaba pegado a su idioma: 56 detecciones erróneas de 216, cero en el primer turno, 29 en el quinto. Cuanto más duraba la conversación, peor.

Abrir una sesión nueva para ese sentido en cada turno eliminaba la deriva. Con esa forma, en conversación limpia, el banco contó 72 turnos sin duplicados ni idioma equivocado, un turno nunca oído y unos 3,1 segundos antes del primer sonido traducido. Pulsar para hablar, la referencia, seguía siendo más rápido: unos 1,9 segundos.

Quedaba una decisión de producto, y le correspondía a Juste. Eligió lo que llamó el comportamiento « film »: « micro jamais coupé, traduction au fur et à mesure ». El micrófono nunca se corta; la traducción sigue al que habla. Fijó el precio que aceptaba pagar: « Jusqu'à 4 s avant le premier son traduit, acceptable. » Hasta 4 segundos antes del primer sonido traducido. También pidió que la app dijera dónde tiene sentido el modo: el manos libres es para un lugar tranquilo.

La sesión se cerró ahí. El servidor y la pantalla quedaron para una sesión nueva.

12 de noviembre: el banco probaba una copia

La sesión siguiente empezó igual, contrastando el brief con el código. Todos los símbolos que citaba el brief existían en el puente de traducción, el módulo que corre en producción. Después Claude miró qué importaba el banco.

No importaba el puente. El banco tenía su propia implementación de la lógica. Los 72 turnos sin errores del 11 de noviembre medían la forma correcta del algoritmo sobre una copia, no el código que iba a salir.

Es una brecha fácil de pasar por alto. La copia se había escrito a partir del mismo diseño, por el mismo agente, el día anterior. Habría sido natural leer sus cifras como cifras sobre producción. No lo eran. Una copia puede estar bien mientras el código real está mal, y el banco no tenía forma de verlo.

La respuesta fue estructural. La lógica de manos libres pasó a un pequeño módulo puro compartido por el worker y el banco, y una nueva variante del banco ejecutaba el propio objeto de dirección del worker en lugar de una reimplementación.

Dio resultado en la primera pasada. La primera versión del cambio de servidor escrita por Claude añadía un método a una dataclass, y ese método tenía el mismo nombre que un campo de la dataclass. En Python, eso sustituye el campo sin avisar. La lista de idiomas que se pasaba a la sesión de traducción contenía una función en lugar de un código de idioma. No se abría ninguna sesión, en ninguno de los tres modos, no solo en manos libres. py_compile pasaba. El archivo era sintácticamente correcto.

La primera pasada del banco sobre el código real falló con un error de validación y el worker rindiéndose. Se renombró el método. Claude escribió en el log de sesión la frase que resume el día: « Sans le banc sur le vrai code, ce défaut partait en prod. » Sin el banco sobre el código real, este fallo habría llegado a producción.

Tras el cambio de nombre, el banco jugó 234 turnos sobre el código que sale: 72 en conversación limpia, 18 en inglés, 72 con una voz de fondo 15 dB por debajo del hablante, 72 en los que la misma persona habla dos veces seguidas. Ningún error de sesión, ninguna reconexión, ningún precalentamiento fallido. En conversación limpia: ningún duplicado, ningún idioma equivocado, un turno nunca oído y 3,5 segundos antes del primer sonido traducido, dentro de los 4 segundos aceptados por Juste. Con la voz de fondo: un duplicado, un sentido equivocado, cinco turnos nunca oídos.

La parte de pantalla se hizo el mismo día con dos sesiones worker, una tras otra, sobre la app móvil.

13 de noviembre: la 1.1.6 y una primera llamada muda

La versión 1.1.6 de Duo pasó a producción en Google Play la mañana del 13 de noviembre. En la App Store, el build 1.1.6 se asoció a la versión y Juste lo envió a revisión. Esa misma mañana, al repasar las fichas de las tiendas, vimos que las preguntas frecuentes de la app anunciaban una prueba gratuita de « 2 minutes » en todos los idiomas. Producción da 10. Las preguntas frecuentes se corrigieron en el código; salen con el próximo build.

Después Juste hizo sus primeras llamadas manos libres con la nueva versión. Silencio, en iPhone y en Android.

Los registros del worker lo mostraban. En Android, el manos libres estaba activo 4,2 segundos después del inicio de la llamada, el audio llegó durante 47 segundos y no se transcribió ni una palabra: silencio digital. En iPhone, la llamada siguió muda hasta que Juste salió y volvió a entrar; la traducción empezó entonces cinco segundos después. En ambos registros, el evento « micrófono conectado » y el evento « manos libres activo » estaban separados por 0,1 segundos.

Esa era la pista. Una condición de carrera en la app. Al conectarse, la app publica el micrófono y luego lo silencia, porque las llamadas empiezan con el micrófono cortado. Pero el estado del worker podía marcar la llamada como activa antes de que terminara esa secuencia. El manos libres abría entonces el micrófono, y el silenciado de arranque llegaba después y lo volvía a cerrar. Dos eventos, a una décima de segundo, en el orden equivocado.

La corrección de fondo está en la app: el silenciado del micrófono espera ahora a que termine su configuración. Claude la escribió y pasó la comprobación de tipos. Pero necesita un nuevo build, y los teléfonos que ya tenían la 1.1.6 habrían seguido mudos hasta actualizar.

Así que, en lugar de gastar un build, Claude añadió una red de seguridad en el servidor para los teléfonos que ya estaban en la 1.1.6. Si el manos libres está activo y el worker no oye nada por encima del silencio procedente del anfitrión durante unos segundos, responde « off »; la app vuelve a pedirlo y reabre el micrófono. Dos reintentos como máximo, y solo antes de la primera trama con voz.

Esa última condición viene de un error. La primera versión de la red de seguridad se disparaba por error en mitad de una conversación. Los teléfonos aplican cancelación de eco, y mientras suena una traducción, la cancelación de eco produce silencio en el micrófono. Para la red de seguridad, eso parecía un micrófono muerto. Reintentaba en mitad de una llamada que funcionaba. Los registros de las 08:44 UTC en iPhone y de las 08:50 UTC en Android lo mostraron. La regla se restringió al inicio de la llamada, antes de cualquier trama con voz, y el worker se reconstruyó.

Lo que está probado y lo que no

A lo largo de las dos reconstrucciones del worker, el registro que pegó Juste mostraba cinco llamadas manos libres en frío, cada una la primera tras abrir la app: a las 08:44 UTC en iPhone y a las 08:50 UTC en Android (las dos llamadas en las que la primera versión se disparó después por error), y luego tres más a las 08:55 y 08:56. Cinco de cinco traducidas, con la primera frase reconocida entre 5 y 6 segundos después de activar el manos libres. Ninguna llamada muda. El veredicto de Juste: « tout marche ».

No lo llamamos corregido, y la razón está en el mismo log. La red de seguridad no se disparó antes de la primera frase en ninguna de esas cinco llamadas. Nunca se la vio reparar el caso para el que se construyó. Las cinco llamadas prueban que el manos libres funcionó cinco veces seguidas, en dos teléfonos, esa mañana. No prueban por qué. La corrección que ataca la causa está en la app y no se ha publicado.

Lo que queda abierto

  • Frases cortas en chino, japonés, español y árabe: no medidas con la forma final. Una sola palabra de un segundo no se transcribe en ningún modo, pulsar para hablar incluido.
  • Cambio de sesión mientras alguien ya está hablando: no medido.
  • Carga por llamada: dos sesiones activas más un precalentamiento por llamada manos libres. La exposición a errores 1011 del servicio de traducción hay que vigilarla en producción.
  • Voces sintéticas: todas las cifras del banco proceden de habla sintética, y el banco no oye el altavoz del teléfono. El eco del altavoz en un micrófono abierto, con volumen alto, solo se prueba en dispositivos reales.
  • La corrección en la app de la primera llamada muda está escrita y pasa la comprobación de tipos. No se ha publicado.

La lección

Un banco solo es tan honesto como lo que importa. El 11 de noviembre, el nuestro produjo cifras limpias y útiles, y orientaron una decisión de producto real. También eran cifras sobre una copia. Nada en los resultados lo decía. La única forma de verlo era leer las líneas de importación del banco a la mañana siguiente, al contrastar el brief con el código.

La regla que sacamos es corta. Una prueba que no ejecuta el módulo de producción mide una intención. Cuando el banco ejecutó el módulo real, encontró en su primer minuto un fallo que el compilador había aceptado y que no habría abierto sesión para nadie. Dos días después, producción encontró un fallo que ningún banco habría encontrado, porque dependía del orden de dos eventos en un teléfono real. Ambos exigían lo real. Por eso también la llamada muda sigue abierta: cinco buenas llamadas son una observación, no una explicación.

Share this article:

Responses

Write a response
0/2000
Loading responses...

Related Articles