Por qué un chatbot olvida tu nombre
Un chatbot guarda los mensajes de una conversación y los envía otra vez con cada pregunta nueva. Importa porque una llamada a un modelo de lenguaje no tiene memoria propia.
Un cliente cuenta al chatbot de una empresa de autobuses que ha perdido su billete. Luego pregunta «¿y para mi hermano?». El bot solo lo entiende si los mensajes anteriores viajan con la pregunta.
Al final de esta lección
Nota
Puedo mantener una conversación entre una petición y otra.
Palabras clave
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Sesión | la conversación de una persona con el chatbot | |
| Id de sesión | una etiqueta que señala una sesión | lucia |
| Estado | lo que el programa recuerda entre peticiones | |
| Almacén | el lugar donde se guardan todas las sesiones | |
| Lista de mensajes | los mensajes que se envían con cada llamada |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Una llamada al modelo parte de cero
Cada llamada a un modelo empieza de nuevo. El modelo solo conoce los mensajes que envías en esa llamada.
Para hacer una conversación, tu programa guarda todos los mensajes. Envía la lista entera cada vez, con la pregunta nueva al final. Esa lista es el estado del chat.
Ejemplo resuelto: tres peticiones
| Petición | Mensajes enviados | Lo que el modelo puede ver |
|---|---|---|
| 1 | system, user | Solo la regla y «Me llamo Lucía.» |
| 2 | system, user, assistant, user | El primer intercambio y «¿Cómo me llamo?» |
| 3 | system, user, assistant, user, assistant, user | Todo lo anterior y la pregunta más reciente |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Cada persona tiene su propia sesión
Un chatbot habla con mucha gente a la vez. Por eso el almacén se organiza por id de sesión: una lista por conversación. Un id nuevo abre una lista nueva. Un id conocido añade a su lista.
El almacén puede ser una variable mientras el programa funciona. Un pequeño archivo JSON en la carpeta del proyecto lo conserva cuando el programa se detiene.
¿De quién es el estado?
El estado es de tu programa, no del modelo. Por eso tú decides qué se guarda y durante cuánto tiempo.
No guardes contraseñas, números de tarjeta ni nada que el chatbot no necesite. Da a la gente una forma de borrar una sesión. Borrar una sesión elimina toda la lista.
Un error frecuente
Advertencia
Mucha gente cree que el modelo recuerda quién habla. No es así. Si dos personas comparten una lista, cada una ve las palabras de la otra. Guarda una lista por cada id de sesión.
Escribir la ayuda del modelo, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' 'async function ask(messages, sample, max) {' ' try {' ' const res = await fetch(base + \"/v1/chat/completions\", {' ' method: \"POST\",' ' headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' ' body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 200 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"estado \" + res.status);' ' return (await res.json()).choices[0].message.content;' > model.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribir la ayuda del modelo, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' } catch (error) {' ' console.log(\"Muestra grabada: ningún modelo respondió (\" + error.message + \").\");' ' const list = [].concat(sample);' ' const turn = messages.filter(function (m) { return m.role === \"assistant\"; }).length;' ' return list[Math.min(turn, list.length - 1)];' ' }' '}' 'module.exports = { ask };' >> model.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribir el almacén de sesiones, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const fs = require(\"fs\");' 'const { ask } = require(\"./model\");' 'const FILE = \"sessions.json\"; // el almacén está en la carpeta del proyecto' 'const RULE = \"Eres un asistente amable. Responde en una frase corta.\";' '' 'function load() {' ' try { return JSON.parse(fs.readFileSync(FILE, \"utf8\")); } catch (error) { return {}; }' '}' 'function save(store) { fs.writeFileSync(FILE, JSON.stringify(store, null, 1)); }' > chat1.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribir el almacén de sesiones, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function say(store, id, text, samples) {' ' if (store[id] === undefined) store[id] = [{ role: \"system\", content: RULE }];' ' store[id].push({ role: \"user\", content: text });' ' const answer = await ask(store[id], samples, 40);' ' store[id].push({ role: \"assistant\", content: answer });' ' save(store);' ' return answer;' '}' >> chat1.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribir el almacén de sesiones, parte 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' const store = {};' ' // Muestras grabadas, usadas solo si ningún modelo responde.' ' const luciaSamples = [\"Encantado, Lucía.\", \"Te llamas Lucía.\"];' ' const pabloSamples = [\"Todavía no sé cómo te llamas.\"];' ' console.log(\"Lucía:\", await say(store, \"lucia\", \"Me llamo Lucía. Recuérdalo.\", luciaSamples));' ' console.log(\"Lucía:\", await say(store, \"lucia\", \"¿Cómo me llamo?\", luciaSamples));' ' console.log(\"Pablo:\", await say(store, \"pablo\", \"¿Cómo me llamo?\", pabloSamples));' ' const saved = load();' ' console.log(\"Guardado: lucia tiene \" + saved.lucia.length + \" mensajes, pablo tiene \" + saved.pablo.length);' '}' 'main();' >> chat1.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejecutar dos conversaciones
docker exec -w /home/node/project agent node chat1.jsSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Una salida posible
| Línea | Lo que puede mostrar |
|---|---|
| Lucía (primera) | Un saludo corto que puede usar el nombre Lucía |
| Lucía (segunda) | Una respuesta que puede decir el nombre, porque recibió los mensajes anteriores |
| Pablo | Una respuesta que no puede saber el nombre de Lucía, porque Pablo tiene su propia lista |
| Guardado | lucia tiene 5 mensajes, pablo tiene 3 |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.