Una memoria que olvida
La memoria de un agente es un pequeño almacén de notas que el agente puede consultar después. Una buena memoria olvida a propósito: las notas viejas caducan y algunas cosas no se guardan nunca.
Importa porque una memoria que lo guarda todo guarda también contraseñas y datos que ya no son ciertos. Construir una memoria es otro tema, el de construir agentes. Esta lección añade el olvido.
Al final de esta lección
Nota
Puedo dar a un agente una memoria que olvida a propósito.
Palabras clave
| Palabra | Qué significa |
|---|---|
| Almacén de memoria | una lista de notas que el agente puede consultar |
| Palabra clave | una palabra que sirve para encontrar una nota |
| Tiempo de vida | cuántos días se conserva una nota |
| Caducar | olvidarse porque una nota es demasiado antigua |
| Regla de no guardar | una prueba que rechaza una nota al entrar |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Las notas caducan con la edad
Cada nota lleva el día en que se escribió. Su edad es hoy menos ese día. Cuando la edad supera el tiempo de vida, la nota ha caducado y no se usa.
Para buscar algo, cuenta cuántas palabras de la consulta están en cada nota. Ordena por ese número y, a igualdad, por el día más reciente.
Ejemplo resuelto: día 40, tiempo de vida 30
| Nota | Día | Edad | ¿Se usa? |
|---|---|---|---|
| desplegar en preproduccion el viernes | 5 | 35 | No: caducada |
| el servidor de preproduccion se llama caja7 | 20 | 20 | Sí, 1 acierto |
| desplegar necesita dos aprobaciones | 33 | 7 | Sí, 1 acierto |
| el arreglo de desplegar en preproduccion ya esta integrado | 39 | 1 | Sí, 2 aciertos, la primera |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Un error frecuente
Advertencia
Mucha gente cree que una memoria debe guardarlo todo, porque más es más útil. No lo es. Las notas viejas pueden ser falsas, y una nota con una contraseña es una filtración esperando. Decide qué olvidar antes de decidir qué guardar.
Hay cosas que no se guardan nunca
Comprueba cada nota al entrar, antes de guardarla. Rechaza palabras como contraseña, una dirección de correo o una tira larga de cifras, como el número de una tarjeta.
Estas reglas son simples y se les escapan cosas. Son una red de seguridad, y van junto a una regla para el agente: no escribir nunca un secreto.
Escribe el ayudante 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\";' '// Los tokens de una respuesta, o null si no trae ninguno.' 'function count(data, name) {' ' return data.usage && typeof data.usage[name] === \"number\" ? data.usage[name] : null;' '}' > 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.
Escribe el ayudante del modelo, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' '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 || 100 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"estado \" + res.status);' ' const d = await res.json();' ' return { text: d.choices[0].message.content, inTokens: count(d, \"prompt_tokens\"), outTokens: count(d, \"completion_tokens\"), recorded: false };' ' } catch (error) {' ' console.log(\"Muestra grabada: ningún modelo respondió (\" + error.message + \").\");' ' return { text: sample, inTokens: null, outTokens: null, recorded: true };' ' }' '}' '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.
Escribe el almacén de memoria, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const blocked = [\"contraseña\", \"secreto\", \"token\"];' 'function check(text) {' ' const low = text.toLowerCase();' ' for (const b of blocked) { if (low.indexOf(b) >= 0) return \"rechazada: lleva la palabra \" + b; }' ' if (text.indexOf(\"@\") >= 0) return \"rechazada: lleva un correo electrónico\";' ' if (/[0-9]{12}/.test(text)) return \"rechazada: lleva un número largo\";' ' return \"ok\";' '}' 'const store = [];' 'function add(text, day) {' ' const why = check(text);' ' if (why === \"ok\") store.push({ text: text, day: day });' ' return why;' '}' > memory.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el almacén de memoria, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function find(query, today, ttl) {' ' const words = query.toLowerCase().split(\" \");' ' const live = store.filter(function (n) { return today - n.day <= ttl; });' ' const scored = live.map(function (n) {' ' const have = n.text.toLowerCase().split(\" \");' ' return { text: n.text, day: n.day, hits: words.filter(function (w) { return have.indexOf(w) >= 0; }).length };' ' });' ' return scored.filter(function (n) { return n.hits > 0; }).sort(function (a, b) { return b.hits - a.hits || b.day - a.day; });' '}' >> memory.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el almacén de memoria, parte 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const notes = [' ' [\"desplegar en preproduccion el viernes\", 5],' ' [\"el servidor de preproduccion se llama caja7\", 20],' ' [\"desplegar necesita dos aprobaciones\", 33],' ' [\"mi contraseña es pez1234\", 38],' ' [\"escribeme a [email protected]\", 38],' ' [\"el arreglo de desplegar en preproduccion ya esta integrado\", 39],' '];' >> memory.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el almacén de memoria, parte 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' ' notes.forEach(function (n) { console.log(\"añadir día \" + n[1] + \": \" + add(n[0], n[1])); });' ' console.log(\"guardadas \" + store.length + \", vigentes \" + store.filter(function (n) { return 40 - n.day <= 30; }).length);' ' find(\"desplegar preproduccion\", 40, 30).forEach(function (n) { console.log(\"aciertos \" + n.hits + \", edad \" + (40 - n.day) + \": \" + n.text); });' ' const q = \"En una frase corta: ¿por qué debe un agente olvidar las notas antiguas?\";' ' const sample = \"Las notas viejas pueden ser falsas hoy, y una memoria que lo guarda todo guarda también lo que no debe.\";' ' const reply = await ask([{ role: \"user\", content: q }], sample, 60);' ' console.log(\"el modelo dijo: \" + reply.text);' '}' 'main();' >> memory.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejecuta el almacén de memoria
docker exec -w /home/node/project agent node memory.jsSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Lo que imprime el Run
| Línea | Qué muestra |
|---|---|
| añadir día 5: ok | La nota se guarda |
| añadir día 38: rechazada: lleva la palabra contraseña | La regla frenó un secreto |
| añadir día 38: rechazada: lleva un correo electrónico | La regla frenó una dirección |
| guardadas 4, vigentes 3 | Una nota ha caducado |
| aciertos 2, edad 1: el arreglo de desplegar ... | La mejor coincidencia va primero |
| el modelo dijo: ... | Una frase. Las palabras cambian en cada ejecución |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.