C'è una grande differenza tra costruire un agente che funziona e uno che non funziona. Come possiamo costruire agenti che rientrano in quest'ultima categoria? In questa guida vedremo le migliori pratiche per la costruzione di agenti.
Se siete alle prime armi con la costruzione di agenti, assicuratevi di leggere prima la sezione introduzione agli agenti e il visita guidata agli smolagent.
I migliori sistemi agenziali sono i più semplici: semplificate il più possibile il flusso di lavoro. #
Dare a un LLM una certa autonomia nel flusso di lavoro comporta un certo rischio di errori.
I sistemi agenziali ben programmati hanno comunque buoni meccanismi di registrazione degli errori e di riprova, in modo che il motore LLM abbia la possibilità di autocorreggere il proprio errore. Ma per ridurre al massimo il rischio di errore di LLM, è necessario semplificare il flusso di lavoro!
Rivediamo l'esempio di [intro_agents]: un bot che risponde alle domande degli utenti per una compagnia di viaggi per surfisti. Invece di lasciare che l'agente faccia due diverse chiamate alle "API della distanza di viaggio" e alle "API del meteo" ogni volta che gli viene chiesto un nuovo posto per il surf, si potrebbe creare uno strumento unificato "return_spot_information", una funzione che chiama entrambe le API in una volta sola e restituisce all'utente i loro risultati concatenati.
In questo modo si riducono i costi, la latenza e il rischio di errori!
La linea guida principale è: Ridurre il più possibile il numero di chiamate LLM.
Questo porta ad alcune considerazioni:
- Quando è possibile, raggruppare 2 strumenti in uno, come nel nostro esempio delle due API.
- Quando possibile, la logica deve basarsi su funzioni deterministiche piuttosto che su decisioni agite.
Migliorare il flusso di informazioni verso il motore LLM #
Ricordate che il vostro motore LLM è come un robot ~intelligente~, inserito in una stanza in cui l'unica comunicazione con il mondo esterno è costituita da note passate sotto una porta.
Non saprà nulla di ciò che è accaduto se non lo si indica esplicitamente nel prompt.
Quindi, per prima cosa, iniziate a rendere il vostro compito molto chiaro! Poiché un agente è alimentato da un LLM, piccole variazioni nella formulazione del compito potrebbero produrre risultati completamente diversi.
Quindi, migliorare il flusso di informazioni verso il vostro agente nell'uso degli strumenti.
Linee guida particolari da seguire:
- Ogni strumento dovrebbe registrare (semplicemente usando
stampa
all'interno dello strumentoin avanti
tutto ciò che può essere utile per il motore LLM.- In particolare, la registrazione degli errori di esecuzione degli strumenti sarebbe di grande aiuto!
Ad esempio, ecco uno strumento che recupera i dati meteo in base alla posizione e alla data:
Innanzitutto, ecco una versione povera:
Copiato
importare datetime da smolagents importare lo strumento def get_weather_report_at_coordinates(coordinate, date_time): # Funzione fittizia, restituisce un elenco di [temperatura in °C, rischio di pioggia su una scala 0-1, altezza delle onde in m]. ritorno [28.0, 0.35, 0.85] def get_coordinates_from_location(location): # Restituisce le coordinate fittizie ritorno [3.3, -42.0] @tool def get_weather_api(location: str, date_time: str) -> str: """ Restituisce il bollettino meteo. Args: location: il nome del luogo per il quale si vuole il meteo. data_ora: la data e l'ora per le quali si desidera il rapporto. """ lon, lat = convert_location_to_coordinates(location) date_time = datetime.strptime(date_time) return str(get_weather_report_alle_coordinate((lon, lat), date_time))
Perché è un male?
- non c'è alcuna precisione sul formato da utilizzare per
data_ora
- non ci sono dettagli su come debba essere specificata la posizione.
- non c'è un meccanismo di logging legato a casi di fallimento espliciti, come la posizione che non è in un formato corretto o la data_ora che non è formattata correttamente.
- il formato di uscita è difficile da capire
Se la chiamata allo strumento fallisce, la traccia degli errori registrata in memoria può aiutare il LLM a fare il reverse engineering dello strumento per correggere gli errori. Ma perché lasciargli un compito così gravoso?
Un modo migliore per costruire questo strumento sarebbe stato il seguente:
Copiato
@tool def get_weather_api(location: str, date_time: str) -> str: """ Restituisce il bollettino meteo. Args: location: il nome del luogo per il quale si desidera il meteo. Dovrebbe essere il nome di un luogo, seguito eventualmente dal nome di una città e poi di un paese, come "Anchor Point, Taghazout, Marocco". date_time: la data e l'ora per cui si desidera il rapporto, formattata come '%m/%d/%y %H:%M:%S'. """ lon, lat = convert_location_to_coordinates(location) try: date_time = datetime.strptime(date_time) except Exception as e: raise ValueError("Conversione di `date_time` in formato datetime fallita, assicurarsi di fornire una stringa nel formato '%m/%d/%y %H:%M:%S'. Traccia completa:" + str(e)) temperatura_celsius, rischio_pioggia, altezza_onda = get_weather_report_at_coordinates((lon, lat), date_time) return f "Rapporto meteo per {località}, {data_ora}: La temperatura sarà di {temperatura_celsius}°C, il rischio di pioggia è di {rischio_di_pioggia*100:.0f}%, l'altezza dell'onda è di {altezza_onda}m".
In generale, per alleggerire il carico del vostro LLM, la domanda da porsi è: "Quanto sarebbe facile per me, se fossi muto e usassi questo strumento per la prima volta, programmare con questo strumento e correggere i miei stessi errori?".
Fornire più argomenti all'agente #
Per passare all'agente alcuni oggetti aggiuntivi, oltre alla semplice stringa che descrive il compito, si può usare l'opzione argomenti_aggiuntivi
per passare qualsiasi tipo di oggetto:
Copiato
da smolagents import CodeAgent, HfApiModel model_id = "meta-llama/Llama-3.3-70B-Instruct" agent = CodeAgent(tools=[], model=HfApiModel(model_id=model_id), add_base_tools=True) agent.run( "Perché Mike non conosce molte persone a New York?", additional_args={"mp3_sound_file_url":'https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/recording.mp3'} )
Per esempio, si può usare questo argomenti_aggiuntivi
per passare immagini o stringhe che si vuole che l'agente sfrutti.
Come eseguire il debug dell'agente #
1. Utilizzare un LLM più forte #
In un flusso di lavoro agenziale, alcuni errori sono errori veri e propri, altri sono colpa del motore LLM che non ragiona correttamente. Per esempio, si consideri questa traccia per un CodiceAgente
che ho chiesto di creare un'immagine dell'auto:
Copiato
==================================================================================================== New task ==================================================================================================== Fammi una foto di un'auto figa ──────────────────────────────────────────────────────────────────────────────────────────────────── Nuovo passo ──────────────────────────────────────────────────────────────────────────────────────────────────── L'agente sta eseguendo il codice sottostante: ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── image_generator(prompt="Un'auto sportiva fresca e futuristica con fari a LED, design aerodinamico e colori vivaci, ad alta risoluzione e fotorealistica") ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Ultimo risultato dello snippet di codice: ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── /var/folders/6m/9b1tts6d5w960j80wbw9tx3m0000gn/T/tmpx09qfsdd/652f0007-3ee9-44e2-94ac-90dae6bb89a4.png Passo 1: - Tempo impiegato: 16,35 secondi - Gettoni in ingresso: 1.383 - Gettoni in uscita: 77 ──────────────────────────────────────────────────────────────────────────────────────────────────── Nuovo passo ──────────────────────────────────────────────────────────────────────────────────────────────────── L'agente sta eseguendo il codice sottostante: ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── final_answer("/var/folders/6m/9b1tts6d5w960j80wbw9tx3m0000gn/T/tmpx09qfsdd/652f0007-3ee9-44e2-94ac-90dae6bb89a4.png") ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── Uscite di stampa: Ultimo output del frammento di codice: ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── /var/folders/6m/9b1tts6d5w960j80wbw9tx3m0000gn/T/tmpx09qfsdd/652f0007-3ee9-44e2-94ac-90dae6bb89a4.png Risposta finale: /var/folders/6m/9b1tts6d5w960j80wbw9tx3m0000gn/T/tmpx09qfsdd/652f0007-3ee9-44e2-94ac-90dae6bb89a4.png
L'utente vede che, invece di un'immagine, gli viene restituito un percorso. Potrebbe sembrare un bug del sistema, ma in realtà non è il sistema agenziale a causare l'errore: è solo che il cervello di LLM ha commesso l'errore di non salvare l'immagine in una variabile. In questo modo non può accedere nuovamente all'immagine se non sfruttando il percorso che è stato registrato durante il salvataggio dell'immagine, quindi restituisce il percorso invece dell'immagine.
Il primo passo per il debug del vostro agente è quindi "Utilizzare un LLM più potente". Alternative come Qwen2/5-72B-Istruzione
non avrebbe commesso questo errore.
2. Fornire più indicazioni/più informazioni #
È possibile utilizzare anche modelli meno potenti, a patto di guidarli in modo più efficace.
Mettetevi nei panni del vostro modello: se foste il modello che risolve il compito, fareste fatica con le informazioni a vostra disposizione (dal prompt del sistema + formulazione del compito + descrizione dello strumento)?
Avete bisogno di ulteriori chiarimenti?
Per fornire ulteriori informazioni, si sconsiglia di cambiare subito il prompt di sistema: il prompt di sistema predefinito ha molte regolazioni che non si vogliono sbagliare, a meno che non si capisca molto bene il prompt. I modi migliori per guidare il motore LLM sono:
- Se si tratta del compito da risolvere: aggiungete tutti questi dettagli al compito. Il compito potrebbe essere lungo 100 pagine.
- Se si tratta di come utilizzare gli strumenti: l'attributo description dei vostri strumenti.
3. Modificare il prompt di sistema (generalmente sconsigliato) #
Se i chiarimenti di cui sopra non sono sufficienti, è possibile modificare il prompt di sistema.
Vediamo come funziona. Ad esempio, controlliamo il prompt di sistema predefinito per il comando CodiceAgente (la versione seguente è abbreviata saltando gli esempi a colpo zero).
Copiato
print(agent.system_prompt_template)
Ecco cosa si ottiene:
Copiato
Sei un assistente esperto in grado di risolvere qualsiasi compito utilizzando i blob di codice. Vi verrà assegnato un compito da risolvere al meglio. Per farlo, vi è stato dato accesso a un elenco di strumenti: questi strumenti sono fondamentalmente funzioni Python che potete richiamare con il codice. Per risolvere il compito, dovete pianificare una serie di passi, in un ciclo di sequenze "Pensiero:", "Codice:" e "Osservazione:". In ogni fase, nella sequenza 'Pensiero:', dovrete innanzitutto spiegare il vostro ragionamento per risolvere il compito e gli strumenti che volete utilizzare. Poi, nella sequenza "Codice:", si deve scrivere il codice in Python semplice. La sequenza di codice deve terminare con la sequenza ''. Durante ogni fase intermedia, si può usare 'print()' per salvare qualsiasi informazione importante di cui si avrà bisogno. Questi output di stampa appariranno poi nel campo "Osservazione:", che sarà disponibile come input per il passo successivo. Alla fine si dovrà restituire una risposta finale utilizzando lo strumento `risposta_finale`. Ecco alcuni esempi che utilizzano strumenti fittizi: --- {esempi} Gli esempi precedenti utilizzano strumenti fittizi che potrebbero non esistere per voi. Oltre a eseguire calcoli negli snippet di codice Python che create, avete accesso solo a questi strumenti: {{descrizioni_strumenti}} {{descrizioni_agenti_gestiti}} Ecco le regole da seguire sempre per risolvere il vostro compito: 1. Fornire sempre una sequenza 'Thought:' e una sequenza 'Code:\n``py' che termina con la sequenza '```', altrimenti si fallirà. 2. Utilizzate solo le variabili che avete definito! 3. Utilizzate sempre gli argomenti giusti per gli strumenti. NON passate gli argomenti come dict, come in "answer = wiki({'query': "Qual è il posto dove vive James Bond?"})", ma usate direttamente gli argomenti come in "answer = wiki(query="Qual è il posto dove vive James Bond?")". 4. Fate attenzione a non concatenare troppe chiamate sequenziali allo strumento nello stesso blocco di codice, soprattutto quando il formato dell'output è imprevedibile. Per esempio, una chiamata a search ha un formato di ritorno imprevedibile, quindi non bisogna avere un'altra chiamata a uno strumento che dipenda dal suo output nello stesso blocco: piuttosto, si devono produrre i risultati con print() per utilizzarli nel blocco successivo. 5. Richiamare uno strumento solo quando è necessario e non ripetere mai una chiamata a uno strumento che è stata fatta in precedenza con gli stessi parametri. 6. Non nominare una nuova variabile con lo stesso nome di uno strumento: ad esempio, non nominare una variabile "final_answer". 7. Non creare mai variabili fittizie nel nostro codice, perché la presenza di queste nei log potrebbe farvi perdere di vista le vere variabili. 8. È possibile utilizzare importazioni nel codice, ma solo dal seguente elenco di moduli: {{importazioni_autorizzate}}. 9. Lo stato persiste tra le esecuzioni del codice: quindi, se in un passaggio si sono create variabili o importati moduli, questi persistono tutti. 10. Non arrendetevi! Siete incaricati di risolvere il compito, non di fornire indicazioni per risolverlo. Ora iniziate! Se risolverete il compito correttamente, riceverete una ricompensa di $1.000.000.
Come si può vedere, ci sono segnaposti come "{{tool_descriptions}}"
: saranno utilizzati all'inizializzazione dell'agente per inserire alcune descrizioni generate automaticamente degli strumenti o degli agenti gestiti.
Quindi, mentre è possibile sovrascrivere questo modello di prompt di sistema passando il prompt personalizzato come parametro al metodo prompt_di_sistema
il nuovo prompt di sistema deve contenere i seguenti segnaposto:
"{{tool_descriptions}}"
per inserire le descrizioni degli strumenti."{{managed_agents_description}}"
per inserire la descrizione degli agenti gestiti, se ce ne sono.- Per
CodiceAgente
solo:"{{authorized_imports}}"
per inserire l'elenco delle importazioni autorizzate.
Quindi è possibile modificare il prompt di sistema come segue:
Copiato
da smolagents.prompts import CODE_SYSTEM_PROMPT modified_system_prompt = CODE_SYSTEM_PROMPT + "\nEcco a voi!" # Modificare il prompt di sistema qui agent = CodeAgent( tools=[], model=HfApiModel(), system_prompt=modified_system_prompt )
Questo funziona anche con l'opzione Agente di chiamata dello strumento.
4. Pianificazione extra #
Forniamo un modello per una fase di pianificazione supplementare, che un agente può eseguire regolarmente tra le normali fasi di azione. In questa fase non c'è una chiamata allo strumento, ma viene semplicemente chiesto all'LLM di aggiornare un elenco di fatti che conosce e di riflettere su quali passi dovrebbe compiere successivamente in base a tali fatti.
Copiato
da smolagents import load_tool, CodeAgent, HfApiModel, DuckDuckGoSearchTool da dotenv importare load_dotenv load_dotenv() # Strumento di importazione da Hub image_generation_tool = load_tool("m-ric/text-to-image", trust_remote_code=True) strumento di ricerca = DuckDuckGoSearchTool() agent = CodeAgent( tools=[search_tool], model=HfApiModel("Qwen/Qwen2.5-72B-Instruct"), intervallo_di_pianificazione=3 # Qui si attiva la pianificazione! ) # Esegui! risultato = agent.run( "Quanto tempo impiegherebbe un ghepardo a tutta velocità per percorrere la lunghezza di Pont Alexandre III?", )