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 due specifiche casistiche: quando l'ultimo nodo eseguto non ha nessuna callback after associata, oppure quando tale funzione restituisce un oggetto BaseAfterOutput privo di node_target.
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ù nodi muti vengono 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 può essere eseguito a seguito di tre eventi distinti:
- immediatamente dopo l'esecuzione dello stesso nodo, se questo ha la proprietà
is_mute = Trueovvero non invia alcun messaggio all'utente; - a seguito della risposta dell'utente se il nodo corrente ha inviato un messaggio all'utente;
- a seguito di un altro step
afterse quest'ultimo ha specificato nell'output la proprietàstep = Step.AFTER
Duarante questo step viene solamente lanciata la callback after del nodo se definita.
Se il nodo appena eseguito ha la proprità is_mute = True, allora la funzione execute non produce alcun output per l'utente, di conseguenze non si deve attendere la risposta di quest'ultimo prima di avviare lo step after con l'esecuzione della callback associata.