docu-ai-flow
Servicio .NET 10 que vigila una carpeta, extrae los datos de las facturas PDF, las archiva y envía el trimestre al asesor. Dos ideas lo sostienen: la extracción vive detrás de un único port —hoy plantillas locales, ayer Google Document AI— y ninguna factura se archiva sola hasta que su proveedor se lo ha ganado. Se ejecuta con dos comandos y sin una sola credencial.
El problema
Gestionar facturas de proveedores es trabajo manual repetitivo: descargar PDFs, abrirlos uno a uno, copiar datos a una hoja de cálculo, enviarlos al asesor cada trimestre. Un error en cualquier paso significa declaraciones incorrectas. Pero automatizarlo del todo tiene su propia trampa: ninguna extracción acierta el 100% de las veces, y una cifra mal leída que nadie mira acaba en una declaración.
La solución
-
Watcher de carpeta
Detecta PDFs en ./data/inbox/ en tiempo real. Un watcher de filesystem más polling de seguridad garantizan que ninguna factura se pierda. Detecta duplicados por hash y también por clave natural (número + CIF), que es lo que caza una factura reenviada con bytes distintos antes de declarar el mismo IVA dos veces.
-
Extracción intercambiable
Un único port, IInvoiceDataExtractor, con dos adaptadores vivos: plantillas locales sobre PdfPig (el que va por defecto: sin red, sin credenciales, 0 € por factura) y Google Document AI. Cuál está activo es un valor de configuración, no un cambio de código.
-
OCR para escaneos
Un escaneo no tiene capa de texto, así que no hay nada que leer. Con el fallback activado, poppler rasteriza la página y Tesseract la lee; el texto recuperado sigue el mismo camino de plantillas. Desactivado, un Null Object devuelve cadena vacía y ninguna rama del extractor necesita preguntar si el OCR existe.
-
Revisión con el PDF al lado
Las facturas que no se archivan solas esperan en una cola. La pantalla de revisión pone el PDF a la izquierda y los campos extraídos a la derecha, cada uno con la confianza con la que se capturó. Lo que el extractor no supo leer se convierte en un formulario en blanco, con su propia validación de que base + IVA = total.
-
Exportación a Excel
ClosedXML genera un Excel trimestral con tipos nativos — fechas y números reales para ordenar, filtrar y sumar sin conversiones. El maestro acumulativo se regenera con cada factura confirmada.
-
Envío al asesor
Resend empaqueta los PDFs del trimestre en un ZIP y los envía al asesor fiscal. Si el ZIP supera el límite de 40 MB de Resend, se divide automáticamente por mes y se envían varios correos.
Pruébalo
No hace falta clonar nada ni tener .NET instalado. La imagen se publica en GHCR desde CI, para amd64 y arm64, y arranca con el extractor local: sin cuentas, sin claves.
- 1 Genera ocho facturas ficticias en la bandeja de entrada
docker run --rm -v "$PWD/data:/app/data" \ ghcr.io/aitorevi/docu-ai-flow:latest make-samples /app/data/inbox - 2 Arranca el watcher y el panel
docker run --rm -p 8080:8080 -v "$PWD/data:/app/data" \ ghcr.io/aitorevi/docu-ai-flow:latest
Abre localhost:8080. Tres de esas facturas son del mismo proveedor: confírmalas sin cambiar nada y verás a Suministros Aurora pasar de «En revisión» a «Automático» — a partir de ahí sus facturas se archivan solas. Esa es la idea que sostiene el proyecto, y se ve en un minuto.
Stack técnico
Backend
- .NET 10
- C#
- Hexagonal
- Result/Error
- SQLite
- PdfPig
- Google Document AI
- Tesseract
- Resend
- Polly
Frontend y despliegue
- Astro 6
- TypeScript
- Docker
- GHCR
- GitHub Actions
Tests
- xUnit
- NSubstitute
- WireMock.Net
- NetArchTest
- WebApplicationFactory
Decisiones técnicas
-
Arquitectura hexagonal
El extractor, SQLite, el archivador y Resend son adaptadores intercambiables detrás de interfaces (ports). Los use cases del Application no conocen ningún detalle de implementación. Un test de arquitectura con NetArchTest verifica que las fronteras nunca se crucen en cada build.
-
El hexágono, demostrado
El proyecto empezó con LlamaParse, se pasó a Google Document AI cuando su precisión se quedó corta, y acabó en un extractor local por plantillas que gana a los dos en las facturas que de verdad ve. En los tres cambios el dominio, los use cases y los tests de pipeline no se tocaron: dependen del port, no del proveedor. Ese es todo el retorno del hexágono, y por eso hoy el que va por defecto es el gratis.
-
Confianza que se gana
La pregunta interesante no es si un humano revisa, sino qué facturas se ganan el derecho a saltarse la revisión — y eso se decide por proveedor, por historial. Tras N confirmaciones seguidas en las que nadie corrigió nada, ese proveedor pasa a archivarse solo. Una sola corrección reinicia el contador y le retira la confianza. La autonomía se gana y se puede perder.
-
Result/Error para errores de negocio
Un error de extracción (confianza baja, total incoherente, número ausente) es un caso de negocio esperado, no una excepción. Result<TValue, TError> lo expresa como un valor de retorno. Las excepciones quedan para lo transitorio — API caída, disco lleno — que Polly reintenta automáticamente.
-
Ejecutable sin credenciales
Un proyecto de portfolio que solo se puede leer vale la mitad. La imagen se publica en GHCR desde CI, trae poppler y Tesseract dentro, y arranca con el extractor local: dos comandos de Docker, sin cuentas ni claves, y ocho facturas ficticias que recorren todos los caminos — incluida una que ningún proveedor conoce y un escaneo sin texto.
-
Desarrollado con agentes de Claude Code
Cada feature siguió el mismo ciclo: el orquestador exploró y debatió el enfoque, el planner diseñó la arquitectura, el coder implementó en TDD y el reviewer auditó contra el plan aprobado. El resultado: código consistente y un historial de commits que cuenta la historia de cada decisión.
Construido con Claude Code
El proyecto es también un experimento sobre cómo organizar el desarrollo asistido por IA. Los agentes y skills están en .claude/ y forman parte del repo público.
-
Orquestador
Coordina el flujo completo: explora, debate, planifica, implementa y revisa. Ninguna fase comienza sin acuerdo explícito. El plan vive en workflow/ y viaja por plans/ → in-progress/ → reviewing/ → done/.
-
Agentes especializados
planner-backend diseña la arquitectura y los contratos; coder-backend implementa en TDD estricto; reviewer audita contra el plan aprobado. Cada agente tiene su propio system prompt en .claude/agents/.
-
Skills reutilizables
Skills en Markdown cargadas al inicio de cada sesión: commit.md define el formato de commits, tdd.md codifica el ciclo Red-Green-Refactor y orchestrate.md el flujo completo de orquestación.
-
TDD asistido por IA
Cada feature empieza con un test en rojo. 262 tests en tres capas: Domain (puro), Application (use cases con dobles NSubstitute) e Integration — SQLite y ficheros reales, la API entera levantada en proceso con WebApplicationFactory, y un test de pipeline que lleva a un proveedor desde la primera revisión hasta archivarse solo.
¿Te interesa el workflow de IA?
El repo es público y se ejecuta sin credenciales. Los agentes, skills y el flujo de orquestación están en .claude/ junto al código fuente. Úsalos como plantilla para tus propios proyectos.