# πŸŽ™οΈ Sibilla Alexa Skill β€” Brainstorming & Architettura > **Versione documento:** 0.1.0 > **Stato:** 🧠 Brainstorming / Pianificazione > **Autore:** Sibilla Team > **Data:** 18 Luglio 2026 --- ## Indice 1. [Panoramica: Come funziona una Skill Alexa](#panoramica) 2. [Architettura di Sistema](#architettura-di-sistema) 3. [Flusso dei Dati](#flusso-dei-dati) 4. [Requisiti](#requisiti) 5. [Interaction Model](#interaction-model) 6. [Lambda Function Design](#lambda-function-design) 7. [SSML e Voce Italiana](#ssml-e-voce-italiana) 8. [Sicurezza](#sicurezza) 9. [Costi](#costi) 10. [Piano di Implementazione](#piano-di-implementazione) 11. [Rischi e Mitigazioni](#rischi-e-mitigazioni) 12. [Riferimenti](#riferimenti) --- ## Panoramica ### Cos'Γ¨ una Skill Alexa? Una **Skill Alexa** Γ¨ un'applicazione vocale che permette agli utenti di interagire con i dispositivi Amazon Echo (Echo Dot, Echo Show, ecc.) usando solo la voce. È l'equivalente di una "app" per lo store di Alexa. ### Come funziona (versione semplificata) ``` Tu dici: "Alexa, chiedi a Sibilla qual Γ¨ l'ultimo film sull'Odissea" ↓ Alexa: Converte voce β†’ testo, capisce l'intenzione (intent) ↓ AWS Lambda: Riceve un JSON con la tua domanda, chiama l'API Sibilla ↓ Sibilla API: Cerca nel web, sintetizza con Gemini, restituisce la risposta ↓ AWS Lambda: Prende `speakable_text`, lo avvolge in SSML, risponde ad Alexa ↓ Alexa: Converte testo β†’ voce e te lo dice ``` ### PerchΓ© Γ¨ giΓ  quasi pronto Sibilla **ha giΓ  tutto il necessario** per una skill Alexa: | Cosa serve | Sibilla ce l'ha? | |-----------|-----------------| | API REST per query testuali | βœ… `GET/POST /api/v1/query` | | CORS abilitato | βœ… `Access-Control-Allow-Origin: *` | | Testo pulito per TTS | βœ… Campo `speakable_text` (strip_markdown) | | Risposta in italiano | βœ… Prompt e risposte in italiano | | Open source, self-hosted | βœ… Nessun costo API di terze parti | L'unica cosa da costruire Γ¨ il "ponte" tra Alexa e Sibilla: una **Lambda AWS** che fa da intermediario. --- ## Architettura di Sistema ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ SIBILLA ALEXA SKILL β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Alexa │────▢│ Alexa Cloud │────▢│ AWS Lambda β”‚ β”‚ β”‚ β”‚ Device β”‚ β”‚ (Amazon) β”‚ β”‚ (ask-sibilla) β”‚ β”‚ β”‚ β”‚ │◀────│ │◀────│ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ HTTPS β”‚ β”‚ β”‚ β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ Sibilla Server β”‚ β”‚ β”‚ β”‚ /api/v1/query β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ### Componenti | Componente | Ruolo | Hosting | Costo | |-----------|-------|---------|-------| | **Alexa Device** | Input/output vocale | Casa dell'utente | Hardware | | **Alexa Cloud** | Speech-to-text, routing, TTS | Amazon (gratuito) | $0 | | **AWS Lambda** | Logica della skill (bridge) | AWS | Gratuito (1M richieste/mese) | | **Sibilla Server** | Ricerca web + sintesi AI | Tuo server / VPS | ~€5-10/mese (VPS) | ### PerchΓ© AWS Lambda? 1. **Gratuito** β€” 1 milione di richieste/mese nel free tier, piΓΉ che sufficiente 2. **Nativamente integrato con Alexa** β€” l'ASK SDK Γ¨ pensato per Lambda 3. **Nessun server da gestire** β€” solo codice Python 4. **ScalabilitΓ  automatica** β€” se 100 persone chiamano Sibilla contemporaneamente, Lambda scala da solo ### ⚠️ CriticitΓ : Il Problema del Timeout Alexa impone un **limite invalicabile di 8 secondi** tra la richiesta e la risposta. Se la Lambda non risponde entro 8s, la connessione viene troncata e l'utente sente: "Mi dispiace, la skill non risponde." La pipeline Sibilla attuale puΓ² facilmente superare questo limite: ``` Query Expansion (LLM) ~1-2s Web Search (Degoog/DDG) ~1-2s Fetch contenuti pagine ~2-4s ← il collo di bottiglia! Sintesi LLM (Gemini) ~2-4s ───────────────────────────────── TOTALE ~6-12s ← spesso oltre gli 8s! ``` ### 🚫 PerchΓ© NON si puΓ² fare async ("fire and forget") L'idea di "lancio la richiesta, chiudo, e quando Sibilla risponde chiamo Alexa" **NON funziona** con le skill Alexa. Il modello Γ¨ **esclusivamente request-response sincrono**. Non esiste un meccanismo per cui un server esterno "spinge" una risposta vocale ad Alexa mentre l'utente ascolta. L'unica via "async" ufficiale sono le **Proactive Events API** (notifiche), ma: - L'utente deve **abilitare le notifiche** manualmente nell'app Alexa - La risposta arriva come **anello giallo** sul dispositivo, non come voce - L'utente deve dire *"Alexa, cosa c'Γ¨ di nuovo?"* per sentirla - Risultato: **3 turni** (domanda β†’ attesa β†’ notifica β†’ "cosa c'Γ¨ di nuovo?" β†’ risposta) - Amazon **rifiuta la certificazione** se usi le notifiche come risposta a domande **Conclusione:** L'unica strada Γ¨ **stare dentro gli 8 secondi**. Ecco come. **Soluzione: Progressive Response (riempie il silenzio) + Fast Path (riduce i tempi)** (vedi sezione dedicata sotto). --- ## Flusso dei Dati ### Step-by-step di una richiesta ``` 1️⃣ UTENTE PARLA "Alexa, chiedi a Sibilla cosa sono i buchi neri" 2️⃣ ALEXA CLOUD β†’ SPEECH-TO-TEXT { "intent": "AskSibillaIntent", "slots": { "query": { "value": "cosa sono i buchi neri" } }, "locale": "it-IT" } 3️⃣ ALEXA CLOUD β†’ LAMBDA (JSON POST) https://lambda.aws.amazon.com/ask-sibilla 4️⃣ LAMBDA β†’ SIBILLA API GET https://mioserver.com/api/v1/query?q=cosa+sono+i+buchi+neri Risposta in ~3-5 secondi: { "query": "cosa sono i buchi neri", "answer": "I **buchi neri** sono regioni dello spaziotempo...", "speakable_text": "I buchi neri sono regioni dello spaziotempo...", "sources": [ {"index": 1, "title": "Buco nero - Wikipedia", "url": "..."}, {"index": 2, "title": "Cosa sono i buchi neri - INAF", "url": "..."} ], "model": "gemini-2.5-flash", "tokens_used": 1200 } 5️⃣ LAMBDA β†’ ALEXA CLOUD (JSON Response) { "version": "1.0", "response": { "outputSpeech": { "type": "SSML", "ssml": "I buchi neri sono regioni dello spaziotempo con una gravitΓ  cosΓ¬ intensa che nulla, nemmeno la luce, puΓ² sfuggire. Ho trovato 2 fonti principali su Wikipedia e INAF." }, "shouldEndSession": true } } 6️⃣ ALEXA CLOUD β†’ TEXT-TO-SPEECH Alexa dice (in italiano, voce naturale): "I buchi neri sono regioni dello spaziotempo con una gravitΓ  cosΓ¬ intensa che nulla, nemmeno la luce, puΓ² sfuggire." ``` ### Gestione degli errori ``` Se Sibilla non risponde entro 5 secondi: Alexa dice: "Mi dispiace, Sibilla non Γ¨ raggiungibile in questo momento. Riprova piΓΉ tardi." Se tutti i motori di ricerca sono down (HTTP 502): Alexa dice: "Non riesco a cercare sul web in questo momento. Controlla che i motori di ricerca siano attivi." Se la query Γ¨ vuota: Alexa dice: "Cosa vuoi chiedere a Sibilla?" (riapre la sessione per riprovare) ``` --- ## Requisiti ### Account e strumenti gratuiti | Requisito | Come ottenerlo | Costo | |-----------|---------------|-------| | **Amazon Developer Account** | [developer.amazon.com](https://developer.amazon.com) | Gratuito | | **AWS Account** | [aws.amazon.com](https://aws.amazon.com) | Gratuito (free tier) | | **ASK CLI** | `npm install -g ask-cli` | Gratuito | | **Python 3.10+** | GiΓ  installato | Gratuito | | **Sibilla Server** | GiΓ  avviato su VPS/home server | Esistente | ### Dipendenze Python per la Lambda ``` ask-sdk-core # Alexa Skills Kit SDK (handler, intent, slot) requests # Per chiamare l'API Sibilla (o urllib3 built-in) boto3 # AWS SDK (se serve logging/DynamoDB) ``` File `requirements.txt` della Lambda: ``` ask-sdk-core>=1.0 ``` > **Nota:** `requests` puΓ² essere evitato usando `urllib.request` (built-in). Meno dipendenze = Lambda piΓΉ leggera. ### Configurazione Sibilla Server Il server Sibilla deve essere: - **Raggiungibile da internet** (IP pubblico o dominio con HTTPS) - **Connessione stabile** (la Lambda ha timeout 8 secondi) - **HTTPS consigliato** (anche se tecnicamente Lambda puΓ² chiamare HTTP) --- ## Interaction Model L'Interaction Model definisce **cosa** l'utente puΓ² dire e **come** Alexa lo interpreta. Si configura in un file JSON. ### Intents Un **Intent** Γ¨ un'azione che l'utente vuole compiere. #### 1. `AskSibillaIntent` β€” Principale L'utente fa una domanda a Sibilla. Supporta sia domanda libera (`query`) che un topic opzionale (`topic`). **Utterances (frasi di esempio):** ``` chiedi a Sibilla {query} domanda a Sibilla {query} cerca con Sibilla {query} interroga l'oracolo {query} cosa dice Sibilla su {query} sibilla cosa sai di {query} sibilla parlami di {query} sibilla {query} ``` **Slots:** | Slot | Tipo | Obbligatorio | Descrizione | |------|------|:---:|-------------| | `query` | `AMAZON.SearchQuery` | βœ… | La domanda completa dell'utente | `AMAZON.SearchQuery` Γ¨ uno slot type built-in di Amazon che cattura frasi intere β€” perfetto per domande libere. #### 2. `AMAZON.HelpIntent` β€” Built-in Attivato quando l'utente chiede aiuto: ``` aiuto cosa puoi fare come funzioni ``` **Risposta suggerita:** > "Sono Sibilla, l'oracolo AI. Puoi chiedermi qualsiasi cosa e io cercherΓ² nel web per darti una risposta. Prova a dirmi 'chiedi a Sibilla qual Γ¨ l'ultimo film sull'Odissea' o semplicemente 'Sibilla, cosa sono i raggi cosmici?'." #### 3. `AMAZON.StopIntent` / `AMAZON.CancelIntent` β€” Built-in ``` stop basta esci cancella ``` **Risposta suggerita:** > "Va bene, alla prossima consultazione!" #### 4. `AMAZON.FallbackIntent` β€” Built-in Quando Alexa non capisce cosa l'utente vuole (nessun intent match). **Risposta suggerita:** > "Non ho capito. Puoi ripetere la domanda per Sibilla?" ### Skill Manifest (`skill.json`) ```json { "manifest": { "publishingInformation": { "locales": { "it-IT": { "name": "Sibilla β€” L'Oracolo AI", "summary": "Interroga Sibilla, l'oracolo AI che cerca nel web e risponde alle tue domande.", "description": "Sibilla Γ¨ un assistente AI che cerca nel web in tempo reale...", "examplePhrases": [ "Alexa, chiedi a Sibilla qual Γ¨ l'ultimo film sull'Odissea", "Alexa, domanda a Sibilla cosa sono i buchi neri", "Alexa, sibilla parlami delle piramidi" ], "keywords": ["ricerca", "intelligenza artificiale", "web", "oracolo"] } } }, "apis": { "custom": { "endpoint": { "uri": "arn:aws:lambda:eu-west-1:123456789:function:ask-sibilla" }, "interfaces": [] } }, "permissions": [] } } ``` --- ## ⚑ Fast Path + Progressive Response β€” La Soluzione Completa Due tecniche che insieme risolvono il problema: **Progressive Response** (riempie il silenzio) e **Fast Path** (riduce i tempi sotto gli 8s). ### Progressive Response: riempire il silenzio Mentre la Lambda aspetta la risposta da Sibilla, l'utente sentirebbe **silenzio**. Con le Progressive Responses, la Lambda puΓ² dire ad Alexa di iniziare subito a parlare: ``` 0.0s Lambda riceve la richiesta 0.2s Lambda invia Progressive Response β†’ Alexa dice: "Sto cercando..." πŸ”Š └─ L'utente sente subito qualcosa, l'attesa Γ¨ tollerabile 0.3s Lambda chiama Sibilla FAST PATH 4.0s Sibilla risponde 4.2s Lambda restituisce risposta finale β†’ Alexa dice la risposta πŸ”Š ``` Richiede il permesso `alexa::async_event:send_progressive_response` nel manifest. ### Fast Path: endpoint ottimizzato su Sibilla Per rispettare il limite degli 8 secondi, serve un **endpoint ottimizzato** che salti i passaggi piΓΉ lenti della pipeline: ### Cosa va aggiunto a Sibilla: `GET /api/v1/voice-query` ``` Pipeline FAST (vocale): 1. Web Search (Degoog/DDG) ~1-2s 2. Sintesi LLM solo su snippet ~2-3s ← NO fetch pagine complete! ───────────────────────────────────── TOTALE ~3-5s βœ… dentro gli 8s! Pipeline STANDARD (web): 1. Query Expansion (LLM) ~1-2s 2. Web Search (Degoog/DDG) ~1-2s 3. Fetch contenuti pagine ~2-4s ← collo di bottiglia 4. Sintesi LLM su contenuti ~2-4s ───────────────────────────────────── TOTALE ~6-12s ❌ troppo lento per voce ``` **Differenza chiave:** l'endpoint vocale usa solo gli **snippet** (i riassunti forniti dal motore di ricerca) invece di scaricare le pagine web complete. Perde un po' di profonditΓ  ma guadagna velocitΓ . ### Prompt TTS-optimized ```python VOICE_SYSTEM_PROMPT = """\ Sei Sibilla, un assistente vocale AI. Rispondi a voce in modo discorsivo e naturale, come se stessi parlando con una persona. Regole fondamentali: - Usa frasi brevi e chiare (max 3 frasi per concetto) - NON usare elenchi puntati, tabelle, URL, markdown, asterischi - NON nominare le fonti nel testo ("secondo Wikipedia...") β€” le fonti sono gestite a parte - Se ci sono date, numeri o nomi propri, pronunciali in italiano - Risposte entro 400-500 caratteri; se l'argomento Γ¨ complesso, fai un riassunto """ ``` --- ## Lambda Function Design ### Struttura del codice ``` lambda/ β”œβ”€β”€ lambda_function.py # Entry point (handler principale) β”œβ”€β”€ sibilla_client.py # Client HTTP per chiamare l'API Sibilla β”œβ”€β”€ speech_builder.py # Costruzione risposte SSML β”œβ”€β”€ requirements.txt # ask-sdk-core └── .env # (opzionale) SIBILLA_API_URL ``` ### lambda_function.py β€” Schema ```python # Pseudo-codice della struttura from ask_sdk_core.skill_builder import SkillBuilder from ask_sdk_core.handler_input import HandlerInput from ask_sdk_core.dispatch_components import AbstractRequestHandler from ask_sdk_core.utils import get_slot_value from ask_sdk_model import Response from ask_sdk_model.ui import SsmlOutputSpeech import ask_sdk_core.utils as ask_utils # ── Costanti ──────────────────────────────────────────── SIBILLA_URL = "https://tuo-server.com/api/v1/voice-query" # endpoint FAST TIMEOUT = 5 # secondi (max Alexa: 8s, teniamo margine) FALLBACK_LONG_ANSWER = 500 # caratteri # ── Handlers ─────────────────────────────────────────── class AskSibillaIntentHandler(AbstractRequestHandler): def can_handle(self, handler_input): return ask_utils.is_intent_name("AskSibillaIntent")(handler_input) def handle(self, handler_input): # 1. Estrai la query query = get_slot_value(handler_input, "query") if not query: return speak(handler_input, "Cosa vuoi chiedere a Sibilla?", keep_open=True) # 2. Invia SUBITO un "thinking sound" (Progressive Response) # cosΓ¬ l'utente non sente il silenzio durante l'attesa send_progressive_response(handler_input, "Sto cercando...") # 3. Chiama l'endpoint vocale FAST di Sibilla try: result = call_sibilla_voice(query) except TimeoutError: return speak(handler_input, "Mi dispiace, la ricerca sta impiegando troppo tempo. " "Prova con una domanda piΓΉ specifica.") except ConnectionError: return speak(handler_input, "Sibilla non Γ¨ raggiungibile in questo momento. " "Controlla che il server sia acceso e riprova.") # 4. Prendi il testo parlato speakable = result["speakable_text"] # 5. Se troppo lungo, tronca con garbo if len(speakable) > FALLBACK_LONG_ANSWER: speakable = speakable[:FALLBACK_LONG_ANSWER] + "." speakable += " La risposta completa Γ¨ molto lunga. Puoi chiedermi di approfondire." # 6. Aggiungi nota sulle fonti (solo numero, niente URL) if result.get("sources"): n = len(result["sources"]) fonti_principali = ", ".join( s["title"].split(" - ")[0] for s in result["sources"][:2] ) speakable += ( f" " f"Ho consultato {n} fonti, tra cui {fonti_principali}." ) # 7. Restituisci risposta SSML return speak(handler_input, speakable) # Registra tutti gli handler sb = SkillBuilder() sb.add_request_handler(AskSibillaIntentHandler()) sb.add_request_handler(HelpIntentHandler()) sb.add_request_handler(CancelOrStopIntentHandler()) sb.add_request_handler(FallbackIntentHandler()) lambda_handler = sb.lambda_handler() ``` ### sibilla_client.py β€” Schema ```python import json import urllib.request import urllib.parse import ssl from typing import Optional SIBILLA_URL = "https://tuo-server.com/api/v1/query" TIMEOUT = 6 def call_sibilla_voice(query: str) -> dict: """Chiama l'endpoint FAST di Sibilla per uso vocale.""" params = urllib.parse.urlencode({"q": query}) url = f"{SIBILLA_URL}?{params}" req = urllib.request.Request(url, method="GET") # API Key per sicurezza (obbligatoria in produzione!) req.add_header("Authorization", f"Bearer {SIBILLA_API_KEY}") ctx = ssl.create_default_context() with urllib.request.urlopen(req, timeout=TIMEOUT, context=ctx) as resp: data = json.loads(resp.read().decode()) return data def send_progressive_response(handler_input, text: str): """Invia un messaggio vocale immediato mentre la ricerca Γ¨ in corso. Questa Γ¨ una Progressive Response β€” Alexa la riproduce subito, prima ancora che la Lambda abbia finito. Essenziale per non far sentire il silenzio all'utente durante l'attesa. Richiede il permesso 'alexa::async_event:send_progressive_response' nel manifest della skill. """ import ask_sdk_model_interfaces as ifaces request_envelope = handler_input.request_envelope directive = ifaces.connections.SendRequestDirective( name="ProgressiveResponse", payload={ "speech": f"{text}" }, token=request_envelope.request.request_id, ) # Nota: la directive va inviata tramite l'API Alexa, non come # risposta diretta. Richiede una chiamata HTTP separata usando # l'API endpoint dal contesto della richiesta. ``` ### speech_builder.py β€” Schema ```python from ask_sdk_model.ui import SsmlOutputSpeech from ask_sdk_model import Response from ask_sdk_core.handler_input import HandlerInput def speak(handler_input: HandlerInput, text: str, keep_open: bool = False) -> Response: """Costruisce una risposta Alexa con SSML.""" ssml = f"{escape_ssml(text)}" return ( handler_input.response_builder .speak(ssml) .set_should_end_session(not keep_open) .response ) def escape_ssml(text: str) -> str: """Escapa caratteri speciali per SSML.""" return ( text.replace("&", "&") .replace("<", "<") .replace(">", ">") .replace('"', """) .replace("'", "'") ) ``` --- ## SSML e Voce Italiana ### PerchΓ© SSML? SSML (Speech Synthesis Markup Language) permette di controllare **come** Alexa parla: - Pause naturali tra frasi - Enfasi su parole importanti - Pronuncia corretta di termini tecnici o stranieri ### Tag SSML utili per Sibilla ```xml I buchi neri sono regioni dello spaziotempo con una gravitΓ  cosΓ¬ intensa che nulla, nemmeno la luce, puΓ² sfuggire. Attenzione: questa Γ¨ una teoria scientifica. Il modello Gemini 2.5 Flash ha analizzato le fonti. Ho consultato 3 fonti. ``` ### Strategia per Sibilla 1. **`speakable_text` Γ¨ giΓ  pulito** β€” niente markdown, URL, grassetti. Solo testo naturale. 2. **Aggiungere pausa prima delle fonti** β€” `` prima di "Ho consultato X fonti." 3. **Risposte troppo lunghe** β€” troncare a ~500 caratteri e suggerire di approfondire 4. **Numeri e date** β€” giΓ  in formato parlato dal LLM ### Voce italiana Alexa supporta nativamente l'italiano con voci naturali (Carla, Giorgio, Bianca). La skill verrΓ  configurata con `locale: it-IT`. --- ## Sicurezza ### Comunicazione Lambda ↔ Sibilla | Area | Rischio | Mitigazione | |------|---------|-------------| | **Esposizione API** | Chiunque puΓ² chiamare `/api/v1/query` | API key condivisa (header `Authorization: Bearer `) | | **HTTPS** | Dati in chiaro tra Lambda e Sibilla | Obbligatorio in produzione (Let's Encrypt gratuito) | | **DDoS** | Troppe richieste crashano il server | Rate limiting su Sibilla (Flask-Limiter) | | **Timeout** | Sibilla impiega >8s a rispondere | Timeout Lambda a 7s, graceful degradation | | **Injection** | Input malevolo nella query | Sanitizzazione giΓ  in Sibilla + strip su Lambda | ### API Key su Sibilla (da aggiungere) ```python # In app.py β€” middleware di autenticazione API @app.before_request def require_api_key(): if request.path.startswith("/api/v1/"): api_key = request.headers.get("Authorization", "").replace("Bearer ", "") if api_key != os.environ.get("SIBILLA_API_KEY", ""): return jsonify({"error": "Unauthorized"}), 401 ``` Poi la Lambda passerΓ  l'API key nell'header. --- ## Costi ### Stima mensile (uso personale / familiare) | Servizio | Uso stimato | Costo | |----------|-------------|-------| | **Alexa Cloud** (speech-to-text, TTS) | Gratuito | $0 | | **AWS Lambda** | ~1,000 chiamate/mese | $0 (free tier: 1M) | | **Sibilla Server** (VPS 2GB) | Hetzner / DigitalOcean | ~€5-10/mese | | **Gemini API** (2.5 Flash) | ~1,000 query/mese | $0 (free tier ampio) | | **TOTALE** | | **~€5-10/mese** (solo VPS) | > Se il server Sibilla Γ¨ giΓ  in esecuzione (come ora), il costo aggiuntivo per la skill Alexa Γ¨ **$0**. --- ## Piano di Implementazione ### Fase 1 β€” Prototipo locale (1-2 ore) - [ ] Installare ASK CLI: `npm install -g ask-cli` - [ ] Configurare credenziali AWS: `ask configure` - [ ] Creare skill da template: `ask new --skill-name sibilla-skill` - [ ] Definire interaction model (intent + utterances) nel developer console - [ ] Scrivere `lambda_function.py` con handler minimale (solo `AskSibillaIntent`) - [ ] Scrivere `sibilla_client.py` che chiama `localhost:5000` (per test) - [ ] Testare con `ask dialog` (simulatore testuale) ### Fase 2 β€” Deploy su AWS (1 ora) - [ ] Modificare `sibilla_client.py` per puntare al server pubblico - [ ] Caricare Lambda con `ask deploy` - [ ] Testare su dispositivo Echo reale: "Alexa, chiedi a Sibilla..." ### Fase 3 β€” Raffinamento (1-2 ore) - [ ] Aggiungere gestione timeout e retry - [ ] Aggiungere SSML con pause naturali - [ ] Gestire risposte troppo lunghe - [ ] Aggiungere intent di follow-up ("approfondisci", "fonti") - [ ] Aggiungere API key su Sibilla per sicurezza ### Fase 4 β€” Pubblicazione (opzionale, 2-4 ore) - [ ] Tradurre descrizione e keywords per lo store Alexa - [ ] Creare icone (108x108 e 512x512) - [ ] Scrivere privacy policy (richiesta per la pubblicazione) - [ ] Sottoporre per certificazione Amazon --- ## Rischi e Mitigazioni | Rischio | ProbabilitΓ  | Impatto | Mitigazione | |--------|:---:|:---:|-------------| | **Timeout Alexa (8s invalicabile)** β€” pipeline standard ~6-12s | **Alta** | **Critico** | Fast Path vocale (salta fetch pagine), Progressive Responses, timeout Lambda a 5s | | **Sibilla server down** | Bassa | Alto | Health check, messaggio vocale di cortesia, retry | | **Gemini API rate limit** | Bassa | Medio | Caching risposte frequenti, query expansion giΓ  limita | | **Risposte non adatte al TTS** (troppo tecniche) | Media | Medio | `speakable_text` giΓ  pulito; prompt di sistema puΓ² enfatizzare risposte discorsive | | **Skill non certificata da Amazon** | Media | Basso | Solo uso personale (beta testing), la certificazione Γ¨ opzionale | | **VPS non raggiungibile da Lambda** | Bassa | Alto | Dominio pubblico, HTTPS, IP statico o DDNS | --- ## Differenze dalla Web UI | Aspetto | Web UI (`/`) | Skill Alexa | |---------|-------------|-------------| | **Input** | Testo scritto (textarea) | Voce β†’ testo (Alexa) | | **Output** | HTML con markdown renderizzato, fonti cliccabili | Solo voce (TTS), niente fonti visibili | | **Lunghezza risposta** | Illimitata | ~500 caratteri parlati (poi "chiedimi di approfondire") | | **Fonti** | Link cliccabili, snippet | "Ho consultato 3 fonti: Wikipedia, INAF..." | | **Conversazione** | Singola domanda | Potenziale multi-turno (follow-up) | | **Latenza percepita** | Spinner visivo | Silenzio β†’ attesa snervante β†’ serve thinking sound | | **Errori** | Messaggio a schermo | Messaggio vocale + suggerimento di riprovare | ### Cosa aggiungere al prompt di sistema per la skill vocale? Attualmente Sibilla usa prompt generici. Per la skill Alexa, potremmo aggiungere un **prompt TTS-optimized**: ``` Sei Sibilla, un assistente vocale AI. Rispondi in modo discorsivo e naturale, come se stessi parlando con una persona. Usa frasi brevi e chiare. Evita elenchi puntati, tabelle, URL, e gergo eccessivamente tecnico. Se la risposta Γ¨ molto lunga, dai prima un riassunto di 2-3 frasi, poi offri di approfondire. ``` Questo prompt verrebbe passato via header o parametro all'API, oppure si puΓ² creare un endpoint dedicato `/api/v1/voice-query`. --- ## Riferimenti ### Documentazione Ufficiale - [Alexa Skills Kit SDK for Python](https://developer.amazon.com/en-US/docs/alexa/alexa-skills-kit-sdk-for-python/overview.html) - [ASK CLI Command Reference](https://developer.amazon.com/en-US/docs/alexa/smapi/ask-cli-command-reference.html) - [SSML Reference](https://developer.amazon.com/en-US/docs/alexa/custom-skills/speech-synthesis-markup-language-ssml-reference.html) - [Skill Manifest Schema](https://developer.amazon.com/en-US/docs/alexa/smapi/skill-manifest.html) - [Request/Response JSON Format](https://developer.amazon.com/en-US/docs/alexa/custom-skills/request-and-response-json-reference.html) ### Tutorial Consigliati - [Develop Your First Skill (Python)](https://developer.amazon.com/en-US/docs/alexa/alexa-skills-kit-sdk-for-python/develop-your-first-skill.html) - [Build an Alexa Skill with AWS Lambda](https://developer.amazon.com/en-US/docs/alexa/custom-skills/host-a-custom-skill-as-an-aws-lambda-function.html) --- ## Domande Aperte per la prossima fase 1. **Sibilla sarΓ  self-hosted su VPS o su rete domestica?** Se domestica, serve IP pubblico/DDNS. 2. **Vuoi pubblicare la skill sullo store Alexa o solo uso personale?** Cambia i requisiti di privacy e certificazione. 3. **Serve supporto multi-turno?** Es: "Alexa, approfondisci le fonti" dopo la prima risposta. 4. **OK a creare l'endpoint fast-path `/api/v1/voice-query` su Sibilla?** Salta il fetch delle pagine e usa solo snippet + sintesi LLM veloce. 5. **Serve caching per domande frequenti?** Riduce latenza e costi Gemini. 6. **Quale authentication per la API?** L'API key via header `Authorization: Bearer ` Γ¨ la soluzione piΓΉ semplice per il caso d'uso Lambda β†’ Sibilla. ## Prossimi Passi Concreti ### Su Sibilla (backend) 1. Aggiungere endpoint `GET /api/v1/voice-query` β€” fast path senza fetch pagine 2. Aggiungere prompt TTS-optimized per risposte vocali 3. Aggiungere autenticazione API key su `/api/v1/*` ### Sulla Skill Alexa (nuovo progetto) 1. `ask new` β€” creare il progetto skill da template Python 2. Configurare interaction model con `AskSibillaIntent` + `AMAZON.SearchQuery` 3. Scrivere `lambda_function.py` con handler, Progressive Response, chiamata a `/api/v1/voice-query` 4. Deploy con `ask deploy` e test su Echo Dot