Las pruebas que sí corro
Cuatro capas de pruebas para un bot que habla con clientes: cuáles frenan un cambio, cuál sólo observa, y lo que cuesta correrlas.
Cada vez que arreglo algo en el bot aparece la misma pregunta: ¿cómo sé que el arreglo de hoy no rompió lo que andaba ayer? Con un programa común la respuesta son los tests. Con un bot que habla con clientes, la respuesta es la misma, pero un test tiene que lidiar con un modelo que no contesta dos veces igual.
La respuesta corta es que hay cuatro capas de pruebas, y sólo dos de ellas frenan un cambio. Las otras dos miran y avisan.
Capa 1: el veredicto, sin modelo
En el post sobre guards conté que lo que recibe el cliente lo decide una función pura: recibe el borrador, las salidas de las herramientas y el historial, y devuelve respuesta, derivación o fallback, con el motivo. Pura quiere decir que no llama a nada de afuera. No hay modelo, no hay base de datos, no hay red.
Eso la hace trivial de probar. Le armo una corrida a mano, con un borrador inventado y salidas de herramientas inventadas, y verifico qué decide. Hay cerca de mil quinientos casos así, y corren en segundos en cada commit. Es la capa que más confianza da por menos plata, porque cubre casi todas las reglas duras y cuesta cero.
Capa 2: el contrato con el sistema de la tienda
El bot lee una copia del catálogo de la tienda. Esa copia se arma desde el sistema donde la tienda carga sus precios y su stock, y ese sistema no es mío. Un martes de julio renombraron los campos de precio sin avisar, la sincronización siguió corriendo como si nada, y durante doce horas el bot trabajó con un catálogo sin precios.
De ahí salió un test de contrato: pega contra el sistema real y verifica que los campos que el bot espera siguen ahí, con la forma que espera. No corre solo, porque depende de un servicio ajeno y de sus horarios. Lo corro a mano cuando algo huele raro, y es la primera cosa que miro cuando el bot dice “no hay stock” de algo que sí hay.
Capa 3: conversaciones con guion
Acá empieza lo específico de un bot. Un escenario es una conversación de varios turnos con el guion del cliente fijo: “hola, tienen el 15 Pro?”, después “de 256”, después “y en cuotas?”. Corre contra el bot real, con el modelo de verdad, pero sobre una base de datos congelada: una foto del catálogo que no cambia, así el escenario pide siempre lo mismo y espera siempre lo mismo.
Cada escenario afirma dos cosas. Qué hizo el bot: si respondió, si derivó y por qué motivo. Y qué contiene o no contiene la respuesta: que nombre el modelo, que no prometa envíos, que no invente una garantía. Hoy son noventa y seis, y son el filtro para mergear: si uno de los que gatean se pone rojo, el cambio no entra.
Dos cosas que aprendí escribiéndolos:
- Una aserción de texto puede quedar vacía sin que te des cuenta. Un escenario pedía que la respuesta contuviera “17 pro”. El bot contestó con el 17 Pro Max, que también contiene “17 pro”, y el escenario dio verde durante semanas. Lo que quiere decir “este modelo y no el otro” se escribe con una regla más estricta, no con un contiene.
- El bot no contesta dos veces igual, y algunos escenarios flapean. Un mismo escenario correcto pasaba una vez y fallaba la siguiente. Dos medidas: bajarle la temperatura al modelo (la perilla que regula cuánto varía), y marcar como “observación” los escenarios que siguen flapeando. Esos corren y se reportan, pero no frenan el cambio. Un rojo en uno de observación es una señal para mirar, no una regresión.
Capa 4: un cliente simulado
Hay conversaciones que un guion fijo no puede capturar sin mentir. Si mejoro cómo el bot cotiza un canje, cambia lo que el bot dice, y el siguiente mensaje del cliente en el guion deja de tener sentido. Para esas charlas de ida y vuelta uso un replay: un modelo juega al cliente, con una persona y un objetivo sacados de una charla real, y reacciona a lo que el bot contesta de verdad. Otro modelo lee el transcript completo y lo puntúa contra una rúbrica en lenguaje natural.
El replay nunca frena un cambio. Son tres modelos en cadena (el bot, el cliente simulado y el juez), y con tres modelos el resultado varía demasiado como para darle un sí o no a un merge. Lo miro como tendencia, y lo corro cuando un cambio toca su área.
La lección más cara de esta capa fue el juez. Al principio un solo juez leía el transcript y juzgaba todo, incluido si el bot decía la verdad sobre el stock. El problema es que el juez no ve el catálogo: juzga contra lo que recuerda del mundo, que se cortó en alguna fecha. Una vez premió como correcta una respuesta donde el bot decía que un modelo todavía no había salido, cuando estaba en el catálogo con precio. El juez estaba de acuerdo con el bot, y los dos estaban equivocados.
De ahí salieron dos jueces. El de conversación sólo lee el transcript y juzga lo que se puede juzgar leyendo: tono, si preguntó antes de cotizar, si cerró bien. El de catálogo mira la base congelada y verifica, turno por turno, si el bot afirmó algo que los datos contradicen. Mezclar los dos criterios en un solo juez daba rojos falsos y verdes falsos, y ninguno de los dos servía.
Lo que cuesta
Todo esto llama al modelo de verdad, y el modelo se cobra por llamada. Una corrida completa de escenarios sale unos tres dólares y medio. Suena barato hasta que mirás cuántas veces corre.
En la semana del 31 de agosto la corrí, entre yo y los agentes que mantienen el bot, treinta y dos veces. Cien dólares en pruebas para proteger un tráfico real que esa semana costó cuatro. La regla que teníamos era “una corrida completa por cada arreglo”, y la cumplimos. El gasto no era un descuido: era la regla.
Lo que cambió:
- El runner se niega a correr la batería completa sin un filtro. Hay que pedirla a propósito, y antes de correr imprime cuánto va a costar. Por defecto se corre el flujo que el cambio tocó, o un conjunto chico de veinte escenarios transversales que sale medio dólar.
- Un rojo se mide, no se re-corre. Antes cada rojo disparaba tres intentos, y tres intentos de una batería completa son tres baterías. Mientras espero que algo se ponga rojo, un solo intento.
- El corpus tiene presupuesto. Un test fija cuántos turnos puede costar la batería, y para subirlo hay que tocar el tope en el mismo PR. Antes de agregar un escenario, primero pruebo fortalecer, absorber o reemplazar uno del mismo flujo.
- Un escenario por mecanismo. En una pasada bajé de ciento cincuenta y nueve escenarios a noventa y dos. Se fueron los que repetían un mecanismo con otro producto, los que sólo afirmaban lo que otro escenario afirmaba mejor, y los que flapeaban sin gatear nada. Lo que se pierde a sabiendas es enterarme de una regresión en una variante que ya no está. Si un feedback real cae ahí, se absorbe en el escenario del mecanismo.
Una prueba que nadie corre porque cuesta cara protege lo mismo que una que no existe.