Callback
Le callback sono delle funzioni python che possono essere definite dentro i nodi in base alla loro tipologia. Tali funzioni consentono di personalizzare in maniera approfondita il comportamento dell'agente durante la sua esecuzione.
Ciascuna callback, in base alla sua tipologia ha una firma predefinita. In input gli argomenti passati sono:
self: un riferimento del componente comune a tutti i nodi in esso contenuti;context: è il contesto dal quale è possibile accedere a diversi strumenti utili tra cui ilsession_state;special: è un contesto specifico che è disponibile in alcuni nodi. Vedere il capitolo sui nodi per approfondire;links: è disponibile solo nelle callbackaftere permette di navigare tra i nodi;
Il self rimane visibile solo ai nodi che sono figli diretti del componente, di conseguenza, se ho dei componenti annidati, i nodi figli del componente interno non possono accedere al self del componente esterno e viceversa!
links è un types.SimpleNamespace pertanto per accedere al suo contentuo richiede la nostazione con il punto, mentre la notazione con le [] non è supportata. Nel caso in cui si volesse accedere in modo dinamico ad una proprietà di links occorre usare il comando getattr(links, prop), con prop una variabile di tipo stringa che contiene il nome della proprietà richiesta.
Tutte le callback sono di default sincrone, tuttavia, in caso di necessità, inserendo il comando async prima della firma è possibile renderle asincrone.
Di seguito sono descritte, una per una, tutte le callback presenti:
Before
Nodi supportati: empty query open close llm switch
La callback before di un nodo viene lanciata nel relativo step, quindi sempre prima della sua esecuzione.
(self, context) => None
Visto che questa callback non produce alcun output, generalmente viene impiegata per fare chiamate API o interagire con il session_state
After
Nodi supportati: empty query open close llm
La callback after di un nodo viene lanciata durante lo step after, ovvero a seguito della sua esecuzione. Grazie all'oggetto BaseAfterOutput restituito da questa funzione, è possibile controllare il successivo svolgimento dell'interazione. In particolare, si può specificare il prossimo nodo e lo step da eseguire tramite node_target e step. Invece outcome permette di specificare un fallimento avvenuto durante l'esecuzione del nodo. Il lancio di un fallimento comporta l'esecuzione del meccanismo reach_anchor e consente di indicare una lista di nodi e chunk dovranno essere ignorati durante lo step di explore per il resto dell'interazine corrente. Il parametro resume permette di sfruttare la gestione multi agente del Tree Agent.
(self, context, special, links) => BaseAfterOutput
con
class BaseAfterOutput:
node_target: int | None = None # nodo che si intende eseguire dopo l'esecuzione della callback
step: Step = Step.EXPLORE # step che si vuole eseguire dopo l'esecuzione della callback
outcome: Outcome = Outcome.SUCCES # se impostato con Outcome.FAILURE vengono ignorati gli altri parametri, e si attiva il meccanismo di reach_anchor
resume: bool = False # viene recuperata l'esecuzione sull'agente indicato tramite node_target (se node_target non è un agent_node viene restituito un errore!)
con
class Outcome(Enum):
SUCCESS = "success"
FAILURE = "failure"
Non è necessario che l'id del nodo indicato tramite node_target sia figlio o discendente del nodo attuale.
Sia per l'after che per il before_run:
- è possibile abilitare il
resume, questo comando è utile solo in caso di multi agenti; - se non si specifica il
node_target, a meno che non si abilitais_failed(solo nel caso delafter) si proseguirà con il normale step diexplore
Gli unici step ammessi sono: BEFORE e EXPLORE!
Extend Message
Nodi supportati: root
La callback extend_message restituisce il prompt che viene poi passato al LLM indicato nel quarto punto dell'esecuzione generale.
(self, context, special) => BaseExtendMessagePromptOutput
con
class BaseExtendMessagePromptOutput:
prompt: str # prompt da passare al LLM per inizializzare context.input.extended
Before run
Nodi supportati: root
Come indicato nel quinto punto dell'esecuzione generale, la callback before_run viene eseguita appena si riceve il messaggio dell'utente e prima di eseguire l'albero.
(self, context, links) => BaseBeforeRunOutput
con
class BaseBeforeRunOutput:
node_target: int | None # nodo che si intende eseguire dopo l'esecuzione della callback
step: Step | None # step che si vuole eseguire dopo l'esecuzione della callback
resume: bool | None # viene recuperata l'esecuzione sull'agente indicato tramite node_target (se node_target non è un agent_node viene restituito un errore!)
Questa callback è particolarmente utile in caso di multi agenti: si possono realizzare degli agenti che si occupano unicamente di analizzare la richiesta dell'utente e selezionano l'agente migliore che si deve occupare di rispondere. Grazie a questo nodo, tramite in node_target è possibile puntare a tali agenti. Se node_target viene lasciato a None, allora il resto degli argomenti vengono ignorati, e viene ripresa l'esecuzione a partire dalla callback after dell'ultimo nodo eseguito.
Ad esempio è possibile sviluppare un agente specializzato sull'analisi dei documenti che l'utente carica in chat e la successiva estrazione delle informazioni rilevanti nel session_state. Grazie a questa callback si possono deviare tutte le interazioni avviate partendo da un messaggio con file allegati.
Per la prima interazione della sessione, se la before_run non specifica alcun nodo da eseguire, sarà avviato lo step di explore a partire dall'unico nodo figlio del root node che ha la proprietà is_primary = True
After run
Nodi supportati: root
La callback after_run viene eseguita appena si conclude l'esecuzione dell'albero.
(self, context, special) => None
Visto che questa callback non produce alcun output, generalmente viene impiegata per fare chiamate API o interagire con il session_state.
Fallback
Nodi supportati: agent
All'inizio di ogni interazione viene impostato un contatore fail_counter = 0. Tale contatore viene incrementato ogni volta che una callback after restituisce is_fail = True e più in generale ogni volta che si attiva il meccanismo di reach_anchor. La callback fallback viene eseguita se durante l'interazione avviene che fail_counter > 2, in tale circostanza, onde evitare possibili loop, tramite questa callback viene restituito l'id di un nodo che sarà immediatamente eseguito. Date le circostanze, il nodo da indicare tramite node_target NON deve avere proprietà is_mute = True, ma deve restituire immediatamente un output all'utente. Per questo motivo sono permessi solo nodi di tipo open o close, oppure nodi llm con proprietà is_mute = False.
(self, context, links) => BaseFallbackOutput
con
class BaseFallbackOutput:
node_target: int # nodo che si intende eseguire dopo l'esecuzione della callback
Prompt
Nodi supportati: llm
La callback prompt è disponibile ai nodi che devono richiamare un LLM per rispondere ad una richiesta. Questa callback consente di specificare il prompt che si intende inviare al LLM.
(self, context, special) => BasePromptOutput
con
class BasePromptOutput:
prompt: str | None = None # prompt da inviare al LLM
system_prompt: str | None = None # system prompt da inviare al LLM
files: List[FileMetadata] | None = None # eventuali file che si intende inviare al LLM
segments: List[Segment] | None = None # prompt composto da una sequenza di testi e file
assets: List[FileMetadata] | None = None # eventuali file che si intende rendere disponibili per il meccanismo {{}}
temperature: float = 0.1 # temperatura per il LLM
web_search: bool = False # abilita l'accesso del modello a Internet
con
class Segment:
type: Literal["text", "files"] # il tipo del segmento
content: str | List[FileMetadata] # il contenuto del segmento
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
Attualemente il parametro web_search è compatibile solo con i modelli offerti dai provider Azure e OpenAI
Query
Nodi supportati: query
La callback query permette di realizzare una ricerca tra i chunk con proprietà for_rag = True presenti sul vector store associato al nodo corrente.
(self, context) => BaseQueryOutput
con
class BaseQueryOutput:
query: str # stringa da cui estrarre l'embedding per fare la ricerca
filter: models.Filter | None = None # eventuali filtri da applicare prima della ricerca sfruttando i tag eventualmente associati a ciascun chunk
black_list: AiBlackList | None = None # eventuali chunk da escludere
chunk_qty: int = 3 # massimo numero di chunk estraibili
threshold: float = 0.25 # soglia minima di similarità richiesta per l'estrazione dei chunk
dove model.Filter è una classe implementata tramite libreria Qdrant. Fare riferimento alla loro documentazione per implementare la logica di filtri.
Ad esempio, se nei chunk del nodo ho associato tramite extra la proprietà "category" ed in fase di estrazione voglio considerare solo i chunk della categoria "food", posso scrivere:
filter = models.Filter(
must=[
models.FieldCondition(
key="category",
match=models.MatchValue(value="food")
)
]
)
Question
La callback question è disponibile per i nodi che restituiscono messaggi controllati (di tipo open e close). La funzione restituisce una stringa: ovvero il messaggio che sarà inviato all'utente. Tale messaggio se viene attivata anche la relativa callback translate, prima di essere restituito all'utente, viene passato ad un LLM per effettuare l'eventuale traduzione nella lingua della conversazione.
(self, context) => BaseQuestionOutput
con
class BaseQuestionOutput:
question: str # testo da inviare all'utente
files: List[FileMetadata] | None # eventuali file che si intende inviare al l'utente
assets: List[FileMetadata] | None # eventuali file che si intende rendere disponibili per il meccanismo {{}}
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
Options
Nodi supportati: close
La callback options è disponibile per i nodi di tipo close per definire le opzioni mutualmente esclusive che si intende proporre all'utente. Tali opzioni saranno presentate all'utente in forma di bottoni sotto al messaggio impostato tramite la callback question dello stesso nodo. La funzione restituisce una lista di stringhe che corrisponde alla lista di opzioni. Tali opzioni se viene attivata anche la relativa callback translate, vengono prima passate ad un LLM per effettuare l'eventuale traduzione nella lingua della conversazione.
Nel caso in cui si seleziona come skill Whatsapp, invece di mostrare le opzioni come bottoni selezionabili, appare un elenco puntato. Di conseguenza, l'utente potrà scrivere liberamente l'opzione che preferisce, ed il framework andrà a recuperare automaticamente, ammettendo un minimo di tolleranza in caso di errori di battitura, l'opzione corrispondente. La risposta libera è ammessa anche in caso di webchat, quindi va sempre previsto e gestito nella relativa callback after il caso in cui l'utente risponda un testo differente da ogni opzione.
(self, context) => BaseOptionsOutput
con
class BaseOptionsOutput:
options: List[str] # lista di opzioni che si intende mostrare all'utente
Translate
La callback translate è disponibile per i nodi che dispongono della callback question (di tipo open e close). Infatti, lo scopo di questa funzione è quello di restituisce prompt da passare ad LLM per eseguire la traduzione.
(self, context, special) => BaseTranslateOutput
con
class BaseTranslateOutput:
prompt: str # prompt per la traduzione