Step
Il ciclo di vita dell'esecuzione dell'albero si articola sempre in 4 step principali:
Explore
Lo step explore ha il compito di determinare automaticamente il prossimo nodo da eseguire. Si attiva in tre specifiche casistiche:
- quando l'ultimo nodo eseguto non ha nessuna callback
afterassociata; - quando la callback
afterrestituisce un oggettoBaseAfterOutputprivo dinode_target; - quando la callback
afterrestituisce un oggettoBaseAfterOutputcon stepstep = Step.EXPLORE;
I chunk nei nodi
I chunk svolgono un ruolo centrale durante lo step explore. Durante questa fase, la navigazione si sposta verso il nodo discendente (rispetto all'ultimo eseguito) che contiene il chunk con similarità maggiore rispetto alla direction.
Di default, la direzione, accessibile tramite l'explore_strategy corrisponde alla richiesta dell'utente.
Per questo motivo, i chunk non sono limitati ai soli nodi query, ma possono essere inseriti in tutte le altre tipologie di nodi impostando la proprietà for_rag = False.
A differenza dei chunk con for_rag = True (riservati ai soli nodi query per essere aggiunti al prompt da passare al LLM), tutti gli altri chunk fungono da veri e propri "cartelli stradali" per guidare la direzione dell'esplorazione.
Questo meccaniscmo permette di esplorare l'albero sfruttando la correlazione semantica tra la query dell'utente ed i chunk, senza dover puntare esplicitamente a un nodo tramite la callback after. Sostituire il routing semantico con l'abuso di riferimenti espliciti ottentui tramite l'esecuzione di un LLM comporta due grandi svantaggi:
- prestazioni: ogni chiamata ad un LLM ha un costo, e richiede tempo;
- affidabilità: l'affidabilità di una soluzione che si basa su LLM si abbassa tanti più
llm nodevengono concatenati prima di arrivare ed eseguire il nodo effettivo. Spesso in questi casi si ha un problema di propagaizone dell'errore!
Funzionamento
In questa fase emerge il criterio di collegamento tra i nodi che compone il grafo, infatti la ricerca si articola in questo modo:
-
Si verifica la proprietà
is_blockingdel nodo corrente:- Se uguale a
Truesi rimane nel nodo attuale e si passa allo stepbefore, quindi la ricerca è conclusa; - Se uguale a
Falsela ricerca prosegue con la fase successiva;
- Se uguale a
-
Se il nodo corrente non ha figli, allora:
- Se il nodo attuale è anche l'ultimo ad essere stato eseguito, allora si incrementa il
fail_counterdi 1 e si raggiunge il nodo ascendente con proprietàis_anchor = Truepiù prossimo; - Altrimenti si rimane nel nodo attuale e si passa allo step
before, quindi la ricerca è conclusa;
- Se il nodo attuale è anche l'ultimo ad essere stato eseguito, allora si incrementa il
-
Viene recuperato il chunk appartenente all'albero sottostante al nodo corrente con la maggiore similarity rispetto all'embedding della stringa inserita dentro l'
explore_strategy:- Se la similarity è maggiore della
similarity_thresholdspecificata nelle proprietà del nodo, allora ci si sposta nel nodo figlio il cui sottoalbero contiene il chunk individuato e si lascia lo stepexplore, il flusso di ricerca si ripete; - Se la similarity è minore della soglia allora:
- Se il nodo attuale è anche l'ultimo ad essere stato eseguito, allora si incrementa il
fail_counterdi 1 e si raggiunge il nodo ascendente con proprietàis_anchor = Truepiù prossimo. Viene anche aggiunto automaticamente il nodo attuale nellablack_listdell'explore_strategy; - Altrimenti si rimane nel nodo attuale e si passa allo step
before, quindi la ricerca è conclusa;
- Se il nodo attuale è anche l'ultimo ad essere stato eseguito, allora si incrementa il
- Se la similarity è maggiore della
Il punto 1 si salta all'inizio dello step, altrimenti un nodo bloccante appena eseguito interromperebbe subito la ricerca.
La similarity è un numero calcolato confrontando l'embedding della query con l'embedding di un singolo chunk. Nell'albero, come misura si utilizza la cosine similarity.
L'explore_strategy permette di personalizzare la logica con cui viene individuato il nodo da eseguire durante lo step explore. Questo oggetto, accessibile tramite il context, contiene sia la direction ovvero il testo che viene embeddato per calcolare la similarity sia dei vincoli che possono escludere dei chunk del grafo.
Before
Lo step before segue sempre explore, oppure può essere manualmente impostato a seguito di una callback after. Lo scopo di questo step è quello di consentire l'esecuzione dell'omonima callback eventualmente associata al nodo corrente.
Execute
Durante questo step viene eseguita la funzione execute del nodo individuato tramite esplorazione, oppure esplicitamente richiesto tramite la proprietà node_target nell'output della callback after. Durante questo step, in base alla tipologia del nodo corrente posso essere eseguite azioni distinte: ad esempio nel nodo query viene fatta una ricerca nel vector store associato, oppure nel nodo llm viene fatta una richiesta al modello indicato, oppure nel nodo empty non viene fatto nulla!
A differenza delle callback, l'esecuzione del nodo non è direttamente personalizzabile. In particolare, durante lo step execute vengono eseguite la maggior parte delle callback associate al nodo, ma l'ordine di esecuzione, ed il modo con cui vengono utilizzati i relativi output non può essere modificato.
After
Lo step after viene eseguito in due momenti distinti:
- immediatamente dopo l'esecuzione dello stesso nodo, se questo non ha interrotto il flusso con un output verso l'utente;
- dopo la risposta dell'utente, se il nodo ha inviato un messaggio (nodo
emit) o un avviso (nodoguardrail). In questo caso l'esecuzione dell'afterviene sospesa in attesa del successivo input. Questo è particolarmente utile per i nodiemitcon opzioni: la callbackafterriceve nel camposelected_optionl'opzione scelta dall'utente.
Durante questo step viene solamente lanciata la callback after del nodo se definita.