Guida di riferimento

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 globale root_agent.
  • __init__.py: il file del modulo Python. Deve contenere almeno una riga from agents import Agent per importare la classe Agent.

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.

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 o gemini-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.
  • 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:

  1. L'agente stesso comprende le proprie funzionalità in base alla descrizione.

  2. 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 e output sono i contenuti di output previsti.
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.
  • BaseExampleProvider

    • Puoi anche fornire un'istanza della classe BaseExampleProvider.
    • La classe BaseExampleProvider ha un metodo get_examples(query: str) e restituisce un elenco di Example.
    • Con BaseExampleProvider, puoi generare dinamicamente gli esempi in base alla query.

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:

  1. 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.

  2. Puoi impostare l'attributo code_executor su un'istanza della classe BaseCodeExecutor per attivare l'esecuzione del codice. Al momento, abbiamo una classe VertexCodeExecutor e UnsafeLocalCodeExecutor (poiché i codici generati dall'LLM potrebbero essere devastanti, utilizza UnsafeLocalCodeExecutor 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 indicare success, 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) in update_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.
  • 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à il Content 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à il Content 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à il LlmResponse 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à il LlmResponse 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à il dict 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à il dict 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 tramite invocation_context.state['key'] = value.
  • Dagli strumenti, puoi accedere alla variabile di stato tramite tool_context.state['key']. Puoi anche aggiornare la variabile di stato tramite tool_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 tramite invocation_context.set_artifact('key', value).
  • Negli strumenti, puoi accedere all'elemento tool_context.get_artifact('key'). Puoi aggiornare l'artefatto tramite tool_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 imposta tool_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.