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:

graph TD; A[Excel File Upload] --> B[LLM Metadata Analysis]; B --> C[Column Type Detection]; C --> D[SQL Schema Generation]; D --> E[Data Insertion]; E --> F[Ready for Queries]; G[Natural Language Query] --> H[LLM SQL Generation]; H --> I[Query Execution]; I --> J[Results & Visualization]; F --> H; style A fill:#e1f5fe; style F fill:#e8f5e8; style J fill:#fff3e0;

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:

graph LR A[Sample Data] --> B[LLM Analysis] A --> C[Statistical Analysis] B --> D[Final Type Decision] C --> D D --> E[SQL Schema]

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

IDNameISINCountryCurrencyExchangeType of SecurityRatingIndustry ClassificationWeighting
1SAPDE0007164600GermanyEURXETRAEquity-Information Technology1.47%
2ASML HOLDING NVNL0010273215NetherlandsEUREuronext AmsterdamEquityBaa2Information Technology1.46%
3NESTLE SACH0038863350SwitzerlandCHFScoach SwitzerlandEquityAa2Consumer Staples1.22%
4NOVARTIS AGCH0012005267SwitzerlandCHFScoach SwitzerlandEquityAa3Health Care1.08%
5ROCHE HOLDING PAR AGCH0012032048SwitzerlandCHFScoach SwitzerlandEquityA1Health Care1.06%
6…..….……..….…%

System Processing Steps

  1. Metadata Analysis

    • L’LLM identifica la natura dei dati
    • Suggerisce il nome della tabella: securities_list
    • Identifica ID come candidato chiave primaria
  2. 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)
  3. 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
);
  1. 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

countryindustry_classificationtotal_weight
-unknown0.00349444
AustraliaCommunication Services0.00139149
AustraliaConsumer Discretionary0.00439915
AustraliaConsumer Staples0.00200737
AustraliaEnergy0.00214571
AustraliaFinancials0.0250382
AustraliaHealth Care0.00195356
AustraliaIndustrials0.00295084
AustraliaInformation Technology0.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

nameCountryweighting
COMMONWEALTH BANK OF AUSTRALIAAustralia0.00917773
ROYAL BANK OF CANADACanada0.00869856
TOYOTA MOTOR CORPJapan0.00762924
MITSUBISHI UFJ FINANCIAL GROUP INCJapan0.00730266
SONY GROUP CORPJapan0.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:

  1. LLM Analysis: Comprende il conteso e il significato di Business
  2. 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.

graph TD A[Holdings Table] --> C[JOIN Operations] B[Sector Mapping] --> C C --> D[Complex Analytics]

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.