- Sacar el texto de un PDF con
/augment/text-parser - Describir el registro que queremos como un esquema JSON
- Extraerlo, con el esquema aplicado en lugar de sugerido
- Manejar el archivo que no contiene texto en absoluto
- Comparar lo que producen los dos caminos a partir de la misma página
Configuración
Necesitas Python 3.9 o más reciente, el paqueterequests y una clave de API de Venice. Consulta Generar una clave de API si no tienes una.
extract.py:
AUTH y JSON_HEADERS están separados. El parser acepta una subida multipart, y establecer Content-Type tú mismo en una petición multipart impide que requests añada el boundary, lo que falla de una forma que es molesta de diagnosticar.
1. Sacar el texto
/augment/text-parser acepta un PDF, DOCX, XLSX o archivo de texto plano de hasta 25 MB y devuelve el texto con un recuento de tokens. Los documentos se procesan en memoria y el contenido no se conserva.
tokens es la parte útil de esa respuesta. Te dice lo que te costará el documento en la próxima petición antes de hacerla, lo cual importa porque un PDF largo puede fácilmente crecer más de lo que pensabas gastar.
2. Describir el registro que quieres
Pedir JSON a un modelo te devuelve JSON con más o menos la forma que pediste. Pasar un esquema te devuelve JSON que coincide, porque el esquema restringe la generación en lugar de aconsejarla.additionalProperties: False en todos los niveles. Sin esto, un modelo que encuentre algo interesante puede añadir una clave que nunca planeaste, y el código que lea el resultado no la esperará.
3. Extraer
Una única llamada, conresponse_format llevando el esquema y strict activado:
200:
affiliation es obligatorio, así que el modelo devolvió una cadena vacía en vez de omitirlo. Eso es el esquema haciendo exactamente lo que le dijiste.
Una cadena vacía y un valor ausente son hechos distintos, y
required los mezcla. Si necesitas distinguir “el documento no lo dice” de “el documento no dice nada aquí”, tipifica el campo como {"type": ["string", "null"]} y pide null en el system prompt. El modo estricto acepta la unión y obtienes null en lugar de "".Apagar el razonamiento
disable_thinking es la línea de esa petición que da para discutir, así que aquí va el argumento. El modelo de texto por defecto razona antes de responder, y el razonamiento se saca del mismo presupuesto de completación que el JSON. Ejecuta la misma extracción cuatro veces y observa lo que gasta el modelo:
Subir el presupuesto no arregla el primer problema, solo eleva el techo que se le permite alcanzar al modelo. La ejecución que gastó 4003 tokens volvió con
finish_reason de length y una cadena vacía.
Apagar el razonamiento hizo que esta extracción fuera cinco veces más barata y, más útil todavía, que fuera igual cada vez. El esquema ya está haciendo el trabajo que haría el razonamiento, que es decidir qué forma tiene la respuesta.
4. Cuando no hay texto que obtener
Un PDF producido por un escáner contiene imágenes de páginas, no texto. Nada en el nombre del archivo lo dice, y nada en el tamaño del archivo lo delata tampoco. No hace falta que lo detectes tú, porque el parser lo hace:400, y es una señal de enrutamiento más que un fallo. La vía del texto no está disponible para este archivo, así que toma la otra: renderiza la página y deja que un modelo la mire.
5. En qué discrepan los dos caminos
Ejecuta ambos contra la misma primera página y los registros vuelven casi idénticos. El “casi” es la parte interesante:
El camino del texto preservó la Ł. El camino de visión devolvió una L ASCII, porque lee formas de letras en vez de códigos de carácter, y un diacrítico es un pequeño detalle visual que sobrevive mal. Si estás emparejando nombres extraídos contra una base de datos, esa diferencia decide si se encuentra la fila.
El octavo autor importa más. La página no indica afiliación para Illia Polosukhin, y el camino del texto lo reporta fielmente como una cadena vacía cada vez. El camino de visión, en algunas ejecuciones, ha rellenado el campo con un vecino plausible de la misma página. Leer píxeles deja más espacio para inferir que leer caracteres, y un campo obligatorio es una invitación a rellenarlo. Cuando no puedes comprobar la salida a mano, esa es una razón para preferir el texto parseado allí donde el documento lo ofrezca.
El coste está más cerca de lo que parece. Con el razonamiento apagado en ambos lados, los dos caminos ejecutaron un prompt de tamaño parecido en esta página:
La imagen tenía 923.732 caracteres de base64, y nada de eso es lo que pagas. Las imágenes se tokenizan por tamaño, no por la longitud de su codificación, así que un PNG grande no cuesta lo que parece.
Prefiere el texto parseado cuando el documento tenga texto. Preserva los caracteres exactos, no cuesta nada extra ir más allá de la primera página, y no le importa cómo estaba maquetada la página. Recurre a la visión cuando el parser diga que no hay nada que leer, o cuando el significado esté en la maquetación, como en un gráfico, un sello o una firma.
Extraer otra cosa
Nada de lo anterior es específico de artículos. Cambia el esquema y el system prompt, y el pipeline extrae facturas:description están haciendo trabajo de verdad. Una fecha solo es inequívoca una vez que has dicho qué formato quieres, y 03/04/2026 significa dos días diferentes según quién lo escribiera.
Próximos pasos
- Valida el resultado contra el esquema con
pydanticojsonschema, para que un registro mal formado falle en la frontera y no tres funciones después. - Almacena el texto extraído con Embeddings para buscar en varios documentos en lugar de volver a extraerlos.
- Adjunta documentos directamente a una completación de chat con File Inputs cuando quieras respuestas en vez de registros.
- Dale el extractor a un agente como herramienta, usando Construir un agente que usa herramientas con llamada a funciones.
Procesamiento de documentos
Referencia del endpoint text-parser.
Respuestas estructuradas
Cómo json_schema restringe una completación.
Visión
Enviar imágenes a un modelo de chat.
File Inputs
Adjunta un documento sin parsearlo tú mismo.