feat(step-7,8): leggi modello da config.py, allinea EMBED_MODELS al README
- step-8/ingest.py: rimuove EMBED_MODEL e OLLAMA_URL hardcoded; li importa da step-9/config.py (fonte di verita unica) - step-7/check_env.py: aggiorna EMBED_MODELS con tutti i modelli del README (aggiunge qwen3-embedding, nomic-embed-text-v2-moe, paraphrase-multilingual); mostra il modello configurato in config.py e verifica proprio quello, non un qualsiasi modello embedding - step-8/README.md: creato
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
# Step 8 — Vettorizzazione
|
||||
|
||||
Legge i chunk prodotti da step-6, genera gli embedding tramite Ollama e li
|
||||
salva in ChromaDB (vector store persistente su disco).
|
||||
|
||||
---
|
||||
|
||||
## Prerequisiti
|
||||
|
||||
- Step-6 completato (esiste `step-6/<stem>/chunks.json`)
|
||||
- Ollama attivo con il modello di embedding scaricato
|
||||
- `chromadb` installato (`pip install -r requirements.txt`)
|
||||
|
||||
---
|
||||
|
||||
## Configurazione modello
|
||||
|
||||
Il modello di embedding viene letto da **`step-9/config.py`**:
|
||||
|
||||
```python
|
||||
# step-9/config.py
|
||||
EMBED_MODEL = "nomic-embed-text" # ← cambia qui
|
||||
```
|
||||
|
||||
> Il modello scelto qui deve corrispondere a quello usato in step-9.
|
||||
> Se lo cambi dopo aver già vettorizzato, devi rieseguire step-8 con `--force`.
|
||||
|
||||
---
|
||||
|
||||
## Uso
|
||||
|
||||
```bash
|
||||
# Vettorizza un singolo documento
|
||||
python step-8/ingest.py --stem <nome>
|
||||
|
||||
# Vettorizza tutti i documenti trovati in step-6/
|
||||
python step-8/ingest.py
|
||||
|
||||
# Sovrascrive una collection già esistente
|
||||
python step-8/ingest.py --stem <nome> --force
|
||||
|
||||
# Override modello (senza modificare config.py)
|
||||
python step-8/ingest.py --stem <nome> --model bge-m3
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Output
|
||||
|
||||
I vettori vengono salvati in `chroma_db/<stem>/` come collection ChromaDB con
|
||||
distanza coseno. La directory è ignorata da git (generata automaticamente).
|
||||
|
||||
---
|
||||
|
||||
## Modelli supportati
|
||||
|
||||
Stessi modelli raccomandati nel [README di step-7](../step-7/README.md).
|
||||
Il modello deve essere scaricato in Ollama prima di eseguire questo script
|
||||
(`ollama pull <modello>`).
|
||||
+32
-22
@@ -3,15 +3,20 @@
|
||||
Step 8 — Vettorizzazione
|
||||
|
||||
Legge i chunk prodotti da step-6, genera gli embedding tramite Ollama
|
||||
(nomic-embed-text) e li indicizza in ChromaDB (persistente).
|
||||
e li indicizza in ChromaDB (persistente).
|
||||
|
||||
Il modello di embedding viene letto da step-9/config.py (EMBED_MODEL).
|
||||
Puoi sovrascriverlo con --model, ma deve corrispondere al modello che
|
||||
userai in step-9 — altrimenti riesegui con --force dopo aver cambiato.
|
||||
|
||||
Input: step-6/<stem>/chunks.json
|
||||
Output: chroma_db/<stem> (collection ChromaDB)
|
||||
|
||||
Uso:
|
||||
python step-8/ingest.py --stem <nome> # singolo documento
|
||||
python step-8/ingest.py # tutti gli stem trovati
|
||||
python step-8/ingest.py --stem <nome> --force # sovrascrive collection
|
||||
python step-8/ingest.py --stem <nome> # singolo documento
|
||||
python step-8/ingest.py # tutti gli stem trovati
|
||||
python step-8/ingest.py --stem <nome> --force # sovrascrive collection
|
||||
python step-8/ingest.py --model bge-m3 # override modello
|
||||
"""
|
||||
|
||||
import argparse
|
||||
@@ -24,23 +29,26 @@ from pathlib import Path
|
||||
|
||||
import chromadb
|
||||
|
||||
# ─── Costanti ─────────────────────────────────────────────────────────────────
|
||||
# ─── Configurazione ────────────────────────────────────────────────────────────
|
||||
|
||||
project_root = Path(__file__).parent.parent
|
||||
|
||||
CHUNKS_DIR = project_root / "step-6"
|
||||
CHROMA_DIR = project_root / "chroma_db"
|
||||
CHUNKS_DIR = project_root / "step-6"
|
||||
CHROMA_DIR = project_root / "chroma_db"
|
||||
|
||||
OLLAMA_URL = "http://localhost:11434"
|
||||
EMBED_MODEL = "nomic-embed-text"
|
||||
EMBED_ENDPOINT = f"{OLLAMA_URL}/api/embeddings"
|
||||
# Legge EMBED_MODEL e OLLAMA_URL da step-9/config.py (fonte di verità).
|
||||
# Per spostare config.py alla root: cambia solo la riga qui sotto.
|
||||
sys.path.insert(0, str(project_root / "step-9"))
|
||||
from config import EMBED_MODEL, OLLAMA_URL # noqa: E402
|
||||
|
||||
EMBED_ENDPOINT = f"{OLLAMA_URL}/api/embeddings"
|
||||
|
||||
|
||||
# ─── Ollama ────────────────────────────────────────────────────────────────────
|
||||
|
||||
def embed(text: str) -> list[float]:
|
||||
def embed(text: str, model: str) -> list[float]:
|
||||
"""Chiama Ollama /api/embeddings e ritorna il vettore."""
|
||||
payload = json.dumps({"model": EMBED_MODEL, "prompt": text}).encode()
|
||||
payload = json.dumps({"model": model, "prompt": text}).encode()
|
||||
req = urllib.request.Request(
|
||||
EMBED_ENDPOINT,
|
||||
data=payload,
|
||||
@@ -52,22 +60,22 @@ def embed(text: str) -> list[float]:
|
||||
return data["embedding"]
|
||||
|
||||
|
||||
def check_ollama() -> bool:
|
||||
"""Verifica che Ollama sia attivo e che nomic-embed-text sia disponibile."""
|
||||
def check_ollama(model: str) -> bool:
|
||||
"""Verifica che Ollama sia attivo e che il modello di embedding sia disponibile."""
|
||||
try:
|
||||
req = urllib.request.Request(f"{OLLAMA_URL}/api/tags", method="GET")
|
||||
with urllib.request.urlopen(req, timeout=10) as resp:
|
||||
data = json.loads(resp.read())
|
||||
models = [m["name"] for m in data.get("models", [])]
|
||||
found = any(
|
||||
m == EMBED_MODEL or m.startswith(EMBED_MODEL + ":")
|
||||
m == model or m.startswith(model + ":")
|
||||
for m in models
|
||||
)
|
||||
if found:
|
||||
print(f"✅ Ollama OK — {EMBED_MODEL} disponibile")
|
||||
print(f"✅ Ollama OK — {model} disponibile")
|
||||
return True
|
||||
print(f"❌ Modello {EMBED_MODEL} non trovato in Ollama")
|
||||
print(f" → ollama pull {EMBED_MODEL}")
|
||||
print(f"❌ Modello {model} non trovato in Ollama")
|
||||
print(f" → ollama pull {model}")
|
||||
return False
|
||||
except (urllib.error.URLError, OSError):
|
||||
print("❌ Ollama non raggiungibile — assicurati che sia in esecuzione")
|
||||
@@ -88,7 +96,7 @@ def collection_exists(client: chromadb.PersistentClient, stem: str) -> bool:
|
||||
|
||||
# ─── Ingestione ───────────────────────────────────────────────────────────────
|
||||
|
||||
def ingest(stem: str, force: bool) -> bool:
|
||||
def ingest(stem: str, force: bool, model: str = EMBED_MODEL) -> bool:
|
||||
"""
|
||||
Legge step-6/<stem>/chunks.json, genera embedding e popola ChromaDB.
|
||||
Ritorna True se completato con successo, False altrimenti.
|
||||
@@ -133,7 +141,7 @@ def ingest(stem: str, force: bool) -> bool:
|
||||
|
||||
for i, chunk in enumerate(chunks, start=1):
|
||||
t0 = time.monotonic()
|
||||
vector = embed(chunk["text"])
|
||||
vector = embed(chunk["text"], model)
|
||||
t1 = time.monotonic()
|
||||
durations.append(t1 - t0)
|
||||
|
||||
@@ -196,11 +204,13 @@ def main() -> int:
|
||||
parser.add_argument("--stem", help="Nome del documento (senza --stem = tutti)")
|
||||
parser.add_argument("--force", action="store_true",
|
||||
help="Sovrascrive la collection se già esistente")
|
||||
parser.add_argument("--model", default=EMBED_MODEL,
|
||||
help=f"Modello embedding Ollama (default da step-9/config.py: {EMBED_MODEL})")
|
||||
args = parser.parse_args()
|
||||
|
||||
print("─── Step 8 — Vettorizzazione ─────────────────────────────────────────\n")
|
||||
|
||||
if not check_ollama():
|
||||
if not check_ollama(args.model):
|
||||
return 1
|
||||
|
||||
stems = [args.stem] if args.stem else find_stems()
|
||||
@@ -213,7 +223,7 @@ def main() -> int:
|
||||
for stem in stems:
|
||||
if len(stems) > 1:
|
||||
print(f"── {stem} ──")
|
||||
results.append(ingest(stem, force=args.force))
|
||||
results.append(ingest(stem, force=args.force, model=args.model))
|
||||
if len(stems) > 1:
|
||||
print()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user