¿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 trazar una llamada a un modelo

Después de esta lección: Puedo registrar lo que hace cada llamada para encontrar de dónde viene un fallo o un retraso.

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

Encuentra el paso lento

Trazar una llamada a un modelo es registrar qué hizo cada paso de una petición y cuánto tardó. Importa porque una función con modelo falla en mitad de una cadena. Una línea de registro al principio y otra al final no dicen dónde.

Un usuario avisa de que las respuestas tardan diez segundos. ¿Fue el prompt, el modelo, un reintento o la revisión posterior? Una traza lo responde de un vistazo.

Al final de esta lección

Nota

Puedo registrar lo que hace cada llamada para encontrar de dónde viene un fallo o un retraso.

Palabras clave

PalabraQué significa
Trazael registro de una petición, de principio a fin
Spanun paso cronometrado dentro de una traza
Padreel span que contiene a otro span
Atributoun dato con nombre guardado en un span
Id de trazael id que comparten todos los spans de una petición
Censuraquitar los secretos antes de guardar un registro

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

Una traza es un árbol de spans

Una traza es una petición. Se compone de spans. Cada span tiene un nombre, un inicio, un final y un padre: el span que lo contiene. Solo un span no tiene padre: la raíz.

Todos los spans de una petición llevan el mismo id de traza. Así los reúne un almacén. Un span guarda también atributos, como la ruta o el resultado.

Ejemplo resuelto: una petición

SpanPadreInicioFinalDuró
peticiónninguno0856856 ms
construir el promptpetición044 ms
llamada al modelopetición4854850 ms
revisar la respuestapetición8548562 ms

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

Lo que una traza nunca debe guardar

Un almacén de trazas lo lee mucha gente y se conserva semanas. Por eso un span nunca guarda un secreto: una clave de API, un token o una contraseña. Nunca guarda datos personales: un correo, un nombre o un teléfono.

Normalmente tampoco debe guardar el prompt ni la respuesta. Guarda tamaños, cuentas y resultados. Quita las claves peligrosas en un solo sitio, antes de guardar el span.

Un error frecuente

Advertencia

Mucha gente cree que guardar el prompt entero en el registro es inofensivo, porque el registro es interno. Pero los prompts contienen lo que escribieron los usuarios, y escriben nombres y secretos. Guarda la longitud y el resultado, no el texto.

Escribe el ayudante 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\";' '// 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

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

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' '' 'let now = 0; // un reloj que movemos nosotros: todos imprimen los mismos tiempos' 'const spans = [];' 'function tick(ms) { now = now + ms; }' 'function start(name, parent) {' '  const span = { id: spans.length + 1, name, parent, start: now, end: now, attrs: {} };' '  spans.push(span);' '  return span;' '}' 'function finish(span) { span.end = now; }' > trace1.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 trazador, parte 2

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// Claves que nunca llegan a una traza: secretos y datos personales.' 'const NEVER = [\"email\", \"api_key\", \"prompt\", \"name\"];' 'function note(span, key, value) {' '  span.attrs[key] = NEVER.includes(key) ? \"[quitado]\" : value;' '}' >> trace1.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 trazador, parte 3

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function show(parent, depth) {' '  for (const s of spans) {' '    if (s.parent === parent) {' '      console.log(\"  \".repeat(depth) + s.name + \" \" + (s.end - s.start) + \" ms \" + JSON.stringify(s.attrs));' '      show(s.id, depth + 1);' '    }' '  }' '}' >> trace1.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 trazador, parte 4

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function modelStep(root) {' '  const call = start(\"llamada al modelo\", root.id);' '  const messages = [{ role: \"user\", content: \"Di un uso de una traza, en una frase.\" }];' '  const reply = await ask(messages, \"Una traza muestra qué paso de una petición fue lento.\", 40);' '  tick(850); // 850 ms fijos en nuestro reloj, tarde lo que tarde la llamada real' '  note(call, \"resultado\", reply.text.length > 0 ? \"ok\" : \"vacío\");' '  note(call, \"prompt\", messages[0].content);' '  finish(call);' '}' >> trace1.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 trazador, parte 5

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  const root = start(\"petición\", 0);' '  note(root, \"ruta\", \"/pregunta\");' '  note(root, \"email\", \"[email protected]\");' '  const build = start(\"construir el prompt\", root.id);' '  tick(4); finish(build);' '  await modelStep(root);' '  const check = start(\"revisar la respuesta\", root.id);' '  tick(2); finish(check); finish(root);' '  console.log(\"traza t-0001\");' '  show(0, 0);' '}' 'main();' >> trace1.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 e imprime la traza

Comando
docker exec -w /home/node/project agent node trace1.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 maqueta y cómo leerla

El laboratorio no tiene servicio de trazas. Por eso los spans están en una lista y el reloj es un número que mueve el script. Un sistema real envía los spans a un servicio de trazas real. La forma es la misma.

Para hallar el paso lento usa el tiempo propio: la duración de un span menos las duraciones de sus hijos. Un padre siempre parece lento, porque contiene a sus hijos.

Una salida posible

LíneaQué muestra
traza t-0001El id de traza
petición 856 msLa raíz, con la ruta y el correo quitado
construir el prompt 4 msUn hijo, con dos espacios de sangría
llamada al modelo 850 msEl paso lento, con el prompt quitado
revisar la respuesta 2 msEl último hijo

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.