Publicat el 7 d’octubre de 2025

per Osledy Bazo

Agentic SOPs: De SOPs a Grafs de Decisió i Agents Fiables.

1- Com els Grafs de Decisió fan els LLMs més fiables.

Els models de llenguatge grans són bons escrivint i raonant en situacions concretes, però es tornen fràgils quan la tasca esdevé un flux de treball complex, amb molta càrrega de polítiques i múltiples passos: els plans es desvien, les eines s’invoquen fora d’ordre i una sola al·lucinació pot descarrilar l’execució. L’article SOP-Agent proposa una solució pragmàtica: codificar els Procediments Operatius Estàndard (SOPs) de l’organització en llenguatge natural, compilar-los en un graf de decisió (nodes = accions, arestes = condicions IF/ALWAYS), i deixar que un agent recorri aquest graf selectivament mentre només veu un conjunt filtrat d’eines vàlides a cada pas. En altres paraules, la política humana continua manant i l’agent aporta el judici i la crida.

Concretament, SOP-Agent té dues peces clau: un SOP-Navigator que formata la part actual del SOP i limita l’espai d’accions, i un Base Agent (sovint un LLM) que proposa la següent crida de funció (i els arguments). El recorregut és d’estil DFS (Depth-first Search), de manera que el sistema pot anar en profunditat quan es compleixen les precondicions d’una branca. A més, inclou un truc elegant per a bifurcacions ambigües: quan dos nodes fills comparteixen la mateixa funció o no en tenen cap, l’agent primer crida una eina fictícia “explore_subtree_X” per comprometre’s amb una branca, i després genera l’acció real dins d’aquell subarbre. Això manté baix el nombre de crides al LLM tot preservant el control.

Per què t’hauria d’importar? Perquè els resultats suggereixen que basar-se en SOPs substitueix allò “enginyós però fràgil” per “intel·ligent i fiable” en tasques molt diverses. Al benchmark de presa de decisions ALFWorld, un agent guiat per SOP arriba a un 0,888 d’èxit (few-shot), superant una línia base ReAct sòlida i deixant molt enrere les variants d’AutoGPT. En un entorn pràctic de neteja de dades, aconsegueix un 100% d’èxit, competint amb un sistema especialitzat en el domini. I en un nou benchmark de Grounded Customer Service (cinc indústries, fluxos guiats per SOP), l’enfocament assoleix una ~99,8% de precisió mantenint-se auditable i conforme a les polítiques —exactament el que volen els equips d’operacions.

Aquest article es basa en el treball “SOP-Agent: Empower General Purpose AI Agent with Domain-Specific SOPs” (Ye et al.), publicat a arXiv el 2025 (arXiv:2501.09316). Els autors formalitzen els SOPs com a grafs de decisió, introdueixen l’arquitectura SOP-Navigator + Base Agent (incloent-hi el patró fictici explore_subtree_* per a branques ambigües), i reporten resultats sòlids en ALFWorld, generació de codi (HumanEval/MBPP), neteja de dades i un nou benchmark de Grounded Customer Service.

2- La idea de SOP-Agent

Moviment clau: escriu el teu SOP en llenguatge natural (amb vinyetes o pseudocodi), compila’l en un graf de decisió i executa aquest graf amb baranes de seguretat. El graf té:

  • Nodes = accions que el sistema pot dur a terme (p. ex., get_order_status, cancel_order, open_ticket).

  • Arestes = condicions que decideixen on anar a continuació (comprovacions simples IF/ALWAYS sobre fets de l’estat).

  • Un tauler compartit (estat) que acumula fets a partir de crides a eines (p. ex., {"status": "shipped", "cancelled": true}).

En temps d’execució, el bucle de l’agent fa: Actuar → Observar → Decidir ruta → Repetir:

  • Cridar l’eina del node (Actuar).

  • Incorporar la seva sortida al tauler (Observar).

  • Avaluar les condicions de les arestes per escollir el següent node (Decidir ruta).

  • Continuar fins a un node terminal.

Dos components ho fan possible:

  • SOP-Navigator (estructura): manté el node actual + el tauler, formata una vista compacta de “què està permès a continuació” i filtra la llista d’eines perquè només apareguin les accions vàlides en aquest pas.

  • Base Agent (decisor): habitualment un LLM que, dins d’aquest conjunt filtrat d’eines, tria quina eina cridar i amb quins arguments quan cal judici. (També es pot executar sense LLM — de manera purament determinista.)

Per què això funciona

  • Baranes de seguretat: el graf SOP dicta l’ordre, la legalitat i les condicions de sortida; les eines desconegudes simplement no són visibles.

  • Judici on importa: quan diverses branques són plausibles o cal sintetitzar arguments, l’agent (LLM) tria —però sempre dins de la tanca del SOP.

  • Recorregut eficient: el sistema tendeix a anar en profunditat (comprometre’s amb una branca i executar-ne els passos) en lloc d’anar saltant, cosa que redueix crides inútils i desviacions en la planificació.

  • Bifurcacions ambigües ben resoltes: si dos nodes fills comparteixen la mateixa funció (o no en tenen cap), el navegador ofereix eines fictícies “explore_subtree_X” perquè l’agent primer triï una branca; l’acció real passa un nivell més avall.

Un petit exemple de SOP (llegible per humans → graf)


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è passa en un pas (p. ex., check_status)

  • Execució de l’eina: get_order_status(order_id) → retorna {"status": "packed"}

  • Actualització del tauler: ara inclou status: "packed"

  • El navegador filtra les eines: els següents nodes fills vàlids impliquen accions {propose_return, cancel_order, open_ticket}

  • L’agent decideix (opcional): l’LLM tria cancel_order i proposa {"order_id": "A3"}

  • Ruta via arestes del SOP: status a ["processing","packed"] és cert → anar a cancel_with_fee.

Què és “agenètic” vs “determinista” aquí?

  • Mode determinista (sense LLM): continues executant el graf SOP de manera fiable — ideal per a auditories i repetibilitat.

  • Mode agenètic (amb LLM decisor): en nodes amb ambigüitat o necessitat de paràmetres, el model tria només entre les eines permeses i omple els arguments; el SOP encara governa les transicions. Obtenim adaptabilitat sense renunciar a la política.

Propietats clau de les quals et refiaràs després

  • Eines filtrades per node (redueix al·lucinacions).

  • DSL de condicions simple i segura per a les arestes (==, !=, <, >, in, and/or/not, més ALWAYS).

  • Recorregut DFS selectiu (comprometre’s, anar en profunditat i després avançar).

  • Accions fictícies “explore” per a branques ambigües (manté els prompts curts i les decisions clares).

3- Implementació A — Motor determinista (sense LLM)

  • Carrega un SOP llegible per humans (YAML).

  • Compila aquest SOP en un graf de decisió (nodes, arestes).

  • Executa el graf en un bucle ajustat: Actuar → Observar → Decidir ruta → Repetir — sense cap model involucrat, de manera que les execucions són deterministes i completament auditables.

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

Dos components ho fan possible:

  • action: l’eina a cridar (una funció de Python o un embolcall d’API).

  • args: els paràmetres amb nom que l’eina espera (sovint provinents del tauler).

  • edges: llista de { condition, to } que indica al motor on anar a continuació.

  • terminal: true per al node final.

Les arestes s’avaluen en ordre; la primera condició certa guanya. Si cap coincideix, el motor genera un error (molt útil per detectar buits en el SOP).

Per què això funciona

  • Booleà: and, or, not

  • args: els paràmetres amb nom que l’eina espera (sovint provinents del tauler).

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

  • Pertinença: in, not in

  • Drecera: ALWAYS (sempre cert)

Els noms a les condicions (p. ex., status) es busquen al tauler — el diccionari compartit de fets acumulats a partir de les sortides de les eines. Això manté el flux de control transparent i verificable.

3.4 Bucle d’execució (com funciona l’execució) Pseudocodi:

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

Decisions de disseny que ajuden en producció:

  • Llista permesa d’eines: només es poden cridar les accions definides al SOP.

  • Temps d’expiració / reintents a la capa de l’eina (idempotent sempre que sigui possible).

  • Registre estructurat: (node, acció, paràmetres, observació, vora escollida) per cada pas.

Totes les execucions comencen amb:

  • authenticate → estableix auth:"ok", customer_id, porta order_id → ALWAYS → check_status

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

Després, el SOP dirigeix segons:

  • A1 (shipped):
    status == "shipped" → offer_return (proposa devolució) → end
    L’estat final inclou return_offered: true.

  • A2 (processing) i A3 (packed):
    status in ["processing", "packed"] → cancel_with_fee → notify_fee → end
    L’estat final inclou cancelled: true, fee: 5.0, notice_sent: true.

  • A4 (error):
    status == "error" → escalate (obre tiquet) → end
    L’estat final inclou ticket_opened: true.

Obtens un Camí (la seqüència exacta de nodes) i un Tauler (tots els fets recopilats). Aquest és el teu registre d’auditoria.

  • Determinisme i compliment: el SOP és la font de veritat; no hi ha sorpreses.

  • Auditoria: cada pas, entrada i observació queda registrada.

  • Sense judici: quan les branques són imprecises o els paràmetres s’han de sintetitzar a partir de text desordenat, el motor determinista no pot improvisar — aquí és on ajuda la variant assistida per LLM (següent secció).

4- Implementació B — Variant amb decisor LLM (agentic, amb protecció SOP)

4.1 Per què afegir un LLM si ja tenim regles?
Les regles (el gràfic SOP) són excel·lents per mantenir l’ordre, la política i la finalització. La vida real, però, requereix criteri:

  • Diverses branques poden ser plausibles amb els mateixos fets (p. ex., reemborsament vs. reenviament).

  • Les eines necessiten arguments derivats d’entrades desordenades (to, text de queixa, captures de pantalla).

  • Alguns fets encara no estan disponibles; cal decidir què cal demanar a continuació.

El motor agenètic afegeix un decisor LLM (sovint un LLM d’ús general) per gestionar aquests casos. L’LLM intervé com a decisor en aquests moments — però només dins dels límits que el SOP defineix.

4.2 El contracte del “chooser” (només JSON, amb eines permeses)
En un node amb múltiples fills vàlids, el Navigator construeix una llista filtrada d’eines permeses per al següent pas. L’LLM rep:

  • Un context breu: descripció del node actual + el tauler (fets).

  • La llista permesa d’eines per a aquest pas (p. ex.,
    ["propose_return", "cancel_order", "open_ticket"]).

  • Una instrucció estricta per retornar només JSON:

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

A continuació validem:

  • `"name"` ∈ eines permeses

  • `"args"` és un objecte (i opcionalment es pot validar segons l’esquema de cada eina)

Si no és vàlid, es torna a intentar amb un breu missatge correctiu. Això manté la sortida de l’LLM previsible i auditable.

4.3 Disseny del prompt: breu, objectiu i basat en l’estat
Un bon prompt de tria és:

  • Compacte: només el resum del node i els fets del tauler.

  • Restringit: enumera explícitament les eines permeses.

  • Operatiu: demana un únic objecte JSON (name, args).

Exemple (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 Mecanismes de control que preserven el domini
Fins i tot amb un decisor LLM, el SOP continua tenint el control:

  • Llista d’eines permeses (per node) — l’agent no pot veure ni cridar res més.

  • Sortida estructurada — només s’accepta `{"name":..., "args":{...}}`, sense text lliure.

  • Enrutament posterior a la tria — després d’executar l’eina escollida, encara s’avaluen les condicions de les vores del SOP per seleccionar el següent node. Si l’eina no fa vàlida una branca, la ruta no canviarà.

  • Validació dels arguments — esquema opcional per a cada eina (tipus, claus obligatòries, intervals).

  • Observabilitat — registre (prompt, eines permeses, sortida del model, acció triada, arguments, resultat de l’eina, següent node).

4.5 Gestió de bifurcacions ambigües amb eines “dummy explore”
De vegades els nodes fills són ambigus:

  • Diversos fills comparteixen la mateixa eina següent, o

  • Alguns fills no tenen cap eina al seu node d’entrada.

Per mantenir les decisions clares, el Navigator substitueix les eines reals per selectors ficticis.

Allowed tools: explore_subtree_a, explore_subtree_b, explore_subtree_c

L’LLM tria un subarbre (ràpid, amb baixa càrrega cognitiva). Després es dirigeix cap a aquest fill i es demana una eina real un pas més endins, on les accions divergeixen. Això reprodueix l’estratègia de l’article per minimitzar trucades innecessàries al model, mantenint una exploració en profunditat i decisiva.

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

4.7 Recorregut d’e-commerce (amb l’LLM en el bucle)
Usant el mateix SOP que abans:

  • A1 (status = "shipped")
    Les eines permeses per al següent pas impliquen un camí de devolució. L’LLM tria `{"name":"propose_return","args":{"order_id":"A1"}}`. L’executem; la condició `status == "shipped"` continua essent certa, i el SOP condueix a `offer_return → end`.

  • A2 / A3 ("processing" / "packed")
    Normalment, l’LLM selecciona `cancel_order` amb el `order_id` correcte. Després de l’execució, l’enrutament segueix `cancel_with_fee → notify_fee → end`.

  • A4 ("error")
    L’LLM selecciona `open_ticket`; el SOP condueix a `escalate → end`.

Si es generés un node ambigu (p. ex., dos fills sense acció d’entrada), el Navigator primer presentaria les opcions `explore_subtree_*`; l’LLM es compromet amb un subarbre, i després se li demana l’eina concreta dins d’aquest.

4.8 Modes de fallada i mitigacions

  • El model proposa una eina no permesa → rebut automàtic + reintent amb un missatge correctiu breu.

  • Arguments absents o malformats → validació de l’esquema; es demana un JSON corregit.

  • Cap vora esdevé certa després d’executar l’eina → es torna a la següent millor vora del SOP o s’aixeca un error clar (útil per refinar el SOP).

  • Decisions al·lucinades → ajusta els prompts, mantén la llista permesa petita i afegeix **postcondicions** (p. ex., assegurar que `status` existeix abans de continuar l’enrutament).

4.9 Què hi guanyes (en comparació amb el determinista)

  • Criteri sota demanda — el model tria entre camins plausibles i sintetitza els arguments.

  • Encara sota control — el gràfic SOP defineix la legalitat i l’ordre; l’enrutament és basat en regles.

  • Millor adaptació a entrades no estructurades — queixes en text lliure → arguments estructurats.

5- Enginyeria i proves del SOP

5.1 Bones pràctiques per a condicions ( mini-DSL)

  • Ancla les condicions a observacions específiques.
    Prefereix `status == "shipped"`, on `status` és generat per `get_order_status`, i evita comprovacions vagues com `if ready`.

  • Evita branques superposades.
    Si dues vores poden ser certes alhora, l’ordre decideix — per tant, fes-les mútuament excloents o estableix un **ordre de prioritat clar** i comenta-ho.

  • Inclou sempre un cas general.
    Afegeix un `ALWAYS` final → `escalate` (o similar) per evitar punts morts quan el món et sorprengui.

  • Prefereix comprovacions positives en lloc de negacions.
    `status in ["processing", "packed"]` és més fàcil de raonar que `not (status == "shipped" or status == "error")`.

  • Mantén les condicions simples i tipades.
    Utilitza comparacions i pertinença senzilles; normalitza els valors a la capa de l’eina (p. ex., passant els estats a minúscules) perquè les condicions es mantenen simples.

Petit exemple (abans/despré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ó de recorregut vs. precisió de resultat final (què mesurar):
Quan proves una execució, pots avaluar-la de dues maneres:

  • Precisió de recorregut: el motor ha seguit exactament la seqüència de nodes que esperaves (p. ex., authenticate → check_status → cancel_with_fee → notify_fee → end)?

 Deterministic engine (no LLM) example: from human-readable to graph
  • Precisió de resultat final: ha arribat al node o acció final correcte, independentment dels passos intermedis?
    Útil quan els passos intermedis són flexibles o s’accepten alternatives, sempre que el resultat final coincideixi amb la política.

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

Cal tenir totes dues: la precisió de recorregut per al compliment de la política i l’experiència d’usuari, i el resultat final, per a la correcció empresarial.

5.3 Comprovacions tipus Monte-Carlo (ràpides i robustes)

  • Genera escenaris: mostra valors de `status` del conjunt `{ "shipped", "processing", "packed", "error" }`, i varia indicadors com `is_vip`, `within_return_window`, etc.

  • Executa el motor per a cada cas i verifica el recorregut i/o el node final esperat.

  • Registra les sorpreses: qualsevol esdeveniment de “no hi ha cap vora vàlida” o “múltiples vores certes” és un error o una llacuna en el teu SOP.

Per a l’exemple d’e-commerce:

  • Els casos tipus A1 (`status="shipped"`) han d’anar a **offer_return → end**.

  • Els casos tipus A2/A3 (`"processing"|"packed"`) han d’anar a **cancel_with_fee → notify_fee → end**.

  • Els casos tipus A4 (`"error"`) han d’anar a **escalate → end**.

Si passa alguna altra cosa, o bé una eina retorna dades inesperades, o bé les teves condicions se superposen o n’hi falta alguna.

5.4 Registres que realment ajuden (observabilitat)
Registra un registre per pas amb:

  • node (nom), descripció

  • allowed_tools (quines eines l’agent podia cridar)

  • acció executada en aquest node (nom + args)

  • observació (diccionari normalitzat afegit al tauler)

  • matched_edge (condició) i >next_node

  • elapsed_ms (per eina i per pas)

Això et dona:

  • Registres d’auditoria per a compliment normatiu.

  • Mapes de calor de quines vores s’activen més o menys (per detectar codi mort).

  • Comparacions (diff) quan un canvi al SOP altera el comportament (han canviat els recorreguts o els resultats finals?).

5.5 Fuzzing i proves de propietats

  • “Si `status=="shipped"`, el recorregut **mai** escala.”

  • “Si `cancelled==True`, aleshores `fee` és present i ≥ 0.”

  • “Cap execució supera `max_steps`” (protecció contra bucles).

Aquestes invariants permeten detectar ràpidament dependències ocultes i bucles accidentals.

5.6 Gestió dels modes de fallada habituals

  • Cap vora vàlida → afegeix o ajusta un cas general; millora la normalització de dades a les eines anteriors.

  • Múltiples vores certes → reordena o divideix les condicions perquè siguin mútuament excloents.

  • Bucles infinits o massa llargs → afegeix comptadors de bucle; mantén un `max_steps` per execució.

  • Errors o temps d’expiració de les eines → reintents amb retrocés; efectes laterals idempotents; escalada quan s’exhaureixen.

  • Fills ambigus → utilitza el patró dummy explore (l’LLM tria primer un subarbre) per mantenir les decisions clares.

5.7 Versionat i gestió de canvis

  • Etiqueta cada SOP amb una versió i un hash del gràfic (derivat del YAML).

  • Registra les execucions amb `(sop_version, graph_hash)` perquè puguis reproduir comportaments anteriors.

  • Fes revisions de PR per comparar diferències al SOP; són llegibles (YAML) i comprensibles per al negoci.

  • Mantén versions de reserva si un nou SOP té pitjor rendiment, per poder fer marxa enrere ràpidament.

5.8 Mètriques que realment importen
A més de la precisió de recorregut i de resultat final:

  • Latència de decisió (p50/p95) i cost (si s’utilitza un LLM).

  • Passos per execució (profunditat) i trucades LLM per execució (mantenir baix amb bons SOPs).

  • Taxa de fallada d’eines i **taxa d’escalada** (estem escalant massa?).

  • Entropia de branques: algunes decisions són pràcticament aleatòries? Això indica un problema de prompt o de SOP.

5.9 Com es connecta tot això amb les dues implementacions

  • Motor determinista: perfecte per construir el sistema de proves — tot és reproduïble.

  • Variant amb decisor LLM: executa les mateixes proves però registra els prompts i sortides del chooser; el teu sistema de proves ha de verificar que l’eina escollida és a la llista permesa i que el SOP continua enrutant correctament. En bifurcacions ambigües, comprova que el model tria primer un dummy i després una acció real dins d’aquell subarbre.

Conclusió i propers passos

SOP-Agent converteix un flux de treball desordenat i ple de polítiques en un sistema que el model no pot desviar fàcilment: les persones escriuen el SOP, nosaltres el compilem en un gràfic de decisions, i un bucle d’execució actua, observa i enruta amb baranes de seguretat. Pots executar-lo de manera determinista per a una reproducció perfecta, o deixar que un LLM decideixi en aquells passos on el criteri humà realment aporta valor — sempre dins dels límits del SOP.

  • Política primer, IA després. El SOP defineix l’ordre, la legalitat i les sortides; el model només tria entre accions permeses i proposa els arguments.

  • Determinista quan cal. El motor sense LLM és econòmic, ràpid i totalment auditable.

  • Agenètic quan importa. El decisor LLM afegeix criteri i gestiona entrades no estructurades mentre el SOP continua governant l’enrutament.

  • Llegible i verificable. El DSL de condicions, la precisió de recorregut/resultat final i els registres per pas fan que el comportament sigui fàcil d’interpretar.

Pròxims passos

  • Connecta les teves eines reals. Substitueix les funcions de demostració per les teves API o consultes a la base de dades, i normalitza les sortides perquè les condicions es mantinguin simples.

  • Ajusta les baranes de seguretat. Afegeix esquemes per eina, límits numèrics i postcondicions (p. ex., “cal establir `status` abans de continuar”).

  • Mesura i iterar. Controla la precisió de recorregut/resultat final, les trucades LLM per execució, la latència/cost i les taxes d’escalada; corregeix vores superposades o absents.

  • Gestiona l’ambigüitat com proposa l’article. Utilitza eines dummy `explore_subtree_*` sempre que nodes germans comparteixin la mateixa acció o no en tinguin cap, perquè l’agent es comprometi amb una branca abans de prendre decisions més profundes.

  • Porta-ho a producció. Afegeix reintents, idempotència, límits de freqüència, `max_steps`, versions del SOP i registres estructurats per a auditoria.

  • Opcional: LangGraph/StateGraph. Si ja els utilitzes, mapeja nodes→estats i vores→transicions; mantén la mateixa llista permesa i el contracte de selecció en JSON.

Construïm junts

Unim experiència i innovació per portar el teu projecte al següent nivell.

Contacta’ns