voiceops
El kit de despliegue que extraje de mi primer agente de voz, probado llevando a producción a un segundo cliente, distinto, con él.

Resumen
voiceops es la parte reutilizable de Slotback, mi despliegue de un agente de voz para un hospital ficticio: salvaguardas por defecto para el agente, una barrera de publicación, un registro a prueba de manipulaciones, comprobaciones de despliegue y una hoja de revisión para llamadas reales, en dos paquetes de Python de código abierto. Extraje solo lo que Slotback ya usaba y pasé Slotback al kit sin ningún cambio de comportamiento. Después probé el kit como lo probaría el terreno: llevando a un segundo cliente, distinto, desde una carpeta vacía hasta un agente en producción, conversaciones simuladas y llamadas reales.
De un vistazo
- 1 h 17 mindesde una carpeta vacía hasta un segundo agente en producción superando su barrera de publicaciónMedido · una sesión de trabajo
- 0 líneasde código del kit copiadas en el segundo despliegue, que escribió 302 propiasMedido
- 6 versionesdel kit, cada una forzada por algo que encontró el segundo despliegueMedido
- 403 de 403registros manipulados juzgados exactamente igual que con el verificador original de SlotbackMedido · prueba de paridad
- 12/12barrera de publicación del segundo despliegue: 9 comprobaciones de herramientas, 3 conversaciones simuladasConversaciones simuladas
- 3 llamadasllamadas reales a mi propio teléfono; cada una encontró un problema que las simulaciones no habían vistoLlamadas reales
El problema
Un agente de voz que habla con el público necesita siempre las mismas salvaguardas: decir que es una IA, verificar la identidad antes de dar ningún dato, pasar con una persona cuando se lo piden, no prometer nada que ningún sistema cumpla y dejar un registro que otro pueda comprobar. En mi primer despliegue, Slotback, esas salvaguardas fueron la mayor parte de la ingeniería.
Si se reconstruyen para cada cliente, se desvían, y el segundo cliente recibe una copia más débil del primero. La respuesta habitual es un framework diseñado de antemano, pero diseñado antes de que exista un segundo cliente, solo puede adivinar qué partes son generales.
El enfoque: extraer solo lo que un despliegue real ya usaba, demostrar que el primer cliente se comporta exactamente igual que antes y dejar que un segundo cliente, distinto, decida qué le falta al kit.
Qué construí
Dos paquetes de Python, de código abierto y en PyPI, controlados por un único archivo de configuración.
- voiceledger: un registro de eventos encadenado por hash y firmado con Ed25519, con reglas configurables, como “ningún dato antes de verificar la identidad” o “toda emergencia pasa a una persona en menos de 120 segundos”, y un verificador que un auditor instala por separado: 4 paquetes, 12 MB.
- voiceops: una línea de comandos para todo el despliegue. init crea el proyecto; build compone las instrucciones del agente a partir de los fragmentos propios del proyecto más siete salvaguardas por defecto que no se pueden quitar en silencio; push publica el agente; gate es la barrera de publicación; check ejecuta comprobaciones contra la API en producción; call, fetch y review hacen una llamada real de prueba a un único número autorizado y la convierten en una hoja de revisión.
- Una guarda para la API a la que llama el agente: tokens por llamada vinculados a una persona verificada, webhooks firmados, límites de frecuencia y una lista de campos permitidos en los logs. Las llamadas sin autenticar se rechazan antes de leer ningún dato.
Mi papel
Traté el kit como un producto con un cliente ya en producción y un segundo por ganar.
- Alcance y criterios de éxito. Cinco criterios fijados antes de construir: ningún cambio de comportamiento para Slotback; un segundo despliegue en producción y superando su barrera en tres días laborables o menos; sobre todo código del dominio; un kit que se prueba a sí mismo; un auditor que verifica con una instalación pequeña. Una regla: extraer solo lo que Slotback ya usa. Plan ↗
- Extracción con pruebas. Slotback pasó al kit módulo a módulo, y cada uno se comparó con el original sobre el propio código y los datos de Slotback antes de integrarse. Resultados ↗
- Un segundo cliente. claims-surge, la línea de siniestros de una aseguradora ficticia después de una tormenta: estado de siniestros, siniestros nuevos y emergencias pasadas a una persona antes de cualquier verificación de identidad. claims-surge ↗
- Pruebas en el terreno. En producción en Render, agentes publicados en ElevenLabs, diez rondas de conversaciones simuladas y tres llamadas reales, cada una revisada línea a línea. Registro de tiempos ↗
- De vuelta al producto. Cada hallazgo se convirtió en una versión del kit con su prueba, o en una decisión escrita. Registro de decisiones ↗
Decisiones de diseño clave
Dónde está el criterio.
Extraer después del primer despliegue, no antes.
El kit solo contiene lo que Slotback usaba. Qué añadir después lo decidió el segundo despliegue, no una opinión.
Salvaguardas que se pueden reformular, pero no perder.
Siete fragmentos de seguridad: aviso de IA, llamante alterado, identidad antes de los datos, buzón de voz, una persona cuando se pide, ninguna promesa sin respaldo y cierre. Un proyecto puede reformular uno, o quitarlo con un motivo escrito. Quitarlo en silencio rompe la compilación, y cada agente lleva un manifiesto de cómo se cumple cada uno.
Nuestras comprobaciones deciden; el juez de la plataforma aconseja.
La barrera ejecuta sus propias comprobaciones deterministas en cada conversación simulada, incluido un detector de fugas de privacidad que debe atrapar cada fuga sembrada antes de que su “0 fugas” cuente. El juez de la plataforma aprobó a un agente que decía “le paso con alguien” cuando la herramienta solo programa una devolución de llamada; nuestra comprobación lo suspendió.
Reglas como datos.
Las reglas del registro son cuatro tipos genéricos definidos en la configuración. Las dos comprobaciones de Slotback pasaron a ser reglas sin cambios; la línea de siniestros añadió dos que Slotback nunca necesitó.
Nunca tocar a otro cliente.
Dos despliegues en el mismo espacio de la plataforma comparten nombres de herramientas. La publicación identifica las herramientas por el endpoint al que llaman, nunca por el nombre, comprobado contra el espacio real con las 13 herramientas de Slotback intactas.
Las llamadas reales forman parte de la publicación.
Las llamadas van solo a un número autorizado. La hoja de revisión enmascara los teléfonos, señala errores de herramientas y promesas, y deja la causa y la solución a una persona.
Lo que encontró el segundo cliente
Cada hallazgo, una versión.
Slotback solo no podría haberlos encontrado. Cada uno es una versión del kit con su prueba.
0.1.1 · La publicación
Hallazgo
Publicar el segundo agente habría redirigido o borrado las herramientas en producción de Slotback: los dos proyectos tienen una herramienta llamada log_outcome.
Cambio en el kit
La publicación solo adopta herramientas que llaman a su propio endpoint.
0.1.2 · La plataforma
Hallazgo
La plataforma rechazó un agente en inglés con un modelo de voz multilingüe.
Cambio en el kit
La compilación falla antes, en CI, donde aprenderlo cuesta menos.
0.1.3 · El juez
Hallazgo
El agente dijo “le paso con alguien” cuando la herramienta programa una devolución de llamada, y el juez de la plataforma lo aprobó.
Cambio en el kit
Una comprobación de frases prohibidas en cada turno del agente; la salvaguarda de traspaso dice que es una devolución de llamada.
0.1.4 · Nuestra propia comprobación
Hallazgo
Una negativa correcta, “no puedo decirle si está cubierto”, suspendió la barrera por una coincidencia de texto.
Cambio en el kit
Las frases pueden ser patrones que ignoran las negativas, y cada frase real queda como respuesta conocida.
0.1.5 · Llamada real 1
Hallazgo
La API se reinició justo al empezar la llamada; el agente dijo a una persona con los datos correctos que no coincidían.
Cambio en el kit
Un error de herramienta es “un problema técnico”, nunca culpa de quien llama; la hoja de revisión cuenta las respuestas de error.
0.1.6 · Llamada real 2
Hallazgo
El número de teléfono dicho en la llamada apareció completo en la hoja de revisión, y una fecha de ejemplo que improvisó el agente se repitió como respuesta.
Cambio en el kit
Todo número con forma de teléfono se enmascara; el agente nunca da respuestas de ejemplo.
La llamada real 3 encontró un siniestro registrado con un año que quien llamaba nunca dijo. Esa corrección era de la línea de siniestros, no del kit: su servidor ahora comprueba la fecha.
Escúchalo
Una llamada real.
Las llamadas van solo a mi propio teléfono autorizado. Hago de asegurado sintético en la línea de siniestros de la aseguradora ficticia Contoso Insurance. El agente habla en inglés.
Aviso de IA, verificación de identidad, pregunta de seguridad, repaso de los datos, siniestro registrado y referencia deletreada. Suena bien, y estaba mal: dije “October 15” y el agente registró el 15 de octubre de 2024, dos años atrás. Ninguna ronda simulada lo había detectado. El servidor ahora rechaza una fecha sin año, futura o de hace más de un año, y el agente repasa la fecha con el año antes de registrar.
Resultados frente al plan
| S1 · Slotback en el kit sin cambio de comportamiento: mismos veredictos en 403 registros manipulados, configuraciones idénticas byte a byte, 117 de 117 pruebas de la guarda, comprobaciones en producción 6 de 6 | Cumplido |
|---|---|
| S2 · Segundo despliegue en producción y superando su barrera en 3 días laborables: en producción a 1 h 07 min, barrera a 1 h 17 min, primera llamada real completa revisada a 17 h 54 min (de un día para otro) | Cumplido |
| S3 · Sobre todo código del dominio: 302 líneas propias sobre 2.841 líneas del kit, ninguna copiada | Cumplido |
| S4 · El kit se prueba a sí mismo: 167 pruebas, respuestas conocidas para el registro y el detector de fugas, la salida de init comprobada en CI | Cumplido |
| S5 · Verificación independiente: el verificador se instala solo, 4 paquetes, 12 MB | Cumplido |
Las conversaciones simuladas costaron 15.901 créditos en diez rondas. Cada ronda fallida encontró algo real o afinó una comprobación, y cada ronda queda registrada con su hallazgo y su solución.
Arquitectura
Tecnología
- Python 3.12
- Espacio de trabajo uv
- pydantic
- Typer
- FastAPI
- PostgreSQL (psycopg 3)
- Cadena de hash SHA-256 + firmas Ed25519
- ElevenLabs Agents (agentes, herramientas, Tests API)
- Twilio
- pytest + Hypothesis (167 pruebas)
- Ruff
- GitHub Actions
- PyPI
- Render (UE)
Qué haría después
- Sustituir las listas de frases por una comprobación calibrada: tres de las diez rondas fallaron por una frase que atrapó una negativa correcta.
- Que el traspaso use el número al que marcó una llamada saliente, para que “llámeme a este número” baste.
- Un tercer despliegue en español, para probar los caminos multilingües que la línea de siniestros no usó.
- Publicar desde GitHub Actions con publicación de confianza en lugar de un token guardado.
Solo clientes ficticios (un hospital en Slotback, Contoso Insurance en claims-surge) y datos sintéticos. Las llamadas reales van solo a mi propio teléfono autorizado. Los resultados a nivel de conversación son simulados en ElevenLabs Tests y están marcados como tales; todo lo demás está medido. ElevenLabs es la plataforma de agentes de voz utilizada; este es un proyecto independiente, sin respaldo ni patrocinio de ElevenLabs.
Siguiente proyectoContinuidad de contratos en n8n