Volver a la Biblioteca
Arquitectura

Arquitectura del motor RFQ: por qué las reservas de disponibilidad y las instantáneas de cancelación importan

Última actualización: 13 de julio de 2026

El problema que la mayoría de la automatización de RFQ pasa por alto

Cuando los equipos automatizan flujos de trabajo de RFQ, típicamente se centran en el front-end: analizar la solicitud del comprador, buscar en el catálogo, comparar productos, aplicar precios. Ese trabajo es real, y existen herramientas para ello — la IA extrae requisitos mientras las reglas determinísticas manejan precios, aprobaciones y seguimiento de SLA, como describe GEP en su análisis de automatización de compras. La guía de sourcing 2026 de Ivalua confirma el mismo patrón: la IA maneja la extracción y coincidencia, las reglas manejan la gobernanza.

Pero dos problemas rompen este modelo en producción, y la mayoría de la automatización no los aborda:

  1. Deriva de disponibilidad. Una cotización que toma tres días en negociar es una cotización contra inventario que puede que ya no exista al precio ofrecido. Si el agente promete 50 unidades a $12.40 y el proveedor tiene 12 cuando el comprador acepta, la cotización nunca fue entregable. El agente no mintió — el mundo cambió debajo de él.

  2. Deriva de políticas. Una penalidad de cancelación cotizada a 30 días puede diferir de la penalidad vigente al momento de la reserva. Si el proveedor endurece los términos a mitad de la negociación y el agente no captura la política al momento de la cotización, el comprador disputa el cargo más tarde. La disputa es legítima — los términos cambiaron.

La arquitectura que resuelve ambos es un motor RFQ con dos mecanismos: reservas de disponibilidad atómicas que reservan inventario restringido por una ventana de tiempo, e instantáneas de políticas de cancelación que congelan los términos comerciales en el momento de la cotización. Estas no son características añadidas a una herramienta de cotización. Son compromisos estructurales en el modelo de datos — campos, transiciones de estado y operaciones de ciclo de vida que el motor aplica.

El modelo de entidades que lo hace funcionar

Un motor RFQ de producción necesita más que una solicitud y una cotización. El modelo de entidades debe separar la intención del comprador de la respuesta del proveedor, y debe rastrear el suministro restringido independientemente del catálogo. Las siguientes entidades forman la columna vertebral:

Request — la intención del comprador: contacto, título, artículos, direcciones de facturación y envío, expiración, estado. Una solicitud se mueve a través de una máquina de estados definida: initialin_progressconfirmedcompleted, con modified como punto de reentrada cuando los artículos cambian después de que existen cotizaciones.

Item — el producto del catálogo que el comprador quiere. Los artículos son genéricos; tienen un UUID, tipo, modo de precio y unidades de medida. El mismo artículo se mapea a múltiples artículos de proveedor.

ProviderItem — la oferta específica del proveedor para ese artículo, con un precio base por unidad de medida. Un artículo de proveedor pertenece a una corporación proveedora y puede tener múltiples lotes.

ProviderItemBatch — la unidad de inventario restringido. Cada lote tiene un número de lote, fecha de producción, fecha de expiración, costo por unidad, indicador de stock y un indicador slow_move_item para inventario de lento movimiento. Los lotes son la granularidad a la que se verifica y reserva la disponibilidad.

Quote — la respuesta con precio del proveedor a una solicitud confirmada. Una cotización tiene un proveedor, un representante de ventas, método y monto de envío, moneda, tipo de cambio y marca de tiempo de bloqueo de FX, conteo de rondas de negociación (auto-calculado) y una máquina de estados: initialin_progressconfirmedcompleted o disapproved.

QuoteItem — una línea en la cotización, vinculando un artículo y lote de proveedor a una cantidad, precio por unidad, subtotal, descuento y subtotal final. Críticamente, el artículo de cotización lleva hold_token, hold_expires_at, guardrail_price_per_uom y slow_move_item — los campos que hacen visibles las reservas de disponibilidad y las protecciones de rentabilidad a nivel de línea.

CancellationPolicy — los términos bajo los cuales una reserva puede cancelarse, con niveles, plazos y reglas de penalidad, definidos por artículo de proveedor.

Installment — el calendario de pagos vinculado a una cotización confirmada, con prioridad, fecha programada, monto, proporción y una máquina de estados: pendingpaid o cancelled.

Esto no es una tabla plana. Es un grafo tipado con resolvers anidados — una solicitud resuelve sus cotizaciones, una cotización resuelve sus artículos, un artículo de cotización resuelve su artículo y lote de proveedor. El motor usa carga por lotes basada en DataLoader para que una cotización con 20 artículos no dispare 20 búsquedas secuenciales en la base de datos.

Reservas de disponibilidad: reservando capacidad atómicamente

La reserva de disponibilidad es el mecanismo que previene la deriva de disponibilidad. Sin ella, un agente cotiza contra una instantánea de inventario que puede estar obsoleta para cuando el comprador decide. Con ella, el agente reserva capacidad por una ventana de tiempo y la cotización está respaldada por suministro real, decrementado.

El ciclo de vida de la reserva tiene cuatro estados y reglas de transición estrictas:

Estado Significado Transiciones permitidas
held Capacidad decrementada, token de reserva emitido con un TTL (15 minutos) confirmed, released, expired
confirmed Reserva comprometida, sin segundo decremento de capacidad terminal
released Reserva cancelada, capacidad restaurada (idempotente) terminal
expired TTL transcurrido, capacidad restaurada (idempotente) terminal

La decisión de diseño crítica está en la transición confirmed: sin segundo decremento de capacidad. La reserva ya reservó la capacidad. Confirmar la reserva convierte la reserva a estado confirmado sin tocar el conteo de inventario de nuevo. Esto previene el doble conteo — el bug donde una reserva decrementa la capacidad, luego una confirmación la decrementa de nuevo, y el sistema cree que tiene menos suministro del que realmente tiene.

La reserva es atómica. La mutación acquireAvailabilityHold decrementa la capacidad disponible y retorna un hold_token con una marca de tiempo expires_at. Si la capacidad es insuficiente, la operación falla sin efectos secundarios — sin reserva parcial, sin estado sucio. Las mutaciones releaseAvailabilityHold y expireAvailabilityHold restauran la capacidad y son idempotentes: llamarlas dos veces sobre el mismo token retorna el mismo resultado. Esto importa para la tolerancia a fallos — si un tiempo de espera de red causa un reintento, el sistema no sobre-restaura la capacidad.

Cómo esto se mapea a un flujo de cotización

Considera un distribuidor cotizando 200 unidades de una bomba industrial a un proveedor que tiene tres lotes con diferentes fechas de expiración y costos. El agente:

  1. Llama check_availability con el UUID del artículo de proveedor, ventana de servicio y cantidad — obtiene lotes coincidentes y cantidad disponible.
  2. Llama acquire_availability_hold — la capacidad se decrementa atómicamente, se retorna un hold_token con un TTL de 15 minutos.
  3. El artículo de cotización ahora lleva hold_token y hold_expires_at. El comprador ve una cotización respaldada por inventario real, no una suposición del catálogo.
  4. Si el comprador acepta dentro de 15 minutos, el agente llama confirm_availability_hold — la reserva transita a confirmed, la capacidad se bloquea, sin segundo decremento.
  5. Si el comprador rechaza o el TTL expira, el agente llama release_availability_hold o deja que expire — la capacidad se restaura, la siguiente cotización puede usarla.

El hold_token es un handle explícito. El modelo lo pasa como argumento, no como estado de sesión oculto. Este es el mismo patrón de handle explícito que el release candidate de MCP 2026-07-28 formaliza: el estado que un agente necesita entre llamadas a herramientas se convierte en un handle visible y auditable en lugar de metadatos de la capa de transporte.

Instantáneas de cancelación: congelando los términos

La deriva de políticas de cancelación es el segundo modo de fallo. Un proveedor puede cambiar las penalidades de cancelación entre la cotización y la reserva — una penalidad del 10% a 30 días se convierte en 50% a 7 días. Si el agente cotizó la penalidad anterior y el comprador aceptó, el comprador disputa el nuevo cargo. La disputa es correcta.

La arquitectura resuelve esto tomando una instantánea de la política de cancelación al momento de la cotización. La entidad CancellationPolicy tiene tiers (una estructura JSON con plazos y porcentajes de penalidad), un label, una description y un status. Cuando el agente genera una cotización, recupera la política de cancelación activa para cada artículo de proveedor y adjunta los términos de la política a la cotización. El UUID de la política y sus niveles se congelan — incluso si el proveedor actualiza la política más tarde, la cotización lleva los términos que estaban vigentes cuando se emitió.

La herramienta search_cancellation_policies filtra por artículo de proveedor y estado, para que el agente pueda recuperar la política activa para cada artículo de línea. La política se asocia entonces con el artículo de cotización, no solo referenciada por una clave externa — la instantánea es la política, no un puntero a un registro mutable.

Este patrón se generaliza más allá de la cancelación. Los tipos de cambio usan el mismo enfoque: el QuoteType lleva fx_rate y fx_rate_locked_at — el tipo de cambio se bloquea al momento de la cotización, no se busca al momento de la reserva. Si la moneda de visualización del comprador difiere de la moneda de cotización del proveedor, el tipo bloqueado es el tipo que el comprador ve, sin importar el movimiento del mercado.

Niveles de precio, búsqueda de segmento y protecciones

El precio no es un campo plano. El motor resuelve niveles de precio por segmento de cliente, cantidad y artículo de proveedor. La herramienta get_item_price_tiers toma un email de cliente y una lista de artículos de cotización (cada uno con UUID de artículo, UUID de artículo de proveedor y cantidad), busca el segmento del cliente a través de contactos de segmento y carga por lotes todos los niveles de precio coincidentes en una sola consulta. Los niveles se filtran por umbrales de cantidad a nivel de base de datos — solo los niveles donde quantity_greater_then y quantity_less_then encuadran la cantidad solicitada se retornan.

Los prompts de descuento funcionan igual, con un sistema de alcance jerárquico: GLOBAL (aplica a todas las cotizaciones), SEGMENT (aplica a un segmento de cliente), ITEM (aplica a un artículo específico) y PROVIDER_ITEM (aplica a la oferta de un proveedor específico de un artículo). La herramienta get_discount_prompts carga desde los cuatro alcances, desduplica y retorna el conjunto combinado con condiciones y reglas. Esto significa que una cotización puede aplicar un descuento por volumen (alcance de segmento), una promoción estacional (alcance de artículo) y una liquidación específica de proveedor (alcance de artículo de proveedor) simultáneamente — cada uno con su propia prioridad.

El campo guardrail_price_per_uom en el artículo de cotización es la protección de rentabilidad — el precio mínimo aceptable para el artículo, cargado desde el lote del artículo de proveedor. Si el agente aplica un descuento que baja el precio por debajo de la protección, la operación no falla (el agente puede tener una razón legítima), pero el indicador es visible en la respuesta del artículo de cotización. El indicador slow_move_item marca inventario que ha estado en stock demasiado tiempo — el agente puede usar esto para aplicar descuentos más profundos, pero la protección previene precios no rentables.

Las máquinas de estados que previenen operaciones inválidas

Un motor RFQ sin aplicación de estados es una hoja de cálculo. El motor aplica cuatro máquinas de estados, cada una con reglas de transición explícitas y guards de operación:

Request: initialin_progressconfirmedcompleted, con modified como punto de reentrada. El RequestOperationGuard aplica que las cotizaciones solo pueden crearse desde solicitudes confirmed, y las modificaciones de artículos solo se permiten en estados initial, in_progress o modified.

Quote: initialin_progressconfirmedcompleted o disapproved. El QuoteOperationGuard aplica que las modificaciones de artículos solo se permiten en initial o in_progress, y las cuotas solo pueden crearse desde cotizaciones confirmed.

Reserva de disponibilidad: heldconfirmed / released / expired. Los tres estados objetivo son terminales.

Installment: pendingpaid o cancelled. Ambos estados objetivo son terminales.

Cuando los artículos de una solicitud confirmada cambian y ya existen cotizaciones, la solicitud transita a modified y todas las cotizaciones existentes se desaprueban automáticamente — el comprador debe obtener nuevas cotizaciones contra los artículos actualizados. Esto previene que cotizaciones obsoletas se acepten después de que la solicitud que las generó ha cambiado.

Cuando todas las cuotas no canceladas de una cotización están pagadas, la cotización transita automáticamente a completed. Cuando al menos una cotización de una solicitud está completada, la solicitud transita automáticamente a completed. Las máquinas de estados no son consultivas — el motor valida cada transición y lanza un error VALIDATION_FAILED con el estado actual y las transiciones permitidas si la operación es inválida.

El patrón de módulo: 38 herramientas, 11 mixins de dominio

La implementación de referencia de esta arquitectura es un único módulo MCP — mcp_hospirfq_processor — con 38 herramientas registradas compuestas desde 11 mixins de dominio: Request, Item, Availability, Quote, Pricing, Installment, Bundle, Cancellation, File, Segment y Catalog. Los mixins comparten una clase base GraphQLBackedProcessor que proporciona el cliente GraphQL, el logger y la configuración. Cada mixin contribuye sus propios métodos de herramienta y accede solo a self.logger, self.setting y self._execute_graphql_query.

Las herramientas se registran a través de un bloque de configuración (MCP_CONFIGURATION) que declara el nombre de cada herramienta, descripción, esquema de entrada (JSON Schema) y enlace de módulo (nombre de clase, nombre de función, tipo de retorno). El runtime de MCP carga esta configuración al inicio y despacha llamadas a herramientas a los métodos de mixin correspondientes. El decorador @handle_errors envuelve cada llamada a herramienta con manejo de errores estructurado, produciendo respuestas de error tipadas con códigos como GRAPHQL_QUERY_FAILED, VALIDATION_FAILED, HOLD_NOT_FOUND, HOLD_ALREADY_EXPIRED, AVAILABILITY_INSUFFICIENT y PRICING_MODE_UNSUPPORTED.

Las 38 herramientas abarcan el ciclo de vida completo de RFQ:

Dominio Herramientas Operaciones clave
Gestión de solicitudes 8 Enviar, actualizar, obtener, buscar, añadir/eliminar artículos, asignar/eliminar artículos de proveedor
Gestión de artículos 3 Buscar artículos, obtener artículo, obtener artículos de proveedor con info de lote
Gestión de cotizaciones 4 Actualizar cotización, obtener cotización, buscar cotizaciones, actualizar artículo de cotización
Precios 3 Obtener niveles de precio, obtener prompts de descuento, calcular precios de cotización
Cuotas 4 Crear, actualizar, crear calendario, obtener cuotas
Disponibilidad 5 Verificar, adquirir reserva, liberar, confirmar, expirar
Bundles 3 Buscar bundles, obtener bundle, buscar componentes
Cancelación 2 Obtener política, buscar políticas
Catálogo 1 Consultar catálogo (búsqueda en grafo de conocimiento)
Segmento 1 Obtener contactos de segmento
Archivo 2 Subir, obtener archivos
Flujo de trabajo 2 Confirmar solicitud + crear cotizaciones, confirmar cotización + crear cuotas

Las dos herramientas de flujo de trabajo — confirm_request_and_create_quotes y confirm_quote_and_create_installments — son operaciones de conveniencia que componen múltiples herramientas atómicas en una sola llamada. La primera confirma una solicitud y crea cotizaciones para proveedores seleccionados en una operación. La segunda confirma una cotización y crea el plan de cuotas en una operación. Estas reducen el número de viajes de ida y vuelta que un agente necesita para los flujos de trabajo multi-paso más comunes.

La herramienta inquire_catalog es el puente al grafo de conocimiento. Toma una consulta en lenguaje natural, un espacio de nombres opcional (hotel, vuelo, actividad) y un límite, y retorna resultados clasificados desde el motor del grafo de conocimiento. Así es como el agente descubre productos antes de cotizar — no con consultas SQL contra una tabla de productos, sino con búsqueda semántica a través de un grafo de catálogo que conoce sustitutos, compatibilidad y disponibilidad.

Por qué esta arquitectura se generaliza

La implementación de referencia se construyó para viajes y hospitalidad — inventario fechado, reglas de ocupación, precios por pasajero, bundles, depósitos, términos de cancelación. Esa es la prueba de estrés. Si el motor puede cotizar y reservar una habitación de hotel para fechas específicas, reservar la disponibilidad con un TTL, tomar una instantánea de la política de cancelación, bloquear el tipo de cambio, aplicar niveles de precio específicos de segmento y entregar a un plan de cuotas, puede soportar cualquier flujo de trabajo RFQ B2B con proveedores, compradores, inventario restringido y términos comerciales.

La generalización es estructural:

  • Distribución y mayorista: reemplazar habitaciones de hotel con stock de almacén, artículos de proveedor con SKU de proveedor, lotes con números de lote y fechas de expiración. La reserva de disponibilidad reserva stock; la instantánea de cancelación preserva términos de devolución.
  • Compras: reemplazar paquetes de viaje con partes y materiales. Los niveles de precio basados en segmento aplican tasas contractuales negociadas; el precio de protección previene compras no rentables.
  • Manufactura: reemplazar ventanas de servicio con slots de producción. La reserva de disponibilidad reserva capacidad en una línea de producción; la política de cancelación preserva la penalidad de reprogramación.
  • Servicios de campo: reemplazar fechas de hotel con ventanas de citas. La reserva de disponibilidad reserva el tiempo de un técnico; el plan de cuotas estructura el calendario de pagos.

El modelo de entidades — Request, Item, ProviderItem, ProviderItemBatch, Quote, QuoteItem, CancellationPolicy, Installment — es agnóstico a la industria. Los mixins de dominio son componibles: un despliegue de compras podría eliminar el mixin Bundle y mantener el resto. Un despliegue de manufactura podría añadir un mixin ProductionSlot junto a Availability. El patrón de módulo, las máquinas de estados y el enfoque de handle explícito permanecen igual.


Un distribuidor que opera NetSuite, BigCommerce y tres catálogos de proveedores obtiene un agente que recibe un RFQ por email o portal, resuelve productos y sustitutos contra el grafo de catálogo, aplica precios por segmento de cliente, reserva stock con una expiración, toma instantáneas de los términos de cancelación, bloquea el tipo de cambio y escribe la cotización aceptada de vuelta a NetSuite — con cada llamada a herramienta registrada y cada transición de estado validada. Ese build es la Fase 2-3 del método de cuatro pasos y está típicamente activo en 5-8 semanas.

Solicita un build con scope definido. Discovery de una semana. Obtienes un inventario de sistemas, mapa de flujo de trabajo y scope fijo — sin importar si construyes con nosotros.

¿Quieres esto construido para tus sistemas?

Cada documento aquí viene de trabajo real de producción. Si tienes un sistema objetivo y un flujo en mente, podemos definir un proyecto en una semana.

Solicitar un proyecto

Descubrimiento de una semana. Obtienes un inventario de sistemas, mapa de flujos y alcance fijo — decidas o no construir con nosotros.