Trova rapidamente informazioni dettagliate su ogni componente di Agent Framework in questo riferimento completo.
Struttura delle cartelle
Per creare un agente, devi creare una cartella per l'agente, che deve avere lo stesso nome dell'agente e contenere almeno i seguenti file:
agent.py
: il file principale dell'agente. Devi definire l'agente principale nella variabile globaleroot_agent
.__init__.py
: il file del modulo Python. Deve contenere almeno una rigafrom agents import Agent
per importare la classeAgent
.
Se vuoi, ti consigliamo di aggiungere i seguenti file:
requirements.txt
: il file dei requisiti Python per l'agente.README.md
: il file README dell'agente. Deve contenere le istruzioni per configurare e eseguire l'agente.
Il file agent.py minimo
La creazione di un agente inizia con la creazione di un'istanza della classe Agent
. L'agente minimo deve avere i seguenti attributi:
name
: il nome dell'agente.model
: il nome del modello LLM da utilizzare.instruction
: istruzioni in linguaggio naturale per l'agente.
Ad esempio:
from agents import Agent
root_agent = Agent(
model='gemini-1.5-flash',
name='root_agent',
instruction="Be polite and answer all users' questions.",
)
Per creare un agente minimo, puoi copiare la cartella _empty_agent
e modificare il file agent.py
.
Attributi
nome
name: str
Il nome dell'agente.
Identificatore
- Il nome deve seguire la convenzione di denominazione degli identificatori.
- Una stringa è considerata un identificatore valido se contiene solo lettere alfanumeriche (a-z) e (0-9) o trattini bassi (_). Un identificatore valido non può iniziare con un numero o contenere spazi.
Unico
- Il nome deve essere univoco nell'intera struttura ad albero dell'agente.
Agente Root
- Sebbene la variabile dell'agente principale debba essere denominata
root_agent
, puoi impostare un attributo name più significativo per l'agente principale.
- Sebbene la variabile dell'agente principale debba essere denominata
modello
model: str
Il nome del modello LLM da utilizzare.
Obbligatorio per LLM Agent
- L'attributo model è obbligatorio solo per l'agente LLM.
- Non è necessario impostare l'attributo del modello per gli agenti sequenziali, Loop o altri agenti non LLM.
Agente principale
- Puoi omettere l'attributo model. In questo caso, l'agente utilizzerà l'attributo model dell'agente anchestor / principale.
Formato
- Il formato del nome del modello varia in base al fornitore dell'LLM.
- Per Gemini, sembra
gemini-1.5-flash
ogemini-2.0-flash-exp
. - Per Anthropic su Vertex, sembra che
claude-3-5-sonnet-v2@20241022
- Sebbene il design del framework consenta qualsiasi fornitore di modelli, al momento supportiamo solo Gemini e Anthropic su Vertex.
istruzione
instruction: str | InstructionProvider
Le istruzioni in linguaggio naturale per l'agente.
Obbligatorio per LLM Agent
- L'istruzione è obbligatoria solo per l'agente LLM.
- Non è necessario impostare l'attributo istruzione per agenti sequenziali, Loop o altri agenti non LLM.
Tipo di dati
- L'istruzione può essere una stringa.
- L'istruzione può anche essere un InstructionProvider invocabile
InstructionProvider
- Un InstructionProvider è definito come
Callable[[InvocationContext], str]
- Ti consente di fornire una funzione per generare dinamicamente l'istruzione in base al contesto di chiamata.
- Un InstructionProvider è definito come
Stato
- L'istruzione è un modello di stringa, puoi utilizzare la sintassi {var} per inserire valori dinamici nell'istruzione.
var
viene utilizzato per inserire il valore della variabile di stato denominata var.artifact.var
viene utilizzato per inserire il contenuto di testo dell'elemento chiamato var.- Se la variabile di stato o l'elemento non esiste, l'agente genererà un errore. Se vuoi ignorare l'errore, puoi aggiungere un
?
al nome della variabile, ad esempio{var?}
.
Linee guida
- Inizia indicando chi è l'agente, cosa può fare e cosa non può fare.
- Puoi utilizzare il formato markdown per rendere le linee guida più leggibili, sia per le persone sia per il modello.
- Se l'agente può svolgere più attività, fornisci un elenco di attività e prevede sezioni separate per ogni attività.
- Se un'attività prevede più passaggi, fornisci un elenco dei passaggi e istruzioni dettagliate per ogni passaggio.
- Se l'agente può utilizzare strumenti, elencali nell'attributo
tools
. La definizione dello strumento contiene istruzioni su come utilizzarlo, ma è possibile aggiungere istruzioni dettagliate su quando utilizzarlo alle istruzioni dell'agente per migliorare il rendimento. - Per un sistema multi-agente, descrivi quando deve passare all'altro agente.
- Puoi includere esempi di input e output previsti nell'istruzione. Puoi anche utilizzare l'attributo
examples
per fornire esempi. - Fornisci istruzioni dettagliate e specifiche. Tratta l'agente come un nuovo dipendente che sta seguendo la procedura di formazione.
- Non fare affidamento su istruzioni relative a regole che devono essere seguite al 100% delle volte. Il modello ha intrinsecamente un certo grado di libertà e può commettere errori. Utilizza strumenti e callback per applicare regole rigorose.
description
description: str
Descrive cosa può fare questo agente. Questa descrizione viene inviata al modello nell'ambito delle istruzioni di sistema in modo che:
L'agente stesso comprende le proprie funzionalità in base alla descrizione.
Un agente comprende cosa possono fare gli altri agenti e decide se eseguire il trasferimento in base alle loro descrizioni.
global_instruction
global_instruction: str | InstructionProvider
L'istruzione globale per l'intera struttura ad albero dell'agente.
Mentre l'istruzione dice a un determinato agente cosa fare e come farlo, l'istruzione globale si applica a tutti gli agenti nell'intera struttura ad albero.
L'utilizzo dell'istruzione globale è simile all'attributo istruzione, incluso il tipo di dati, il supporto di InstructionProvider e la possibilità di accedere a variabili e artefatti dello stato.
- Identificazione e comportamento
- Utilizzi l'istruzione globale per impostare l'identità e il comportamento/ lo standard per l'intera struttura ad albero dell'agente, anziché per eseguire un'attività specifica per un determinato agente.
generate_content_config
generate_content_config: google.genai.types.GenerateContentConfig
Configurazione aggiuntiva del modello per l'agente. Verranno uniti alla richiesta al modello.
Esistono alcuni attributi che non devi impostare in questo campo perché sono gestiti dal framework: - tools
- system_instruction
-
response_schema
esempi
examples: list[Example] | BaseExampleProvider
Gli esempi con pochi esempi per l'agente. La ricerca mostra che fornire esempi con pochi esempi puoi migliorare il rendimento dell'agente.
- Esempi statici
- Puoi fornire un elenco di esempi statici. Un esempio è definito come segue, dove
input
sono i contenuti di input eoutput
sono i contenuti di output previsti.
- Puoi fornire un elenco di esempi statici. Un esempio è definito come segue, dove
class Example(BaseModel):
input: types.Content
output: list[types.Content]
Elenco di output
- L'output può essere un elenco di
Content
. - In questo modo puoi definire una sequenza di contenuti come output previsto. Ad esempio, il modello deve prima effettuare una chiamata di funzione, quindi generare del testo.
- L'output può essere un elenco di
BaseExampleProvider
- Puoi anche fornire un'istanza della classe
BaseExampleProvider
. - La classe
BaseExampleProvider
ha un metodoget_examples(query: str)
e restituisce un elenco diExample
. - Con
BaseExampleProvider
, puoi generare dinamicamente gli esempi in base alla query.
- Puoi anche fornire un'istanza della classe
greeting_prompt
greeting_prompt: str
Puoi impostare un prompt che verrà inviato al modello per generare un messaggio di saluto. Il prompt di saluto verrà utilizzato quando chiami l'agente con una sessione vuota e un input utente vuoto.
attuazione
planning: bool
Se imposti planning
su True, verrà attivata la modalità di pianificazione per l'agente. In
modalità di pianificazione, l'agente genera prima un piano per rispondere alla query dell'utente,
quindi lo esegue passo passo.
code_executor
code_executor: BaseCodeExecutor
L'agente che hai creato ha la capacità di risolvere i problemi scrivendo codice e eseguendolo.
Esistono due modi per attivare l'esecuzione del codice:
Alcuni modelli hanno la possibilità di eseguire direttamente il codice. Ad esempio, il modello Gemini 2.0 in modalità live genera ed esegue automaticamente il codice, senza la necessità di uno strumento di esecuzione del codice separato.
Puoi impostare l'attributo
code_executor
su un'istanza della classeBaseCodeExecutor
per attivare l'esecuzione del codice. Al momento, abbiamo una classeVertexCodeExecutor
eUnsafeLocalCodeExecutor
(poiché i codici generati dall'LLM potrebbero essere devastanti, utilizzaUnsafeLocalCodeExecutor
solo per il prototipizzazione) che puoi utilizzare per eseguire codice su Vertex AI. In futuro verranno aggiunti altri executor di codice.
input_schema
input_schema: type[BaseModel]
L'agente applicherà lo schema di input quando specifichi un modello Pydantic come
input_schema
. I contenuti di input devono essere una stringa JSON conforme allo schema.
output_schema
output_schema: type[BaseModel]
L'agente applicherà lo schema di output quando specifichi un modello Pydantic come
output_schema
. I contenuti di output saranno sempre una stringa JSON conforme allo schema.
output_key
output_key: str
L'agente memorizza il proprio output nella variabile di stato con il nome specificato dall'attributo output_key
.
include_contents
include_contents: Literal['default', 'none']
Per impostazione predefinita, l'agente includerà i contenuti della cronologia della sessione (cronologia chat). Puoi impostare l'attributo include_contents
su none
per disattivare questo comportamento. In questo caso, l'agente specifico non vedrà la cronologia chat. Questa funzionalità è utile quando l'agente non ha bisogno di conoscere la cronologia della chat.
Strumenti
La possibilità di utilizzare gli strumenti distingue gli agenti dal solo modello. Proprio come la capacità di utilizzare gli strumenti in modo complesso e versatile è considerata una caratteristica distintiva degli esseri umani.
Nel framework dell'agente, fornisci gli strumenti all'agente tramite l'attributo tools
.
L'attributo tools
è un elenco di strumenti, ognuno dei quali può essere:
- Una funzione Python.
- Un'entità che implementa la classe
BaseTool
.
Strumento di funzioni Python
Puoi definire una funzione Python come strumento.
Parametri
- La funzione può avere un numero qualsiasi di parametri.
- Ogni parametro può essere di qualsiasi tipo, purché sia serializzabile in JSON.
- Non impostare un valore predefinito per i parametri, poiché non sono supportati dal modello.
Tipo di reso
- Il tipo restituito deve essere un dizionario.
- Se restituisci un valore diverso da un dizionario, il framework lo inserirà in un dizionario con una singola chiave
result
. - Cerca di essere descrittivo nel valore restituito. Ad esempio, anziché restituire un codice di errore numerico, restituisci un
error_message: str
con un messaggio di errore leggibile. Ricorda che questo valore restituito è destinato al modello da leggere e comprendere, anziché a un pezzo di codice da eseguire. - È buona prassi avere una chiave
status
per indicaresuccess
,error
,pending
e così via, in modo che il modello comprenda lo stato generale dell'operazione.
Docstring
- La docstring della funzione verrà utilizzata come descrizione dello strumento e comunicata al modello. Pertanto, più la docstring è completa, meglio il modello può utilizzare lo strumento.
Semplicità
- Sebbene tu abbia molta libertà nella definizione della funzione, devi mantenere la formula semplice e facile in modo che il modello possa utilizzarla in modo più accurato.
- È preferibile avere meno parametri rispetto a molti.
- Utilizza il più possibile tipi di dati semplici, ad esempio
str
,int
, anziché classi personalizzate. - Il nome della funzione e i parametri sono molto importanti. Se hai una funzione chiamata
do_stuff()
, anche se dici al modello che viene utilizzata per annullare un volo, il modello potrebbe comunque rifiutarsi di utilizzarla. - Separa le funzioni complesse in altre più piccole. Ad esempio, separa
update_profile(profile: Profile)
inupdate_name(name: str)
,update_age(age: int)
e così via.
Riferimento nelle istruzioni
- Puoi fare riferimento allo strumento nell'istruzione utilizzando il relativo nome.
- Se il nome e la docstring della funzione sono sufficientemente dettagliati, puoi concentrarti solo su quando utilizzare lo strumento nell'istruzione.
- Indica all'agente come gestire valori di ritorno diversi. Ad esempio, se lo strumento restituisce un messaggio di errore, l'agente deve rinunciare, riprovare o chiedere maggiori informazioni?
- Gli strumenti possono essere utilizzati in sequenza e uno strumento può dipendere dall'output di un altro. Descrivi la sequenza nell'istruzione.
Contesto dello strumento
Nella funzione dello strumento, puoi aggiungere un parametro speciale tool_context:
ToolContext
per ottenere ulteriori informazioni sul contesto in cui viene chiamato lo strumento.
La classe ToolContext
si trova nel modulo agents.types
e ha i seguenti
attributi:
function_call_event_id: str
- L'ID dell'evento in cui viene attivata la chiamata allo strumento.
function_call_id: str
- L'ID della chiamata di funzione.
state: State
- Un oggetto simile a un dizionario per leggere e aggiornare le variabili di stato.
actions: EventActions
- Azioni aggiuntive che lo strumento può eseguire.
La classe EventActions
si trova nel modulo agents.events
e ha i seguenti attributi per consentire allo strumento di eseguire azioni aggiuntive:
skip_summarization: bool
- Se impostato su True, il framework salta il passaggio di sintesi per l'evento in cui viene chiamato lo strumento.
transfer_to_agent: str
- Se impostato, il framework verrà trasferito all'agente con il nome specificato dall'attributo
transfer_to_agent
.
- Se impostato, il framework verrà trasferito all'agente con il nome specificato dall'attributo
escalate: bool
- Se impostato su True, l'agente riassegna la query all'agente principale. La riassegnazione da un agente secondario all'interno di un LoopFlow indica la fine del loop.
AsyncFunctionTool
AsyncFunctionTool è una sottoclasse di FunctionTool. È progettato per gli strumenti che impiegano molto tempo per essere completati.
Per creare un AsyncFunctionTool, devi definire una normale funzione Python e inserirla nella classe AsyncFunctionTool
. Ad esempio:
AsyncFunctionTool(func=your_function)
AsyncFunctionTool chiamerà comunque la tua funzione Python, in cui puoi avviare
l'attività che potrebbe richiedere molto tempo. Puoi restituire un risultato intermedio al
modello per comunicargli che l'attività non è ancora completata. L'aggiunta di informazioni come status: 'pending'
, progress: 20
, estimated_completion_time: '...'
e così via aiuterà il modello a fornire una risposta significativa all'utente.
In un secondo momento, al termine dell'operazione, puoi chiamare l'agente con una nuova risposta della funzione per fornire il risultato finale. A quel punto l'agente genererà una risposta finale all'utente.
AgentTool
AgentTool ti consente di chiamare un altro agente per eseguire un'attività. Ciò equivale a creare una funzione Python, chiamare un altro agente con gli argomenti della funzione e utilizzare la risposta dell'agente come valore restituito della funzione.
Un AgentTool è diverso da un agente secondario:
- Quando l'agente A chiama l'agente B come agente di servizio, la risposta dell'agente B viene passata all'agente A, che la riassume e genera una risposta per l'utente. Alle future richieste degli utenti continuerà a rispondere l'agente A.
- Quando l'agente A chiama l'agente B come agente secondario, la responsabilità di rispondere all'utente viene trasferita completamente all'agente B. L'agente A non sarà visibile. In questo caso, alle future richieste dell'utente risponderà l'agente B.
Per utilizzare un agente come strumento, puoi utilizzare la classe AgentTool
per racchiuderlo.
Ad esempio: tools=[AgentTool(agent=agent_b)]
AgentTool ha i seguenti attributi per personalizzare il suo comportamento:
skip_summarization
- Se impostato su True, il framework salta la chiamata all'LLM per riepilogare la risposta dell'agente dello strumento.
Callback
Tipi di callback
Puoi personalizzare ulteriormente il comportamento dell'agente definendo i callback. Supportiamo due tipi di callback:
- I BeforeCallbacks vengono chiamati prima che l'agente esegua un'azione. Puoi modificare l'azione, saltarla o eseguire altre azioni.
- I metodi AfterCallbacks vengono chiamati dopo che l'agente esegue un'azione. Puoi utilizzare questo callback per modificare il risultato dell'azione o eseguire altre azioni.
Azioni supportate
Abbiamo BeforeCallbacks e AfterCallbacks per le seguenti azioni:
- Chiama un agente.
- Chiamata a un LLM.
- Chiamata di uno strumento.
Elenco di callback
Di conseguenza, abbiamo i seguenti 6 callback, che sono tutti attributi della classe
Agent
:
before_agent_callback
def before_agent_callback(invocation_context: InvocationContext) -> Content | None
- Un'invocazione può includere più chiamate all'agente. Pertanto, questo callback potrebbe essere chiamato più volte.
- Se restituisci un
Content
da questo callback, l'agente salterà la chiamata dell'agente corrente e utilizzerà ilContent
restituito come risposta.
after_agent_callback
def after_agent_callback(invocation_context: InvocationContext) -> Content | None
- Un'invocazione può includere più chiamate all'agente. Pertanto, questo callback potrebbe essere chiamato più volte.
- Se restituisci un
Content
da questo callback, l'agente aggiungerà ilContent
restituito come dopo la propria risposta.
before_model_callback
def before_model_callback(
invocation_context: InvocationContext,
llm_request: LlmRequest) -> LlmResponse | None
- Una chiamata all'agente può includere più chiamate LLM. Pertanto, questo callback potrebbe essere chiamato più volte.
- Se restituisci un
LlmResponse
da questo callback, l'agente utilizzerà ilLlmResponse
restituito come risposta e salterà la chiamata al modello.
before_model_callback
def after_model_callback(
invocation_context: InvocationContext,
llm_response: LlmResponse) -> LlmResponse | None
- Una chiamata all'agente può includere più chiamate LLM. Pertanto, questo callback potrebbe essere chiamato più volte.
- Se restituisci un
LlmResponse
da questo callback, l'agente utilizzerà ilLlmResponse
restituito come risposta anziché la risposta generata dal modello.
before_tool_callback
def before_tool_callback(
invocation_context: InvocationContext,
tool: BaseTool,
args: dict[str, Any],
tool_context: ToolContext) -> dict | None
- Una chiamata al modello può includere più chiamate allo strumento. Pertanto, questo callback potrebbe essere chiamato più volte.
- Se restituisci un
dict
da questo callback, l'agente utilizzerà ildict
restituito come risposta e salterà la chiamata allo strumento.
after_tool_callback
def after_tool_callback(
invocation_context: InvocationContext,
tool: BaseTool,
args: dict[str, Any],
tool_context: ToolContext,
response: dict) -> dict | None
- Una chiamata al modello può includere più chiamate allo strumento. Pertanto, questo callback potrebbe essere chiamato più volte.
- Se restituisci un
dict
da questo callback, l'agente utilizzerà ildict
restituito come risposta anziché la risposta generata dallo strumento.
Sessioni
Non è necessario manipolare direttamente l'oggetto sessione durante la creazione di un agente. Il framework gestirà l'oggetto sessione per te. Tuttavia, è comunque utile comprendere che cos'è e come funziona la sessione.
Una sessione nell'Agent Framework ha due componenti principali:
- Eventi: un elenco di eventi.
- Stato: un oggetto simile a un dizionario di variabili di stato.
Eventi
Events è solo un semplice elenco di oggetti evento. Puoi considerarlo come una cronologia chat tra un utente e un agente o tra agenti diversi. Non registra solo le parole dell'utente o del modello, ma anche tutte le azioni eseguite dall'agente, tra cui l'attivazione di uno strumento, la risposta dello strumento, la chiamata di un altro agente e così via.
L'elenco di eventi è un elenco di tipo append-only. Puoi solo aggiungere eventi all'elenco, ma non puoi rimuoverli o modificarli. Quando un evento si è verificato, non c'è modo di cambiarlo. Lo progettiamo in questo modo in modo che il sistema sia semplice e possiamo sempre tornare a un momento specifico per vedere lo snapshot esatto del sistema.
Stato
Lo stato è un oggetto simile a un dizionario che contiene tutte le variabili di stato. Puoi accedervi da:
- Dall'istruzione, puoi utilizzare la sintassi
{var}
per inserire il valore della variabile di stato denominata var. - Dai callback, puoi accedere alla variabile di stato tramite
invocation_context.state['key']
. Puoi anche aggiornare la variabile di stato tramiteinvocation_context.state['key'] = value
. - Dagli strumenti, puoi accedere alla variabile di stato tramite
tool_context.state['key']
. Puoi anche aggiornare la variabile di stato tramitetool_context.state['key'] = value
.
Lo stato è associato a una determinata sessione. È quindi il luogo ideale per memorizzare informazioni utili nel contesto di questa sessione.
Tutti gli agenti nell'albero degli agenti possono accedere allo stato, che rappresenta quindi un luogo ideale per la comunicazione tra gli agenti. Un agente può eseguire un'azione e memorizzare il risultato nello stato, mentre un altro agente può leggere il risultato dello stato e continuare il lavoro.
Artefatti
Quando il modello o lo strumento crea un file, che si tratti di un'immagine, un video, un documento o qualsiasi altro formato, puoi archiviarlo come artefatto. Un artefatto è un file associato a una determinata sessione a cui possono accedere gli agenti o il tuo codice.
Casi d'uso
- Quando l'agente collabora con l'utente per creare / modificare un file. Ad esempio, un agente che aiuta l'utente a generare e modificare un'immagine.
- Quando vuoi che l'agente risponda a domande su un file o lo modifichi in base alle istruzioni dell'utente.
Vantaggi in termini di rendimento
Un altro modo per gestire i file di grandi dimensioni è archiviarli come byte nella cronologia chat. Tuttavia, questo approccio presenta alcuni svantaggi:
- Rende la cronologia delle sessioni lenta.
- È difficile recuperare il file dalla cronologia chat.
- I byte verranno inviati al modello per tutte le richieste, anche se la conversazione non ha nulla a che fare con questi file.
Accesso agli elementi
Esistono diversi modi per accedere agli elementi:
- Nell'istruzione, puoi utilizzare la sintassi
{artifact.var}
per inserire il contenuto di testo dell'elemento chiamato var. Gli elementi di origine binari non sono ancora supportati. - Nei callback, puoi accedere all'elemento
invocation_context.get_artifact('key')
. Puoi aggiornare l'artefatto tramiteinvocation_context.set_artifact('key', value)
. - Negli strumenti, puoi accedere all'elemento
tool_context.get_artifact('key')
. Puoi aggiornare l'artefatto tramitetool_context.set_artifact('key', value)
.
Sistemi multi-agente
Un singolo agente e uno strumento di elenco possono fare molto per creare un sistema complesso. Tuttavia, talvolta, la separazione della logica in più agenti può migliorare le prestazioni e la manutenibilità del sistema complessivo.
Di seguito sono riportate le situazioni in cui puoi prendere in considerazione l'utilizzo di più agenti:
- Quando le istruzioni dell'agente diventano troppo lunghe, con più attività e passaggi per ogni attività.
- Quando devi eseguire un flusso di lavoro più deterministico. Ad esempio, per un agente di ricerca, genera sempre un piano, lo esegue e poi riassume i risultati.
Albero di agenti gerarchico
children: list[BaseAgent]
Puoi creare un sistema multi-agente creando una struttura ad albero gerarchica di agenti. L'agente principale è il punto di contatto del sistema e può chiamare altri agenti in base alla sua configurazione.
Un agente può avere più agenti secondari. Anche gli agenti secondari possono avere agenti secondari. L'albero di agenti può essere arbitrariamente profondo, ma per motivi di rendimento consigliamo un albero meno profondo.
Per formare l'albero di agenti, inserisci altri agenti come secondari di un agente principale.
Flussi
flow: str | BaseFlow | FlowCallable
Quando un agente riceve una query da un utente, può scegliere di gestirla personalmente o di trasferirla a un altro agente. Questo valore è definito dall'attributo flow
.
Esistono alcuni flussi predefiniti, ma puoi anche definirne di personalizzati.
sequential
: l'agente chiamerà i suoi agenti secondari uno alla volta in sequenza.loop
: l'agente chiamerà i suoi agenti secondari in un ciclo. Fino a quando uno degli agenti secondari non impostatool_context.actions.escalate
su True.single
: si tratta di un flusso basato su LLM. L'agente chiamerà l'LLM per rispondere alla query dell'utente e chiamerà i suoi strumenti se necessario.auto
: si tratta di un flusso basato su LLM. L'agente chiamerà l'LLM per rispondere alla query dell'utente e chiamerà i suoi strumenti in caso di necessità. Può anche trasferire la query ai suoi elementi secondari, ai suoi elementi fratelli o al suo elemento principale.- Flussi personalizzati: puoi definire i tuoi flussi implementando la classe
BaseFlow
o semplicemente definendo una funzione Python in base all'interfaccia.