Componenti¶
Questo documento descrive i componenti principali di SearchMuse, le loro responsabilità, le interfacce e le interazioni.
Modelli di Dominio¶
SearchQuery¶
Rappresenta una query di ricerca fornita dall'utente.
@dataclass(frozen=True)
class SearchQuery:
query: str
max_iterations: int
language: str
metadata: dict[str, Any]
Responsabilità: Encapsulare i parametri della ricerca iniziale.
SearchResult¶
Rappresenta un risultato di ricerca da una fonte esterna.
@dataclass(frozen=True)
class SearchResult:
title: str
url: str
snippet: str
source: str
retrieved_at: datetime
Responsabilità: Mantenere un singolo risultato recuperato da una ricerca.
Citation¶
Rappresenta una citazione con metadata completo.
@dataclass(frozen=True)
class Citation:
source_url: str
source_title: str
excerpt: str
page_number: int | None
retrieved_at: datetime
Responsabilità: Tracciare le fonti utilizzate per le risposte.
IterationResult¶
Rappresenta il risultato di una singola iterazione di ricerca.
@dataclass(frozen=True)
class IterationResult:
iteration_number: int
refined_query: str
search_results: list[SearchResult]
llm_analysis: str
citations_used: list[Citation]
Responsabilità: Aggregare i risultati di ogni iterazione.
ResearchSession¶
Rappresenta una sessione di ricerca completa.
@dataclass(frozen=True)
class ResearchSession:
session_id: str
initial_query: SearchQuery
iterations: list[IterationResult]
final_answer: str
total_sources: int
created_at: datetime
completed_at: datetime | None
Responsabilità: Aggregare tutti i dati di una sessione di ricerca.
Interfacce (Ports)¶
LLMPort¶
Interfaccia per i modelli linguistici.
class LLMPort(Protocol):
def generate(self, prompt: str) -> str:
"""Genera testo basato sul prompt fornito."""
...
def summarize(self, text: str, max_length: int) -> str:
"""Riassume un testo."""
...
def refine_query(self, current_query: str, context: str) -> str:
"""Affina una query di ricerca basata sul contesto."""
...
Implementazioni Pianificate:
- OllamaLLMAdapter: Comunica con Ollama
- OpenAILLMAdapter: Supporto OpenAI (futuro)
SearchEnginePort¶
Interfaccia per i motori di ricerca.
class SearchEnginePort(Protocol):
def search(self, query: str, num_results: int) -> list[SearchResult]:
"""Esegue una ricerca e ritorna i risultati."""
...
def search_with_filters(
self,
query: str,
num_results: int,
language: str,
safe_search: bool
) -> list[SearchResult]:
"""Esegue una ricerca con filtri."""
...
Implementazioni Pianificate:
- DuckDuckGoAdapter: DuckDuckGo search API
- GoogleAdapter: Google Custom Search (futuro)
WebScraperPort¶
Interfaccia per il web scraping.
class WebScraperPort(Protocol):
def fetch(self, url: str, timeout: int) -> str:
"""Recupera il contenuto HTML di una URL."""
...
def extract_text(self, html: str) -> str:
"""Estrae testo pulito da HTML."""
...
Implementazioni Pianificate:
- PlaywrightAdapter: Web scraping con Playwright
- HttpxAdapter: Fetch semplice con httpx
RepositoryPort¶
Interfaccia per la persistenza.
class RepositoryPort(Protocol):
def save_session(self, session: ResearchSession) -> str:
"""Salva una sessione di ricerca."""
...
def get_session(self, session_id: str) -> ResearchSession | None:
"""Recupera una sessione di ricerca."""
...
def list_sessions(self, limit: int) -> list[ResearchSession]:
"""Elenca le sessioni recenti."""
...
Implementazioni Pianificate:
- SQLiteRepository: SQLite database
- PostgresRepository: PostgreSQL (futuro)
Adapter (Implementazioni)¶
OllamaLLMAdapter¶
Comunica con il server Ollama locale.
Responsabilità: - Mantiene la connessione con Ollama - Formatta i prompt secondo lo stile di Ollama - Gestisce timeout e retry - Parsea le risposte
Configurazione Richiesta:
DuckDuckGoAdapter¶
Integrazione con DuckDuckGo Search API.
Responsabilità: - Costruisce query per DuckDuckGo - Parsea i risultati della ricerca - Gestisce rate limiting - Gestisce errori di connessione
PlaywrightAdapter¶
Web scraping con Playwright.
Responsabilità: - Lancia il browser - Naviga verso le URL - Estrae il contenuto - Gestisce JavaScript rendering
Configurazione Richiesta:
SQLiteRepository¶
Persistenza su SQLite.
Responsabilità: - Crea schema del database - Salva sessioni di ricerca - Recupera sessioni storiche - Gestisce transazioni - Pulisce i dati obsoleti
Service Layer (Application)¶
SearchService¶
Orches tra il processo di ricerca iterativa.
class SearchService:
def __init__(
self,
llm: LLMPort,
search_engine: SearchEnginePort,
scraper: WebScraperPort,
repository: RepositoryPort
):
...
def research(
self,
query: SearchQuery
) -> ResearchSession:
"""Esegue una ricerca iterativa completa."""
...
Flusso: 1. Ottiene i risultati iniziali della ricerca 2. Itera N volte (configurabile) 3. Per ogni iterazione: - Analizza i risultati con LLM - Affina la query se necessario - Recupera nuovi risultati - Estrae citations 4. Ritorna ResearchSession con risposta finale
Diagramma di Interazione dei Componenti¶
graph TB
CLI["CLI<br/>Typer Commands"]
SS["SearchService<br/>Orchestrazione"]
DOMAIN["Domain Models<br/>SearchQuery, Result, Citation"]
LLM_PORT["LLMPort<br/>Protocol"]
SE_PORT["SearchEnginePort<br/>Protocol"]
WS_PORT["WebScraperPort<br/>Protocol"]
REPO_PORT["RepositoryPort<br/>Protocol"]
OLLAMA["OllamaLLMAdapter"]
DDG["DuckDuckGoAdapter"]
PW["PlaywrightAdapter"]
SQLITE["SQLiteRepository"]
CLI -->|invoca| SS
SS -->|usa| DOMAIN
SS -->|dipende| LLM_PORT
SS -->|dipende| SE_PORT
SS -->|dipende| WS_PORT
SS -->|dipende| REPO_PORT
LLM_PORT -->|implementato da| OLLAMA
SE_PORT -->|implementato da| DDG
WS_PORT -->|implementato da| PW
REPO_PORT -->|implementato da| SQLITE
OLLAMA -->|HTTP| EXT1["Ollama Server<br/>localhost:11434"]
DDG -->|HTTP| EXT2["DuckDuckGo API"]
PW -->|Automation| EXT3["Browser<br/>Web Pages"]
SQLITE -->|File| EXT4["SQLite DB<br/>searchmuse.db"]
style DOMAIN fill:#ff9999
style SS fill:#99ccff
style LLM_PORT fill:#ffcc99
style SE_PORT fill:#ffcc99
style WS_PORT fill:#ffcc99
style REPO_PORT fill:#ffcc99
style OLLAMA fill:#cc99ff
style DDG fill:#cc99ff
style PW fill:#cc99ff
style SQLITE fill:#cc99ff
Flusso di Dati tra Componenti¶
Ricerca Iniziale¶
User Query
↓
CLI.search_command()
↓
SearchService.research()
↓
SearchEnginePort.search()
↓
DuckDuckGoAdapter
↓
SearchResult list
Analisi e Raffinamento¶
SearchResult list
↓
LLMPort.refine_query()
↓
OllamaLLMAdapter
↓
Refined Query string
↓
SearchEnginePort.search()
↓
Nuovi SearchResult
Salvataggio dei Risultati¶
Estensibilità e Nuovi Componenti¶
Per aggiungere un nuovo componente:
- Definire il Port in
ports/ - Implementare l'Adapter in
adapters/ - Aggiornare SearchService per usare il nuovo port
- Scrivere i test per il nuovo adapter
Esempio per un nuovo LLM provider:
# In ports/llm_port.py (aggiungere al Protocol esistente)
# In adapters/claude/claude_adapter.py
class ClaudeLLMAdapter:
def generate(self, prompt: str) -> str:
# Implementazione specifica per Claude
pass
Versione: 1.0 Ultimo Aggiornamento: Febbraio 2026 Stato: Stabile