¿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

Cómo construir un chatbot que funcione

Después de esta lección: Puedo reunir estado, recorte, mensaje system, flujo y fuentes en un solo chatbot.

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

Junta las cinco partes

Un chatbot que funciona reúne cinco partes en un solo bucle. Guarda estado, lo recorta, sigue un mensaje system, envía su respuesta en flujo y muestra sus fuentes. Importa porque cada parte funciona sola, y los fallos aparecen donde se juntan.

El chatbot de una biblioteca responde tres preguntas seguidas. Debe recordar la primera, mantenerse en su presupuesto y citar una fuente cada vez.

Al final de esta lección

Nota

Puedo reunir estado, recorte, mensaje system, flujo y fuentes en un solo chatbot.

Palabras clave

PalabraQué significa
Bucle de chatlos pasos que se repiten con cada mensaje del usuario
Turnoun mensaje del usuario y la respuesta que recibe
Historialtodos los mensajes del chat hasta ahora
Presupuestoel máximo de tokens que decides enviar
Plan Blo que hace el bot cuando ningún modelo responde

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

Un turno usa las cinco partes

Cada turno sigue los mismos pasos. Carga el historial. Añade el mensaje del usuario. Recorta lo que vas a enviar. Llama al modelo. Comprueba la respuesta. Guarda el historial.

Escribe esos pasos en una sola función, turn. Entonces el chat es esa función llamada una y otra vez.

Ejemplo resuelto: los pasos de un turno

PasoDe la lecciónQué ocurre
Cargar y añadirRecordar la conversaciónEl mensaje del usuario se une a la lista de la sesión
RecortarRecortar el historialLos mensajes viejos quedan fuera de esta llamada
Mensaje y fuentesMensaje system, CitasEl mensaje system lleva las reglas y las fuentes numeradas
Llamar y comprobarCitasSe comprueba que la respuesta no tenga marcas sin fuente
Flujo y guardarRespuesta en flujoLos trozos van a la página y se guarda el historial

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

Guardar no es enviar

El historial guarda todos los mensajes. La llamada envía solo lo que cabe en el presupuesto. Por eso recortar nunca cambia el registro. Cambia una llamada.

Así también puedes cambiar el presupuesto más tarde, y una persona sigue pudiendo leer el chat entero.

Si el modelo no responde

A veces ningún modelo responde: la IA está desactivada, se acabó el crédito o cae la red. Un plan B es un mensaje claro que lo dice, para que el chat no se cuelgue ni se rompa.

En este laboratorio, cada Run imprime entonces una muestra grabada, señalada como tal. Un producto real mostraría su propio aviso.

Un error frecuente

Advertencia

Mucha gente cree que un chatbot está terminado cuando la demostración funciona. Una demostración usa preguntas amables. Prueba preguntas difíciles, un chat muy largo y ningún modelo. Cuenta los tokens que cuesta un chat antes de que lo use más gente.

Escribir la ayuda del modelo, parte 1

Comando
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

Comando
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 chatbot, parte 1

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const fs = require(\"fs\");' 'const { ask } = require(\"./model\");' 'const BUDGET = 150; // tokens para el mensaje system y el historial juntos' 'const SOURCES = [' '  \"Los socios pueden llevarse hasta seis libros durante tres semanas.\",' '  \"Un libro devuelto tarde cuesta diez céntimos al día.\",' '  \"La biblioteca abre a las nueve y cierra a las cinco de la tarde en días laborables.\",' '];' > chatbot6.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 chatbot, parte 2

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function tokens(m) { return Math.ceil(m.content.length / 4) + 4; }' 'function total(list) {' '  return list.reduce(function (sum, m) { return sum + tokens(m); }, 0);' '}' 'function systemMessage() {' '  const numbered = SOURCES.map(function (t, at) { return \"[\" + (at + 1) + \"] \" + t; }).join(\" \");' '  const rule = \"Eres el asistente de la biblioteca. Responde en dos frases como máximo, solo con estas fuentes. Escribe el número de la fuente entre corchetes después de cada dato. Si no lo dicen, responde: No lo sé. \";' '  return { role: \"system\", content: rule + numbered };' '}' >> chatbot6.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 chatbot, parte 3

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function trim(history, system) {' '  const kept = history.slice();' '  while (total([system].concat(kept)) > BUDGET && kept.length > 1) kept.shift();' '  return kept;' '}' 'function badMarkers(reply) {' '  const bad = [];' '  const re = /\[(\d+)\]/g;' '  let m = re.exec(reply);' '  while (m) {' '    if (Number(m[1]) < 1 || Number(m[1]) > SOURCES.length) bad.push(m[1]);' '    m = re.exec(reply);' '  }' '  return bad;' '}' >> chatbot6.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 chatbot, parte 4

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function turn(state, text, sample) {' '  state.history.push({ role: \"user\", content: text });' '  const system = systemMessage();' '  const sent = trim(state.history, system);' '  const held = state.history.length;' '  const reply = await ask([system].concat(sent), sample, 70);' '  state.history.push({ role: \"assistant\", content: reply });' '  fs.writeFileSync(\"chat.json\", JSON.stringify(state, null, 1));' '  console.log(\"Tú: \" + text);' '  console.log(\"Bot: \" + reply);' '  console.log(\"  enviados \" + sent.length + \" de \" + held + \" mensajes, marcas falsas: \" + badMarkers(reply).length);' '}' >> chatbot6.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 chatbot, parte 5

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  const state = { history: [] };' '  // Muestras grabadas, usadas solo si ningún modelo responde.' '  await turn(state, \"¿Cuántos libros puedo llevarme?\", \"Puedes llevarte seis libros durante tres semanas [1].\");' '  await turn(state, \"¿Y cuánto cuesta un retraso?\", \"Un libro tarde cuesta diez céntimos al día [2].\");' '  await turn(state, \"¿A qué hora cierran el viernes?\", \"La biblioteca cierra a las cinco de la tarde en días laborables [3].\");' '  console.log(\"Guardado: \" + state.history.length + \" mensajes en chat.json\");' '}' 'main();' >> chatbot6.js"

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

Mantener un chat de tres turnos

Comando
docker exec -w /home/node/project agent node chatbot6.js

Se 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íneaLo que puede mostrar
Tú y BotTres preguntas, cada una con una respuesta corta que puede citar una fuente
enviados n de mCon la muestra grabada: 1 de 1, 3 de 3 y 3 de 5. En el tercer turno se supera el presupuesto y se dejan fuera dos mensajes viejos. Con un modelo real, el último número puede cambiar
marcas falsasCasi siempre 0. Un número mayor que 0 indica que la respuesta cita una fuente que no existe
GuardadoGuardado: 6 mensajes en chat.json

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

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.