¿Te resulta más fácil leer con fondo crema, un tono de color, más contraste o un texto más grande? Elige tus colores y tu texto una sola vez, para todas las páginas.

Lección gratuita

Mensajes system, user y assistant

Después de esta lección: Puedo construir la lista de mensajes que toma una llamada a un modelo de lenguaje.

Lección de 60 min4 comandosLaboratorio de agente de programación con IA: usa opencode para explicar y corregir código, y comprueba su trabajo

Una llamada toma una lista de mensajes

Una llamada de chat a un modelo de lenguaje toma una lista de mensajes. Cada mensaje tiene un rol y un texto. El modelo lee toda la lista y escribe el mensaje siguiente.

Esto importa porque un modelo no recuerda nada entre una llamada y otra. Un chat que parece tener memoria es un programa que vuelve a enviar los mensajes anteriores cada vez.

Al final de esta lección

Nota

Puedo construir la lista de mensajes que toma una llamada a un modelo de lenguaje.

Palabras clave

PalabraQué significaEjemplo
Mensajeun turno del chat: un rol y un texto
Rolquién escribe un mensajeuser
Mensaje systemla instrucción que fija cómo se comporta el modelo
Historiallos mensajes anteriores, que se reenvían en cada llamada
Cuerpo de la peticiónel JSON que un programa envía con una llamada

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

Un mensaje es un rol y un texto

Un mensaje es un objeto con dos claves. role dice quién habla y content guarda las palabras.

Hay tres roles. El mensaje system dice cómo debe comportarse el modelo. Un mensaje user es lo que dice la persona. Un mensaje assistant es algo que el modelo dijo antes.

El cuerpo de la petición de una llamada

Código: node
{
  "model": "lab",
  "max_tokens": 60,
  "messages": [
    { "role": "system", "content": "Respondes con una sola frase corta." },
    { "role": "user", "content": "¿Qué es una variable en programación?" }
  ]
}

Envía una llamada al modelo del laboratorio

Comando
docker exec -w /home/node/project agent node -e '/* El cuerpo de la petición: un nombre de modelo, un límite de respuesta y una lista de mensajes. */ const body = { model: "lab", max_tokens: 60, messages: [ { role: "system", content: "Respondes con una sola frase corta." }, { role: "user", content: "¿Qué es una variable en programación?" } ] }; (async () => { try { const url = (process.env.AI_URL || "http://ai:4000") + "/v1/chat/completions"; const answer = await fetch(url, { method: "POST", body: JSON.stringify(body), headers: { "content-type": "application/json", authorization: "Bearer lab" } }); if (!answer.ok) throw new Error("HTTP " + answer.status); const data = await answer.json(); console.log("Respuesta del modelo: " + data.choices[0].message.content); } catch (error) { console.log("Ningún modelo respondió (" + error.message + ")."); console.log("Muestra grabada: Una variable guarda un valor que el programa puede cambiar."); } })();'

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

El modelo no recuerda nada entre llamadas

Cada llamada es independiente. El modelo solo ve los mensajes de esa llamada.

Para mantener una conversación, tu programa guarda una lista. Tras cada respuesta añade la pregunta y la respuesta. En la llamada siguiente reenvía el historial, con la pregunta nueva al final.

Escribe el archivo de ayuda lab.js

Comando
docker exec -w /home/node/project agent node -e 'require("fs").writeFileSync("lab.js", ["// lab.js, parte 1: el único sitio que envía una llamada al modelo del laboratorio.", "const base = process.env.AI_URL || \"http://ai:4000\";", "", "async function send(body, timeout) {", "  return fetch(base + \"/v1/chat/completions\", {", "    method: \"POST\",", "    headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },", "    body: JSON.stringify({ model: \"lab\", ...body }),", "    signal: AbortSignal.timeout(timeout || 30000),", "  });", "}"].join("\n") + "\n")'

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Añade preguntar con una muestra grabada

Comando
docker exec -w /home/node/project agent node -e 'require("fs").appendFileSync("lab.js", ["", "// Parte 2: preguntar una vez. Si el modelo no puede responder, devuelve la muestra grabada.", "async function ask(body, sample) {", "  try {", "    const answer = await send(body);", "    if (!answer.ok) throw new Error(\"HTTP \" + answer.status);", "    const data = await answer.json();", "    return { label: \"Respuesta del modelo\", text: data.choices[0].message.content, data: data };", "  } catch (error) {", "    return { label: \"Muestra grabada\", text: sample, note: error.message };", "  }", "}", "", "module.exports = { send, ask };"].join("\n") + "\n")'

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Pregunta con el historial y luego sin él

Comando
docker exec -w /home/node/project agent node -e 'const lab = require("./lab.js"); const system = { role: "system", content: "Responde con una frase corta." }; const history = [ { role: "user", content: "Me llamo Marta." }, { role: "assistant", content: "Encantado, Marta." } ]; const question = { role: "user", content: "¿Cómo me llamo?" }; (async () => { const withHistory = await lab.ask({ max_tokens: 30, messages: [system, ...history, question] }, "Te llamas Marta."); console.log("Con el historial, " + withHistory.label + ": " + withHistory.text); const without = await lab.ask({ max_tokens: 30, messages: [system, question] }, "No sé cómo te llamas."); console.log("Sin él, " + without.label + ": " + without.text); console.log("Mensajes enviados en cada llamada: " + [system, ...history, question].length + " y " + [system, question].length); })();'

Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.

Un error frecuente

Advertencia

Mucha gente cree que el modelo recuerda la llamada anterior. No es así. La segunda llamada, sin historial, no podía saber el nombre. Si quieres que el modelo use algo, ponlo en los mensajes que envías.

Ejemplo resuelto: lo que llevó cada llamada

LlamadaMensajes enviadosLo que el modelo puede saber
Con el historialsystem, user, assistant, user (4)El nombre Marta
Sin élsystem, user (2)Nada sobre Marta

Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.

El orden de los mensajes importa

Pon primero el mensaje system. Después los turnos se alternan: user, assistant, user, y así. El último mensaje es del usuario, porque es lo que el modelo responde.

La mayoría de las API de modelos rechazan una lista que rompe este orden, o dan respuestas raras. Comprueba el orden en tu código antes de enviar.

Para ir más lejos: cambia el mensaje system

Abre el Run del historial y cambia el mensaje system por «Responde en inglés». Pulsa Run otra vez.

Anota qué cambió y qué no. Después di en una frase por qué la instrucción tenía que estar en los mensajes que enviaste.

Sobre este laboratorio y su código: Laboratorio de agente de programación con IA: usa opencode para explicar y corregir código, y comprueba su trabajo

¿Enseñas a una clase? Asigna este módulo a tus alumnos con guía del profesor, tareas con corrección automática y un laboratorio para cada alumno.