In questa esercitazione, vedremo come implementare un agente che sfrutta SQL utilizzando smolagents
.
Cominciamo con la domanda d'oro: perché non mantenere le cose semplici e utilizzare una pipeline standard da testo a SQL?
Una pipeline standard da testo a sql è fragile, poiché la query SQL generata può essere errata. Ancor peggio, la query potrebbe essere errata, ma non generare un errore, fornendo invece alcuni output errati/inutili senza dare l'allarme.
👉 Invece, un sistema ad agenti è in grado di ispezionare criticamente gli output e decidere se la query deve essere modificata o meno, ottenendo così un enorme aumento delle prestazioni.
Costruiamo questo agente! 💪
Per prima cosa, configuriamo l'ambiente SQL:
Copiato
da sqlalchemy import ( create_engine, MetaDati, Tabella, Colonna, Stringa, intero, variabile, inserire, ispezionare, testo, ) motore = create_engine("sqlite:///:memory:") metadata_obj = MetaData() # creare tabella SQL città nome_tabella = "ricevute" ricevute = Tabella( nome_tabella, metadata_obj, Colonna("receipt_id", Integer, primary_key=True), Colonna("nome_cliente", String(16), primary_key=True), Colonna("prezzo", Float), Colonna("mancia", Float), ) metadata_obj.create_all(engine) righe = [ {"receipt_id": 1, "nome_cliente": "Alan Payne", "prezzo": 12,06, "mancia": 1.20}, {"receipt_id": 2, "nome_cliente": "Alex Mason", "prezzo": 23,86, "mancia": 0.24}, {"receipt_id": 3, "nome_cliente": "Woodrow Wilson", "prezzo": 53,43, "mancia": 5.43}, {"receipt_id": 4, "nome_cliente": "Margaret James", "prezzo": 21,11, "mancia": 1.00}, ] per riga in righe: stmt = insert(receipts).values(**row) con engine.begin() as connection: cursore = connection.execute(stmt)
Costruire il nostro agente #
Ora rendiamo la nostra tabella SQL richiamabile da uno strumento.
L'attributo description dello strumento sarà incorporato dal sistema agente nel prompt dell'LLM: fornisce all'LLM informazioni su come utilizzare lo strumento. Qui si vuole descrivere la tabella SQL.
Copiato
inspector = inspect(engine) columns_info = [(col["nome"], col["tipo"]) for col in inspector.get_columns("receipts")] table_description = "Colonne:\n" + "\n".join([f" - {nome}: {col_tipo}" per nome, col_tipo in columns_info]) print(descrizione_tabella)
Copiato
Colonne: - receipt_id: INTEGER - nome_cliente: VARCHAR(16) - prezzo: VARIABILE - mancia: VARIABILE
Ora costruiamo il nostro strumento. Ha bisogno di quanto segue: (leggi lo strumento doc per maggiori dettagli)
- Una stringa di documenti con un elemento
Args:
parte che elenca gli argomenti. - Suggerimenti di tipo su entrambi gli ingressi e le uscite.
Copiato
da smolagents importiamo lo strumento @strumento def sql_engine(query: str) -> str: """ Consente di eseguire query SQL sulla tabella. Restituisce una rappresentazione in stringa del risultato. La tabella si chiama "ricevute". La sua descrizione è la seguente: Colonne: - receipt_id: INTEGER - nome_cliente: VARCHAR(16) - prezzo: VARIABILE - mancia: VARIABILE Args: query: La query da eseguire. Dovrebbe essere un SQL corretto. """ output = "" con engine.connect() as con: rows = con.execute(text(query)) per riga in righe: output += "\n" + str(row) restituire l'output
Ora creiamo un agente che sfrutti questo strumento.
Utilizziamo il CodiceAgente
che è la classe agente principale di smolagents: un agente che scrive azioni in codice e può iterare sui risultati precedenti secondo il framework ReAct.
Il modello è l'LLM che alimenta il sistema di agenti. HfApiModel consente di chiamare gli LLM utilizzando l'API di inferenza di HF, tramite endpoint Serverless o Dedicated, ma si può anche utilizzare qualsiasi API proprietaria.
Copiato
da smolagents import CodeAgent, HfApiModel agent = CodeAgent( tools=[sql_engine], model=HfApiModel("meta-llama/Meta-Llama-3.1-8B-Instruct"), ) agent.run("Può darmi il nome del cliente che ha ricevuto la ricevuta più costosa?")
Livello 2: join di tabelle #
Ora rendiamolo più impegnativo! Vogliamo che il nostro agente gestisca le giunzioni tra più tabelle.
Creiamo quindi una seconda tabella che registra i nomi dei camerieri per ogni ID scontrino!
Copiato
nome_tabella = "camerieri" ricevute = Tabella( nome_tavola, metadata_obj, Colonna("receipt_id", Integer, primary_key=True), Colonna("nome_cameriere", String(16), primary_key=True), ) metadata_obj.create_all(engine) righe = [ {"receipt_id": 1, "nome_cameriere": "Corey Johnson"}, {"receipt_id": 2, "nome del cameriere": "Michael Watts",} {"receipt_id": 3, "nome del cameriere": "Michael Watts"}, {"receipt_id": 4, "nome del cameriere": "Margaret James"}, ] per riga in righe: stmt = insert(receipts).values(**row) con engine.begin() as connection: cursore = connection.execute(stmt)
Dal momento che abbiamo cambiato la tabella, aggiorniamo il file Strumento SQLExecutor
con la descrizione di questa tabella, per consentire all'LLM di sfruttare correttamente le informazioni di questa tabella.
Copiato
updated_description = """Consente di eseguire query SQL sulla tabella. Si noti che l'output di questo strumento è una rappresentazione in stringa dell'output dell'esecuzione. Può utilizzare le seguenti tabelle:""" inspector = inspect(engine) for table in ["receipts", "waiters"]: columns_info = [(col["nome"], col["tipo"]) for col in inspector.get_columns(table)] table_description = f "Tabella '{tabella}':\n" table_description += "Colonne:\n" + "\n".join([f" - {nome}: {col_tipo}" per nome, col_tipo in columns_info]) updated_description += "\n\n" + table_description print(descrizione_aggiornata)
Poiché questa richiesta è un po' più difficile della precedente, si cambierà il motore LLM per usare il più potente Qwen/Qwen2.5-Coder-32B-Istruzione!
Copiato
sql_engine.description = updated_description agent = CodeAgent( tools=[sql_engine], model=HfApiModel("Qwen/Qwen2.5-Coder-32B-Instruct"), ) agent.run("Quale cameriere ha ottenuto più soldi in totale dalle mance?")
Funziona direttamente! La configurazione è stata sorprendentemente semplice, non è vero?
Questo esempio è finito! Abbiamo toccato questi concetti:
- Costruire nuovi strumenti.
- Aggiornamento della descrizione di uno strumento.
- Il passaggio a un LLM più forte aiuta il ragionamento dell'agente.
Ora potete costruire il sistema text-to-SQL che avete sempre sognato! ✨