← Todas las publicaciones
IngenieríaPublicado el 20 de agosto de 20264 min de lectura

El asistente no podía saberlo

Ulyssify EngineeringConstruir un mecanismo de compromiso a la vista de todos

Ulyssify bloquea apps y sitios web que te distraen. Tiene un asistente, Anka, que configura los bloqueos por ti en lenguaje natural. La semana pasada falló de una forma muy instructiva:

UsuarioAgrega este sitio a mi lista de bloqueo Night dev.
Asistente¡Listo! Ya está en la lista Night dev. 🔒
UsuarioEstoy viendo Night dev. No está ahí.

El sitio había ido a parar a otra lista, con otro comportamiento. El asistente confirmó algo que nunca ocurrió y, cuando se le cuestionó, adivinó.

El diagnóstico tentador es "el modelo no es lo bastante inteligente". En lugar de eso, rastreamos la conversación y encontramos algo mejor: ningún modelo, de ningún tamaño, habría podido responder correctamente. Los datos nunca estuvieron en su contexto. Se perdieron en dos lugares.

Primera fuga: la vista que el agente tiene de tus datos se vuelve obsoleta

Nuestras pantallas de Configuración nunca se quedan atrás, porque muestran lo que sea que devuelva el backend. La vista del asistente era distinta: una función de resumen que alguien escribió a mano, con unos pocos campos, el día en que se lanzó la función. Cuando más tarde el producto incorporó horarios con horas reales, nadie actualizó ese resumen, y nada obligaba a nadie a hacerlo.

Tus datos UI de Configuración se actualiza sola Vista del asistente escrita a mano, congelada ✓ ve los campos nuevos ✗ nunca los conoce
La UI lee directamente de la fuente. El asistente leía una copia escrita a mano.

Así que el asistente no podía ver que ya existía un horario de medianoche, y propuso reconstruir cosas que el usuario ya tenía. No era estupidez. Era ceguera.

La solución: la paridad es una prueba, no una promesa

Dejamos de confiar en que alguien se acordara. Ahora una prueba de CI lee los campos reales que muestra la UI y exige que cada uno de ellos se le muestre al asistente o se excluya con un motivo por escrito. Si agregas un campo a la UI sin decidir qué pasa con el asistente, la compilación falla en rojo con un mensaje que te dice qué hacer.

Campo nuevo lo agrega cualquiera Control de CI visible para el asistente → ✓ pasa excluido, con motivo → ✓ pasa olvidado → la compilación falla
Olvidar ya no pasa en silencio. Olvidar es rojo.

Segunda fuga: el asistente narraba su plan, no el resultado

El error de la lista equivocada era peor. El servidor había ejecutado la acción y había guardado exactamente lo que pasó, incluida la lista en la que realmente quedó el sitio. Pero el mensaje de seguimiento del asistente se armaba a partir de su propio plan anterior, más un genérico "ejecutado correctamente". La verdad guardada se descartaba un paso antes de que el modelo la viera.

Ahora el seguimiento se construye a partir del resultado guardado: la lista real, el estado real, los ids reales. Si algo termina en un lugar inesperado, el asistente lo dice, porque eso es lo que se le informó. Y nada de la solicitud puede inyectarse en ese mensaje; cada dato lo genera el servidor.

ANTES plan del asistente "¡Listo! Bloqueado 🔒" confirmó su propia intención DESPUÉS se ejecuta la acción resultado guardado lo que realmente pasó confirmación honesta
Ahora las confirmaciones salen de lo que pasó, no de lo que se pretendía.

La parte que nunca falló: dos puertas, un solo control

Una propiedad evitó que esta fuera una historia peligrosa en lugar de una vergonzosa, y es la pieza de nuestro diseño que defenderíamos con más firmeza. El asistente no tiene su propia API. Sus acciones entran al backend una capa por debajo de HTTP y ejecutan exactamente las mismas funciones con controles que ejecuta un clic humano.

Clic humano en Configuración Acción del asistente tras tu aprobación Mismo núcleo con control las mismas reglas para ambos reforzar un bloqueo: inmediato aflojarlo: espera a que termine tu propio período de espera
No hay una API aparte para el agente. Dos puertas hacia un mismo control, así que las reglas no pueden divergir.

Esto importa por quién es el adversario. En un mecanismo de compromiso, la persona más motivada para burlar un bloqueo es el propio usuario, a la 1 a.m., negociando con sus propias decisiones pasadas. Si el asistente tuviera su propia vía de escritura, cada prompt sería un posible resquicio. Como comparte la vía humana, cualquier cosa que afloje el cumplimiento queda en cola detrás del propio período de espera del usuario, sin importar cómo se haya formulado la solicitud. El asistente podía equivocarse. No podía ser una forma de saltarse el bloqueo.

Fíjate en lo que esto no es: no es "pedirle permiso a un humano antes de actuar", algo que ya está bien cubierto en otros lados. Es una afirmación más fuerte sobre la plomería. "El asistente puede hacer todo lo que puede hacer un humano" debería significar paridad de capacidades, no una superficie de API paralela, porque un humano tampoco puede saltarse el período de espera.

La mitad de la aprobación está bien cubierta en el campo: 12-Factor Agents defiende mantener a un humano en el ciclo, y la guía de Anthropic para escribir herramientas para agentes trata sobre devolverle al modelo resultados significativos y con mucha señal. Las afirmaciones sobre la plomería (que la vía de escritura del agente ES la vía humana con controles, y que sus confirmaciones se reconstruyen a partir de la verdad guardada en el servidor) son la parte que no hemos visto documentada, así que la estamos documentando.

Cerramos el ciclo convirtiendo el incidente en una prueba permanente: la configuración exacta de esa conversación ahora es un fixture, y CI verifica que el contexto del asistente contenga todos los datos necesarios para acertar.

Si estás construyendo un agente dentro de tu app
  1. Un resumen de tus datos escrito a mano ya está obsoleto. Haz que la paridad entre la UI y el agente sea una prueba que falle, no una promesa que alguien cumpla.
  2. Dale al modelo resultados del servidor, nunca su propio plan. Un agente que narra intenciones terminará confirmando una ficción.
  3. Haz pasar las escrituras del agente por las mismas rutas de código con controles que los clics humanos. Así, un agente confundido resulta vergonzoso, nunca peligroso.
  4. Arregla lo que el modelo puede ver antes de debatir qué tan inteligente es. Un modelo más grande razonando sobre datos que faltan solo confabula con una prosa más bonita.