Introduzione
Come sviluppatori AI, cerchiamo sempre modi migliori per accedere ai dati e analizzarli in linguaggio naturale. Sebbene la Retrieval-Augmented Generation (RAG) abbia rivoluzionato il modo in cui interagiamo con documenti testuali non strutturati, essa mostra dei limiti quando si prova ad adottare le medesime tecniche sui dati strutturati.
D’altro canto, l’approccio RAG si è dimostrato talmente efficace e versatile che utenti e sviluppatori AI alle prime armi possono cadere nell’illusione che sia applicabile a qualsiasi tipo di dato, inclusi i file strutturati o semistrutturati come gli Excel. Tuttavia, questa è una convinzione errata che può portare a frustrazione e inefficienza.
Uno dei formati di file più diffusi in tutte le organizzazioni è proprio l’Excel, che può essere considerato strutturato o almeno “semi-strutturato”. Chiunque abbia provato a processare un file Excel con il classico approccio RAG si sarà accorto rapidamente che applicare l’approccio RAG standard sui file Excel non porta praticamente da nessuna parte.
In questo post, spiego brevemente come ho costruito un sistema che combina alcune tecniche di prompting per creare uno strumento potente di analisi dei file Excel tramite l’utilizzo dell’SQL.
Tutto il codice è disponibile su GitHub
Perché il RAG non funziona con i file Excel
Il RAG è stato progettato per arricchire il prompt passato all’LLM con dati non strutturati provenienti da un ampio corpus documentale. Per identificare i chunk di testo rilevanti, viene utilizzata la similarità semantica ed ecco perché questo approccio fatica a mostrare risultati sensati con i dati Excel:
1. L’architettura RAG
- L’architettura RAG è pensata per dati non strutturati dove la similarità semantica, utilizzata soprattutto nella fase di retrieval , è di fondamentale importanza. Tuttavia, l’analisi di dati su Excel richiede quasi sempre corrispondenze esatte, aggregazioni e relazioni complesse che non possono espresse semplicemente in termini di similarità.
- Il processo di suddivisione in chunk di RAG rompe la struttura intrinseca dei file Excel, portando alla perdita di informazioni essenziali come intestazioni di colonna, tipi di dato e relazioni tra le colonne.
2. La natura dei dati nei file Excel
- I file Excel sono spesso strutturati o semi-strutturati, con righe e colonne che definiscono chiare relazioni tra i dati. In molti casi, si tratta di vere e proprie estrazioni da database o report finanziari.
- Verosimilmente, l’intento dell’utente non è quello di estrarre informazioni in maniera “narrativa”, ma richiede invece l’applicazione di calcoli, aggregazioni e analisi statistiche.
- Nei dati strutturati, le intestazioni di colonna, i tipi di dato e le relazioni tra celle sono fondamentali e l’approccio RAG può completamente ignorare questi elementi chiave.
La soluzione: Pipeline Excel-to-SQL potenziata da LLM
Le capacità degli LLM stanno crescendo rapidamente e oggi una delle aree di applicazione più mature è senza dubbio la generazione di codice. In particolare, gli LLM sono molto bravi a generare codice SQL, che è un linguaggio di analisi potente e preciso per l’analisi dei dati strutturati. Quindi, anziché forzare l’architettura RAG sui dati strutturati (ad esempio con tecniche di chunking o augmentation creative), ho costruito una piccola soluzione di natura completamente diversa, che utilizza un LLM per convertire i dati letti da Excel in uno schema di database SQL e che dunque ci consente di sfruttare tutta la potenza espressiva del linguaggio SQL per interrogare e analizzare i dati.
Ecco l’architettura:
Per quanto riguarda il database, ho utilizzato SQLite per semplicità, ma questa architettura può essere adattata a qualsiasi database SQL. Per quanto riguarda l’LLM, ho utilizzato il modello gpt-4.1-mini di OpenAI, ma si può usare qualsiasi LLM comparabile.
Componenti del sistema
La pipeline è composta da quattro componenti principali:
1. Metadata Analyzer
Utilizza un LLM per analizzare i nomi degli sheet e le intestazioni delle colonne, deducendo la semantica del dato e la struttura dei dati:
metadata_prompt = PromptTemplate(
input_variables=["sheet_name", "columns"],
partial_variables={
"format_instructions": metadata_parser.get_format_instructions()
},
template="""
Analyze the following metadata of an Excel sheet:
Sheet name: {sheet_name}
Columns: {columns}
Based on the column names, please return the following information:
- suggested_table_name
- description of the table content
- primary_key_column_name_if_present
- category (e.g., financial, sales, inventory, etc.)
{format_instructions}
Use table names in snake_case and in English.
"""
)
2. Type Detection Engine
Combina l’analisi LLM con un sampling dei dati per determinare i tipi di dato SQL corretti:
Al termine del processo, il sistema genera uno schema SQL che rappresenta la struttura dei dati e lo esegue per creare la tabella nel database.
3. SQL Generator
Converte le domande dell’utente espresse sotto forma di linguaggio naturale in query SQL, utilizzando lo schema del database come contesto di arricchimento del prompt:
sql_prompt = PromptTemplate(
input_variables=["question", "schema", "sample_data"],
template="""
Generate an SQL query to answer the following question:
Question: {question}
Database schema:
{schema}
Sample data:
{sample_data}
Generate ONLY the SQL query without any additional explanations.
Use standard SQLite syntax.
"""
)
4. Query Executor
Esegue le query SQL generate e restituisce i risultati in un formato leggibile.
Real-World Example: ETF Portfolio Analysis
Vediamo ora un caso d’uso concreto, utilizzando come file di esempio la composizione delle holdings di un ETF XTrackers.
Il file Excel è molto semplice e contiene il dettaglio delle posizioni dell’ETF “Xtrackers MSCI World ex USA UCITS”, con i relativi titoli sottostanti, il loro valore di mercato, il peso nel portafoglio e la classificazione settoriale.
Di seguito sono riportati a titolo esemplificativo i primi record del file Excel:
Input Excel File Structure
ID | Name | ISIN | Country | Currency | Exchange | Type of Security | Rating | Industry Classification | Weighting |
---|---|---|---|---|---|---|---|---|---|
1 | SAP | DE0007164600 | Germany | EUR | XETRA | Equity | - | Information Technology | 1.47% |
2 | ASML HOLDING NV | NL0010273215 | Netherlands | EUR | Euronext Amsterdam | Equity | Baa2 | Information Technology | 1.46% |
3 | NESTLE SA | CH0038863350 | Switzerland | CHF | Scoach Switzerland | Equity | Aa2 | Consumer Staples | 1.22% |
4 | NOVARTIS AG | CH0012005267 | Switzerland | CHF | Scoach Switzerland | Equity | Aa3 | Health Care | 1.08% |
5 | ROCHE HOLDING PAR AG | CH0012032048 | Switzerland | CHF | Scoach Switzerland | Equity | A1 | Health Care | 1.06% |
6 | … | ….. | …. | … | … | …… | .. | …. | …% |
System Processing Steps
Metadata Analysis
- L’LLM identifica la natura dei dati
- Suggerisce il nome della tabella:
securities_list
- Identifica
ID
come candidato chiave primaria
Type Detection
ID
: NUMBER (sequence number)Name
: TEXT (Company Name)ISIN
: TEXT (Security Identifier)Country
: TEXT (Country of origin)Currency
: TEXT (Currency of the security)Exchange
: TEXT (Trading exchange)Type of Security
: TEXT (e.g., Equity, Bond)Rating
: TEXT (Credit rating)Industry Classification
: TEXT (Sector classification)Weighting
: REAL (Percentage weight in the portfolio)
SQL Schema Generation DDL della tabella generata automaticamene:
CREATE TABLE securities_list (
id INTEGER NOT NULL,
name TEXT NOT NULL,
isin TEXT NOT NULL,
country TEXT,
currency TEXT NOT NULL,
exchange TEXT,
type_of_security TEXT NOT NULL,
rating TEXT,
industry_classification TEXT,
weighting REAL
);
- Data Insertion
Qui l’LLM genera automaticamente le istruzioni SQL INSERT per popolare la tabella con i dati del file Excel:
- Gestisce la conversione dei formati (B per miliardi, % per percentuali, etc…)
- Valida l’integrità dei dati
- Inserisce tutte le righe delle posizioni
Query Examples
Una volta processato il file, l’utente può fare domande in linguaggio naturale.
Iniziamo con una semplice domanda per verificare che il sistema funzioni correttamente:
Query: “How many rows are there in total?”
Generated SQL
SELECT COUNT(*) as N FROM securities_list;
Output
N |
---|
796 |
Ok, adesso vediamo una query più complessa che richiede aggregazione e comprensione della struttura dei dati:
Query: “Can you show me the weight of the portfolio for each Country and Sector?”
Generated SQL
SELECT country, industry_classification AS sector, SUM(weighting) AS total_weight
FROM securities_list
GROUP BY country, industry_classification;
Output
country | industry_classification | total_weight |
---|---|---|
- | unknown | 0.00349444 |
Australia | Communication Services | 0.00139149 |
Australia | Consumer Discretionary | 0.00439915 |
Australia | Consumer Staples | 0.00200737 |
Australia | Energy | 0.00214571 |
Australia | Financials | 0.0250382 |
Australia | Health Care | 0.00195356 |
Australia | Industrials | 0.00295084 |
Australia | Information Technology | 0.000675708 |
…. | …. | …. |
Adesso facciamo un passo avanti e applichiamo alcune “where” conditions:
Query: “Show me the top 5 Non-European holdings by weight”
Generated SQL
SELECT name, country, weighting
FROM securities_list
WHERE country NOT IN ('Austria', 'Belgium', 'Bulgaria', 'Croatia', 'Cyprus', 'Czech Republic', 'Denmark', 'Estonia', 'Finland', 'France', 'Germany', 'Greece', 'Hungary', 'Iceland', 'Ireland', 'Italy', 'Latvia', 'Lithuania', 'Luxembourg', 'Malta', 'Netherlands', 'Norway', 'Poland', 'Portugal', 'Romania', 'Slovakia', 'Slovenia', 'Spain', 'Sweden', 'Switzerland', 'United Kingdom')
ORDER BY weighting DESC
LIMIT 5;
Output
name | Country | weighting |
---|---|---|
COMMONWEALTH BANK OF AUSTRALIA | Australia | 0.00917773 |
ROYAL BANK OF CANADA | Canada | 0.00869856 |
TOYOTA MOTOR CORP | Japan | 0.00762924 |
MITSUBISHI UFJ FINANCIAL GROUP INC | Japan | 0.00730266 |
SONY GROUP CORP | Japan | 0.00721053 |
Davvero non male!
Risultati
Premettendo che non ho fatto un’analisi esaustiva, ho comunque fatto qualche verifica a campione e i risultati sono stati quasi sempre corretti. Considerando che ho sviluppato il tutto in poche ore, il sistema ha dimostrato di essere molto efficace e preciso:
- Ha processato tutte le righe dell’Excel già al primo tentativo
- Ha identificato e convertito correttamente i tipi di dato
- Ha generato SQL accurato anche per query complesse
- Ha fornito risposte immediate alle domande di analisi del portafoglio
Implementation Highlights
Smart Type Detection
Il sistema utilizza un approccio a 2 stadi:
- LLM Analysis: Comprende il conteso e il significato di Business
- Statistical Validation: Conferma i pattern di validazione dei dati tramite le primitive di pandas, come
pd.is_integer_dtype
,pd.is_float_dtype
, etc.
Conversione robusta del formato per i casi più comuni
Gestisce i problemi comuni di formattazione Excel:
- Simboli di valuta e abbreviazioni (K, M, B)
- Formattazione delle percentuali
- Variazioni nei formati data
- Celle vuote e validazione dei dati
Context-Aware SQL Generation
All’interno del prompt, l’LLM riceve:
- lo schema completo del database
- Alcuni dati di esempio
- le relazioni tra le colonne
- le domande precedenti per apprendere e migliorare le risposte
Limitazioni
Sebbene questo approccio sia potente, presenta alcune limitazioni:
- Efficacia: la qualità del file Excel è fondamentale. L’esempio utilizzato in questo post è un file “tabellare” piuttosto standard, ma spesso si incontrano strutture più complesse, come tabelle pivot, celle unite o formule articolate che possono risultare difficili da interpretare. Per ottenere i migliori risultati, il file Excel dovrebbe dunque sempre essere ripulito prima dell’analisi.
- Limiti degli LLM: la capacità dell’LLM di comprendere query complesse è ancora in evoluzione. Potrebbe avere difficoltà con domande molto tecniche o specifiche di un dominio.
- Performance: con file Excel molto grandi, l’analisi iniziale e la generazione dello schema SQL potrebbero richiedere molto tempo. Tuttavia, una volta creato lo schema, le query consentono dei tempi di analisi rapidissimi.
- Integrità dei dati: il sistema presuppone che i dati Excel siano puliti e ben strutturati. Se il file contiene errori o incongruenze, si rischia di generare SQL statement non ottimali (per esempio, con join non necessari o condizioni di filtro errate).
Conclusioni e sviluppi futuri
Questo approccio risolve il problema di come sfruttare efficacemente la Generative AI per analizzare i dati su file Excel, combinando la potenza degli LLM con la precisione del linguaggio SQL. I vantaggi principali sono:
- Conservazione delle relazioni e della struttura dei dati
- Possibilità di eseguire query analitiche complesse
- Restituzione di risultati esatti e verificabili
- Mantenimento dell’integrità e dei tipi di dato
Potenziali sviluppi futuri
1. Multi-Table Relationships
Per gestire file Excel più complessi con più fogli e relazioni tra tabelle, il sistema potrebbe essere esteso per gestire relazioni tra più tabelle. Ad esempio, se il file Excel contiene un foglio con le holdings e un altro con la mappatura dei settori, il sistema potrebbe generare automaticamente le JOIN necessarie per analizzare i dati in modo più complesso.
2. Advanced Analytics
- Funzioni statistiche (correlazione, regressione)
- Analisi delle serie temporali su dati storici
- Integrazione di modelli di machine learning
3. Visualization Pipeline
# Future enhancement: Auto-generate charts
def generate_visualization(query_result, question):
# Analyze result structure
# Choose appropriate chart type
# Generate visualization code
pass
4. Multi-Format Support
- Google Sheets integration
- CSV batch processing
- Database export
5. Query Optimization
- Query caching and reuse
- Index suggestions
- Performance monitoring
Business Applications
Questo sistema apre numerosi casi d’uso:
- Analisi finanziaria: composizione del portafoglio, valutazione del rischio
- Sales Analytics: monitoraggio delle performance, analisi dei trend
- Gestione dell’inventario: livelli di stock, previsione della domanda
- HR Analytics: composizione della forza lavoro, metriche di performance
- Marketing Analytics: performance delle campagne, segmentazione dei clienti
L’aspetto chiave è che i dati strutturati richiedono soluzioni strutturate. Combinando la comprensione del linguaggio naturale degli LLM con la potente capacità espressiva di SQL, possiamo creare strumenti potenti che rendono l’analisi di dati complessi accessibile a tutti.