Publicado el 7 de octubre de 2025

por Osledy Bazo

Agentic SOPs: De POS a Grafos de Decisión y Agentes Fiables.

Cómo los Grafos de Decisión hacen a los LLMs más fiables.

Los modelos de lenguaje grandes son buenos escribiendo y razonando en lo pequeño, pero se vuelven frágiles en el momento en que una tarea se convierte en un flujo de trabajo complejo, con muchas políticas y múltiples pasos: los planes se desvían, las herramientas se llaman en desorden y una sola alucinación puede descarrilar la ejecución. El artículo SOP-Agent propone una solución pragmática: codificar los Procedimientos Operativos Estándar (SOPs) de la organización en lenguaje natural, compilarlos en un grafo de decisión (nodos = acciones, aristas = condiciones IF/ALWAYS), y dejar que un agente recorra ese grafo selectivamente mientras solo ve un conjunto filtrado de herramientas válidas en cada paso. En otras palabras, la política humana sigue mandando y el agente aporta el juicio y la ejecución.

Concretamente, SOP-Agent tiene dos componentes clave: un SOP-Navigator que formatea la parte actual del SOP y limita el espacio de acciones, y un Base Agent (a menudo un LLM) que propone la siguiente llamada de función (y sus argumentos). El recorrido es de estilo DFS (Depth-first Search), de modo que el sistema puede profundizar cuando se cumplen las precondiciones de una rama. Además, incluye un truco elegante para bifurcaciones ambiguas: cuando dos nodos hijos comparten la misma función o no tienen ninguna, el agente primero llama a una herramienta ficticia “explore_subtree_X” para comprometerse con una rama, y luego genera la acción real dentro de ese subárbol. Esto mantiene bajo el número de llamadas al LLM mientras se preserva el control.

¿Por qué debería importarte? Porque los resultados sugieren que basarse en SOPs cambia lo “ingenioso pero frágil” por “inteligente y fiable” en tareas muy diversas. En el benchmark de toma de decisiones ALFWorld, un agente guiado por SOP alcanza un 0,888 de éxito (few-shot), superando una línea base ReAct sólida y dejando muy atrás a las variantes de AutoGPT. En un escenario práctico de limpieza de datos, logra un 100% de éxito, compitiendo con un sistema especializado en el dominio. Y en un nuevo benchmark de Grounded Customer Service (cinco industrias, flujos guiados por SOP), el enfoque alcanza una ~99,8% de precisión manteniéndose auditable y conforme a las políticas —exactamente lo que buscan los equipos de operaciones.

Este artículo se basa en el trabajo “SOP-Agent: Empower General Purpose AI Agent with Domain-Specific SOPs” (Ye et al.), publicado en arXiv en 2025 (arXiv:2501.09316). Los autores formalizan los SOPs como grafos de decisión, introducen la arquitectura SOP-Navigator + Base Agent (incluyendo el patrón ficticio explore_subtree_* para ramas ambiguas), y reportan resultados sólidos en ALFWorld, generación de código (HumanEval/MBPP), limpieza de datos y un nuevo benchmark de Grounded Customer Service.

2- La idea de SOP-Agent

Movimiento clave: escribe tu POS en lenguaje natural (con viñetas o pseudocódigo), compílalo en un grafo de decisión y ejecuta ese grafo con barandillas de seguridad. El grafo tiene:

  • Nodos = acciones que el sistema puede realizar (p. ej., get_order_status, cancel_order, open_ticket).

  • Aristas = condiciones que deciden a dónde ir a continuación (comprobaciones simples IF/ALWAYS sobre hechos en el estado).

  • Un tablero compartido (estado) que acumula hechos a partir de llamadas a herramientas (p. ej., {"status": "shipped", "cancelled": true}).

En tiempo de ejecución, el bucle del agente hace: Actuar → Observar → Decidir ruta → Repetir:

  • Llamar a la herramienta del nodo (Actuar).

  • Integrar su salida en el tablero (Observar).

  • Evaluar las condiciones de las aristas para elegir el siguiente nodo (Decidir ruta).

  • Continuar hasta un nodo terminal.

Dos componentes lo hacen posible:

  • SOP-Navigator (andamiaje): mantiene el nodo actual + el tablero, formatea una vista compacta de “qué está permitido a continuación” y filtra la lista de herramientas para que solo aparezcan las acciones válidas en ese paso.

  • Base Agent (decisor): habitualmente un LLM que, dentro de ese conjunto filtrado de herramientas, elige qué herramienta llamar y con qué argumentos cuando se requiere juicio. (También se puede ejecutar sin LLM — de forma puramente determinista.)

Por qué esto funciona

  • Barandillas de seguridad: el grafo SOP dicta el orden, la legalidad y las condiciones de salida; las herramientas desconocidas simplemente no son visibles.

  • Juicio donde importa: cuando varias ramas son plausibles o es necesario sintetizar argumentos, el agente (LLM) elige —pero siempre dentro de la cerca del SOP.

  • Recorrido eficiente: el sistema tiende a ir en profundidad (comprometerse con una rama y ejecutar sus pasos) en lugar de saltar de un lado a otro, lo que reduce llamadas innecesarias y desvíos en la planificación.

  • Bifurcaciones ambiguas bien resueltas: si dos nodos hijos comparten la misma función (o no tienen ninguna), el navegador ofrece herramientas ficticias “explore_subtree_X” para que el agente elija primero una rama; la acción real ocurre un nivel más abajo.

Un pequeño ejemplo de SOP (legible por humanos → grafo)


id: order_flow
root: authenticate
nodes:
  authenticate:
    action: auth_and_fetch(order_id)
    edges:
      - condition: ALWAYS
        to: check_status

  check_status:
    action: get_order_status(order_id)
    edges:
      - condition: status == "shipped"
        to: offer_return
      - condition: status in ["processing","packed"]
        to: cancel_with_fee
      - condition: status == "error"
        to: escalate

  offer_return:
    action: propose_return(order_id)
    edges:
      - condition: ALWAYS
        to: end

  cancel_with_fee:
    action: cancel_order(order_id)
    edges:
      - condition: ALWAYS
        to: notify_fee

  notify_fee:
    action: send_fee_notice(order_id)
    edges:
      - condition: ALWAYS
        to: end

  escalate:
    action: open_ticket(order_id)
    edges:
      - condition: ALWAYS
        to: end

  end:
    terminal: true
SOP-Agent example: from human-readable to graph

Qué ocurre en un paso (p. ej., check_status)

  • Ejecución de la herramienta: get_order_status(order_id) → devuelve {"status": "packed"}

  • Actualización del tablero: ahora incluye status: "packed"

  • El navegador filtra las herramientas: los siguientes nodos hijos válidos implican acciones {propose_return, cancel_order, open_ticket}

  • El agente decide (opcional): el LLM elige cancel_order y propone {"order_id": "A3"}

  • Ruta vía aristas del SOP: status en ["processing","packed"] es verdadero → ir a cancel_with_fee.

¿Qué es “agéntico” vs “determinista” aquí?

  • Modo determinista (sin LLM): sigues ejecutando el grafo SOP de manera fiable — ideal para auditorías y repetibilidad.

  • Modo agéntico (con LLM decisor): en nodos con ambigüedad o necesidad de parámetros, el modelo elige solo entre las herramientas permitidas y rellena los argumentos; el SOP sigue gobernando las transiciones. Obtienes adaptabilidad sin renunciar a la política.

Propiedades clave de las que dependerás después

  • Herramientas filtradas por nodo (reduce alucinaciones).

  • DSL de condiciones simple y seguro para las aristas (==, !=, <, >, in, and/or/not, más ALWAYS).

  • Recorrido DFS selectivo (comprometerse, ir en profundidad y luego avanzar).

  • Acciones ficticias “explore” para bifurcaciones ambiguas (mantiene los prompts cortos y las decisiones claras).

3- Implementación A — Motor determinista (sin LLM)

  • Carga un POS legible por humanos (YAML).

  • Compila ese SOP en un grafo de decisión (nodos, aristas).

  • Ejecuta el grafo en un bucle ajustado: Actuar → Observar → Decidir ruta → Repetir — sin ningún modelo implicado, por lo que las ejecuciones son deterministas y completamente auditables.

 Deterministic engine (no LLM) example: from human-readable to graph

Dos componentes lo hacen posible:

  • action: la herramienta a invocar (una función de Python o un wrapper de API).

  • args: los parámetros con nombre que la herramienta espera (a menudo tomados del tablero).

  • edges: lista de { condition, to } que indica al motor a dónde ir a continuación.

  • terminal: true para el nodo final.

Las aristas se evalúan en orden; la primera condición verdadera gana. Si ninguna coincide, el motor lanza un error (muy útil para detectar huecos en el POS).

Por qué esto funciona

  • Booleano: and, or, not

  • args: los parámetros con nombre que la herramienta espera (a menudo tomados del tablero).

  • Comparación: ==, !=, <, <=, >, >=

  • Pertenencia: in, not in

  • Atajo: ALWAYS (siempre verdadero)

Los nombres en las condiciones (p. ej., status) se buscan en el tablero — el diccionario compartido de hechos acumulados a partir de las salidas de las herramientas. Esto mantiene el flujo de control transparente y comprobable.

3.4 Bucle de ejecución (cómo funciona la ejecución) Pseudocódigo:

bb = {order_id: ...}     # blackboard starts with inputs
node = root
while not node.terminal:
  # Act
  obs = call(node.action, args from bb)
  # Observe
  bb.update(obs)
  # Route (first matching edge)
  node = first child where eval(condition, bb) == True
# done

Decisiones de diseño que ayudan en producción:

  • Lista permitida de herramientas: solo se pueden invocar las acciones definidas en el POS.

  • Tiempos de espera / reintentos en la capa de herramienta (idempotente siempre que sea posible).

  • Registro estructurado: (nodo, acción, parámetros, observación, borde elegido) por cada paso.

Todas las ejecuciones comienzan con:

  • authenticate → establece auth:"ok", customer_id, lleva order_id → ALWAYS → check_status

  • check_status → establece status ("shipped", "processing", "packed" o "error")

Luego, el POS enruta según:

  • A1 (shipped):
    status == "shipped" → offer_return (propone devolución) → end
    El estado final incluye return_offered: true.

  • A2 (processing) y A3 (packed):
    status in ["processing", "packed"] → cancel_with_fee → notify_fee → end
    El estado final incluye cancelled: true, fee: 5.0, notice_sent: true.

  • A4 (error):
    status == "error" → escalate (abre ticket) → end
    El estado final incluye ticket_opened: true.

Obtienes una Ruta (la secuencia exacta de nodos) y un Tablero (todos los datos recopilados). Ese es tu registro de auditoría.

  • Determinismo y cumplimiento: el POS es la fuente de verdad; no hay sorpresas.

  • Auditabilidad: cada paso, entrada y observación queda registrada.

  • Sin juicio: cuando las ramas son imprecisas o los parámetros deben sintetizarse a partir de texto desordenado, el motor determinista no puede improvisar — ahí es donde ayuda la variante asistida por LLM (siguiente sección).

4- Implementación B — Variante con decisor LLM (agéntica, protegida por POS)

4.1 ¿Por qué añadir un LLM si ya tenemos reglas?
Las reglas (el grafo POS) son excelentes para mantener el orden, la política y la finalización. La vida real, sin embargo, exige criterio:

  • Varias ramas pueden ser plausibles con los mismos hechos (p. ej., reembolso vs. reenvío).

  • Las herramientas necesitan argumentos derivados de entradas desordenadas (tono, texto de queja, capturas de pantalla).

  • Algunos hechos aún no están disponibles; hay que decidir qué pedir a continuación.

El motor agéntico añade un decisor LLM (a menudo un LLM de propósito general) para manejar estos casos. El LLM interviene como decisor en esos momentos, pero solo dentro de los límites que el POS establece.

4.2 El contrato del “chooser” (solo JSON, con herramientas permitidas)
En un nodo con múltiples hijos válidos, el Navigator construye una lista filtrada de herramientas permitidas para el siguiente paso. El LLM recibe:

  • Un contexto breve: descripción del nodo actual + el tablero (hechos).

  • La lista permitida de herramientas para este paso (p. ej.,
    ["propose_return", "cancel_order", "open_ticket"]).

  • Una instrucción estricta para devolver solo JSON:

{"name": "", "args": { /* minimal, valid args */ }}

A continuación validamos:

  • `"name"` ∈ herramientas permitidas

  • `"args"` es un objeto (y opcionalmente se puede validar según el esquema de cada herramienta)

Si no es válido, se reintenta con un breve mensaje correctivo. Esto mantiene la salida del LLM predecible y auditable.

4.3 Diseño del prompt: breve, objetivo y basado en el estado
Un buen prompt de elección es:

  • Compacto: solo el resumen del nodo y los hechos del tablero.

  • Restringido: enumera explícitamente las herramientas permitidas.

  • Accionable: solicita un único objeto JSON (name, args).

Ejemplo (conceptual):

You are at SOP node 'check_status': Inspect the order status.
Observations: {"order_id":"A3","status":"packed","auth":"ok"}
Allowed tools: propose_return, cancel_order, open_ticket
Return ONLY: {"name":"","args":{...}}

4.4 Mecanismos de control que preservan el dominio
Incluso con un decisor LLM, el POS sigue estando al mando:

  • Lista de herramientas permitidas (por nodo) — el agente no puede ver ni invocar nada más.

  • Salida estructurada — solo se acepta `{"name":..., "args":{...}}`, sin texto libre.

  • Enrutamiento posterior a la elección — tras ejecutar la herramienta elegida, todavía se evalúan las condiciones de los bordes del POS para seleccionar el siguiente nodo. Si la herramienta no valida una rama, la ruta no cambiará.

  • Comprobación de argumentos — esquema opcional por herramienta (tipos, claves requeridas, rangos).

  • Observabilidad — registro (prompt, herramientas permitidas, salida del modelo, acción elegida, argumentos, resultado de la herramienta, siguiente nodo).

4.5 Manejo de bifurcaciones ambiguas con herramientas “dummy explore”
A veces los nodos hijos son ambiguos:

  • Varios hijos comparten la misma herramienta siguiente, o

  • Algunos hijos no tienen herramienta en su nodo de entrada.

Para mantener las decisiones claras, el Navigator sustituye las herramientas reales por selectores ficticios.

Allowed tools: explore_subtree_a, explore_subtree_b, explore_subtree_c

El LLM elige un subárbol (rápido y con baja carga cognitiva). Luego se enruta hacia ese hijo y se solicita una herramienta real un paso más adentro, donde las acciones divergen. Esto replica la técnica del artículo para minimizar llamadas innecesarias al modelo, manteniendo una exploración en profundidad y decisiva.

 Deterministic engine (no LLM) example: from human-readable to graph

4.7 Recorrido de e-commerce (con el LLM en el bucle)
Usando el mismo POS que antes:

  • A1 (status = "shipped")
    Las herramientas permitidas para el siguiente paso implican una ruta de devolución. El LLM elige `{"name":"propose_return","args":{"order_id":"A1"}}`. Lo ejecutamos; la condición `status == "shipped"` sigue siendo verdadera, y el POS enruta a `offer_return → end`.

  • A2 / A3 ("processing" / "packed")
    Normalmente, el LLM selecciona `cancel_order` con el `order_id` correcto. Tras la ejecución, el enrutamiento sigue `cancel_with_fee → notify_fee → end`.

  • A4 ("error")
    El LLM selecciona `open_ticket`; el POS enruta a `escalate → end`.

Si se generara un nodo ambiguo (p. ej., dos hijos sin acción de entrada), el Navigator presentaría primero las opciones `explore_subtree_*`; el LLM se compromete con un subárbol y luego se solicita la herramienta concreta dentro de él.

4.8 Modos de fallo y mitigaciones

  • El modelo propone una herramienta no permitida → rechazo automático + reintento con un breve mensaje correctivo.

  • Argumentos ausentes o mal formados → validación del esquema; se solicita un JSON corregido.

  • Ningún borde se vuelve verdadero tras ejecutar la herramienta → se recurre al siguiente borde más adecuado del POS o se lanza un error claro (útil para refinar el POS).

  • Decisiones alucinadas → ajustar los prompts, mantener la lista permitida pequeña y añadir **postcondiciones** (p. ej., asegurar que `status` exista antes de continuar el enrutamiento).

4.9 Lo que ganas (comparado con el determinista)

  • Juicio bajo demanda — el modelo elige entre rutas plausibles y sintetiza los argumentos.

  • Aún bajo control — el grafo POS define la legalidad y el orden; el enrutamiento sigue reglas.

  • Mejor adaptación a entradas no estructuradas — quejas en texto libre → argumentos estructurados.

5- Ingeniería y pruebas del POS

5.1 Mejores prácticas para condiciones ( mini-DSL)

  • Ancla las condiciones a observaciones específicas.
    Prefiere `status == "shipped"`, donde `status` es producido por `get_order_status`, y evita comprobaciones vagas como `if ready`.

  • Evita ramas superpuestas.
    Si dos bordes pueden ser verdaderos a la vez, el orden decide — por tanto, hazlas mutuamente excluyentes o establece un **orden de prioridad claro** y coméntalo.

  • Incluye siempre un caso general.
    Añade un `ALWAYS` final → `escalate` (o similar) para evitar callejones sin salida cuando el mundo te sorprenda.

  • Prefiere comprobaciones positivas sobre negaciones.
    `status in ["processing", "packed"]` es más fácil de razonar que `not (status == "shipped" or status == "error")`.

  • Mantén las condiciones pequeñas y tipadas.
    Usa comparaciones y pertenencia simples; normaliza los valores en la capa de herramienta (p. ej., pasando los estados a minúsculas) para que las condiciones se mantengan simples.

Pequeño ejemplo (antes/después):

# Before: overlapping & implicit
- condition: status != "error"
  to: cancel_with_fee
- condition: status == "shipped"
  to: offer_return

# After: mutually exclusive and explicit
- condition: status == "shipped"
  to: offer_return
- condition: status in ["processing","packed"]
  to: cancel_with_fee
- condition: status == "error"
  to: escalate

5.2 Precisión de recorrido vs. precisión de resultado final (qué medir):
Cuando pruebas una ejecución, puedes evaluarla de dos maneras:

  • Precisión de recorrido: ¿el motor siguió exactamente la secuencia de nodos que esperabas (p. ej., authenticate → check_status → cancel_with_fee → notify_fee → end)?

 Deterministic engine (no LLM) example: from human-readable to graph
  • Precisión de resultado final: ¿llegó al nodo o acción final correctos, independientemente de los pasos intermedios?
    Útil cuando los pasos intermedios son flexibles o se permiten alternativas, siempre que el resultado final cumpla con la política.

 Deterministic engine (no LLM) example: from human-readable to graph

Conviene medir ambas: la precisión de recorrido para el cumplimiento de la política y la experiencia de usuario, y el resultado final, para la corrección del negocio.

5.3 Comprobaciones tipo Monte-Carlo (rápidas y sólidas)

  • Genera escenarios: toma muestras de `status` del conjunto `{ "shipped", "processing", "packed", "error" }` y varía indicadores como `is_vip`, `within_return_window`, etc.

  • Ejecuta el motor para cada caso y verifica la ruta y/o el nodo final esperado.

  • Registra las sorpresas: cualquier evento de “no hay borde válido” o “múltiples bordes verdaderos” es un error o un vacío en tu POS.

Para el ejemplo de e-commerce:

  • Los casos tipo A1 (`status="shipped"`) deben ir a **offer_return → end**.

  • Los casos tipo A2/A3 (`"processing"|"packed"`) deben ir a **cancel_with_fee → notify_fee → end**.

  • Los casos tipo A4 (`"error"`) deben ir a **escalate → end**.

Si ocurre algo distinto, o una herramienta está devolviendo datos inesperados, o tus condiciones se solapan o falta algún caso.

5.4 Registros que realmente ayudan (observabilidad)
Registra un registro por paso con:

  • node (nombre), descripción

  • allowed_tools (qué herramientas el agente podía invocar)

  • acción ejecutada en este nodo (nombre + args)

  • observación (diccionario normalizado añadido al tablero)

  • matched_edge (condición) y >next_node

  • elapsed_ms (por herramienta y por paso)

Esto te proporciona:

  • Registros de auditoría para cumplimiento normativo.

  • Mapas de calor de qué bordes se activan más o menos (para detectar código muerto).

  • Comparaciones (diff) cuando un cambio en el POS altera el comportamiento (¿cambiaron las rutas o los resultados finales?).

5.5 Fuzzing y pruebas de propiedades

  • “Si `status=="shipped"`, la ruta **nunca** escala.”

  • “Si `cancelled==True`, entonces `fee` está presente y ≥ 0.”

  • “Ninguna ejecución supera `max_steps`” (protección contra bucles).

Estas invariantes permiten detectar rápidamente dependencias ocultas y bucles accidentales.

5.6 Manejo de los modos de fallo habituales

  • Sin borde válido → añade o ajusta un caso general; mejora la normalización de datos en las herramientas previas.

  • Múltiples bordes verdaderos → reordena o divide las condiciones para que sean mutuamente excluyentes.

  • Bucles infinitos o demasiado largos → añade contadores de bucle; mantén un `max_steps` por ejecución.

  • Errores o tiempos de espera de herramientas → reintentos con retroceso; efectos secundarios idempotentes; escalar cuando se agoten los intentos.

  • Hijos ambiguos → usa el patrón dummy explore (el LLM elige primero un subárbol) para mantener las decisiones claras.

5.7 Versionado y gestión de cambios

  • Etiqueta cada POS con una versión y un hash del grafo (derivado del YAML).

  • Registra las ejecuciones con `(sop_version, graph_hash)` para poder reproducir comportamientos anteriores.

  • Usa revisiones de PR para comparar cambios en el POS; son legibles (YAML) y comprensibles para el negocio.

  • Mantén versiones de respaldo si un nuevo POS rinde peor, para poder revertir rápidamente.

5.8 Métricas que realmente importan
Además de la precisión de recorrido y de resultado final:

  • Latencia de decisión (p50/p95) y coste (si se usa un LLM).

  • Pasos por ejecución (profundidad) y llamadas LLM por ejecución (mantener bajo con buenos POS).

  • Tasa de fallo de herramientas y **tasa de escalado** (¿estamos escalando demasiado?).

  • Entropía de ramas: ¿algunas decisiones son prácticamente aleatorias? Eso indica un problema en el prompt o en el POS.

5.9 Cómo se conecta todo esto con las dos implementaciones

  • Motor determinista: perfecto para construir el entorno de pruebas — todo es reproducible.

  • Variante con decisor LLM: ejecuta las mismas pruebas pero registra los prompts y las salidas del chooser; tu sistema de pruebas debe verificar que la herramienta elegida está en la lista permitida y que el POS sigue enroutando correctamente. En bifurcaciones ambiguas, verifica que el modelo primero elige un dummy y luego una acción real dentro de ese subárbol.

Conclusión y próximos pasos

SOP-Agent transforma un flujo de trabajo complejo y cargado de políticas en algo que un modelo no puede desviar fácilmente: las personas escriben el POS, nosotros lo compilamos en un grafo de decisiones, y un bucle de ejecución actúa, observa y enruta con límites seguros. Puedes ejecutarlo de forma determinista para obtener reproducibilidad perfecta, o dejar que un LLM decida en los pocos pasos donde el juicio realmente aporta valor — siempre dentro de los márgenes del POS.

  • Política primero, IA después. El POS define el orden, la legalidad y las salidas; el modelo solo elige entre acciones permitidas y propone los argumentos.

  • Determinista cuando se necesita. El motor sin LLM es económico, rápido y completamente auditable.

  • Agéntico cuando importa. El decisor LLM añade criterio y maneja entradas no estructuradas mientras el POS sigue gobernando el enrutamiento.

  • Legible y comprobable. El DSL de condiciones, la precisión de recorrido/resultado final y los registros por paso hacen que el comportamiento sea fácil de analizar.

Próximos pasos

  • 1. Conecta tus herramientas reales. Sustituye las funciones de demostración por tus propias API o consultas de base de datos y normaliza las salidas para mantener las condiciones simples.

  • 2. Refuerza los límites de seguridad. Añade esquemas por herramienta, límites numéricos y postcondiciones (p. ej., “debe establecer `status` antes de continuar”).

  • 3. Mide e itera. Supervisa la precisión de recorrido/resultado final, las llamadas LLM por ejecución, la latencia/coste y las tasas de escalado; corrige bordes solapados o ausentes.

  • 4. Gestiona la ambigüedad como propone el artículo. Usa herramientas dummy `explore_subtree_*` siempre que los nodos hermanos compartan la misma acción o carezcan de una, para que el agente se comprometa con una rama antes de tomar decisiones más profundas.

  • 5. Llévalo a producción. Añade reintentos, idempotencia, límites de tasa, `max_steps`, versiones del POS y registros estructurados para auditoría.

  • 6. Opcional: LangGraph/StateGraph. Si ya los utilizas, mapea nodos→estados y bordes→transiciones; mantén la misma lista permitida y el contrato del selector JSON.

Iniciemos tu proyecto

Te acompañamos con soluciones a medida, desde la idea hasta la implementación.

Contactar