Sistema RAG (Retrieval-Augmented Generation) production-ready con embedding locale e vector database Qdrant su Docker.
- Jina Embeddings v3: Modello multilingue state-of-the-art (1024 dimensioni)
- Qdrant Docker: Vector database scalabile e performante
- Ingestione Incrementale: Rileva automaticamente file nuovi, modificati ed eliminati
- Chunking Semantico: Rispetta confini frasi, protegge abbreviazioni scientifiche
- Reranking Cross-Encoder: Riordina risultati per rilevanza semantica reale
- Threshold Adattivo: Soglia qualità dinamica basata su GAP analysis
- PDF Strutturato: Estrae tabelle in Markdown con PyMuPDF4LLM
- Tracciabilità Fonti: Posizione caratteri e citazioni precise
- MCP Server: Compatibile con Antigravity, Claude Desktop, VS Code, Cursor
- Production-Ready: Retry logic, input validation, logging strutturato, metriche
docker run -d -p 6333:6333 -p 6334:6334 \
-v ./qdrant_data:/qdrant/storage \
--name qdrant-rag \
qdrant/qdrant:latestWindows:
docker run -d -p 6333:6333 -p 6334:6334 -v %cd%/qdrant_data:/qdrant/storage --name qdrant-rag qdrant/qdrant:latestVerifica:
curl http://localhost:6333/health# Clone repository
git clone <repo-url>
cd file-search
# Virtual environment
python -m venv .venv
.venv\Scripts\activate # Windows
# source .venv/bin/activate # Linux/Mac
# Dipendenze
pip install -r requirements.txt# Esegui una volta per scaricare e cachare i modelli
python mcp_server.pyAttendi fino a vedere:
[MCP] Caricamento modello AI e connessione Qdrant...
[MCP] Sistema pronto.
Poi premi Ctrl+C per terminare. I modelli saranno salvati nella cache di HuggingFace (~/.cache/huggingface/) e i successivi avvii saranno molto più veloci.
Nota: Il primo download richiede ~5-10 minuti (modello ~1-2GB). Le esecuzioni successive caricano dalla cache in ~30-60 secondi.
Crea file .env con le tue API keys (opzionale, solo per chat.py):
GOOGLE_API_KEY=your_google_api_key_hereVerifica config.yaml:
# Qdrant Docker
qdrant_mode: "http"
qdrant_host: "localhost"
qdrant_port: 6333
# Quality threshold (0.6-1.0, raccomandato 0.7 per uso scientifico)
similarity_threshold: 0.7Copia i tuoi file nella cartella data/, poi:
# Prima indicizzazione (completa)
python ingest.py --clean
# Aggiornamenti incrementali (rileva modifiche)
python ingest.pyL'ingestione incrementale:
- ✅ Rileva file nuovi e li aggiunge
- ✅ Rileva file modificati e li aggiorna
- ✅ Rileva file eliminati e rimuove i vettori
- ✅ Salta file già indicizzati e non modificati
# Test rapido
python -c "from mcp_server import search_knowledge_base; print(search_knowledge_base('test query', 3))"| Categoria | Estensioni |
|---|---|
| Testo | .txt, .md, .csv, .log |
| Codice | .py, .js, .ts, .json, .yaml, .xml, .html, .css, .java, .cpp, .c, .cs, .go, .rb, .php, .sh, .sql |
| Documenti | .pdf, .xlsx, .ipynb |
| Audio | .mp3, .m4a, .wav, .ogg, .flac (richiede FFmpeg) |
Il server MCP espone il sistema RAG a IDE e assistenti AI.
Il server può essere avviato in 3 modalità diverse:
python mcp_server.pyComunicazione diretta stdin/stdout - consigliata per la maggior parte degli IDE.
python mcp_server_http.py- URL:
http://127.0.0.1:8765/sse - Usa Server-Sent Events (SSE) su HTTP
- Ideale per sviluppo e testing
python mcp_server_https.py- URL:
https://127.0.0.1:8766/sse - Usa Server-Sent Events (SSE) su HTTPS con certificato auto-firmato
- Richiesto da alcuni client (es. Claude Code)
- Genera automaticamente certificati SSL self-signed alla prima esecuzione
Windows:
start_servers.batQuesto avvia contemporaneamente:
- Server HTTP su porta
8765 - Server HTTPS su porta
8766
Entrambi condividono lo stesso backend RAG e possono essere usati in parallelo senza conflitti.
Requisiti per HTTPS:
pip install cryptography # Per generazione automatica certificatiOppure genera manualmente i certificati:
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout key.pem -out cert.pem -days 365 \
-subj "/CN=localhost"Cerca nei documenti indicizzati tramite ricerca semantica vettoriale.
Parametri:
query(string): Domanda o ricerca (3-2000 caratteri)limit(int, optional): Numero risultati (1-50, default: 10)
Output:
✅ Trovati N risultati rilevanti (soglia qualità: 0.72 (adattivo)):
[Risultato 1/N]
📄 Fonte: papers/document.pdf (chunk 12)
📍 Posizione: caratteri 4520-5480
🎯 Rilevanza: 0.887 🟢 Eccellente
📝 Citazione: "papers/document.pdf", char. 4520-5480
[contenuto del chunk...]
Mostra statistiche del server (query totali, success rate, uptime, ecc.)
File: C:\Users\<username>\.gemini\antigravity\mcp_config.json
Linux/Mac: ~/.gemini/antigravity/mcp_config.json
{
"mcpServers": {
"rag-search": {
"command": "C:\\path\\to\\file-search\\.venv\\Scripts\\python.exe",
"args": ["C:\\path\\to\\file-search\\mcp_server.py"],
"description": "RAG search engine for documents"
}
}
}Linux/Mac:
{
"mcpServers": {
"rag-search": {
"command": "/path/to/file-search/.venv/bin/python",
"args": ["/path/to/file-search/mcp_server.py"],
"description": "RAG search engine for documents"
}
}
}- Salva il file di configurazione
- Riavvia Antigravity completamente
- In Antigravity scrivi: "Usa search_knowledge_base per cercare 'machine learning'"
Claude Desktop è l'applicazione standalone di Anthropic. Supporta due modalità di connessione al server MCP.
File Windows: %APPDATA%\Claude\claude_desktop_config.json
File Linux/Mac: ~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"rag-search": {
"command": "C:\\Path\\to\\file-search\\.venv\\Scripts\\python.exe",
"args": ["C:\\Path\\to\\file-search\\mcp_server.py"],
"env": {
"PYTHONUNBUFFERED": "1"
}
}
}
}Step 1: Avvia il server SSE in un terminale separato:
cd C:\Users\lucam\Desktop\file-search
.\.venv\Scripts\Activate.ps1
python mcp_server_http.pyStep 2: Configura Claude Desktop per connettersi via SSE:
{
"mcpServers": {
"rag-search": {
"url": "http://127.0.0.1:8765/sse"
}
}
}- Salva il file di configurazione
- Riavvia Claude Desktop completamente
| Modalità | Pro | Contro |
|---|---|---|
| STDIO (default) | Setup semplice | Timeout al primo avvio se il modello non è in cache |
| SSE (pre-avviato) | Nessun timeout, modello già caricato | Richiede avviare il server separatamente |
Apri un terminale e lascialo in esecuzione:
cd C:\Users\lucam\Desktop\file-search
.\.venv\Scripts\Activate.ps1
python mcp_server_http.pyAttendi fino a vedere:
[MCP] ✅ Server SSE in ascolto su http://127.0.0.1:8765/sse
In un altro terminale, esegui:
# Rimuovi eventuale configurazione precedente
claude mcp remove rag-search --scope user
# Aggiungi il server SSE
claude mcp add --transport sse --scope user rag-search http://127.0.0.1:8765/sse
# Verifica la connessione
claude mcp listDovresti vedere:
rag-search: http://127.0.0.1:8765/sse (SSE) - ✓ Connected
Ora puoi usare Claude Code normalmente. I tool RAG saranno disponibili automaticamente:
# Esempio di utilizzo in Claude Code
> Cerca nei documenti indicizzati informazioni su "machine learning"| Comando | Descrizione |
|---|---|
claude mcp list |
Mostra tutti i server MCP configurati |
claude mcp remove rag-search --scope user |
Rimuove il server |
claude mcp add --transport sse --scope user rag-search http://127.0.0.1:8765/sse |
Aggiunge il server |
Quando il server è connesso, Claude ha accesso a questi tool:
search_knowledge_base: Ricerca semantica nei documentilist_sources: Elenco file indicizzatiget_server_stats: Statistiche del serversearch_by_source: Ricerca filtrata per fonteget_document_by_id: Recupera documento specificoget_document_context: Ottiene contesto attorno a un chunk
Problema: Failed to connect
- Verifica che il server SSE sia in esecuzione nel terminale
- Controlla che la porta 8765 non sia usata da altri processi
Problema: Tool non disponibili
- Riavvia Claude Code (chiudi e riapri VS Code)
- Verifica con
claude mcp listche il server sia connesso
Sistema di chat interattivo con RAG usando Google Gemini:
python chat.pyComandi disponibili:
/help- Mostra aiuto/stats- Statistiche database/exit- Esci
Nota: Richiede GOOGLE_API_KEY in .env
# ===== EMBEDDING MODEL =====
embedding_model: "jinaai/jina-embeddings-v3"
embedding_dimension: 1024
embedding_task_passage: "retrieval.passage" # Task per indicizzazione
embedding_task_query: "retrieval.query" # Task per query
trust_remote_code: true
# ===== CHUNKING =====
chunk_size: 1024 # Dimensione chunk in caratteri
chunk_overlap: 200 # Overlap tra chunks consecutivi
chunking_mode: "sentence" # "sentence" (semantico) o "character" (legacy)
min_text_length: 50 # Lunghezza minima testo per indicizzazione
# ===== PDF EXTRACTION =====
pdf_extraction_mode: "markdown" # "markdown" (tabelle/struttura) o "text" (legacy)
# ===== QDRANT =====
qdrant_mode: "http" # "http" (Docker) o "local" (embedded)
qdrant_host: "localhost"
qdrant_port: 6333
qdrant_collection: "documents"
# ===== RICERCA =====
top_k: 10 # Numero default risultati
similarity_threshold: 0.7 # Soglia base (fallback)
adaptive_threshold: true # Abilita threshold adattivo
adaptive_threshold_min: 0.5 # Minimo threshold adattivo
adaptive_threshold_max: 0.9 # Massimo threshold adattivo
# ===== RERANKING =====
rerank_enabled: true # Abilita cross-encoder reranking
rerank_model: "cross-encoder/ms-marco-MiniLM-L-6-v2"
rerank_top_n: 30 # Candidati per reranking
rerank_alpha: 0.4 # Hybrid scoring: peso vector_score (0=solo rerank, 1=solo vector)
# ===== LLM (per chat.py) =====
model: "gemini-3-pro-preview"
temperature: 0.2
max_tokens: 2048Il sistema supporta threshold adattivo basato su GAP analysis:
- Modalità Adattiva (default): Analizza la distribuzione degli score e trova automaticamente il cutoff ottimale
- Modalità Statica: Usa
similarity_thresholdfisso come fallback
Range consigliati:
- 0.9-1.0: Solo match quasi perfetti (molto restrittivo)
- 0.7-0.9: Match rilevanti (raccomandato per uso scientifico) ✅
- 0.5-0.7: Match moderatamente rilevanti
- 0.0-0.5: Anche match poco rilevanti (sconsigliato)
Default: adaptive_threshold: true con range 0.5-0.9
- 3 tentativi automatici di connessione a Qdrant
- Timeout configurabili (30s Qdrant, 60s embedding)
- Logging completo di ogni errore
- Zero silent failures
- Query: 3-2000 caratteri
- Type checking completo
- Limit: 1-50 risultati
- Protezione DoS
- Threshold adattivo: GAP analysis per cutoff ottimale automatico
- Cross-encoder reranking: Riordina per rilevanza semantica reale
- Score sempre visibile con indicatori qualitativi:
- 🟢 Eccellente (≥0.9)
- 🟡 Buona (≥0.8)
- 🟠 Sufficiente (≥0.7)
- Verifica connessione Qdrant all'avvio
- Validazione collection esistente
- Check documenti indicizzati
- Verifica schema vettori
- File log:
mcp_server.log - Metriche: total queries, success rate, avg time, uptime
- Tool
get_server_stats()per monitoring real-time
- Modalità HTTP (Docker) per multi-processo
- Singleton client Qdrant
- Timeout espliciti
- Graceful shutdown
file-search/
├── data/ # Documenti da indicizzare (crea questa cartella)
├── .venv/ # Virtual environment Python
├── chat.py # Chatbot interattivo (opzionale)
├── config.yaml # Configurazione centralizzata
├── extractors.py # Estrattori testo (PDF, audio, Excel, etc.)
├── ingest.py # Script ingestione documenti
├── mcp_server.py # Server MCP production-ready
├── utils.py # Funzioni utility
├── requirements.txt # Dipendenze Python
├── .env.example # Template variabili ambiente
└── README.md # Questa documentazione
File generati automaticamente:
qdrant_data/- Dati Qdrant Docker (se volume montato localmente).cache/- Registry file indicizzati & Log server MCP
Verifica Docker:
docker ps | grep qdrantAvvia se necessario:
docker start qdrant-ragVerifica connettività:
curl http://localhost:6333/healthSoluzione: Esegui ingestione
python ingest.py --cleanCause possibili:
- Query troppo generica o non correlata ai documenti
- Threshold troppo alto
Soluzioni:
- Riformula la query in modo più specifico
- Abbassa
similarity_thresholdinconfig.yaml(es. 0.6) - Verifica che i documenti siano stati indicizzati correttamente
Soluzione:
pip install -r requirements.txtAntigravity:
- Verifica path corretti in
mcp_config.json(usa\\su Windows) - Riavvia Antigravity completamente
- Verifica log:
tail -f .cache/mcp_server.log
Claude Desktop:
- Verifica path in
claude_desktop_config.json - Riavvia Claude Desktop
- Verifica che Qdrant sia attivo
Causa: Gli IDE (Antigravity, Claude Desktop, VS Code) hanno un timeout per l'avvio del server MCP. Il caricamento dei modelli AI (Jina Embeddings v3 + Cross-Encoder) può richiedere 1-2 minuti alla prima esecuzione, superando questo timeout.
Sintomi:
- L'IDE mostra errore di timeout o connessione fallita
- Il server MCP non compare tra i tool disponibili
- Messaggi tipo "failed to load model" o "connection closed"
Soluzione: Pre-caricare i modelli una volta eseguendo il server manualmente:
cd c:\path\to\file-search
.\.venv\Scripts\Activate.ps1 # Windows
# source .venv/bin/activate # Linux/Mac
python mcp_server.pyAttendi fino a vedere [MCP] Sistema pronto., poi chiudi con Ctrl+C.
I modelli vengono salvati nella cache HuggingFace (~/.cache/huggingface/). Le esecuzioni successive saranno più veloci (~30-60s invece di minuti).
Soluzioni alternative:
-
Disabilita reranking per dimezzare il tempo di avvio:
# config.yaml rerank_enabled: false
-
Usa un modello più leggero (meno preciso ma più veloce):
# config.yaml embedding_model: "sentence-transformers/all-MiniLM-L6-v2" embedding_dimension: 384 trust_remote_code: false
Poi rigenera:
python ingest.py --clean -
Aumenta il timeout dell'IDE (se supportato), es. per Antigravity:
{ "mcpServers": { "rag-search": { "command": "...", "args": ["..."], "timeout": 120000, "startupTimeout": 120000 } } }
Causa: Claude Desktop con connettore HTTP/HTTPS richiede un certificato SSL valido. Se usi un MCP server custom su localhost, OpenSSL potrebbe non avere il file di configurazione necessario.
Sintomi:
- Errore SSL durante la connessione tra Claude Desktop e MCP server
- OpenSSL fallisce con
Can't open openssl.cnf - Message tipo "missing equal sign" nel log di OpenSSL
Soluzione: Genera certificati auto-firmati con OpenSSL
- Crea file di configurazione OpenSSL (
openssl.cnf):
# PowerShell - Esegui uno per uno
Remove-Item .\openssl.cnf -Force -ErrorAction SilentlyContinue
@'
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
CN = localhost
[v3_req]
basicConstraints = CA:TRUE
keyUsage = critical, digitalSignature, keyEncipherment, keyCertSign
subjectAltName = @alt_names
[alt_names]
DNS.1 = localhost
IP.1 = 127.0.0.1
'@ | Out-File -FilePath .\openssl.cnf -Encoding ASCII- Genera certificato auto-firmato:
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -sha256 -config .\openssl.cnf- Verifica creazione file:
Get-ChildItem .\key.pem, .\cert.pem -ErrorAction SilentlyContinueDovrai vedere:
Directory: C:\Users\username\Desktop\file-search
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2025-01-03 14:30 1704 cert.pem
-a--- 2025-01-03 14:30 1708 key.pem
- Configura Claude Desktop per usare il certificato:
Modifica ~/.claude_desktop_config.json:
{
"mcpServers": {
"rag-search": {
"command": "python",
"args": ["c:\\path\\to\\file-search\\mcp_server.py"],
"env": {
"SSL_CERT_FILE": "c:\\path\\to\\file-search\\cert.pem",
"SSL_KEY_FILE": "c:\\path\\to\\file-search\\key.pem"
}
}
}
}- Riavvia Claude Desktop e verifica che il connettore sia visibile.
- CPU: Standard (no GPU)
- RAM: 8GB
- Qdrant: Docker locale
- Dataset: 6400+ chunks
- Avvio server: ~45s (caricamento modello)
- Prima query: ~5-8s (warm-up)
- Query successive: ~1-2s
- Throughput: ~0.5 query/sec (CPU), ~10 query/sec (GPU)
- GPU: Riduce encoding a <0.1s
- Modello più leggero: 3x più veloce (es. all-MiniLM-L6-v2, 384D)
- Qdrant remoto: Ricerca <0.05s con più RAM
- Caching: Redis per query frequenti
# Ultimi errori
grep ERROR .cache/mcp_server.log | tail -20
# Statistiche query
grep "Query completata" .cache/mcp_server.log | wc -lDa Antigravity/Claude:
Usa get_server_stats per vedere le metriche
Output:
📊 === STATISTICHE SERVER RAG ===
🔧 Configurazione:
• Collection: documents
• Documenti indicizzati: 6401
• Similarity threshold: 0.7
📈 Metriche Query:
• Totale query: 150
• Successi: 142 (94.7%)
• Tempo medio: 1.8s
# Status generale
curl http://localhost:6333/health
# Info collection
curl http://localhost:6333/collections/documents
# Numero documenti
curl -s http://localhost:6333/collections/documents | grep points_count# Stop container
docker stop qdrant-rag
# Backup
tar -czf qdrant_backup_$(date +%Y%m%d).tar.gz qdrant_data/
# Restart
docker start qdrant-ragcp .cache/registry.json registry_backup_$(date +%Y%m%d).json# Stop Qdrant
docker stop qdrant-rag
# Restore data
tar -xzf qdrant_backup_YYYYMMDD.tar.gz
# Restart
docker start qdrant-rag
# Restore registry
cp registry_backup_YYYYMMDD.json .cache/registry.json- ✅ Input validation (previene injection, overflow)
- ✅ Timeout (previene DoS)
- ✅ Logging completo (audit trail)
- ✅ Error handling sicuro (no info sensibili in output)
- ✅ Secrets in .env (git-ignored)
- 🔒 Firewall: Limitare accesso Qdrant porta 6333 solo a localhost
- 🔒 TLS: Usare HTTPS per Qdrant in ambienti remoti
- 🔒 Auth: Implementare autenticazione MCP se esposto
- 🔒 Rate limiting: Limite query/minuto per utente
Q: Posso usare un modello embedding diverso?
A: Sì, modifica config.yaml:
embedding_model: "sentence-transformers/all-MiniLM-L6-v2"
embedding_dimension: 384Poi rigenera: python ingest.py --clean
Q: Posso usare Qdrant locale invece di Docker?
A: Sì, ma non raccomandato per produzione:
qdrant_mode: "local"
vectorstore_path: "./qdrant_storage"Q: Come aggiungo nuovi documenti?
A: Copia i file in data/ ed esegui python ingest.py (incrementale)
Q: Posso indicizzare più collection?
A: Modifica qdrant_collection in config.yaml e usa ingest.py separatamente
Q: Il sistema funziona offline?
A: Sì, dopo il primo download del modello embedding (Jina v3). Solo chat.py richiede internet (Google Gemini).
MIT
- Embedding Model: Jina AI Embeddings v3
- Vector Database: Qdrant
- MCP Protocol: Anthropic
Status: ✅ Production Ready
Version: 2.0
Last Update: 2025-12-27