← Volver a proyectos

CASO DE ESTUDIO · PROYECTO PROPIO · IA Y AUTOMATIZACIÓN

Automatización de facturas con IA

Pipeline de extremo a extremo que convierte facturas en PDF y texto en datos estructurados, guardados en PostgreSQL y expuestos en una API REST desplegada en Azure.

TIPO Proyecto propio
ROL Diseño y desarrollo
ESTADO En producción
5 campos extraídos por factura: proveedor, fecha, CUIT, número y total
PDF + TXT formatos de entrada, también por lotes

Video de presentación del proyecto.

Contexto

Las facturas en PDF y texto son documentos no estructurados. Este sistema automatiza de extremo a extremo la extracción, validación, persistencia y exposición de sus datos contables.

El resultado se expone como una API REST documentada con Swagger, lista para integrarse con sistemas externos y con flujos de automatización en n8n.

Lo que construí

  • Extracción con IA: usa la API de Groq con el modelo gpt-oss-120b para estructurar proveedor, fecha, CUIT, número y total en JSON validado, a partir de PDF y TXT.
  • API REST: FastAPI con documentación OpenAPI (Swagger) pública. Endpoints para consultar y filtrar facturas e integrarlas con otros sistemas.
  • Persistencia: PostgreSQL en Neon con SQLAlchemy y psycopg2 (con SSL), con inserción transaccional y consultas parametrizadas.
  • Procesamiento por lotes: scripts para ingestar directorios completos de PDF y TXT (pypdf, pathlib), con manejo de excepciones y transacciones por archivo.
  • Automatización: un flujo de n8n consume la API (HTTP Request), separa la respuesta en facturas individuales (Split Out) y aplica una regla condicional sobre los montos (If). El JSON del flujo está en el repositorio.
  • Despliegue: Azure App Service (Linux) con integración continua en GitHub Actions.

Cómo lo trabajé

Un problema real: conexiones que se caían

Neon es PostgreSQL serverless y suspende las conexiones inactivas. Durante el procesamiento de lotes de PDFs y en consultas esporádicas a la API, eso provocaba errores psycopg2.OperationalError: SSL connection has been closed unexpectedly y respuestas HTTP 500 en producción.

Cómo lo resolví

  • En la API: configuré el motor de SQLAlchemy con pool_pre_ping=True, que valida la conexión antes de cada sentencia y reconecta de forma transparente, y pool_recycle=300.
  • En el script de lotes: dejé de mantener una conexión abierta durante la lectura de archivos. Ahora abre una conexión nueva por cada archivo y la cierra en un bloque finally, así un archivo con problemas no frena el resto del lote.

Cómo verifiqué que funciona

  • Pruebas de integración locales con lotes mixtos de PDFs (válidos, vacíos y con anomalías) para comprobar que los errores quedan aislados por archivo (try/except con rollback).
  • Validé los endpoints en Swagger UI y confirmé cada despliegue en Azure en los logs de GitHub Actions.
SIGUIENTE CASO Procesamiento inteligente de documentos