Una librería no es un servicio: lo que implica poner un extractor de información basado en LLMs frente a tráfico real


En la primera parte salimos a buscar la mejor manera de extraer cuatro campos de un NDA, y el benchmark devolvió un ganador claro: extracción single-pass con Gemini 3 Flash, con un F1 de 91.5% a ~$0.007 por documento y menos de diez segundos de latencia, por encima del baseline pre-LLM e igualando a la estrategia agéntica en precisión a la mitad de su costo y latencia.
Ese es un lugar satisfactorio para terminar un benchmark. Pero un lugar inútil para terminar un proyecto.
Una configuración ganadora no es más que el resultado de un benchmark, y el problema empresarial con el que abrimos la primera parte (personas leyendo documentos y tecleando el mismo puñado de campos en un sistema de registro) no se resuelve con ese resultado. Se resuelve con un servicio: algo a lo que un sistema pueda entregarle un documento y recibir datos estructurados de vuelta, de forma confiable, un martes por la mañana cuando cientos de documentos llegan a la vez.
Una librería no es un servicio, y cerrar esa brecha es un problema de ingeniería por derecho propio, uno en el que el benchmark nunca tuvo que pensar. La librería (agentic-kie) deja cuatro preguntas sin responder que un sistema en producción no tiene más remedio que contestar:
- ¿Cómo dejas que un cliente te entregue un documento de cualquier tamaño, cuando la puerta de entrada tiene un límite de payload?
- ¿Cómo desacoplas al cliente de una llamada lenta a un LLM, para que nadie se quede sentado esperando diez segundos por una respuesta?
- ¿Cómo vuelves reintentable una extracción que falla a medio camino, sin obligar a que el documento se suba de nuevo?
- ¿Cómo metes las dependencias pesadas de un LLM en un runtime que se supone debe escalar a cero?
Responde esas cuatro y un documento se convierte en un registro estructurado. Ese registro le rinde cuentas entonces a dos audiencias: el sistema que disparó la subida, que necesita enterarse en el momento en que su resultado está listo, y el equipo dueño de la calidad de la extracción, que necesita cada resultado conservado en un dataset consultable contra el cual evaluar el sistema en vivo, muestrear fallas, y construir conjuntos de fine-tuning.
El resto de este post es esa arquitectura, las decisiones que le dieron forma, y las pruebas de carga que la sometieron a presión de llegada real. Como el pipeline corre ambas estrategias detrás de un mismo contrato, esas pruebas permiten resolver algo que la primera parte no podía: si un agente alguna vez vale su costo bajo carga.
Dónde vive la infraestructura
La capa de despliegue es su propio repositorio: agentic-kie-deploy. Todo lo que aparece en este post (los módulos de Terraform, los Lambda handlers, el harness de pruebas de carga, y los ADRs que justifican cada elección) está ahí y es auditable; donde una decisión amerita más explicación de la que cabe aquí, enlazo el ADR relevante.
La forma de la solución
El sistema entero es asíncrono y orientado a eventos: ningún cliente espera por una extracción, y nada aguas abajo es invocado directamente, cada etapa simplemente reacciona a la salida de la anterior.
Desde afuera, el cliente toca solamente la puerta de entrada y la puerta de salida:
- El cliente pide un espacio de subida y recibe de vuelta un ID de documento y una URL de corta vida.
- El cliente sube el documento directamente a esa URL.
- El cliente lee el resultado estructurado desde una dirección que ya conocía desde el paso uno, o recibe una notificación en el instante en que aterriza.
Un servicio interno con un contrato pequeño
Esto no es una API pública. Un despliegue sirve a un conjunto conocido de clientes dentro de la misma cuenta, y hace un solo trabajo: un tipo de documento, un esquema de resultado. Ese esquema único es el diseño, no una limitación: es una plantilla, desplegada una vez por equipo, de modo que una docena de necesidades de extracción divergentes corren como una docena de servicios pequeños, en vez de una sola plataforma compartida ruteándolas todas.
El pipeline de extremo a extremo
Un documento recorre el pipeline de la siguiente manera: la solicitud de un espacio llega a una pequeña función que acuña un ID y devuelve una URL de subida prefirmada. La subida aterriza directamente en un bucket de S3, que emite un evento; EventBridge lo rutea hacia una cola de SQS, que alimenta al extractor, el contenedor que corre agentic-kie.
El extractor escribe su respuesta en una tabla de DynamoDB; esa escritura se propaga a través de un stream hacia una función que deposita el resultado final como JSON en un segundo bucket de S3, en la dirección que el cliente aprendió en el paso uno. Una capa de consulta (Glue y Athena) opera sobre esos resultados para su análisis posterior.

La estrategia de DevOps es parte del diseño
El stack completo es Terraform de extremo a extremo, y se despliega a sí mismo: los merges a develop y main despliegan a staging y a producción a través de CI, autenticándose con AWS mediante tokens OIDC de corta vida en lugar de llaves almacenadas. El modelo de ambientes y los roles de despliegue están documentados en CONTRIBUTING.md.
El diseño, decisión por decisión
El diagrama de arquitectura es el qué. Lo que sigue es el porqué: el razonamiento detrás de las decisiones de diseño más consecuentes.
La puerta de entrada es un handshake
El diseño ingenuo rutea el documento a través de una API: el cliente hace POST del PDF, el servidor lo recibe, y lo pasa hacia adelante. Eso es una tubería, y es la forma equivocada por las siguientes razones:
- Cada byte pasa por un proxy en tus propios recursos de cómputo, así que pagas por mover datos que nunca necesitaste retener.
- Los gateways administrados limitan el tamaño del cuerpo de la solicitud, así que un documento grande es rechazado antes de que la extracción comience.
- Sus timeouts se miden en segundos, mucho antes de que una llamada lenta a un LLM termine.
Por eso la puerta de entrada no carga con el documento. Carga con un handshake: el presigner acuña un document_id y devuelve una URL prefirmada, un permiso de tiempo limitado para escribir un único objeto directamente en S3. El cliente sube directo al almacenamiento, esquivando la API por completo.
El ID existe antes que el documento
El document_id se crea al momento de solicitar el espacio, no después de la extracción. Nombra la ubicación en el almacenamiento, se convierte en la llave bajo la cual se escribe el resultado, y, como el cliente lo aprende antes de subir, le entrega por adelantado la dirección futura del resultado. El ciclo de vida completo está en ADR-0006.
El mismo handshake es también su propio modelo de seguridad: la solicitud del espacio viaja sobre la identidad de nube que el cliente ya tiene, así que no hay API keys que rotar. La URL que devuelve es una credencial al portador (cualquiera que la tenga puede escribir en esa única dirección), y exactamente por eso expira en minutos: la expiración acota el valor de un enlace filtrado a unos pocos minutos de acceso de escritura sobre una sola llave.
El handshake solo arregla cómo entra el documento. El timeout, el cliente esperando una llamada lenta a un LLM, no es un problema de la puerta de entrada; es donde retoma la siguiente sección.
La parte lenta está desacoplada
La llamada al LLM toma cerca de diez segundos, y a veces mucho más. Si el cliente tuviera que esperarla, el sistema entero sería solamente tan rápido y tan disponible como su extracción más lenta. Así que el cliente no espera: la subida retorna de inmediato, y el trabajo lento sucede por su cuenta.
Cuando la subida aterriza, S3 emite un evento, y ese evento, no el cliente, maneja el resto del pipeline. Entre la subida y el extractor hay una cola que actúa como amortiguador: cuando un lote de documentos llega de golpe, se apilan y drenan al ritmo que el extractor pueda sostener. Una tubería síncrona empezaría a rechazar solicitudes o a expirar en el momento en que el tráfico excediera la capacidad; una cola simplemente se hace más profunda, y luego drena.
La cola es un freno de costos deliberado, no solo un amortiguador
La concurrencia del extractor está limitada a propósito: el escalamiento infinito de una llamada a un LLM significa una factura sin límite. La cola retiene la ráfaga que el tope no deja que el extractor trague de una sola vez. Las dos mitades de esa decisión viven en registros separados: la cola intencionalmente no restringe el fan-out (ADR-0005), y el tope que lo acota vive junto al extractor (ADR-0009).
Cada intento es reintentable
La extracción va a fallar eventualmente: un error transitorio del proveedor, un timeout, un rate limit. Dos cosas tienen que ser ciertas cuando eso pase: una extracción fallida debe ser reintentable sin volver a subir el documento, y un reintento nunca debe corromper un resultado que ya tuvo éxito.
La primera se desprende de la cola. Un mensaje que falla al procesarse no se pierde: se vuelve visible de nuevo y se reintenta, un número fijo de veces, contra el documento que sigue en el almacenamiento. El cliente no hace nada. Si todos los reintentos se agotan, el mensaje aterriza en una dead-letter queue: cuarentena para documentos venenosos, para que un solo archivo malo no pueda drenar presupuesto para siempre.
La segunda es más sutil: la entrega at-least-once significa que el mismo documento puede llegarle al extractor dos veces. El arreglo es la idempotencia: el extractor reclama un documento con una escritura condicional antes de hacer cualquier trabajo, y escribe su resultado terminal solo si la fila sigue en el estado que espera. Un mensaje reentregado que encuentra el trabajo ya hecho es un no-op.
Hay un caso límite que se deja a un humano a propósito. Un worker que muere a media extracción deja su reclamo en pie, y un mensaje reentregado retrocede en lugar de arrebatárselo: el sistema no puede distinguir a un reclamante muerto de uno lento, y no va a arriesgarse a pagar la misma extracción dos veces. Esos reintentos se agotan y el mensaje termina en la dead-letter queue, donde la recuperación es decisión de un operador.
Pesado donde tiene que serlo
Las dependencias que hacen funcionar la extracción (el cliente del LLM, el parser de documentos, los SDKs de los modelos) no caben en los límites de paquete comprimido de Lambda, y un runtime frío toma segundos en cargarlas. El error sería dejar que ese peso se esparza; en vez de eso, está confinado a la única función que ejecuta el modelo.
El extractor se despacha como una imagen de contenedor desde un registro privado, lo suficientemente grande como para contener todo lo que la llamada al modelo necesita. Todas las demás funciones se mantienen ligeras: el presigner que acuña el espacio de subida y el publisher que escribe el resultado son un puñado de líneas sin dependencia alguna, zips delgados que arrancan al instante.
Y esa única función pesada aún escala a cero, gracias al desacople de la sección anterior. Una imagen de ese tamaño tiene un arranque en frío de varios segundos: inaceptable en una puerta de entrada síncrona, imperceptible detrás de una cola, donde unos segundos en el primer documento de una ráfaga son una demora que nadie está esperando. El extractor no paga por capacidad ociosa y despierta solo cuando llegan documentos.
La concurrencia provisionada no aporta nada aquí
La concurrencia provisionada borraría el arranque en frío de varios segundos, pero factura capacidad caliente las veinticuatro horas del día: pagar por escalar a cero, y luego pagar de nuevo por no hacerlo. Para un consumidor manejado por una cola al que ningún cliente está esperando, queda fuera. El tamaño de la imagen, su fijado por digest para rollbacks deterministas, y el registro desde el que se despacha están en ADR-0008; el dimensionamiento de la función y su postura frente al arranque en frío están en ADR-0009.
¿Dónde aterrizan los resultados?
Un resultado necesita llegar a quien lo pidió. El diseño obvio es un endpoint de estado que el cliente sondea, pero el polling es ineficiente, agrega latencia, y silenciosamente convierte al resultado en un problema que el cliente tiene que perseguir. Un servicio debería gastar su propia complejidad en comprarle simplicidad a sus consumidores, no al revés.
En vez de eso, el resultado se escribe como un objeto JSON en una dirección conocida de S3: la que se deriva del document_id que el cliente ya tiene. Consumirlo es barato: leer esa dirección directamente, o suscribirse al bucket y recibir una notificación en el instante en que el objeto aparece.
El objeto en sí son los campos extraídos, envueltos en apenas la metadata suficiente para hacerlo auditable:
{
"document_id": "0190c3b2-7f4e-7a21-9c3d-1f2e3a4b5c6d",
"status": "succeeded",
"extracted_fields": {
"effective_date": "2019-03-14",
"jurisdiction": "Delaware",
"party": [{ "name": "Nike_Inc." }, { "name": "Acme_LLC" }],
"term": "2_years"
},
"model_version": "gemini-3-flash-preview",
"token_usage": { "input": 8123, "output": 142 },
"processing_ms": 8299
}No existe un objeto 'en progreso'
Un objeto de resultado existe solo para un desenlace terminal: éxito o fracaso. Un documento todavía en vuelo simplemente no tiene objeto aún: “¿ya está listo?” es una pregunta que la capa de almacenamiento responde con la sola existencia del archivo. Por qué S3 en vez de una API se argumenta en ADR-0011.
Los resultados también son un dataset
El dataset cuesta casi nada de exponer, porque los objetos de resultado que el sistema ya escribe son el dataset. AWS Glue rastrea los objetos JSON y registra un esquema sobre ellos, así que una carpeta de archivos se convierte en una tabla catalogada; Athena luego lee esa tabla directamente desde S3 con SQL puro. Nada se copia ni se remodela: el mismo objeto que entrega un resultado es la fila que una consulta devuelve.
El equipo que calibra la extracción puede entonces muestrear las fallas, medir la precisión a nivel de campo sobre tráfico real, y construir un conjunto de fine-tuning a partir de documentos que el sistema realmente ha procesado.
La primera parte evaluó la librería una vez, sobre ochenta y tres documentos de una partición de prueba congelada. Producción le entrega un flujo de documentos reales que nunca deja de llegar, cada uno un caso que la siguiente evaluación puede usar.
Dos planos de observabilidad
Un servicio de extracción en producción plantea dos preguntas muy distintas que no pertenecen al mismo lugar.
Una es operacional: ¿corrió la función, dio error, se está llenando la dead-letter queue, está el tope estrangulando? Eso es minuto a minuto, y vive en métricas y alarmas de la nube.

La otra es telemetría del modelo: ¿qué vio el modelo, cuántos tokens quemó, validó la salida contra el esquema? Esas son las preguntas que haces a lo largo de semanas de iterar un prompt, y viven en una herramienta de trazas de LLMs construida para ese propósito.

LangSmith está bien aquí, no en producción
El backend de modelo mostrado arriba es un SaaS alojado: los prompts y las respuestas salen de AWS y viven en la API del proveedor durante su ventana de retención. Ese es un intercambio aceptable a escala de portafolio; deja de serlo en cuanto llegan documentos reales, por dos disparadores: la privacidad de los datos, y el gasto. El cambio es barato (los datos de LangSmith son compatibles con OTel, así que moverse es un cambio de configuración), aunque el destino, un almacén auto-alojado como Langfuse, es un servicio siempre encendido con su propio cómputo y mantenimiento. Cuándo moverse está en ADR-0009.
Los dos planos comparten exactamente una cosa: el document_id que correlaciona una traza con una invocación. Mantenerlos separados permite que cada pregunta reciba su propia respuesta a su propia cadencia.
¿Se sostiene bajo carga?
Cada afirmación hasta ahora (amortiguar en vez de fallar, drenar en vez de atragantarse, acotar la factura) es una hipótesis hasta que el tráfico la prueba. Así que, en el espíritu del benchmark de la primera parte, el pipeline recibe el mismo tratamiento: escribir las predicciones primero, y luego correr documentos reales a través del sistema real y calificar lo que pasó.
La pregunta que una prueba de carga responde aquí:
¿El sistema se degrada de forma controlada (amortigua y drena) en vez de fallar (errores y dead-letters) bajo un patrón de llegada realista?
Un momento de calma, un pico severo
Los dos escenarios marcan los bordes de la operación realista, más que empujar al sistema hasta que se rompa; ambos hacen recorrer los mismos 200 NDAs reales (muestreados de la partición de entrenamiento de Kleister) a través del sistema en vivo, de extremo a extremo.
Dos escenarios: un momento de calma y un pico severo
Ambos empujan los mismos 200 NDAs reales a través del sistema en vivo, de extremo a extremo
| Escenario | Qué es | Qué caracteriza |
|---|---|---|
| Sostenido | 200 documentos a un ritmo constante | El momento normal, de calma (la cola nunca se acumula) |
| Ráfaga | 200 documentos lanzados tan rápido como el cliente puede subirlos | El estresor real (la cola se llena al instante) |
La ejecución sostenida es deliberadamente aburrida: transcurrir sin incidentes es el resultado. La ráfaga es donde la arquitectura se prueba a sí misma.
La cola absorbe, el tope se sostiene, el backlog drena
Esto es lo que 200 documentos llegando a la vez le hicieron al pipeline de single-pass: la cola se dispara hasta casi 200, y luego drena limpiamente hasta cero en menos de cuatro minutos mientras la concurrencia del extractor se mantiene plana en su tope de 10 (saturada, nunca excedida).
La cola absorbe el pico, el tope marca el ritmo del drenado
Ráfaga de single-pass: 200 documentos llegando a la vez, reconstruida a partir de los tiempos de espera en cola por documento
Esa curva está reconstruida a partir de los tiempos por documento. La consola de SQS cuenta la misma historia en sus propias métricas: los mensajes visibles se disparan y drenan hasta cero, y el mensaje más antiguo envejece hasta ~2.8 minutos antes de que el backlog se despeje.

Detrás de esa vista de consola hay un marcador de predicciones, registradas antes de la ejecución, todas cumplidas:
¿Qué pasa cuando 200 documentos llegan todos a la vez?
Cada predicción registrada antes de la corrida, calificada contra el pipeline de pasada única
| Métrica | Resultado | La predicción que puso a prueba |
|---|---|---|
| Documentos exitosos | 200 / 200 | Corrección: sin fallas, dead-letter queue vacía |
| Profundidad máxima de la cola | 192 | El amortiguador se llena casi al instante |
| Edad del mensaje más antiguo | 167s (vs. límite de 720s) | Drena antes de que algún mensaje expire hacia una reentrega |
| Concurrencia | Clavada en el tope de 10, 0 throttles | El control de costos se sostiene; la cola marca el ritmo del trabajo |
| Tiempo de drenado | ~3.8 min | El backlog se despeja en minutos |
| Alarmas disparadas | Ninguna | El plano operacional permanece en silencio en una corrida sana |
Cada predicción se cumplió, sin un solo throttle y sin una alarma en esta ejecución: degradación controlada, medida en vez de afirmada.
Ese drenado limpio no es suerte; es la única restricción que toda cola saturada obedece. La Ley de Little dice que el número promedio de elementos en un sistema es igual a la tasa a la que llegan multiplicada por el tiempo que cada uno pasa adentro:
Apúntala al pool de extractores y colapsa. Bajo una ráfaga el pool está clavado en su tope de concurrencia, así que el trabajo en vuelo queda fijo en , y cada documento retiene a un worker durante su tiempo de servicio . Reordenada, la tasa a la que el pool despeja trabajo es:
El tope sobre el tiempo de servicio: ni la tasa de llegada, ni el tamaño de la ráfaga, ni qué tan profundo se hace el backlog. Con un tope de 10 y los ~10s de extracción del benchmark, eso da ~60 documentos por minuto; la ejecución despejó 200 en 3.8 minutos, unos 52 por minuto, siendo la brecha el overhead del mundo real que un benchmark nunca carga: el arranque en frío del primer documento, el salto de publicación.
La misma ley desactiva el único número que parece alarmante. Los últimos documentos de la ráfaga regresaron a ~178s en el percentil 90, contra ~10s de extracción real. Esa brecha no es un cuello de botella; es espera en cola, el tiempo que un documento pasó formado. Una ráfaga de documentos drenando a se vacía en aproximadamente , y el final de la fila espera casi todo ese tiempo antes de que su propia extracción siquiera comience. Los 178s son el tope de concurrencia haciendo exactamente aquello para lo que fue configurado.
Dos regímenes gobernados por una misma ley
Es tentador decir que la plomería de AWS es básicamente gratis y que el costo y la latencia son todos del LLM. Bajo la ejecución sostenida así es: sin backlog, la latencia de extremo a extremo colapsa hasta aproximadamente el tiempo de extracción mismo (~9s mediana, ~18s p90), el costo marginal por documento es de ~$0.007, y el data plane de AWS es error de redondeo.
Pero di solo eso y contradirías el gráfico de la ráfaga: los dos regímenes son la misma ley leyéndose de dos maneras. Descompón el viaje del documento promedio en sus tres etapas (la espera en cola, la llamada al LLM, el paso de publicación) y no se parecen en nada:
¿A dónde se fue el tiempo?
Single-pass: la latencia de extremo a extremo del documento promedio
Sin backlog, y el LLM es toda la historia. Bajo una ráfaga, y la espera en cola inunda todo lo demás. Pero nota lo que la ráfaga no toca: el costo. La latencia tiene dos regímenes; el costo tiene uno. Un documento que espera 178s en la fila se factura exactamente igual que uno que sale en 9s, porque SQS cobra por solicitud, no por segundo en cola, así que el costo marginal se sostiene en ~$0.007 a lo largo de todo el pico.
La cola convierte un problema de capacidad contra el que de otro modo pagarías por aprovisionarte, en un problema de latencia que es gratis dejar reposar en el backlog. El sistema es barato y rápido en estado estable, y bajo un pico intercambia latencia, nunca costo, por estabilidad.
El alcance honesto de estos números
Esta es carga sintética, y vale la pena nombrar dos fronteras. La ejecución es de doscientos documentos porque cada documento es una llamada real a un LLM; pero la tasa de drenado es el tope sobre el tiempo de servicio, una cantidad en la que nunca entra, así que una ejecución más grande cuesta más y drena más tiempo con su estabilidad intacta. Y el backlog drena por espera en cola real, no por fallas del proveedor inyectadas: la topología de reintentos y dead-letters está diseñada y razonada, no llevada al límite bajo fallas. Lo que estos números prueban es el comportamiento en estado estable y bajo ráfaga de un sistema funcionando tal como fue construido.
¿Vale la pena un agente, en producción?
Hay un hilo de la primera parte que quedó colgando deliberadamente: el veredicto del benchmark de que la estrategia agéntica pierde contra una sola llamada fue un veredicto offline, puntuado sobre documentos individuales. No dice nada sobre lo que el enfoque agéntico le cuesta al sistema en funcionamiento. Así que el pipeline se construyó para desplegar cualquiera de las dos estrategias como un cambio de una línea, y la variante agéntica se sometió a la ráfaga idéntica, puramente para medir la prima que el benchmark nunca pudo ver.
El costo de volverse agéntico
El mismo pipeline, los mismos 200 documentos, los mismos escenarios de carga
| Métrica | Pasada única | Agéntica |
|---|---|---|
| Costo de extracción por documento | ~$0.0069 | ~$0.0147 |
| Latencia de extracción (p90) | ~13.5s | ~28.0s |
| Latencia de extremo a extremo, sostenida (p90) | ~18.0s | ~31.3s |
| Latencia de extremo a extremo, en ráfaga (p90) | ~178s | ~366s |
| Throughput de drenado en ráfaga | ~51.9 docs/min | ~27.3 docs/min |
Los números por documento cuentan la historia que la primera parte ya contó: el enfoque agéntico cuesta aproximadamente 2× los dólares y 2× el tiempo, sin ganancia de precisión en estos documentos. El benchmark offline había estimado la prima de latencia en ~1.5×; bajo presión de llegada real llegó más cerca de 2×. El impuesto más profundo está en las últimas dos filas. El costo por documento es idéntico entre las ejecuciones agénticas de ráfaga y sostenida (~$0.0145 en ambos casos: los mismos documentos, las mismas llamadas), y sin embargo la latencia de extremo a extremo explota bajo la ráfaga (366s) y se mantiene mansa bajo la sostenida (31s). El costo en dólares es un hecho por documento; el costo en tiempo es un hecho de la cola, y la cola es donde lo agéntico se vuelve caro.
Pon ambos drenados de ráfaga sobre el mismo eje (los mismos 200 documentos, la misma llegada, el mismo tope de concurrencia; la única diferencia es cuánto tiempo cada documento retiene a un worker) y la cola agéntica toma casi el doble en despejarse:
La cola agéntica drena a la mitad de la velocidad
Drenado de la ráfaga: single-pass vs. agéntica
La Ley de Little dice por qué: el enfoque agéntico eleva el tiempo de servicio , y se mueve de forma inversa, así que duplicar el tiempo que cada documento retiene a un worker parte a la mitad la tasa a la que el pool los despeja. Ese drenado más lento es también lo que disparó la alarma de profundidad de la cola que apareció en la sección de observabilidad: el backlog agéntico permaneció visible el tiempo suficiente para que CloudWatch lo notara.
Descomponer la latencia del documento promedio a lo largo de las cuatro ejecuciones vuelve explícito el mecanismo: el enfoque agéntico agrega una prima fija de procesamiento en ambos regímenes (el segmento del LLM más alto), pero bajo la ráfaga el documento al final de la fila paga esa prima aproximadamente veces, así que una desaceleración de segundos por documento aterriza sobre el último documento de una ráfaga como minutos.
La prima se paga en espera en cola
Latencia promedio de extremo a extremo, a través de estrategias y patrones de llegada
Y hay un hallazgo arquitectónico más incisivo debajo. En el mundo single-pass, un documento equivale a una llamada al LLM, así que un solo parámetro (el tope de concurrencia) controla tres cosas a la vez: cuántos documentos corren en paralelo, cuántas solicitudes al LLM están en vuelo, y qué tanto te estás apoyando en el límite de tasa del proveedor. La estrategia agéntica se ramifica dentro de un solo documento (hace varias llamadas al LLM por documento), lo que significa que documentos-en-vuelo ya no equivale a solicitudes-en-vuelo. Ese supuesto (un parámetro controlando ambos, el throughput y la exposición al proveedor) se rompe silenciosamente.
El enfoque agéntico cambia el modelo de control, no solo una constante
El tope sigue acotando el throughput de documentos, pero acotar la exposición al proveedor ahora pide una segunda superficie de control: un limitador a nivel de solicitud dimensionado contra el presupuesto de RPM del modelo. A los volúmenes probados se mantuvo holgado, así que queda marcado para después en vez de construido ahora, pero es el tipo de acoplamiento que muerde en silencio a escala. El razonamiento completo está en ADR-0016.
Listo para el martes por la mañana
Vale la pena ser preciso sobre lo que esto es y lo que no: una plantilla desplegable para un caso de uso de extracción (un tipo de documento, un esquema, propiedad del equipo que lo opera), no una plataforma multi-tenant ruteando muchos esquemas. Esa frontera es una simplificación deliberada: el handshake puede apoyarse en la identidad dentro de la cuenta, y el esquema único es lo que permite que la dirección de almacenamiento sea el contrato y cada resultado un registro de evaluación. El intercambio es multiplicación operacional (una docena de casos de uso significa una docena de pipelines que monitorear), que solo paga por debajo de cierto número de tenants.
Algunas cosas quedan honestamente abiertas. Las pruebas de carga acotan el comportamiento de un día normal, pero no empujan hasta que algo se rompa; encontrar el punto de quiebre real del throughput es un ejercicio aparte. El limitador a nivel de solicitud al que apuntan los hallazgos agénticos está documentado y dimensionado, pero no construido; los volúmenes probados todavía no lo ameritaban.
Demos un paso atrás hacia donde empezó todo esto: no un benchmark, un problema de negocio. Un equipo ahogándose en documentos necesita algo a lo que pueda entregarle un documento y confiar, el martes por la mañana en que cientos llegan a la vez. Esa mañana fue ensayada aquí: la ráfaga de single-pass fue absorbida, el backlog drenó en menos de cuatro minutos a ~$0.007 por documento, y no se disparó ninguna alarma en esa ejecución. Ese es el artefacto que este post deja atrás: infraestructura a la que un negocio puede apuntar tráfico real, probada antes de que el tráfico sea real.
La ingeniería que lleva ahí sigue una sola regla: no cargues con lo que no tienes que cargar. Convierte problemas contra los que pagarías por aprovisionarte (ancho de banda, clientes esperando, cómputo ocioso) en problemas que no cuesta nada tener. Una vez saturado el sistema entero es el tope sobre el tiempo de servicio, y esa aritmética es lo que finalmente le puso precio al enfoque agéntico: un benchmark ve costos por documento, pero la espera en cola y las superficies de control son hechos del sistema, visibles solo una vez que la cosa completa está construida y bajo carga. Y como cada resultado que el servicio entrega es también una fila en un dataset, el negocio recibe de vuelta más que sus campos: la evidencia para que cada decisión sea mejor que la anterior.
El proyecto completo, de extremo a extremo
La mayoría de los proyectos muestran una sola capa de este stack; este abarca las cuatro, cada una en su propio repositorio, ejecutable de forma independiente:
kleister-nda-preparationconvierte un dataset académico crudo en un corpus canónico, el trabajo de datos sobre el que todo lo demás se apoya;agentic-kiees la librería de extracción construida encima;agentic-kie-evalses el harness de benchmarking que encontró la configuración ganadora;agentic-kie-deployes la infraestructura que este post acaba de poner bajo carga.
Cada uno está fijado al eslabón anterior, así que la misma forma canónica fluye sin quebrarse desde el primer parseo hasta el resultado en producción: datos, librería, evaluación, operaciones. El ciclo de vida completo de un sistema de machine learning a la vista de todos.