# ποΈ Sibilla β Architettura di Sistema > **Autore:** Sibilla Team > **Versione:** 0.2.0 > **Ultimo aggiornamento:** 18 Luglio 2026 --- ## Indice 1. [Visione d'insieme](#visione-dinsieme) 2. [Stack tecnologico](#stack-tecnologico) 3. [Componenti principali](#componenti-principali) 4. [Struttura del progetto](#struttura-del-progetto) 5. [Pipeline di ricerca](#pipeline-di-ricerca) 6. [Modello dati](#modello-dati) 7. [Flusso delle richieste](#flusso-delle-richieste) 8. [Strategia di fallback](#strategia-di-fallback) 9. [Sicurezza e sanitizzazione](#sicurezza-e-sanitizzazione) 10. [EstendibilitΓ ](#estendibilitΓ ) --- ## Visione d'insieme **Sibilla** Γ¨ un motore di ricerca AI self-hosted che emula l'esperienza di Perplexity, combinando: - **Ricerca web multi-engine** con fallback a cascata - **Sintesi LLM** tramite Google Gemini - **Query expansion** per migliorare il recall - **Fetching full-content** delle pagine per una sintesi accurata - **Interfaccia web** reattiva e mobile-first - **API REST** per consumo programmatico e skill vocali (Alexa) - **Server MCP** per integrazione con agent AI esterni ``` ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β SIBILLA SYSTEM β β β β ββββββββββββ ββββββββββββ ββββββββββββββββββββββββββββ β β β Browser β β curl / β β AI Agent (MCP Client) β β β β (Web UI) β β Alexa β β Claude/Gemini Desktop β β β ββββββ¬ββββββ ββββββ¬ββββββ βββββββββββββ¬βββββββββββββββ β β β β β β β β HTTP/HTML β REST/JSON β stdio JSON-RPC β β βΌ βΌ βΌ β β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β β Flask Application β β β β β β β β GET / β Web UI (index.html) β β β β POST /api/ask β Search pipeline (Web) β β β β GET/POST /api/v1/query β Search pipeline (API) β β β β GET /api/health β Health check β β β β β β β β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β β β β β _run_search_pipeline() β shared β β β β β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β β β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β β β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β β MCP Server (stdlib) β β β β β β β β tools: web_search(query) β Degoog β SearXNG β DDG β β β β tools: fetch_page(url) β PageFetcher β β β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β β β β βββββββββββββ βββββββββββββββββ ββββββββββββββββ ββββββββββ β β β Degoog β β SearXNG β β DuckDuckGo β β Gemini β β β β (primary) β β (secondary) β β (fallback) β β LLM β β β βββββββββββββ βββββββββββββββββ ββββββββββββββββ ββββββββββ β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ ``` --- ## Stack tecnologico | Livello | Tecnologia | Ruolo | |---------|-----------|-------| | **Web framework** | Flask 3.x | Application server, routing, API | | **LLM** | Google Gemini (gemini-2.5-flash) | Sintesi, query expansion | | **SDK LLM** | google-genai β₯ 1.0 | Client Python per Gemini | | **Search engines** | Degoog, SearXNG, DuckDuckGo | Ricerca web multi-source | | **HTML parsing** | BeautifulSoup 4 | Scraping e estrazione contenuti | | **Markdown** | Python-Markdown 3.5+ | Rendering Markdown β HTML | | **Frontend** | Vanilla HTML/CSS/JS | Interfaccia utente mobile-first | | **Testing** | Pytest | Unit e integration test | | **Linting** | Ruff | Code quality e formattazione | | **Protocol** | MCP (Model Context Protocol) | Integrazione agent AI esterni | --- ## Componenti principali ### 1. `app.py` β Flask Application Factory Entry point dell'app web. Utilizza il pattern **Application Factory** per creare un'istanza Flask configurabile. ```python def create_app(testing: bool = False) -> Flask: ``` **ResponsabilitΓ :** - Inizializzazione lazy dei servizi (Degoog, SearXNG, DuckDuckGo, PageFetcher, Gemini) - Routing degli endpoint (`/`, `/api/ask`, `/api/v1/query`, `/api/health`) - Pipeline di ricerca condivisa (`_run_search_pipeline`) - Gestione CORS per accesso cross-origin - Configurazione via variabili d'ambiente (`.env`) ### 2. `search.py` β Client di Ricerca Implementa tre backend di ricerca con interfaccia unificata: | Classe | Backend | Tipo | |--------|---------|------| | `DegoogClient` | Degoog (self-hosted) | Primario | | `SearXNGClient` | SearXNG (self-hosted) | Secondario | | `DuckDuckGoClient` | DuckDuckGo HTML | Fallback | | `PageFetcher` | HTTP + BeautifulSoup | Content extraction | Ogni client restituisce `SearchResponse` contenente `list[SearchResult]`. ### 3. `ai.py` β Gemini LLM Client Wrapper intorno all'SDK `google-genai` con due funzionalitΓ : - `synthesize()` β Sintesi di risposte basate su fonti - `rewrite_query()` β Generazione di varianti di query per migliorare il recall Gestione robusta degli errori con `_clean_error()`. ### 4. `markdown.py` β Conversione Markdown Due funzioni principali: - `render()` β Converte Markdown β HTML con sanitizzazione XSS - `strip_markdown()` β Converte Markdown β plain text per TTS (Alexa, Google Home) ### 5. `prompts.py` β Template dei Prompt Contiene i system prompt che guidano il comportamento di Gemini: - `SYNTHESIS_SYSTEM_PROMPT` β Prompt principale per la sintesi con citazioni - `FALLBACK_PROMPT` β Prompt quando non ci sono risultati di ricerca - `QUERY_REWRITE_PROMPT` β Prompt per la query expansion - `build_synthesis_prompt()` β Assembla il prompt completo con fonti ### 6. `mcp.py` β MCP Server Server Model Context Protocol su stdio che espone due tool: - `web_search(query)` β Ricerca web con fallback a cascata - `fetch_page(url)` β Estrazione contenuto pagina web ### 7. `cli.py` / `__main__.py` β Entry Point Avvio del server web Flask con configurazione via variabili d'ambiente. --- ## Struttura del progetto ``` sibilla/ βββ docs/ # π Documentazione β βββ ARCHITECTURE.md # Architettura di sistema (questo file) β βββ FLOW.md # Diagramma di flusso della pipeline β βββ API.md # Documentazione API REST β βββ WIKI.md # Wiki generale β βββ DEPLOYMENT.md # Guida al deployment β βββ CONTRIBUTING.md # Guida alla contribuzione β βββ src/sibilla/ # π¦ Codice sorgente β βββ __init__.py # Package init β βββ __main__.py # python -m sibilla β βββ app.py # Flask app factory + pipeline β βββ cli.py # CLI entry point β βββ search.py # Degoog, SearXNG, DDG, PageFetcher β βββ ai.py # GeminiClient (sintesi + query rewriting) β βββ markdown.py # Markdown β HTML + Markdown β plain text β βββ prompts.py # System prompt templates β βββ mcp.py # MCP server stdio β βββ templates/ # πΌοΈ Template HTML (Jinja2) β βββ index.html # Interfaccia di ricerca β βββ static/ # π¨ Asset statici β βββ style.css # Stili CSS (mobile-first, dark theme) β βββ app.js # Logica frontend (fetch, render, UX) β βββ favicon.svg # Icona del sito β βββ tests/ # π§ͺ Test β βββ __init__.py β βββ test_app.py # Test endpoint Flask β βββ test_ai.py # Test GeminiClient β βββ test_search.py # Test client di ricerca β βββ test_prompts.py # Test template prompt β βββ test_markdown.py # Test conversione Markdown β βββ pyproject.toml # Configurazione progetto Python βββ README.md # README principale βββ .gitignore ``` --- ## Pipeline di ricerca La pipeline di ricerca Γ¨ il cuore di Sibilla. Eseguita dalla funzione condivisa `_run_search_pipeline()`: ``` 1. INPUT: query utente + num_sources β 2. QUERY EXPANSION ββ Gemini genera 3 varianti della query originale ββ Output: [query_originale, variante1, variante2, variante3] β 3. MULTI-ENGINE SEARCH (cascading fallback) ββ Degoog: cerca con le prime 2 varianti β ββ Successo? β risultati aggregati β ββ Fallimento? β passa a SearXNG ββ SearXNG: cerca con le prime 2 varianti β ββ Successo? β risultati aggregati β ββ Fallimento? β passa a DuckDuckGo ββ DuckDuckGo: cerca con la query originale ββ Successo? β risultati aggregati ββ Fallimento? β fallback pure LLM β 4. DEDUPLICAZIONE ββ Elimina URL duplicati (set di seen_urls) ββ Ordine di prioritΓ mantenuto β 5. FETCH CONTENT ββ PageFetcher scarica fino a num_sources pagine ββ Estrazione testo con BeautifulSoup ββ Rimozione script, nav, ads, sidebar ββ Rate limiting: 300ms tra una fetch e l'altra β 6. PROMPT ASSEMBLY ββ build_synthesis_prompt() assembla: β ββ Contesto temporale (data di oggi) β ββ Domanda dell'utente β ββ Fonti numerate con titolo, URL, contenuto ββ Istruzioni per citazioni inline [1], [2], ... β 7. LLM SYNTHESIS ββ Gemini genera risposta con: β ββ Sistema: SYNTHESIS_SYSTEM_PROMPT β ββ Utente: prompt assemblato β ββ Temperatura: 0.4 (bassa per fedeltΓ alle fonti) β ββ Max token: 4096 ββ Output: SynthesisResult (answer, model, tokens_used, error) β 8. OUTPUT: JSON con answer, sources, metadata ``` --- ## Modello dati ### SearchResult ```python @dataclass class SearchResult: title: str # Titolo del risultato url: str # URL della pagina snippet: str # Snippet dal motore di ricerca full_content: str # Contenuto completo (dopo fetch) fetched: bool # Flag: contenuto scaricato? ``` ### SearchResponse ```python @dataclass class SearchResponse: query: str # Query originale results: list[SearchResult] # Lista risultati total_results: int # Totale risultati trovati engine: str # Motore utilizzato error: Optional[str] # Eventuale errore ``` ### SynthesisResult ```python @dataclass class SynthesisResult: answer: str # Testo della risposta model: str # Modello LLM usato tokens_used: int # Token consumati error: Optional[str] # Eventuale errore ``` ### Response JSON (`/api/ask`) ```json { "answer": "Testo in Markdown della risposta...", "html_answer": "
Versione HTML della risposta...
", "speakable_text": "Testo pulito per TTS...", "sources": [ { "index": 1, "title": "Titolo della fonte", "url": "https://example.com", "snippet": "Estratto della pagina..." } ], "model": "gemini-2.5-flash", "search_engine": "degoog", "tokens_used": 1234, "error": null } ``` ### Response JSON (`/api/v1/query`) β formato semplificato per API ```json { "query": "domanda originale", "answer": "Risposta in Markdown...", "speakable_text": "Risposta pulita per TTS...", "sources": [ {"index": 1, "title": "...", "url": "..."} ], "model": "gemini-2.5-flash", "search_engine": "degoog", "tokens_used": 1234 } ``` --- ## Flusso delle richieste ### Web UI (`GET /`) ``` Browser β GET / β Flask β render_template("index.html") ββ Jinja2 template HTML ββ Static assets (CSS, JS, favicon) Utente scrive query β POST /api/ask {query: "..."} β app.js fetch β render risposta ``` ### API REST (`GET/POST /api/v1/query`) ``` curl / Lambda / Alexa Skill β ββ GET /api/v1/query?q=domanda ββ POST /api/v1/query {"query": "domanda"} β βΌ _run_search_pipeline(query, num_sources=5) β βΌ JSON response con speakable_text ``` ### MCP Server (`sibilla-mcp`) ``` AI Agent (Claude/Gemini Desktop) β ββ stdio JSON-RPC β βΌ MCPServer.run() ββ tools/list β [{web_search}, {fetch_page}] ββ tools/call β esecuzione tool β risultato text ``` --- ## Strategia di fallback Sibilla implementa una **strategia di fallback a cascata** su tre livelli: ``` Livello 1: Degoog (primary) β ββ Disponibile? β Usa Degoog ββ Non disponibile? β Livello 2: SearXNG (secondary) β ββ Disponibile? β Usa SearXNG ββ Non disponibile? β Livello 3: DuckDuckGo (fallback) β ββ Disponibile? β Usa DuckDuckGo (HTML scraping) ββ Non disponibile? β Livello 4: Pure LLM (degraded mode) ββ Gemini risponde senza fonti, spiegando i limiti ``` **Vantaggi:** - Nessun single point of failure - DuckDuckGo non richiede server esterni (scraping HTML) - Pure LLM garantisce sempre una risposta (in degraded mode) --- ## Sicurezza e sanitizzazione ### Sanitizzazione HTML Il Markdown renderizzato viene sanitizzato per prevenire XSS: - **Allowlist di tag HTML:** solo tag sicuri (`h1-h6`, `p`, `strong`, `a`, ecc.) - **Allowlist di attributi:** `href`, `title`, `alt`, `src`, `class`, `id` - **Blocco `javascript:` URL** su `href` e `src` - **Nessun event handler** (`onclick`, `onerror`, ecc.) - **Strip di `