Qué cambia cuando la entrada no es texto
Una entrada multimodal es una petición que lleva imágenes o documentos además de palabras. El modelo los lee, pero tu servicio ahora transporta archivos grandes, comprueba su tipo y paga por imagen o por página. Importa porque una petición de unos cientos de bytes puede pasar a pesar varios megabytes.
Un usuario fotografía un ticket de compra y sube un contrato de diez páginas. Una sola petición tiene ahora tres partes y tres maneras de ser rechazada.
Al final de esta lección
Nota
Puedo explicar qué cambia cuando la entrada es una imagen o un documento.
Palabras clave
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Parte | un trozo de la petición: texto, imagen o documento | |
| Tipo de medio | la etiqueta del formato de un archivo | image/png |
| Tamaño de la carga | los bytes que lleva una petición | |
| Límite de páginas | el máximo de páginas de un documento | |
| Precio unitario | el coste de una imagen o una página |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
El laboratorio solo admite texto
Advertencia
El punto de acceso al modelo del laboratorio acepta texto. Un Run no puede enviarle una imagen ni un documento. Esta lección parte de una forma de petición grabada, inventada para la lección, y la comprueba con código.
Una petición es una lista de partes
Una entrada de texto es una sola cadena. Una entrada multimodal es una lista de partes, cada una con un tipo y sus datos. Los datos de un archivo viajan como bytes, a menudo codificados como texto, lo que los hace un tercio más grandes.
Debajo hay una forma grabada, inventada para esta lección. Cada proveedor real tiene sus propios nombres de campo: lee siempre su documentación.
Una forma de petición grabada
{ "messages": [{ "role": "user", "content": [
{ "type": "text", "text": "¿Qué hay en este ticket de compra?" },
{ "type": "image", "media": "image/png", "data": "<base64, 412000 bytes>" },
{ "type": "document", "media": "application/pdf", "data": "<base64, 880000 bytes>" }
] }] }Comprueba cada parte antes de enviar
Comprueba cada parte con una regla que fijes tú: el tipo de medio, el tamaño en bytes y el número de páginas. Comprueba también los totales: cuántas imágenes, cuántos bytes en conjunto.
Rechaza pronto, con un motivo claro. Una petición que falla en el proveedor ya te ha costado la subida y quizá dinero.
Ejemplo resuelto: límites y un precio
| Parte | Qué es | Comprobación | Coste en unidades |
|---|---|---|---|
| 1 | texto, 1800 caracteres | sin límite aquí | 2 |
| 2 | imagen, png, 412000 bytes | permitida, bajo 5 MB | 4 |
| 3 | imagen, gif, 90000 bytes | gif no está en la lista | 4 |
| 4 | documento, 24 páginas | supera el límite de 20 páginas | 48 |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
El coste es por imagen y por página
El texto se paga por token. Una imagen o una página suele pagarse con una cantidad fija o con un número de tokens que depende de su tamaño. Suma las partes antes de la llamada, para que una regla de presupuesto pueda rechazar una petición cara.
Siempre que puedas, envía menos: reduce el tamaño de la imagen o envía solo las páginas que importan.
Un error frecuente
Advertencia
Mucha gente se fía del nombre o de la extensión del archivo. Un nombre puede decir png en un archivo que no lo es. Comprueba el tipo a partir de los bytes, en el servidor, y mantén también los límites de tamaño. La extensión es una pista, no un hecho.
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\";' '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.
Escribe el ayudante del modelo, parte 2
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.
Escribe el comprobador, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' '// Una petición grabada e inventada: cada proveedor tiene su propia forma.' 'const request = { parts: [' ' { type: \"texto\", chars: 1800 },' ' { type: \"imagen\", kind: \"png\", bytes: 412000 },' ' { type: \"imagen\", kind: \"gif\", bytes: 90000 },' ' { type: \"documento\", pages: 24, bytes: 880000 },' '] };' 'const LIMITS = { images: 3, imageBytes: 5000000, pages: 20, kinds: [\"png\", \"jpeg\"] };' 'const PRICE = { image: 4, page: 2, thousandChars: 1 }; // unidades inventadas' > shapes.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 comprobador, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function check(req) {' ' const problems = [];' ' let images = 0;' ' let cost = 0;' ' req.parts.forEach(function (p, i) {' ' const n = i + 1;' ' if (p.type === \"texto\") cost += Math.ceil(p.chars / 1000) * PRICE.thousandChars;' ' if (p.type === \"imagen\") {' ' images++;' ' cost += PRICE.image;' ' if (LIMITS.kinds.indexOf(p.kind) < 0) problems.push(\"parte \" + n + \": formato \" + p.kind + \" no permitido\");' ' if (p.bytes > LIMITS.imageBytes) problems.push(\"parte \" + n + \": imagen demasiado grande\");' ' }' >> shapes.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 comprobador, parte 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' if (p.type === \"documento\") {' ' cost += p.pages * PRICE.page;' ' if (p.pages > LIMITS.pages) problems.push(\"parte \" + n + \": demasiadas páginas\");' ' }' ' });' ' if (images > LIMITS.images) problems.push(\"demasiadas imágenes\");' ' return { problems: problems, cost: cost };' '}' >> shapes.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 comprobador, parte 4
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const result = check(request);' 'result.problems.forEach(function (p) { console.log(\"Problema:\", p); });' 'console.log(\"Coste:\", result.cost, \"unidades (un precio inventado)\");' 'async function main() {' ' const text = \"Texto del ticket: pan 2,10, leche 1,05, total 3,15. ¿Cuál es el total?\";' ' const sample = \"El total es 3,15.\";' ' console.log(\"Solo texto, llamada real:\", await ask([{ role: \"user\", content: text }], sample, 30));' '}' 'main();' >> shapes.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 la petición grabada
docker exec -w /home/node/project agent node shapes.jsSe 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ínea | Qué muestra |
|---|---|
| Problema: parte 3: formato gif no permitido | El tipo de medio no está en la lista |
| Problema: parte 4: demasiadas páginas | 24 páginas superan el límite de 20 |
| Coste: 58 unidades | Texto 2, dos imágenes 8 y un documento 48 |
| Solo texto, llamada real | Una respuesta corta a una pregunta de texto, o una Muestra grabada con su etiqueta |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.