Jesús Martínez
EN
← Trabajo · Caso de estudio

Un coach de voz que se califica a sí mismo.

Charming Chad es un juego para practicar conversaciones de verdad. Hablas en voz alta con un personaje de IA en un lugar simulado, y un árbitro de IA califica cómo te fue. Construí todo el backend, y también el banco de pruebas que califica a la IA antes de que cualquier cambio le llegue a un jugador.

Cliente
Palm Island
Rol
Arquitecto de IA y único ingeniero de backend
Período
Feb 2026 a sep 2026
Construido con
Python, FastAPI, Supabase, LiveKit, Deepgram, Cartesia, OpenRouter

El contexto

Para practicar necesitas a alguien que te diga la verdad.

Mejorar en una conversación exige practicar con alguien dispuesto a darte una opinión honesta y concreta, y eso es justo lo difícil de conseguir. Un chatbot de texto no reproduce la presión de hablar en voz alta y en tiempo real. No te puede decir si sonaste natural o si incomodaste, si te apresuraste o si te la pasaste repitiendo lo mismo.

El problema

Un árbitro que nadie puede revisar no es un árbitro.

El producto necesitaba voz en vivo, personajes lo bastante consistentes como para que valiera la pena practicar con ellos y puntajes que los jugadores aceptaran como justos. Eso abría la pregunta difícil: cada vez que cambia un modelo o un prompt, ¿cómo sabes que el coach mejoró y no que simplemente cambió?

El enfoque

Un juego, no un chatbot.

Cada sesión tiene inicio, desarrollo y desenlace, y al jugador lo están evaluando todo el tiempo. Por eso el sistema lleva la conversación en vivo por un lado y el puntaje por otro, y el puntaje nunca depende de que el proceso de la conversación siga vivo.

01

Entrar

El jugador entra a una sala de voz en tiempo real con un personaje en un lugar, y cada lugar tiene su propia dificultad. Una máquina de estados lleva la sesión del saludo a la evaluación.

02

Hablar

La voz se vuelve texto, un modelo de lenguaje responde en personaje y la respuesta vuelve como voz, con un avatar en video si el jugador lo activa.

03

Reaccionar

Cada respuesta trae la emoción del personaje, que mueve un puntaje de receptividad de 0 a 100. Si presionas demasiado o te pones vulgar, el personaje se enfría. El abuso de verdad termina la sesión.

04

Calificar

Al terminar, el árbitro propio de ese lugar califica la sesión de 0 a 100 según su rúbrica y devuelve comentarios por categoría: qué funcionó y qué no.

Decisiones y trade-offs

Tres decisiones que lo hicieron funcionar.

01

Medir, no adivinar.

Cada cambio de modelo pasa por un banco de evaluación. Dos modelos responden las mismas conversaciones, un juez LLM elige la mejor respuesta con el orden invertido para anular su sesgo, y un candidato solo sale a producción si supera los umbrales de tasa de victorias y de formato. Cada corrida reporta cuánto costó.

Elegí un banco de evaluación en vez de ajustar prompts a ojo
02

Llevar el puntaje gratis.

El personaje ya devuelve una emoción y su intensidad con cada respuesta. Convertir eso en el puntaje de receptividad lo hace código simple, no otra llamada al modelo, así que el estado del juego no cuesta nada extra y cada regla tiene su prueba unitaria.

Elegí reglas deterministas en vez de una llamada extra al LLM por turno
03

Nunca perder un puntaje.

Al principio la calificación corría como tarea en segundo plano dentro del servidor, y un deploy en plena sesión simplemente perdía el resultado. Ahora cada calificación es un trabajo persistente: se reclama con un lease, se reintenta con espera creciente y se cierra una sola vez. Un deploy ahora cuesta tiempo, no resultados.

Elegí una cola de trabajos persistente en vez de tareas en segundo plano dentro del proceso

El giro

El mejor modelo no salió a producción.

El banco de evaluación guió la migración del modelo que estaba en producción, y un candidato quedó en primer lugar. Luego medimos su latencia: casi el doble que la del segundo, demasiado lento para una conversación de voz en vivo, donde cada pausa se escucha. Salió el segundo. Aun así, le ganó al modelo anterior en las seis dimensiones de la rúbrica.

#1en calidad, y casi el doble de lento
100 %de victorias del segundo, el que salió

El resultado

Dónde terminó.

12,5 % → 82,3 %de respuestas en el formato exacto que lee el juego, antes y después de la migración
100 %de victorias en el juicio frente al modelo anterior, en las seis dimensiones de la rúbrica
4.568pruebas unitarias, de integración y de punta a punta, sin una sola falla en el último lanzamiento
20personajes, cada uno ajustable desde un archivo de datos y no con un deploy

En producción con jugadores reales. Lo construí solo en unos seis meses, mientras un equipo de frontend aparte trabajaba con el contrato de la API y las guías de integración que les escribí.

Para el lector técnico

Notas de ingeniería

Ejemplos con los que el juez no se deja engañar

Cada personaje habla a partir de una biblioteca curada de frases de ejemplo. El juez mide la consistencia contra un conjunto aparte, reservado, así que un modelo no puede ganar repitiendo sus propios ejemplos, y una métrica de solapamiento de n-gramas marca cualquier respuesta que los repita por encima de un umbral fijo.

Un agente de voz en 512 MB

El worker de voz se caía por falta de memoria, y cada caída dejaba sesiones abiertas que bloqueaban al jugador detrás de la regla de una sola sesión abierta. Ahora el control de admisión lee la memoria real del contenedor en vez de la CPU, las importaciones pesadas quedan fuera de los procesos de trabajo y el avatar se renderiza en la nube, así que el video nunca cae en el worker. Un proceso de limpieza cierra lo que deje una caída.

Filtros con un núcleo reemplazable

El abuso, las propuestas vulgares, los límites de contenido, la inyección de prompts y las peticiones de datos de contacto dentro de la ficción pasan cada uno por un control angosto. Las primeras versiones son deterministas y fáciles de revisar; un clasificador puede reemplazar cualquiera más adelante sin tocar a quien lo llama. Los turnos del jugador siempre viajan como mensajes de usuario, nunca empalmados en las instrucciones, y la detección de inyección solo registra, porque borrar las palabras sospechosas dañaría mensajes honestos.

Investigar los avatares antes de programar

Revisé catorce proveedores de avatares en tiempo real. La restricción real resultó ser la política de contenido de cada proveedor, no la tecnología, así que la recomendación fue conseguir una aprobación escrita de uso aceptable antes de comprometerse. Salió detrás de una interfaz independiente del proveedor y un feature flag, opcional en cada sesión, con un respaldo de solo audio para cada falla.

Dos procesos, una sola vía de escritura

La API y el worker de voz corren como servicios separados, coordinados por despacho de trabajos. El worker nunca escribe en la base de datos; llama de vuelta a endpoints internos con un token de servicio, así que cada escritura tiene una sola vía autorizada. Los lanzamientos salían cada semana, con listas de verificación escritas y revisión del alcance antes de cualquier migración que cambiara datos.

  • Python
  • FastAPI
  • Supabase
  • PostgreSQL
  • Pydantic
  • LiveKit Agents
  • Deepgram
  • Cartesia
  • OpenRouter
  • LemonSlice
  • Langfuse
  • Stripe
  • Sentry
  • Docker
  • Render
  • React
  • TypeScript

Siguiente proyecto · IA legal · 2026

Respuestas de inmigración que un abogado puede respaldar

¿Tienes una función de IA que nadie puede demostrar que está mejorando?