El discurso habitual de un bot de soporte con IA es este: conecta tus fuentes de datos, nosotros las indexamos y tu bot responderá a partir de ellas.
Vuelve a leerlo con ojos de ingeniero. Conecta tus fuentes de datos significa construir una exportación. Nosotros las indexamos significa que los registros de tus clientes ahora también viven en el sistema de un proveedor. Y «responderá a partir de ellas» significa responder a partir de lo que la última sincronización haya llegado a copiar.
Para la documentación, ese trato está bien: los artículos cambian cada semana y un índice es una forma razonable de guardarlos. Para cualquier dato en vivo, es un mal trato. Los precios cambian. El inventario cambia. El estado de un pedido cambia mientras el cliente escribe. Un índice es una foto de algo que se mueve.
La otra opción
Hay una idea mucho más antigua a mano, y no implica mover tus datos a ninguna parte: dale al modelo una función y deja que la llame.
Eso es la llamada a herramientas (tool calling) desde que existe. Lo inusual es dónde se ejecuta la función. En la mayoría de las implementaciones se ejecuta en los servidores del proveedor, y eso te devuelve al punto de partida: darles credenciales, añadir sus IP a una lista de permitidas y construir una integración.
Kookee la ejecuta en el navegador del visitante.
KookeeChat.init({
apiKey: 'YOUR_API_KEY',
tools: [{
name: 'search_orders',
description: "Look up the signed-in customer's orders.",
parameters: { type: 'object', properties: { status: { type: 'string' } } },
handler: ({ status }) =>
fetch('/api/my/orders?status=' + status, { credentials: 'include' })
.then((r) => r.json()),
}],
});
Fíjate en qué es ese handler. Es un fetch a tu API, con credentials: 'include', que se ejecuta en tu página, como el visitante que ya inició sesión.
De ahí salen tres consecuencias, y son la razón de todo esto:
No construiste ninguna integración. Ninguna clave de API intercambiada, ninguna lista de IP permitidas, ningún webhook, ninguna app OAuth. El endpoint al que llamas es uno al que tu frontend ya llama.
Kookee nunca guarda tus datos. El nombre, la descripción y el esquema de parámetros de la herramienta llegan a Kookee: lo justo para que el modelo sepa que la herramienta existe y qué tiene que pasarle. El handler se ejecuta en el navegador; su resultado pasa por Kookee hasta el modelo para esa respuesta y no se almacena. Tu tabla de pedidos no está en la base de datos de Kookee, porque nunca te la pedí.
La autenticación ya está resuelta. La pregunta más difícil de «una IA que puede ver datos de clientes» es qué cliente. Aquí ni siquiera es una pregunta. La solicitud lleva la cookie de sesión del propio visitante, así que devuelve sus pedidos y los de nadie más, y eso lo garantiza el mismo código de autorización que protege el resto de tu app.
Cómo lo ve el visitante
Visitante: ¿dónde está mi último pedido?
Revisando tus pedidos…
Asistente: Tu pedido #4821 se envió el 21 de agosto y llega el martes. Seguimiento: DHL 0034…
La pausa del medio es la parte interesante. El turno se detiene de verdad: el modelo pide una herramienta, la respuesta se interrumpe, el navegador ejecuta tu función, el resultado vuelve y la respuesta continúa. Para el visitante es una sola respuesta seguida, con una línea de estado.
Cómo llegué a construirlo así
Mi primer diseño devolvía el historial de la conversación al navegador entre las dos mitades y dejaba que el cliente lo reenviara. Sin estado, sin memoria en el servidor, elegante en una pizarra.
Dos revisores lo tumbaron, por razones que vale la pena repetir:
Filtraba datos. La búsqueda de artículos de ayuda de Kookee se ejecuta en el servidor y devuelve el texto completo de los artículos, incluidos los chatbot_only, que no aparecen en el centro de ayuda público. Reenviar los mensajes del turno a través del navegador los habría dejado en la pestaña de red de cualquiera que abriera las herramientas de desarrollo.
Se podía falsificar. Si el cliente guarda el estado de la conversación, el cliente puede editarlo. Quita unos cuantos elementos del historial y el contador de rondas vuelve a cero: un bucle sin límite de llamadas al modelo a costa del presupuesto de otro.
Así que el turno en pausa vive en el servidor, en Redis, detrás de un token de un solo uso que caduca pronto. El navegador recibe solo lo que necesita: qué funciones llamar, con qué argumentos y un token opaco para reanudar. No ve los resultados de las herramientas del servidor ni puede reescribir el turno.
Esa comprobación del token tiene su propia sutileza, y al principio la hice mal: el código original borraba el token antes de verificar a qué proyecto pertenecía. Nadie podría adivinar un token aleatorio de 24 bytes, pero una solicitud desde el proyecto equivocado igualmente destruiría un turno legítimo en curso. Primero leer, luego verificar y después borrar.
Salvaguardas, y dónde se detienen
Algunas cosas que Kookee impone:
Los resultados de las herramientas se envuelven como datos no confiables, nunca como instrucciones.
Se les quitan los marcadores de cita, para que un resultado de herramienta no pueda inventarse una fuente.
Cada handler tiene un tiempo límite de 15 segundos; los resultados se cortan a 4000 caracteres.
Si una herramienta lanza un error, ese error vuelve al modelo en lugar de tumbar la respuesta.
Y una cosa que a propósito no hace: no hay confirmación automática antes de que se ejecute una herramienta. Si registras algo con efectos secundarios (cancelar un pedido, enviar un correo), se ejecuta cuando el modelo decide llamarlo.
Pensé en añadir un diálogo de confirmación para cada herramienta y lo descarté. Una confirmación en una búsqueda de productos de solo lectura es fricción sin ninguna ventaja, y a partir de un esquema JSON no puedo saber cuál es cuál. Tú sí. Así que registra sin miedo las herramientas de lectura y pon un window.confirm dentro de cualquier handler que escriba.
Cuándo no deberías usar esto
Si la respuesta está en tu documentación, escribe un artículo. Las herramientas son para datos que cambian más rápido de lo que puedes publicar: precios, inventario, estado de los pedidos, estado de la cuenta. Todo lo que sea estable sale más barato, más rápido y más fiable como artículo de ayuda, y se cita con un enlace que el visitante puede abrir.
La regla práctica que uso: si para cambiar la respuesta tendrías que volver a desplegar, es una herramienta. Si bastaría con editar una página, es un artículo.
Ya disponible en @kookee/react 1.5.0 y @kookee/sdk 1.5.0, y en la etiqueta script sin instalar nada. Diez herramientas por widget, más de las que necesita de verdad cualquier asistente de soporte que yo haya visto.