AI Assistant - Executive Summary

AI Assistant - Executive Summary

Data: 22 Ottobre 2025 Destinatario: Nicola Scapaticci Oggetto: Chiarimento Architettura AI Assistant Contestualizzato

---

🎯 La Tua Richiesta (Chiarita)

Cosa Vuoi Ottenere

1. AI Assistant Contestualizzato: Non un assistente generico, ma specializzato per ambiti specifici del tuo sistema i40 2. Addestramento Profondo: Preparare una Knowledge Base dettagliata per ridurre i costi e aumentare la precisione 3. Risposte Deterministiche: Limitare la "creatività" dell'AI, preferendo risposte basate su dati storici preparati 4. Interpretazione Strutturata: Capire l'intent dell'utente senza chiamare sempre l'LLM 5. Risposte Ibride: Storico preparato (80%) + AI generativa (20%) solo quando necessario

Le Tue Preoccupazioni (Valide!)

Costi API: Con approccio standard (sempre LLM), costi imprevedibili e alti ✅ Allucinazioni AI: Risposte inventate o imprecise ✅ Libertà eccessiva AI: Vuoi controllo su cosa può/non può dire ✅ Manutenzione: Modificare prompt è più difficile che modificare template JSON

---

📊 Sistema Attuale (Analisi)

Cosa Hai Già Implementato

`` 3 Macchine i40 ↓ Profili Import Configurabili (schema CSV dinamico) ↓ MachineOperationLog (raw_data JSON con campi variabili) ↓ Analyze View (filtri, KPI, grafici) `

Caratteristiche Chiave

  • Schema Dati Dinamico: Ogni macchina può avere campi diversi in raw_data
  • Profili con Metadati: Campi hanno is_filterable, is_aggregatable, column_names mnemonici
  • Operations Log: Storico completo operazioni macchina con timestamp
  • CSV Imports: Pipeline validazione → processamento → archiviazione
  • ---

    🗂️ Ambiti Contestuali (Proposta Finale)

    Perché Dividere per Ambiti?

    Ogni ambito ha:

  • Vocabolario specifico (parole chiave ricorrenti)
  • Intent patterns (tipi di domande simili)
  • Logica di business (calcoli/confronti tipici)
  • Formato risposta (template standard)
  • Dividere per ambiti permette:

  • ✅ Rilevamento intent senza LLM (pattern matching regex)
  • ✅ Template risposte deterministiche
  • ✅ Costi ridotti (LLM solo quando necessario)
  • ---

    Ambito 1: PRODUZIONE 📊

    Dominio: Quantità prodotte, pezzi lavorati, conteggi operazioni

    Query Tipiche:

  • "Qual è stata la produzione di ieri?"
  • "Quanti pezzi abbiamo fatto oggi?"
  • "Totale operazioni questa settimana"
  • "Mostrami il trend mensile"
  • Entità da Estrarre:

  • Periodo temporale (ieri, oggi, questa settimana, questo mese)
  • Metrica (pezzi, operazioni, lavorazioni)
  • Filtri opzionali (operatore, materiale)
  • Dati Necessari: `sql -- Campi in raw_data (dipende dal profilo macchina) TotalPieces, NumberOfPlies, Quantity, etc.

    -- Query tipo SELECT SUM(CAST(JSON_UNQUOTE(JSON_EXTRACT(raw_data, '$.TotalPieces')) AS DECIMAL)) FROM machine_operations_log WHERE machine_id = :machine_id AND timestamp BETWEEN :date_from AND :date_to `

    Risposta Template: ` 📊 Produzione di {periodo}:

    ✅ Totale pezzi: {total_quantity} 📈 Operazioni: {total_operations} ⏱️ Durata media: {avg_duration}h

    Rispetto alla media degli ultimi 30 giorni ({avg_30d} pezzi), sei {delta_percent}% {emoji_trend}. `

    Richiede LLM?: ❌ No (risposta completamente deterministica)

    ---

    Ambito 2: DIAGNOSTICA 🔧

    Dominio: Errori, fermi macchina, anomalie, performance scadenti

    Query Tipiche:

  • "Perché la macchina si è fermata?"
  • "Ci sono stati errori oggi?"
  • "Qual è il problema?"
  • "Perché ieri abbiamo prodotto meno?"
  • Entità da Estrarre:

  • Periodo temporale
  • Tipo problema (fermo, errore, performance)
  • Macchina specifica
  • Logica Diagnostica (Decision Tree): ` 1. Verifica gaps timestamp > 30 min → "Fermo macchina rilevato dalle {start_time} alle {end_time}"

    2. Verifica campi ErrorCode/AlarmCode in raw_data → "Errore registrato: {error_code} - {error_description}"

    3. Verifica produzione < media - 20% → "Produzione sotto media del {delta}%" 4. Verifica Duration > avg + 2σ → "Operazioni più lente del solito (+{percent}%)"

    5. Confronta con stesso giorno settimana scorsa → "Rispetto a {last_week_same_day}, produzione {comparison}" `

    Risposta Template: ` 🔍 Analisi {periodo}:

    ⚠️ Problemi identificati: 1. {problema_1} 2. {problema_2} ...

    💡 Cause probabili:

  • {causa_1}
  • {causa_2}
  • 📊 Dati a supporto:

  • Operazioni: {total_ops} (atteso: {expected_ops})
  • Durata media: {avg_duration}h (atteso: {expected_duration}h)
  • `

    Richiede LLM?: ⚠️ Parziale (solo per interpretare ErrorCode sconosciuti o suggerire cause)

    ---

    Ambito 3: OPERATORI 👥

    Dominio: Performance operatori, confronti, ranking

    Query Tipiche:

  • "Chi è l'operatore più produttivo?"
  • "Performance di Mario Rossi"
  • "Confronta operatori"
  • "Ranking operatori del mese"
  • Entità da Estrarre:

  • Nome operatore
  • Periodo temporale
  • Metrica di confronto (pezzi, velocità, qualità)
  • Dati Necessari: `sql SELECT JSON_UNQUOTE(JSON_EXTRACT(raw_data, '$.UserLog')) as operator, COUNT(*) as operations, SUM(CAST(JSON_UNQUOTE(JSON_EXTRACT(raw_data, '$.TotalPieces')) AS DECIMAL)) as total_pieces, AVG(CAST(JSON_UNQUOTE(JSON_EXTRACT(raw_data, '$.Duration')) AS DECIMAL)) as avg_duration FROM machine_operations_log WHERE machine_id = :machine_id AND timestamp BETWEEN :date_from AND :date_to GROUP BY operator ORDER BY total_pieces DESC `

    Risposta Template: ` 👥 Ranking Operatori ({periodo}):

    🥇 1. {operator_1} - {pieces_1} pezzi ({ops_1} operazioni) 🥈 2. {operator_2} - {pieces_2} pezzi ({ops_2} operazioni) 🥉 3. {operator_3} - {pieces_3} pezzi ({ops_3} operazioni)

    ⚡ Insight: {operator_1} è il più veloce con {pieces_per_hour} pezzi/ora (+{percent}% rispetto alla media di {avg_pieces_per_hour}). `

    Richiede LLM?: ❌ No

    ---

    Ambito 4: PIANIFICAZIONE 📅

    Dominio: Previsioni, deadline, capacità produttiva

    Query Tipiche:

  • "Riusciremo a finire 5000 pezzi entro venerdì?"
  • "Quanto ci vuole per produrre 3000 pezzi?"
  • "Quando finiremo l'ordine X?"
  • Entità da Estrarre:

  • Quantità target
  • Deadline (data)
  • Macchina
  • Logica Predittiva: ` 1. Calcola produzione_media_giornaliera (ultimi 30 giorni) 2. Calcola pezzi_già_prodotti (se order in corso) 3. Calcola pezzi_rimanenti = target - già_prodotti 4. Calcola giorni_lavorativi_rimanenti (escludi weekend) 5. Proiezione: giorni_necessari = pezzi_rimanenti / produzione_media_giornaliera 6. Confronto: margin = giorni_lavorativi_rimanenti - giorni_necessari `

    Risposta Template: ` 📅 Previsione Completamento:

    🎯 Target: {target_quantity} pezzi ✅ Già prodotti: {already_produced} pezzi ⏳ Rimanenti: {remaining} pezzi

    📊 Proiezione:

  • Media giornaliera: {avg_daily} pezzi/giorno
  • Giorni necessari: {days_needed} giorni lavorativi
  • Deadline: {deadline_date}
  • Giorni disponibili: {days_available}
  • {verdict_emoji} {verdict_message}

    💡 Scenario worst-case (-20% produzione): Servirebbero {worst_case_days} giorni → {worst_case_deadline} `

    Richiede LLM?: ❌ No (logica completamente deterministica)

    ---

    Ambito 5: COMPARAZIONE ⚖️

    Dominio: Confronti temporali, confronti macchine, delta

    Query Tipiche:

  • "Confronta questa settimana con la scorsa"
  • "Oggi vs ieri"
  • "Differenza tra macchina A e B"
  • "Trend ultimi 7 giorni"
  • Entità da Estrarre:

  • Periodo 1 (es. "questa settimana")
  • Periodo 2 (es. "scorsa settimana")
  • Metrica (produzione, durata, operazioni)
  • Entità confrontate (macchine, operatori, periodi)
  • Logica Comparazione: ` 1. Query periodo 1 2. Query periodo 2 3. Calcola delta assoluto = valore1 - valore2 4. Calcola delta percentuale = (delta / valore2) * 100 5. Determina trend: - Positivo (+): emoji 📈 - Negativo (-): emoji 📉 - Stabile (±3%): emoji ➡️ `

    Risposta Template: ` ⚖️ Confronto {periodo_1} vs {periodo_2}:

    📊 {metrica_label}:

  • {periodo_1}: {value_1} {unit}
  • {periodo_2}: {value_2} {unit}
  • 📈 Variazione: {delta_abs} {unit} ({delta_percent}% {emoji_trend})

    {insight_message} `

    Richiede LLM?: ❌ No

    ---

    Ambito 6: CONFIGURAZIONE ⚙️

    Dominio: Info macchine, profili, setup, metadati

    Query Tipiche:

  • "Quali profili ha la macchina?"
  • "Configurazione attuale"
  • "Campi disponibili"
  • "Ultimo import CSV"
  • Dati Necessari: `sql -- Tabelle metadata machines_i40.profiles_config import_profiles.columns, column_names csv_imports (status, arrived_at) `

    Risposta Template: ` ⚙️ Configurazione {machine_name}:

    📋 Profili Attivi: 1. {profile_1_name} - Campi: {fields_count} - Ultimo import: {last_import_date} 2. {profile_2_name} - Campi: {fields_count} - Ultimo import: {last_import_date}

    📊 Campi Disponibili (Profilo {active_profile}): {field_1}, {field_2}, {field_3}, ...

    📁 Ultimo Import CSV:

  • File: {filename}
  • Data: {import_date}
  • Righe: {rows_processed}/{rows_total}
  • Status: {status_badge}
  • `

    Richiede LLM?: ❌ No

    ---

    Ambito 7: OTTIMIZZAZIONE 🚀

    Dominio: Suggerimenti, best practices, insights, miglioramenti

    Query Tipiche:

  • "Come posso migliorare la produttività?"
  • "Suggerimenti di ottimizzazione"
  • "Dove posso migliorare?"
  • "Quali sono i colli di bottiglia?"
  • Logica Insight Engine: ` 1. Analizza distribuzione temporale (orari/giorni più produttivi) 2. Confronta operatori (best practices da replicare) 3. Identifica anomalie ricorrenti (pattern errori) 4. Calcola OEE (Overall Equipment Effectiveness) 5. Identifica downtime patterns 6. Confronta con benchmark aziendali `

    Insight Rules: ` IF orario_picco_produzione EXISTS THEN → "Le operazioni tra {start_hour}:{end_hour} sono {percent}% più veloci" → Suggerimento: "Concentra lavorazioni complesse in questa fascia"

    IF operatore_best_performer EXISTS THEN → "L'operatore {name} ha resa {percent}% superiore alla media" → Suggerimento: "Organizza training con {name} per condividere best practices"

    IF downtime_medio > threshold THEN → "Fermi macchina: {downtime_hours}h/giorno in media" → Cause: {top_3_causes} → Suggerimento: "Implementa pre-staging materiali" `

    Risposta Template: ` 🚀 Analisi Ottimizzazione ({periodo}):

    📊 Situazione Attuale:

  • Produttività media: {current_productivity}
  • OEE: {oee_percent}%
  • Downtime: {downtime_hours}h/giorno
  • 💡 Opportunità Identificate:

    1. {opportunity_1_title} (Impatto: +{impact_1}%) {description_1} ✅ Azione: {action_1}

    2. {opportunity_2_title} (Impatto: +{impact_2}%) {description_2} ✅ Azione: {action_2}

    3. {opportunity_3_title} (Impatto: +{impact_3}%) {description_3} ✅ Azione: {action_3}

    📈 Potenziale Totale: +{total_impact}% `

    Richiede LLM?: ⚠️ Sì (per formulare suggerimenti articolati e insights)

    ---

    🧠 Interpretazione Query - Architettura

    Livello 1: Pattern Matching (Deterministico - 0 costi)

    Obiettivo: Identificare ambito senza chiamare LLM

    Metodo: Regex patterns su query normalizzata

    `php // Input $userQuery = "Qual è stata la produzione di ieri?";

    // Normalization $normalized = normalize($userQuery); // → "qual e stata la produzione di ieri"

    // Pattern Matching foreach ($patterns['AMBITO_PRODUZIONE'] as $regex) { if (preg_match($regex, $normalized, $matches)) { return [ 'ambito' => 'AMBITO_PRODUZIONE', 'confidence' => 1.0, // Deterministico 'entities' => extractEntities($matches), // entities: ['periodo' => 'ieri', 'metrica' => 'produzione'] ]; } } `

    Coverage Atteso: 70-80% delle query

    ---

    Livello 2: Entity Extraction (Deterministico - 0 costi)

    Obiettivo: Estrarre parametri dalla query

    Metodo: Regex + Lookup Tables

    `php // Estrazione periodo temporale $periodMap = [ 'ieri' => [ 'date_from' => now()->subDay()->startOfDay(), 'date_to' => now()->subDay()->endOfDay(), 'label' => 'Ieri', ], 'oggi' => [ 'date_from' => now()->startOfDay(), 'date_to' => now()->endOfDay(), 'label' => 'Oggi', ], 'questa settimana' => [ 'date_from' => now()->startOfWeek(), 'date_to' => now()->endOfWeek(), 'label' => 'Questa Settimana', ], // ... altri 20-30 periodi ];

    // Estrazione nomi operatori (fuzzy matching) $operatorNames = getOperatorNamesFromDB($machineId); $matchedOperator = findClosestMatch($userQuery, $operatorNames);

    // Estrazione numeri preg_match_all('/\d+/', $userQuery, $numbers); // "5000 pezzi entro venerdì" → entities['quantity'] = 5000 `

    ---

    Livello 3: Template Lookup (Deterministico - 0 costi)

    Obiettivo: Trovare template pre-costruito

    Metodo: Database lookup con pattern matching

    `php // Query database template $template = DB::table('ai_knowledge_templates') ->where('ambito', $detectedAmbito) ->whereRaw('? REGEXP pattern', [$userQuery]) ->first();

    if ($template) { // Template trovato → Esegui senza LLM return executeTemplate($template, $entities, $machineId); } `

    Template Structure (JSON in DB): `json { "template_id": "prod_daily", "pattern": "produzione di (ieri|oggi)", "sql_query": { "query": "SELECT SUM(...) FROM ...", "parameters": { "machine_id": ":machine_id", "date_from": ":date_from", "date_to": ":date_to" } }, "response_template": "Produzione di {periodo}: {total} pezzi.", "enrichment": { "add_comparison": true, "compare_with": "avg_last_30_days" } } `

    ---

    Livello 4: LLM Fallback (Costoso - solo se necessario)

    Quando Usare LLM:

  • ❌ Ambito non rilevato (confidence < 0.7)
  • ❌ Query ambigua/complessa
  • ❌ Richiesta ottimizzazione/suggerimenti
  • ❌ Template non disponibile
  • Come Minimizzare Costi: `php // Prompt MINIMO (no schema completo) $systemPrompt = "Sei assistente produzione. Macchina: {$machine->name}. Rispondi in italiano, max 3 frasi.";

    // Context injection selettivo (solo dati necessari) $context = [ 'avg_production_last_30d' => 1500, // Pre-calcolato 'last_operation_timestamp' => '2025-10-22 10:30', 'total_operations_today' => 12, ];

    // Chiamata LLM con limite tokens $response = OpenAI::chat()->create([ 'model' => 'gpt-4o-mini', // Modello più economico 'messages' => [ ['role' => 'system', 'content' => $systemPrompt], ['role' => 'user', 'content' => $userQuery], ], 'temperature' => 0.2, // Basso = più deterministico 'max_tokens' => 300, // Limita risposta ]); `

    Costo per Query LLM:

  • Input: 200 tokens × $0.15 / 1M = $0.00003
  • Output: 150 tokens × $0.60 / 1M = $0.00009
  • Totale: ~$0.00012 per query
  • ---

    🔄 Flusso Completo - Esempio Pratico

    Scenario: Utente chiede "Produzione di ieri"

    ` USER INPUT: "Qual è stata la produzione di ieri?"

    STEP 1: Intent Router (Pattern Matching)

  • Normalizza: "qual e stata la produzione di ieri"
  • Match pattern: /produz(?:ione) di (ieri|oggi)/i
  • Ambito rilevato: AMBITO_PRODUZIONE
  • Confidence: 1.0
  • Entities: { periodo: 'ieri', metrica: 'produzione' }
  • STEP 2: Entity Extraction

  • Periodo: 'ieri' → date_from: 2025-10-21 00:00, date_to: 2025-10-21 23:59
  • Metrica: 'produzione' → campo_raw_data: 'TotalPieces'
  • STEP 3: Cache Lookup

  • Query hash: md5("produzione_ieri_machine_5_2025-10-21")
  • Cache hit? NO (prima volta oggi)
  • STEP 4: Template Lookup

  • Template trovato: "prod_daily"
  • Requires LLM: false
  • STEP 5: SQL Execution `sql SELECT SUM(CAST(JSON_UNQUOTE(JSON_EXTRACT(raw_data, '$.TotalPieces')) AS DECIMAL)) as total_quantity, COUNT(*) as total_operations, AVG(CAST(JSON_UNQUOTE(JSON_EXTRACT(raw_data, '$.Duration')) AS DECIMAL)) as avg_duration FROM machine_operations_log WHERE machine_id = 5 AND timestamp BETWEEN '2025-10-21 00:00:00' AND '2025-10-21 23:59:59' ` Result: { total_quantity: 1850, total_operations: 23, avg_duration: 2.3 }

    STEP 6: Context Enrichment

  • Carica media ultimi 30 giorni: 1750 pezzi
  • Calcola delta: +100 pezzi (+5.7%)
  • Emoji trend: 📈
  • STEP 7: Response Formatting ` 📊 Produzione di Ieri:

    ✅ Totale pezzi: 1.850 📈 Operazioni: 23 ⏱️ Durata media: 2.3h

    Rispetto alla media degli ultimi 30 giorni (1.750 pezzi), sei +5.7% 📈. Ottimo lavoro! `

    STEP 8: Follow-up Suggestions

  • "Confronta con l'altro ieri"
  • "Mostrami il trend settimanale"
  • "Chi ha prodotto di più ieri?"
  • STEP 9: Save to Cache

  • Salva risposta con TTL 1 ora
  • Prossima richiesta identica → risposta istantanea da cache
  • STEP 10: Save to Conversation History

  • User message + Assistant response → DB
  • Metadata: { ambito, template_id, execution_time_ms, used_llm: false }
  • TOTAL TIME: ~200ms TOTAL COST: $0 (no LLM) `

    ---

    Scenario: Utente chiede "Come posso migliorare?"

    ` USER INPUT: "Come posso migliorare la produttività?"

    STEP 1: Intent Router

  • Ambito: AMBITO_OTTIMIZZAZIONE
  • Confidence: 0.9
  • STEP 2: Template Lookup

  • Template: "optimization_suggestions"
  • Requires LLM: true (per suggerimenti articolati)
  • STEP 3: Data Analysis (pre-LLM)

  • Analizza distribuzione oraria → Picco: 9-11am (+18%)
  • Analizza operatori → Best: "Mario Rossi" (+25%)
  • Analizza downtime → Media: 45 min/giorno
  • STEP 4: LLM Call (con dati pre-elaborati) `php $context = [ 'peak_hours' => '9-11am (+18%)', 'best_operator' => 'Mario Rossi (+25%)', 'avg_downtime' => '45 min/day', 'avg_production' => 1750, ];

    $prompt = "Basandoti su questi dati analitici, suggerisci 3 azioni concrete per migliorare produttività:\n" . json_encode($context); `

    STEP 5: LLM Response ` 🚀 Suggerimenti Ottimizzazione:

    1. Sfrutta le ore di picco (Impatto: +15%) Le operazioni tra 9-11am sono 18% più veloci. ✅ Azione: Pianifica lavorazioni critiche in questa fascia.

    2. Training con Mario Rossi (Impatto: +20%) Mario ha resa 25% superiore alla media. ✅ Azione: Organizza sessione formativa per condividere best practices.

    3. Riduzione downtime (Impatto: +8%) 45 minuti/giorno di fermi non pianificati. ✅ Azione: Pre-staging materiali e checklist pre-operazione.

    📈 Potenziale: +43% produttività `

    TOTAL TIME: ~3s TOTAL COST: ~$0.00012 (LLM usato) `

    ---

    💡 Riepilogo: Risposta alla Tua Domanda

    "Come Organizzare Interpretazione Query?"

    ` 1. Pattern Matching Regex (Livello 1) → Identifica ambito → Estrae entità (periodo, metrica, nomi) → 70-80% query risolte QUI

    2. Template Lookup (Livello 2) → Cerca template pre-costruito per ambito + pattern → Esegue SQL deterministico → Formatta risposta da template → 15-20% query risolte QUI

    3. LLM Fallback (Livello 3) → Solo per query ambigue/complesse → Prompt minimale con dati pre-elaborati → 5-10% query risolte QUI `

    "Come Organizzare Risposte da Storico + AI?"

    ` STORICO PREPARATO (80% query):

  • Database: 30-40 template JSON in ai_knowledge_templates
  • Ogni template = pattern + SQL + response_template + enrichment_rules
  • Risposte completamente deterministiche
  • Arricchite con contesto (medie, confronti, trend)
  • AI GENERATIVA (20% query):

  • Solo quando template non disponibile
  • Prompt minimale (no schema completo)
  • Context injection selettivo (dati pre-calcolati)
  • Temperature bassa (0.2-0.3) per risposte coerenti
  • ``

    "Cosa Evitare (Incongruenze Nella Tua Richiesta)"

    NON fare: Inviare schema completo database in ogni chiamata LLM ✅ Fare invece: Inviare solo dati pre-elaborati (medie, totali, anomalie)

    NON fare: Chiedere all'AI di generare SQL dinamico sempre ✅ Fare invece: Template SQL con parametri (prepared statements)

    NON fare: Usare GPT-4 per query semplici ✅ Fare invece: Pattern matching + template (0 costi)

    NON fare: Lasciare all'AI troppa "libertà creativa" ✅ Fare invece: Temperature bassa + response template + validation

    ---

    🎯 Piano di Azione Concreto

    Settimana 1-2: Setup Foundation

    1. Crea tabelle database (migrations) 2. Seeding 30-40 template (JSON) per i 7 ambiti 3. Implementa IntentRouterService (pattern matching)

    Settimana 3: Services Core

    1. KnowledgeBaseService (template execution) 2. QueryNormalizationService (entity extraction) 3. CacheService (redis/database)

    Settimana 4: LLM Integration (Fallback)

    1. LLMFallbackService (OpenAI) 2. Prompt optimization (minimale) 3. Cost tracking

    Settimana 5: Controller & Frontend

    1. AiAssistantController (API) 2. Chat Widget (Blade component) 3. Integrazione in analyze.blade.php

    Settimana 6: Testing & Refinement

    1. User testing con operatori 2. Template refinement basato su feedback 3. Analytics dashboard (admin)

    ---

    📊 KPI di Successo

    Tecnici

  • ✅ 80%+ query gestite senza LLM
  • ✅ Latency media < 1s
  • ✅ Cache hit rate > 40%
  • ✅ Costi < $5/mese (50 utenti)
  • Business

  • ✅ Satisfaction rate > 85%
  • ✅ 60%+ utenti usano AI settimanalmente
  • ✅ 50%+ domande risolte autonomamente
  • Qualità

  • ✅ Zero allucinazioni
  • ✅ Risposte sempre verificabili
  • ✅ Feedback negativo < 10%
  • ---

    ✅ Conclusione

    La tua richiesta è chiarissima e condivisibile:

    Vuoi un AI Assistant che: 1. ✅ Sia contestualizzato per ambiti specifici 2. ✅ Usi una Knowledge Base preparata (80% risposte) 3. ✅ Chiami LLM solo quando necessario (20% risposte) 4. ✅ Abbia risposte deterministiche e verificabili 5. ✅ Costi prevedibili e bassi

    L'architettura proposta risolve tutti questi requisiti:

  • Pattern matching → Ambiti contestuali
  • Template JSON → Knowledge Base preparata
  • LLM fallback → AI solo quando serve
  • Response templates → Risposte deterministiche
  • Caching → Costi ottimizzati

Pronto per iniziare? 🚀

Analisi Codice

Blocco 1
3 Macchine i40
    ↓
Profili Import Configurabili (schema CSV dinamico)
    ↓
MachineOperationLog (raw_data JSON con campi variabili)
    ↓
Analyze View (filtri, KPI, grafici)
Blocco 2 sql
-- Campi in raw_data (dipende dal profilo macchina)
TotalPieces, NumberOfPlies, Quantity, etc.

-- Query tipo
SELECT SUM(CAST(JSON_UNQUOTE(JSON_EXTRACT(raw_data, '$.TotalPieces')) AS DECIMAL))
FROM machine_operations_log
WHERE machine_id = :machine_id
  AND timestamp BETWEEN :date_from AND :date_to
Blocco 3
📊 Produzione di {periodo}:

✅ Totale pezzi: {total_quantity}
📈 Operazioni: {total_operations}
⏱️ Durata media: {avg_duration}h

Rispetto alla media degli ultimi 30 giorni ({avg_30d} pezzi), 
sei {delta_percent}% {emoji_trend}.
Blocco 4
1. Verifica gaps timestamp > 30 min
   → "Fermo macchina rilevato dalle {start_time} alle {end_time}"

2. Verifica campi ErrorCode/AlarmCode in raw_data
   → "Errore registrato: {error_code} - {error_description}"

3. Verifica produzione < media - 20%
   → "Produzione sotto media del {delta}%"
   
4. Verifica Duration > avg + 2σ
   → "Operazioni più lente del solito (+{percent}%)"

5. Confronta con stesso giorno settimana scorsa
   → "Rispetto a {last_week_same_day}, produzione {comparison}"
Blocco 5
🔍 Analisi {periodo}:

⚠️ Problemi identificati:
1. {problema_1}
2. {problema_2}
...

💡 Cause probabili:
- {causa_1}
- {causa_2}

📊 Dati a supporto:
- Operazioni: {total_ops} (atteso: {expected_ops})
- Durata media: {avg_duration}h (atteso: {expected_duration}h)
Blocco 6 sql
SELECT 
  JSON_UNQUOTE(JSON_EXTRACT(raw_data, '$.UserLog')) as operator,
  COUNT(*) as operations,
  SUM(CAST(JSON_UNQUOTE(JSON_EXTRACT(raw_data, '$.TotalPieces')) AS DECIMAL)) as total_pieces,
  AVG(CAST(JSON_UNQUOTE(JSON_EXTRACT(raw_data, '$.Duration')) AS DECIMAL)) as avg_duration
FROM machine_operations_log
WHERE machine_id = :machine_id
  AND timestamp BETWEEN :date_from AND :date_to
GROUP BY operator
ORDER BY total_pieces DESC
Blocco 7
👥 Ranking Operatori ({periodo}):

🥇 1. {operator_1} - {pieces_1} pezzi ({ops_1} operazioni)
🥈 2. {operator_2} - {pieces_2} pezzi ({ops_2} operazioni)
🥉 3. {operator_3} - {pieces_3} pezzi ({ops_3} operazioni)

⚡ Insight:
{operator_1} è il più veloce con {pieces_per_hour} pezzi/ora 
(+{percent}% rispetto alla media di {avg_pieces_per_hour}).
Blocco 8
1. Calcola produzione_media_giornaliera (ultimi 30 giorni)
2. Calcola pezzi_già_prodotti (se order in corso)
3. Calcola pezzi_rimanenti = target - già_prodotti
4. Calcola giorni_lavorativi_rimanenti (escludi weekend)
5. Proiezione:
   giorni_necessari = pezzi_rimanenti / produzione_media_giornaliera
6. Confronto:
   margin = giorni_lavorativi_rimanenti - giorni_necessari
Blocco 9
📅 Previsione Completamento:

🎯 Target: {target_quantity} pezzi
✅ Già prodotti: {already_produced} pezzi
⏳ Rimanenti: {remaining} pezzi

📊 Proiezione:
- Media giornaliera: {avg_daily} pezzi/giorno
- Giorni necessari: {days_needed} giorni lavorativi
- Deadline: {deadline_date}
- Giorni disponibili: {days_available}

{verdict_emoji} {verdict_message}

💡 Scenario worst-case (-20% produzione):
   Servirebbero {worst_case_days} giorni → {worst_case_deadline}
Blocco 10
1. Query periodo 1
2. Query periodo 2
3. Calcola delta assoluto = valore1 - valore2
4. Calcola delta percentuale = (delta / valore2) * 100
5. Determina trend: 
   - Positivo (+): emoji 📈
   - Negativo (-): emoji 📉
   - Stabile (±3%): emoji ➡️
Blocco 11
⚖️ Confronto {periodo_1} vs {periodo_2}:

📊 {metrica_label}:
- {periodo_1}: {value_1} {unit}
- {periodo_2}: {value_2} {unit}

📈 Variazione: {delta_abs} {unit} ({delta_percent}% {emoji_trend})

{insight_message}
Blocco 12 sql
-- Tabelle metadata
machines_i40.profiles_config
import_profiles.columns, column_names
csv_imports (status, arrived_at)
Blocco 13
⚙️ Configurazione {machine_name}:

📋 Profili Attivi:
1. {profile_1_name}
   - Campi: {fields_count}
   - Ultimo import: {last_import_date}
   
2. {profile_2_name}
   - Campi: {fields_count}
   - Ultimo import: {last_import_date}

📊 Campi Disponibili (Profilo {active_profile}):
{field_1}, {field_2}, {field_3}, ...

📁 Ultimo Import CSV:
- File: {filename}
- Data: {import_date}
- Righe: {rows_processed}/{rows_total}
- Status: {status_badge}
Blocco 14
1. Analizza distribuzione temporale (orari/giorni più produttivi)
2. Confronta operatori (best practices da replicare)
3. Identifica anomalie ricorrenti (pattern errori)
4. Calcola OEE (Overall Equipment Effectiveness)
5. Identifica downtime patterns
6. Confronta con benchmark aziendali
Blocco 15
IF orario_picco_produzione EXISTS THEN
  → "Le operazioni tra {start_hour}:{end_hour} sono {percent}% più veloci"
  → Suggerimento: "Concentra lavorazioni complesse in questa fascia"

IF operatore_best_performer EXISTS THEN
  → "L'operatore {name} ha resa {percent}% superiore alla media"
  → Suggerimento: "Organizza training con {name} per condividere best practices"

IF downtime_medio > threshold THEN
  → "Fermi macchina: {downtime_hours}h/giorno in media"
  → Cause: {top_3_causes}
  → Suggerimento: "Implementa pre-staging materiali"
Blocco 16
🚀 Analisi Ottimizzazione ({periodo}):

📊 Situazione Attuale:
- Produttività media: {current_productivity}
- OEE: {oee_percent}%
- Downtime: {downtime_hours}h/giorno

💡 Opportunità Identificate:

1. {opportunity_1_title} (Impatto: +{impact_1}%)
   {description_1}
   ✅ Azione: {action_1}

2. {opportunity_2_title} (Impatto: +{impact_2}%)
   {description_2}
   ✅ Azione: {action_2}

3. {opportunity_3_title} (Impatto: +{impact_3}%)
   {description_3}
   ✅ Azione: {action_3}

📈 Potenziale Totale: +{total_impact}%
Blocco 17 php
// Input
$userQuery = "Qual è stata la produzione di ieri?";

// Normalization
$normalized = normalize($userQuery); 
// → "qual e stata la produzione di ieri"

// Pattern Matching
foreach ($patterns['AMBITO_PRODUZIONE'] as $regex) {
    if (preg_match($regex, $normalized, $matches)) {
        return [
            'ambito' => 'AMBITO_PRODUZIONE',
            'confidence' => 1.0, // Deterministico
            'entities' => extractEntities($matches),
            // entities: ['periodo' => 'ieri', 'metrica' => 'produzione']
        ];
    }
}
Blocco 18 php
// Estrazione periodo temporale
$periodMap = [
    'ieri' => [
        'date_from' => now()->subDay()->startOfDay(),
        'date_to' => now()->subDay()->endOfDay(),
        'label' => 'Ieri',
    ],
    'oggi' => [
        'date_from' => now()->startOfDay(),
        'date_to' => now()->endOfDay(),
        'label' => 'Oggi',
    ],
    'questa settimana' => [
        'date_from' => now()->startOfWeek(),
        'date_to' => now()->endOfWeek(),
        'label' => 'Questa Settimana',
    ],
    // ... altri 20-30 periodi
];

// Estrazione nomi operatori (fuzzy matching)
$operatorNames = getOperatorNamesFromDB($machineId);
$matchedOperator = findClosestMatch($userQuery, $operatorNames);

// Estrazione numeri
preg_match_all('/\d+/', $userQuery, $numbers);
// "5000 pezzi entro venerdì" → entities['quantity'] = 5000
Blocco 19 php
// Query database template
$template = DB::table('ai_knowledge_templates')
    ->where('ambito', $detectedAmbito)
    ->whereRaw('? REGEXP pattern', [$userQuery])
    ->first();

if ($template) {
    // Template trovato → Esegui senza LLM
    return executeTemplate($template, $entities, $machineId);
}
Blocco 20 json
{
  "template_id": "prod_daily",
  "pattern": "produzione di (ieri|oggi)",
  "sql_query": {
    "query": "SELECT SUM(...) FROM ...",
    "parameters": {
      "machine_id": ":machine_id",
      "date_from": ":date_from",
      "date_to": ":date_to"
    }
  },
  "response_template": "Produzione di {periodo}: {total} pezzi.",
  "enrichment": {
    "add_comparison": true,
    "compare_with": "avg_last_30_days"
  }
}
Blocco 21 php
// Prompt MINIMO (no schema completo)
$systemPrompt = "Sei assistente produzione. Macchina: {$machine->name}. Rispondi in italiano, max 3 frasi.";

// Context injection selettivo (solo dati necessari)
$context = [
    'avg_production_last_30d' => 1500, // Pre-calcolato
    'last_operation_timestamp' => '2025-10-22 10:30',
    'total_operations_today' => 12,
];

// Chiamata LLM con limite tokens
$response = OpenAI::chat()->create([
    'model' => 'gpt-4o-mini', // Modello più economico
    'messages' => [
        ['role' => 'system', 'content' => $systemPrompt],
        ['role' => 'user', 'content' => $userQuery],
    ],
    'temperature' => 0.2, // Basso = più deterministico
    'max_tokens' => 300, // Limita risposta
]);
Blocco 22
USER INPUT:
"Qual è stata la produzione di ieri?"

↓

STEP 1: Intent Router (Pattern Matching)
- Normalizza: "qual e stata la produzione di ieri"
- Match pattern: /produz(?:ione) di (ieri|oggi)/i
- Ambito rilevato: AMBITO_PRODUZIONE
- Confidence: 1.0
- Entities: { periodo: 'ieri', metrica: 'produzione' }

↓

STEP 2: Entity Extraction
- Periodo: 'ieri' → date_from: 2025-10-21 00:00, date_to: 2025-10-21 23:59
- Metrica: 'produzione' → campo_raw_data: 'TotalPieces'

↓

STEP 3: Cache Lookup
- Query hash: md5("produzione_ieri_machine_5_2025-10-21")
- Cache hit? NO (prima volta oggi)

↓

STEP 4: Template Lookup
- Template trovato: "prod_daily"
- Requires LLM: false

↓

STEP 5: SQL Execution
Blocco 23
**Result**: { total_quantity: 1850, total_operations: 23, avg_duration: 2.3 }

↓

STEP 6: Context Enrichment
- Carica media ultimi 30 giorni: 1750 pezzi
- Calcola delta: +100 pezzi (+5.7%)
- Emoji trend: 📈

↓

STEP 7: Response Formatting
Blocco 24

↓

STEP 8: Follow-up Suggestions
- "Confronta con l'altro ieri"
- "Mostrami il trend settimanale"
- "Chi ha prodotto di più ieri?"

↓

STEP 9: Save to Cache
- Salva risposta con TTL 1 ora
- Prossima richiesta identica → risposta istantanea da cache

↓

STEP 10: Save to Conversation History
- User message + Assistant response → DB
- Metadata: { ambito, template_id, execution_time_ms, used_llm: false }

↓

TOTAL TIME: ~200ms
TOTAL COST: $0 (no LLM)
Blocco 25
USER INPUT:
"Come posso migliorare la produttività?"

↓

STEP 1: Intent Router
- Ambito: AMBITO_OTTIMIZZAZIONE
- Confidence: 0.9

↓

STEP 2: Template Lookup
- Template: "optimization_suggestions"
- Requires LLM: true (per suggerimenti articolati)

↓

STEP 3: Data Analysis (pre-LLM)
- Analizza distribuzione oraria → Picco: 9-11am (+18%)
- Analizza operatori → Best: "Mario Rossi" (+25%)
- Analizza downtime → Media: 45 min/giorno

↓

STEP 4: LLM Call (con dati pre-elaborati)
Blocco 26

↓

STEP 5: LLM Response
Blocco 27

↓

TOTAL TIME: ~3s
TOTAL COST: ~$0.00012 (LLM usato)
Blocco 28
1. Pattern Matching Regex (Livello 1)
   → Identifica ambito
   → Estrae entità (periodo, metrica, nomi)
   → 70-80% query risolte QUI

2. Template Lookup (Livello 2)
   → Cerca template pre-costruito per ambito + pattern
   → Esegue SQL deterministico
   → Formatta risposta da template
   → 15-20% query risolte QUI

3. LLM Fallback (Livello 3)
   → Solo per query ambigue/complesse
   → Prompt minimale con dati pre-elaborati
   → 5-10% query risolte QUI
Blocco 29
STORICO PREPARATO (80% query):
- Database: 30-40 template JSON in ai_knowledge_templates
- Ogni template = pattern + SQL + response_template + enrichment_rules
- Risposte completamente deterministiche
- Arricchite con contesto (medie, confronti, trend)

AI GENERATIVA (20% query):
- Solo quando template non disponibile
- Prompt minimale (no schema completo)
- Context injection selettivo (dati pre-calcolati)
- Temperature bassa (0.2-0.3) per risposte coerenti