← Volver a artículos

Una regla que tira la respuesta a la basura

Qué pasa en los segundos entre que el modelo escribió y el cliente recibe: guards, reintento dentro del mismo turno, y lo que ningún guard atrapa.

chattybotsiaagentesingeniería

En el post anterior conté que el prompt es el último escalón para arreglar un problema, y que antes están los datos, las herramientas y los guards. Este post es sobre el tercer escalón: qué pasa en los segundos que van desde que el modelo terminó de escribir hasta que el cliente recibe algo.

La respuesta corta es que el modelo no manda nada. Escribe un borrador, y el borrador pasa por una fila de reglas que pueden tirarlo a la basura.

Qué es un guard

Un guard es una función común y corriente. Recibe el borrador, las salidas de las herramientas que se llamaron en ese turno y el historial de la charla, y devuelve una de dos cosas: pasa, o un motivo por el que no pasa. Hoy hay veintiocho motivos. Los que más disparan:

  • Monto sin respaldo. El borrador cita una cifra que no aparece en la salida de ninguna herramienta. Un precio inventado, una cuota calculada de cabeza, una conversión a ojo.
  • Stock que contradice la búsqueda. El borrador afirma que hay un equipo que la búsqueda no devolvió, o niega uno que sí devolvió.
  • Cotización sin preguntar. El borrador cotiza un usado sin haber preguntado modelo, capacidad y batería.
  • Agotado como disponible. Un producto marcado como agotado, ofrecido como si estuviera.
  • Garantía sin dato. Una garantía afirmada para un equipo cuya ficha no la trae.
  • Fuga de vocabulario. El borrador le habla al cliente de “la herramienta”, “el sistema” o “el catálogo”. Palabras de adentro que no tienen que salir.

Ninguno de estos guards entiende de lenguaje. Comparan texto contra datos. Por eso son baratos y por eso son confiables: si el monto no está en ninguna salida de herramienta, no está, y no hay interpretación posible.

El reintento

Cuando un guard falla, el borrador no llega al cliente. Pero tampoco se pasa la charla a una persona de una. Primero el modelo tiene una segunda oportunidad.

Un borrador rechazado, dentro del mismo turno
Borrador "El 15 Pro sale $1.200.000."
Guard: monto sin respaldo Esa cifra no está en la salida de ninguna herramienta.
Nota correctiva "Citaste un monto que no consultaste. Usá sólo cifras de las herramientas."
Segundo intento Mismo run, mismo trace. El modelo vuelve a escribir.
Pasa La respuesta sale. El cliente nunca vio el primer borrador.
Reincide Se descarta. La charla pasa a un asesor con una nota de lo que pasó.
Un solo intento de corrección. Si el modelo repite el error, la decisión ya no es suya.

El detalle que más me costó entender es dónde ocurre el reintento. No es un segundo turno, ni una segunda llamada desde afuera. Ocurre adentro del mismo run del agente, como un paso más antes de devolver. Eso deja un solo trace, una sola escritura de memoria y un solo mensaje para el cliente. Si el reintento fuera un turno nuevo, el cliente vería dos respuestas, o la memoria guardaría el borrador malo como si hubiera sido dicho.

Cuando el segundo intento también falla, la respuesta se descarta y la charla pasa a una persona con una nota que dice qué guard falló y por qué. Lo que recibe el cliente es una frase corta y honesta: se complicó armar la respuesta, te paso con un asesor. Es raro: en una semana típica pasa en cerca de uno de cada cien turnos. Pero son justo los turnos donde el modelo iba a decir algo falso.

Un guard también se equivoca

Esta es la parte que me hubiera gustado leer antes de escribir el primero. Un guard demasiado estricto es peor que no tener guard, porque descarta respuestas buenas y las convierte en derivaciones.

Me pasó con el guard de montos. Empezó siendo simple: si el borrador tiene una cifra y ninguna herramienta la devolvió, rechazar. Sonaba correcto. En producción rechazaba cosas como estas:

  • El cliente escribe “tengo 500 mil, qué me alcanza?” y el bot responde “con 500 mil te alcanza para…”. El 500 mil no salió de una herramienta, salió del cliente.
  • El bot había cotizado un equipo dos mensajes atrás y ahora lo repite para comparar. La cifra estaba en la charla, no en las herramientas de este turno.
  • El cliente dice “novecientos” y el bot escribe “900”. Mismo número, distinta forma.

En un mes, casi nueve de cada diez derivaciones por fallback eran el guard de montos rechazando respuestas correctas. El bot no estaba inventando nada; la regla no sabía leer.

Lo que salió de ahí es una práctica, no un parche. Todo guard nuevo se calibra contra unos seis mil turnos reales de producción antes de mergear, midiendo cuántas respuestas buenas rechazaría. Cada falso positivo que aparece se convierte en un test con ese caso exacto. Los últimos tres guards que escribí cambiaron de diseño por esa medición, antes de tocar un solo cliente.

El guard que tiró el proceso

Un guard es código de producción, y como todo código de producción puede explotar. El de montos tenía un detector de aritmética: si el borrador decía 1.500.000 y las herramientas habían devuelto 1.000.000 y 500.000, probaba combinaciones para ver si la cifra era una suma legítima. Probaba todas las combinaciones. Con tres montos es nada. Con una foto de un comprobante llena de números, descripta por visión y convertida a texto, es exponencial. Dos veces se quedó sin memoria y tiró el proceso abajo.

Hoy el detector tiene un tope de cuántas cifras mira, y hay un presupuesto de tiempo por turno. Un guard que tarda más que el modelo no está protegiendo nada.

Lo que ningún guard atrapa

Los guards comparan contra datos. Cuando no hay datos, no hay guard. Un ejemplo reconstruido de una charla real:

Cliente: Ya fui esta mañana y pagué. Tengo que pasar a retirar.

Bot: Si ya abonaste, necesito el número de venta o el comprobante para ayudarte con el retiro. ¿Lo tenés a mano?

Suena razonable. El problema es que el bot no tiene ninguna herramienta para consultar un número de venta: la tienda no tiene un sistema de ventas al que el bot pueda pegarle. Está pidiendo un dato que no va a poder usar. Lo correcto era derivar a una persona sin preguntar nada.

Ningún guard lo detecta, porque no hay una salida de herramienta contra la cual comparar. El bot no citó ninguna cifra falsa ni afirmó un stock inexistente. Prometió, de manera implícita, algo que no puede cumplir. Ese tipo de error no se resuelve en el escalón 3: entra al loop de mejora como un caso nuevo, con su test, y termina siendo una regla en la herramienta de derivación o, como último recurso, una línea en el prompt.

Los guards son la red de seguridad para lo que se puede verificar. Para lo demás, sigue haciendo falta mirar las conversaciones.