¿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

Leer una página: texto y campos

Después de esta lección: Puedo convertir una página de cuadros de texto en líneas en orden de lectura y sacar sus campos con su confianza.

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

Una factura fotografiada aún no es texto

Una ferretería fotografía una factura en papel y quiere el total en su base de datos. La foto son solo píxeles. Un programa lector tiene que encontrar primero las palabras.

El lector devuelve cada trozo de texto con su lugar en la página. Luego tu código los ordena y saca los campos.

Al final de esta lección

Nota

Puedo convertir una página de cuadros de texto en líneas en orden de lectura y sacar sus campos con su confianza.

Palabras clave

PalabraQué significaEjemplo
Cuadroun trozo de texto y el lugar donde está en la página
Orden de lecturael orden en que una persona leería los cuadros
Campouna etiqueta y el valor que va a su ladoTotal: 46,50
Confianzalo seguro que está el lector, de 0 a 100
Toleranciacuánto pueden separarse dos cuadros y seguir en una línea

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

Una página es una lista de cuadros

Un lector de páginas no te da un solo bloque de texto. Te da una lista de cuadros. Cada cuadro tiene un texto, su lugar (x a lo ancho, y a lo alto) y una confianza de 0 a 100.

La lista no viene en un orden útil. Dos cuadros que parecen una línea pueden estar a unos píxeles de distancia.

Cuatro cuadros de una página

TextoxyConfianza
Total:4020098
46,5012020161
Cliente:406097
Marta1206295

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

El orden de lectura necesita una tolerancia

Para leer una página ordenas los cuadros de arriba abajo, luego los agrupas en líneas y después ordenas cada línea de izquierda a derecha.

Los cuadros de una línea casi nunca tienen exactamente la misma y. Por eso un cuadro se une a una línea cuando su y está dentro de una tolerancia respecto al primer cuadro de la línea. Aquí se usa una tolerancia de 5 píxeles.

Un error frecuente

Advertencia

Mucha gente ordena los cuadros solo por y y los lee en ese orden. Un valor unos píxeles más arriba que su etiqueta sale entonces antes. Primero agrupa los cuadros en líneas con una tolerancia, y luego ordena cada línea por x.

Un campo es una etiqueta y su valor

En un formulario, una etiqueta como Total: va seguida de su valor en la misma línea. Para sacar un campo, busca un cuadro que acabe en dos puntos y toma el siguiente de la línea.

Guarda la confianza junto al valor. Una cifra baja significa que una persona debe comprobarlo, porque un dígito mal leído parece igual que uno correcto.

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 la página, parte 1

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' 'const boxes = [' '  { text: \"46,50\", x: 120, y: 201, conf: 61 },' '  { text: \"Total:\", x: 40, y: 200, conf: 98 },' '  { text: \"Factura\", x: 40, y: 10, conf: 99 },' '  { text: \"Marta\", x: 120, y: 62, conf: 95 },' '  { text: \"Cliente:\", x: 40, y: 60, conf: 97 },' '  { text: \"12/03/2026\", x: 120, y: 99, conf: 88 },' '  { text: \"Fecha:\", x: 40, y: 100, conf: 98 },' '];' > page3.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 la página, parte 2

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function lines(list, tol) {' '  const sorted = list.slice().sort(function (a, b) { return a.y - b.y || a.x - b.x; });' '  const out = [];' '  for (const b of sorted) {' '    const last = out[out.length - 1];' '    if (last && Math.abs(b.y - last[0].y) <= tol) last.push(b);' '    else out.push([b]);' '  }' '  return out.map(function (l) { return l.sort(function (a, b) { return a.x - b.x; }); });' '}' >> page3.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 la página, parte 3

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function fields(ls) {' '  const found = {};' '  for (const l of ls) {' '    for (let i = 0; i < l.length - 1; i++) {' '      if (l[i].text.endsWith(\":\")) found[l[i].text.slice(0, -1).toLowerCase()] = { value: l[i + 1].text, conf: l[i + 1].conf };' '    }' '  }' '  return found;' '}' >> page3.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 la página, parte 4

Comando
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'async function main() {' '  const ls = lines(boxes, 5);' '  for (const l of ls) console.log(l.map(function (b) { return b.text; }).join(\" \"));' '  const f = fields(ls);' '  console.log(JSON.stringify(f));' '  const low = Object.keys(f).filter(function (k) { return f[k].conf < 90; });' '  console.log(\"revisar a mano: \" + low.join(\", \"));' '  const q = \"Escribe una pregunta corta que pida a una persona confirmar un total leído con poca confianza.\";' '  const reply = await ask([{ role: \"user\", content: q }], \"Comprueba este total con la hoja en papel: ¿son 46,50 euros?\", 40);' '  console.log(\"respuesta: \" + reply.text);' '}' 'main();' >> page3.js"

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

Lee la página y saca los campos

Comando
docker exec -w /home/node/project agent node page3.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
Cliente: MartaDos cuadros unidos en una línea, primero la etiqueta
Fecha: 12/03/2026y 99 e y 100 están dentro de la tolerancia de 5
{"cliente":{...},"fecha":{...},"total":{...}}Cada campo con su valor y su confianza
revisar a mano: fecha, totalLos dos valores por debajo de 90

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.