Passa al contenuto principale

Tipologie di nodi

I nodi sono i mattoni che compongono il grafo del Tree Agent.

informazioni

Ogni volta che si compila con successo un grafo, questo viene convertito in un file pronto per l'esecuzione. Durante questo processo, alcuni nodi, detti virtuali, vengono eliminati e le informazioni in essi contenute trasferite nel file finale. I nodi virtuali sono quelli di tipo: root, component, head, tail.

warning

Nessun nodo può mai essere figlio di più nodi, e tutti i nodi, ad eccezione di quelli virtuali root, head e tail devono sempre avere un genitore!

Per offrire la massima flessibilità, esistono diverse tipologie di nodi, ognuno dotato di un differente set di callback e di proprietà, e con una funzione di esecuzione distinta. Indipendentemente dalla loro tipologia, tutti i nodi non virtuali condividono questo set di proprietà e callback:

  • name (str): è il nome del nodo;
  • instruction (str | None): è una stringa richiamabile in fase di creazione del prompt;
  • context (str | None): è una stringa richiamabile in fase di creazione del prompt;
  • before (Callable | None): la callback before;
  • after (Callable | None): la callback after;
  • is_blocking (bool): è True se il nodo è bloccante (fase 1 dello step explore), False altrimenti (a livello grafico si ha l'icona 🔒 attiva quando la proprietà è True);
  • is_anchor (bool): è True se il nodo è un anchor (fase 2 dello step explore explore), False altrimenti (a livello grafico si ha l'icona ⚓ attiva quando la proprietà è True);
  • similarity_threshold (float | None): se specificato sovrascrive la similarità minima che si deve raggiungere in fase di explore per scendere su un nodo figlio. In caso di un query node specifica invece la similarità minima che si deve raggiungere per estrarre i chunk dal vector store.
warning

Seppure le proprietà sopra indicate siano comuni, alcune di queste potrebbero avere dei valori predefiniti per alcune tipologie di nodi, e pertanto apparire nascoste a livello di interfaccia grafica.

Root node

Questo nodo virtuale viene aggiunto automaticamente durante la creazione di un nuovo albero, e non può: nè essere duplicato nè essere cancellato. Il root node è utile per specificare alcune proprietà associate all'intero albero che, come tali, non appartengono ad alcun nodo specifico.

Nell'interfaccia, tramite il nodo root è possibile specificare:

  • init (Callable | None): funzione di inizializzazione del self disponibile a tutti i nodi definiti allo stesso livello della root, quindi al di fuori di ogni componente;
  • before_run (Callable | None): callback before_run;
  • after_run (Callable | None): callback after_run;
  • default_models: dove è possibile definire il modello LLM da impiegare di default per ciascuna tipologia di nodo;
  • default_similarity_threshold: dove è possibile definire la similarità minima da raggiungere per scendere in un nodo figlio durante lo step explore;
  • memory_agent_props: le proprietà richieste dal Memory Agent.
suggerimento

Tutte le proprietà di default possono essere sovrascritte nodo per nodo tramite apposite proprietà

informazioni

L'argomento special della callback extend message contiene:

class SimpleNamespace:
topics: str | None # i topics eventualmente inseriti tramite interfaccia grafica
conversation_goal: str | None # il conversation goal eventualmente inserito tramite interfaccia grafica
informazioni

L'argomento special della callback after_run contiene il messaggio di risposta prodotto dal Tree Agent accessibile con special.agent_output

warning

Il root può avere solo nodi figli di tipo agent

informazioni

Anche se i nodi di tipo llm sono gli unici a poter richiamare liberamente un LLM per rispondere all'utente, i nodi di tipo open e close li utilizzano per fare la traduzione quando la callback translate è definita

Agent node

Tramite i nodi di tipo agent è possibile definire più agenti distinti.

warning

L'agent node deve avere come genitore il root node e avere sempre e solo un nodo figlio.

Il nodo agent ha le seguenti proprietà extra:

  • is_primary (bool): se True allora in caso di primo messaggio, in mancanza di una callback before_run che specifica un nodo da eseguire, lo step di explore sarà avviato dall'agente composto dai nodi appartenenti al sottoalbero di questo nodo;
  • role (str) è il ruolo dell'agente. Questa informazione di default sarà accessibile tramite gli argomenti delle callback prompt di tutti i nodi appartenenti all'agente;
  • fallback (Callable) funzione di fallback associata.
informazioni

Sempre e solo un nodo agent ha la proprietà is_primary = True. A livello grafico è possibile riconoscerlo perchè ha l'icona ☆ attiva

LLM node

Il nodo di tipo llm è utile quando si vuole richiamare un LLM per interagire direttamente con l'utente, oppure per fare dei ragionamenti guidati.

Il nodo llm ha le seguenti proprietà extra:

  • model (str | None): se specificato, sovrascrive il modello LLM utilizzato di default;
  • prompt (Callable): è la callback prompt;
  • is_mute (bool): se True allora l'output del nodo NON sarà inviato all'utente, ma sarà comunque accessibile nella callback after del nodo stesso tramite l'argomento special.llm_response;
  • response_format (dict): se abilitato il dizionario dovrà contenere la forma che si intende imporre alla risposta del LLM. Per definire questa struttura si deve far riferimento al response format impiegato da OpenAI. In caso di provider LLM diverso, il framework convertirà autimaticamente il response format per mantenere la compatibilità;
  • attempts specifica il massimo numero di chiamate al LLM che verranno fatte in caso di mancato rispetto dei vincoli imposti dal response_format. In caso di superamento di tale limite, l'esecuzione del nodo sarà interotta e si passerà immediatamente ad eseguire la callback after del nodo. Per gestire questi casi, tramite special.fail_response_format si può verificare se l'output è stato prodotto con successo (False) o meno (True).
informazioni

L'argomento special delle callback after e prompt nel nodo di tipo llm contiene le seguenti informazioni:

class SpecialLlmNodeContext:
prompt_obj: dict | None # l'insieme di informazioni utili per la composizione del prompt
llm_response: str | None # la risposta prodotta dal LLM
fail_response_format: bool | None # diventa True se è stato impostato un response_format ed il LLM ha fallito la sua applicazione per attempts volte

con prompt_obj:

{
"role": str # il ruolo definito dentro l'agent node a capo del nodo attuale
"context": List[str] # una lista che contiene, concatenati uno dopo l'altro, i contesti definiti nella catena di nodi che parte dal nodo agente e finisce nel nodo corrente
"instructions": str # le istruzioni eventualmente inserite nel nodo attuale
}

Il prompt_obj è disponibile solo quando la callback è prompt. La llm_response è disponibile solo quando la callback è after e il nodo ha la proprietà is_mute = True, in caso contrario è None. Quando si vuole accedere alla risposta di un LLM non muto, visto che la riposta è stata restituita all'utente, è suffciente accedere all'ultimo messaggio dello storico della chat attraverso il Memory Agent.

La fail_response_format è disponibile solo quando la callback è after e il nodo ha specificato un response_format, in caso contrario è None.

Switch node

Il nodo di tipo switch è una versione più specifica e ottimizzata del llm node da utilizzare in quei casi in cui si ha bisogno di eseguire un LLM per individuare il corretto nodo da eseguire.

Il nodo switch ha le seguenti proprietà extra:

  • model (str | None): se specificato, sovrascrive il modello LLM utilizzato di default;
  • alternatives: permette di indicare quali nodi eseguire in base alle descrizioni associate;
  • is_mute (bool): impostato automaticamente a True, quindi l'output del nodo NON sarà mai inviato all'utente, ma sarà accessibile nella callback after del nodo stesso tramite l'argomento special.llm_response.

In particolare le alternatives permettono di specificare i possibili nodi da eseguire in base a delle condizioni indicate via testo. Ogni alternatives richiede le seguenti informazioni:

  • keyword: una parola chiave che descrive la condizione che attiva l'alternativa;
  • node_target: il nodo da raggiungere quando la condizione si verifica secondo la descrizione fornita;
  • step: lo step da settare quando si verifica la condizione;
  • description: condizione che il LLM deve verificare per attivare o meno quell'alternativa;
informazioni

Seguendo alternatives il framework imposta automaticamente il response_format, il prompt e la callback after.

warning

Il sistema verifica che la risposta fornita dall'LLM rispetti il response_format indicato. Nel caso in cui:

  • il LLM seleziona la condizione associata a FALLBACK;
  • oppure il response_format viene ignorato per 2 volte consecutive;
  • oppure il LLM seleziona 0 o più di 1 opzione;

il sistema attiverà comunque l'alternativa associata a FALLBACK. Quindi è sconsigliato mettere delle alternative che si sovrappongono, poichè in quel caso si rischia di finire in FALLBACK.

suggerimento

Più alternatives vengono aggiunte maggiore è il rischio che il LLM fallisca nel riconoscimento di quella corretta. In caso di più di 5-6 alternative, considerare di annidarle su più livelli con 2 o più nodi in serie.

Open node

Il nodo di tipo open serve quando si vuole avere il massimo controllo sull'output da inviare, infatti questo nodo delega la produzione dell'output da produrre alla callback question.

Un nodo di tipo open ha le seguenti proprietà extra:

  • question (Callable) callback question;
  • translate (Callable) callback translate;
informazioni

L'argomento special della callback translate e after nel nodo di tipo open contiene le seguenti informazioni:

class SpecialOpenNodeContext:
question: str # la question indicata tramite l'apposita callback
files: List[FileMetadata] | None # i file inviati all'utente
informazioni

Come specificato nella sezione del File Agent il FileMetadata può essere ricavato dai precedenti file scambiati in chat, dai file estratti tramite RAG e da quelli generati direttamente in code

Close node

Il nodo di tipo close serve quando si vuole fornire all'utente una serie di opzioni di risposta alternative. Come nel nodo open, la produzione dell'output è delegata alla callback question, ma a differenza di questo, a seguto della risposta è possibile specificare delle opzioni alternative tramite la callback options. Le opzioni appariranno sulla chat come pulsanti cliccabili.

Un nodo di tipo close ha le seguenti proprietà extra:

  • question (Callable): callback di question;
  • options (Callable): callback options;
  • translate (Callable): callback translate;
informazioni

L'argomento special delle callback translate e after nel nodo di tipo close contiene le seguenti informazioni:

class SpecialCloseNodeContext:
question: str # la question indicata tramite callback question
original_options: List[str] # le opzioni specificate tramite callback options
options: List[str] # le opzioni specificate tramite callback options o eventualmente tradotte tramite callback translate
selected_option: str | None # l'opzione selezionata dall'utente (sempre nella lingua originale)

Il campo selected_option è popolato solo dentro la callback after

informazioni

L'opzione selezionata viene automaticamente individuata facendo un controllo tra stringhe non rigido (se c'è un piccolo errore di battitura comunque viene individuata la risposta più simile). selected_option, se definito è sempre un elemento di original_options, quindi riporta la voce selezionata dall'utente in lingua originale.

warning

Visto che comunque l'utente può evitare di cliccare un'opzione e scrivere liberamente, nel caso in cui nessuna opzione sia individuata, la proprietà selected_option sarà None

Query node

Il nodo di tipo query è l'unico che permette di immagazzinare e cercare tra i chunk definiti al suo interno. Questo nodo, infatti, oltre ai chunk con proprietà for_rag = False che vengono utilizzati durante lo step explore per individuare il prossimo nodo da eseguire, può contenere i chunk con proprietà for_rag = True.

Un nodo di tipo query ha le seguenti proprietà extra:

  • query (Callable) callback query;
informazioni

L'argomento special della callback after nel nodo di tipo query contiene le seguenti informazioni:

class SpecialQueryNodeContext:
chunks: List[Chunk] # i chunk estratti dal vector store
assets: List[FileMetadata] # tutti i file presenti dentro i chunk estratti

con Chunk descritto in seguito nella sezione dedicata ai chunk e FileMetadata descritto in seguito nella sezione dedicata al File Agent:

warning

Durante lo step explore vengono considerati sia i chunk con proprietà for_rag = True che quelli con for_rag = False

Empty node

Il nodo di tipo empty è un nodo privo di proprietà specifiche. Lo scopo di questo nodo è quello di consentire l'esecuzione delle sue callback before e after senza che venga lanciata alcun altra funzione.


Di seguito sono riportati i nodi relativi alla logica a componenti:

Component node

Il nodo virtuale di tipo component rappresenta un componente dall'esterno. Come per i nodi non virtuali, anche il nodo componente deve sempre avere un genitore.

Un nodo di tipo component ha le seguenti proprietà:

  • name (str): il nome del nodo;
  • icon (str): l'icona che si vuole associare al componente recuperata da Lucid;
informazioni

Per cambiare l'icona del nodo è sufficiente scegliere nel catalogo di lucid e copiare il nome in kebab-case

Head node

Il nodo virtuale di tipo head rappresenta sempre il primo nodo dentro un componente, di conseguenza NON ha mai alcun nodo genitore.

Un nodo di tipo head ha le seguenti proprietà:

  • init (Callable | None): funzione di inizializzazione del self disponibile a tutti i nodi definiti allo stesso livello della head, quindi all'interno del componente;
warning

Se il componente contiene altri componenti, i nodi in essi contenuti avranno un loro self distinto ed isolato.

Tail node

Il nodo virtuale di tipo tail può essere considerato come l'ultimo nodo del componente. Questo è l'unico nodo che può essere sia collegato che non; infatti la conseguenza di collegare questo nodo è quello di permettere all'esterno di associare dei nodi figli al rispettivo nodo componente. In tal caso, i nodi figli all'esterno saranno da considerarsi come collegati direttamente al genitore del nodo tail.