¿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 enviar una imagen a un modelo

Después de esta lección: Puedo construir una petición que lleve una imagen y rechazar un archivo que incumple una regla antes de enviarlo.

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

Un usuario sube un archivo llamado vacaciones.png

Un usuario añade un archivo llamado vacaciones.png al chat de tu aplicación. No puedes saber qué hay dentro. Puede ser una foto o un programa con nombre de imagen.

Enviar una imagen a un modelo con seguridad es comprobarla antes de que salga de tu servidor. Esta lección enseña las comprobaciones.

Al final de esta lección

Nota

Puedo construir una petición que lleve una imagen y rechazar un archivo que incumple una regla antes de enviarlo.

Palabras clave

PalabraQué significaEjemplo
Base64una forma de escribir bytes con letras y cifras de texto
Tipo de mediouna etiqueta que dice qué clase de archivo esimage/png
Primeros bytesel comienzo de un archivo, que dice lo que es de verdad
Cargalos datos que lleva una petición
Límitelo máximo que acepta el servicio

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

Una imagen viaja como texto

Una petición suele ser texto, pero una imagen son bytes. Base64 convierte cada 3 bytes en 4 caracteres normales, y así la petición sigue siendo texto.

Tiene un precio. El texto es un tercio más largo que los bytes. Por eso un límite sobre los bytes no es un límite sobre la petición.

Cómo base64 agranda un archivo

BytesCaracteresPor qué
34Un grupo de 3 pasa a 4
48Se empieza un segundo grupo
2840Diez grupos de 4
30004000Un tercio más, más o menos

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

Fíate de los primeros bytes, no del nombre

Un nombre es solo lo que alguien escribió. Los primeros bytes de un archivo los fija su formato. Un PNG empieza siempre con los mismos 8 bytes, y un JPEG con 3.

Por eso el tipo de medio se decide leyendo los primeros bytes. Si el nombre no coincide, rechaza el archivo o al menos avisa.

Un error frecuente

Advertencia

Mucha gente se fía del nombre del archivo y del tipo que informa el navegador. Los escribe quien envía. Comprueba los primeros bytes en tu propio servidor, porque un programa llamado vacaciones.png pasaría una comprobación del nombre.

Primero se comprueba, luego se envía

Haz todas las comprobaciones antes de que salga un solo byte. Reúne todos los problemas en una lista, en un orden fijo, para que la persona los arregle de una vez. Solo un archivo sin problemas se codifica y entra en la carga.

Una llamada rechazada también cuesta tiempo, y el modelo nunca ve un archivo que no comprobaste.

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 script de comprobación, parte 1

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const head = [137, 80, 78, 71, 13, 10, 26, 10];' 'const png = Buffer.concat([Buffer.from(head), Buffer.alloc(40, 7)]);' 'const fake = Buffer.concat([Buffer.from(\"MZ\"), Buffer.alloc(40, 7)]);' 'const huge = Buffer.concat([Buffer.from(head), Buffer.alloc(142, 7)]);' 'const LIMIT = 100;' > send2.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 script de comprobación, parte 2

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function sniff(b) {' '  if (b.length >= 8 && b[0] === 137 && b[1] === 80 && b[2] === 78 && b[3] === 71) return \"png\";' '  if (b.length >= 3 && b[0] === 255 && b[1] === 216 && b[2] === 255) return \"jpeg\";' '  return \"desconocido\";' '}' 'function check(b) {' '  const problems = [];' '  if (b.length > LIMIT) problems.push(\"demasiado grande\");' '  if (sniff(b) === \"desconocido\") problems.push(\"tipo no permitido\");' '  return problems;' '}' >> send2.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 script de comprobación, parte 3

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  const list = [[\"gato.png\", png], [\"apuntes.png\", fake], [\"enorme.png\", huge]];' '  for (const item of list) {' '    const b = item[1];' '    console.log(item[0] + \": \" + sniff(b) + \", \" + b.length + \" bytes, base64 \" + b.toString(\"base64\").length + \" caracteres\");' '    console.log(\"  problemas: \" + JSON.stringify(check(b)));' '  }' >> send2.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 script de comprobación, parte 4

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' '  const part = { type: \"image\", media_type: \"image/png\", data: png.toString(\"base64\").slice(0, 12) + \"...\" };' '  console.log(JSON.stringify({ messages: [{ role: \"user\", content: [{ type: \"text\", text: \"Descríbela.\" }, part] }] }));' '  const q = \"Escribe una frase corta y amable que diga a un usuario que su imagen era demasiado grande para enviarla.\";' '  const reply = await ask([{ role: \"user\", content: q }], \"Esa imagen es demasiado grande para enviarla. Elige una más pequeña, por favor.\", 40);' '  console.log(\"respuesta: \" + reply.text);' '}' 'main();' >> send2.js"

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

Comprueba tres archivos

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

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

Las líneas que imprime

LíneaQué muestra
gato.png: png, 48 bytes, base64 64 caracteresBytes de un PNG de verdad, y 48 bytes pasaron a 64 caracteres
apuntes.png: desconocido ... tipo no permitidoEl nombre dice png, los bytes no
enorme.png: png, 150 bytes ... demasiado grandeTipo correcto, pero sobre el límite de 100
{"messages":[...]}El cuerpo de la petición, con los datos recortados

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.