let SYSTEM be "You extract data from invoices (OCR text, it may contain errors such as O for 0, l for 1, S for 5). Reply ONLY with JSON containing: supplier (text, as it appears at the top, in normal capitalization), number (text NNNN-NNNNNNNN), date (YYYY-MM-DD; dates are written day/month/year), currency (ARS or USD), subtotal, discount (0 if none), vat, total (numbers without thousands separators; the valid TOTAL DUE), and confidence: a number from 0 to 1 saying how sure you are that EVERYTHING extracted is correct."← Todos los casos
Caso 4 · Medido el 2026-10-16 · Synsema 0.6.44 · mismos datos y mismo modelo de los dos lados
Qué facturas se pueden pagar sin una persona
Si el modelo extrae bien una factura, ¿alcanza su confianza para pagarla sin revisión?
0 vs 7
de 7 facturas riesgosas atrapadas: la confianza del LLM contra Jev
El LLM leyó bien las 60 facturas, y su propia confianza habría pagado los duplicados. Jev atrapó 7 de 7.
El problema
Las áreas de cuentas a pagar ya usan LLM para leer facturas. Con los modelos actuales, leer dejó de ser lo difícil.
Lo difícil es decidir qué se paga sin que nadie lo mire. Un duplicado leído perfecto no se paga, y un total corregido a mano hay que verificarlo.
Qué construimos
- 60 facturas sintéticas de empresas ficticias, en texto tipo OCR, con sus respuestas correctas. 28 traen dificultades reales: ruido de OCR, fechas ambiguas, moneda extranjera, descuentos, totales corregidos a mano, duplicados.
- DeepSeek extrae los campos e informa su propia confianza. Una validación aritmética revisa los totales. Jev, un modelo juez calibrado, responde cuatro preguntas sobre el documento y lo extraído en una sola llamada.
- Lo que no se aprueba espera en approve, la compuerta humana de Synsema, con un enlace de un solo uso enviado por webhook.
Resultados
| Regla | Aprueba solas | Atrapa las 7 a revisar | Se pagarían sin revisar | Falsas alarmas |
|---|---|---|---|---|
| Confianza propia del LLM ≥ 0,9 | 60 (100 %) | ✗0 de 7 | ✗las 7 | 0 |
| Solo validación aritmética | 60 (100 %) | ✗0 de 7 | ✗las 7 | 0 |
| Jev (≥ 0,85 por campo, ambiguo < 0,5) | 46 (77 %) | ✓7 de 7 | ✓ninguna | 7 (todas con ruido de OCR) |
| Prueba | Resultado |
|---|---|
| POST /approvals/{id} con un token falso, como intentaría un agente | ✓403, la aprobación sigue esperando |
| Rechazar el duplicado con su enlace | ✓200, decisión registrada |
| Usar el mismo enlace otra vez | ✓404, un solo uso |
| Correr sin terminal (CI, un agente) | ✓se rechaza al instante |
El código, lado a lado
Lo habitual
Synsema
let j be jev.systemone({"document": f.text, "extracted": remove(e, "confidence")}, {
"total_ok": {"type": "noul", "instructions": "The extracted total is exactly the valid total due on the document"},
"date_ok": {"type": "noul", "instructions": "The extracted date (YYYY-MM-DD) is the issue date of the document, read as day/month/year"},
"supplier_ok": {"type": "noul", "instructions": "The extracted supplier is the company issuing the invoice"},
"ambiguous": {"type": "noul", "instructions": "The document has contradictory data, hand corrections, is a duplicate, or has reading errors that cast doubt on some value"}})agent Approver
let ok be approve message within 30m
-- One key per invoice: state_* has no atomic read-modify-write, and several agents decide at once
state_set(`decision:{fid}`, {"approved": ok, "t": now()})Por qué Synsema
- judge devuelve probabilidades calibradas sobre las que se puede decidir. La confianza que declaró el modelo fue de 0,9 o más en todo, duplicados incluidos.
- approve es una primitiva del lenguaje. Bajo serve nunca se resuelve sola: queda en cola, una persona recibe un token de un solo uso y se rechaza al vencer el plazo.
- Un agente no puede fingir una aprobación. No hay camino en el código para que un programa apruebe su propio pago.
Lo que no resuelve
- Facturas sintéticas en texto. Con PDF o fotos reales, la extracción no va a ser perfecta.
- El juicio calibrado no reemplaza las reglas de negocio. Una copia de una factura ya pagada sacó 0,47 y pasó a Jev; la atrapó la regla «mismo proveedor y número ya procesados».
- Los umbrales (0,85 y 0,5) se fijaron antes de medir. Con datos reales, conviene calibrarlos sobre un conjunto aparte.
Reproducirlo
git clone https://github.com/synsema/why-synsema && cd why-synsema # cases/04-invoice-approval python cases/04-invoice-approval/data/generate_invoices.py synsema run cases/04-invoice-approval/process.syn SYNSEMA_HUMAN_WEBHOOK=http://127.0.0.1:8105/channel SYNSEMA_HUMAN_PUBLIC_URL=http://localhost:8105 \ synsema serve cases/04-invoice-approval/review.syn # approval queue on :8105
github.com/synsema/why-synsema/tree/main/cases/04-invoice-approval