# CLAUDE.md — Contesto Progetto Tesi

## Panoramica

Tesi di laurea in **Ingegneria Informatica**, corso di **Natural Language Processing** (Prof.ssa Cristina Giannone).

Chatbot **multi-agente per e-commerce fashion** basato su **LangGraph**. Il sistema supporta l'utente nella ricerca e scelta di prodotti tramite: ricerca per attributi, raccomandazione personalizzata e (in futuro) visual recommending.

---

## Architettura

Il sistema e' composto da **3 agenti operativi + 2 nodi semplici**, orchestrati da **LangGraph** (StateGraph con routing condizionale):

### 1. Router Agent (`agents/router.py`)
- Punto di ingresso. Classifica l'intent e smista al sotto-agente appropriato.
- **Intent classification**: FAISS few-shot su dataset [ecommerce-intent-routing](https://huggingface.co/datasets/vansh-khaneja/ecommerce-intent-routing) (~2.750 esempi, 5 classi) + LLM structured output (`RouteDecision`).
- **Session memory**: MemorySaver di LangGraph mantiene il contesto conversazionale.
- **Risoluzione contesto**: se ci sono messaggi precedenti (len > 1), l'LLM estrae keyword pulite dal contesto conversazionale:
  - Rimuove parole conversazionali: "consigliami", "simili", "cerco", "voglio"
  - Genera solo sostantivi/aggettivi: "scarpe rosse donna" invece di "consigliami scarpe rosse donna simili"
  - Salva in `resolved_query` per Search e Recommender Agent
  - Al primo turno, `resolved_query = ultimo messaggio` (nessuna risoluzione)
- **Routing**:
  - `product` / `sales` → Search Agent o Recommender Agent
  - `support` / `account` / `bugs` → risposta diretta fuori scope
  - Saluti / chit-chat → risposta diretta
- **Chiamate LLM**: 1 (classificazione) + 1 (risoluzione contesto, solo se multi-turno) = max 2

### 2. Search Agent (`agents/search.py`)
- Ricerca prodotti nel catalogo Amazon Fashion.
- **Pipeline**:
  1. Riceve `resolved_query` dal Router (keyword pulite IT)
  2. Traduzione IT→EN (LLM): "scarpe rosse donna" → "red women shoes"
  3. Ricerca FAISS (k=20 prodotti)
  4. Filtraggio e risposta IT (LLM): seleziona solo prodotti pertinenti, formato fisso senza markdown
- **Formato output**: Lista numerata `1. Nome Prodotto (Brand) - Prezzo: $XX.XX` (no `**bold**`)
- **Chiamate LLM**: 2 (traduzione + risposta)

### 3. Recommender Agent (`agents/recommender.py`)
- Raccomandazione item-to-item via **LightFM** (hybrid filtering, algoritmo WARP).
- **Approccio ibrido** (content-based + collaborative filtering):
  - **Content-based**: 408 categorie prodotto + 5 fasce di prezzo come item features. Prodotti della stessa categoria hanno embedding piu' vicini anche senza utenti in comune.
  - **Collaborative**: interazioni user-item da 131k recensioni (rating >= 4).
  - LightFM combina i due segnali in un unico embedding per prodotto via `get_item_representations()`.
- **Anchor contestuale**:
  1. Riceve `resolved_query` dal Router (keyword pulite IT)
  2. Traduzione IT→EN (LLM): "scarpe rosse donna" → "red women shoes"
  3. Ricerca FAISS → trova primo prodotto attinente nel modello LightFM
  4. Dot product vettorizzato → top 5 prodotti piu' simili all'anchor
  5. Fallback a random se FAISS non trova match
- **Formato output**: Lista numerata `1. Nome Prodotto (Brand) - Prezzo: $XX.XX` (formato fisso, no LLM per risposta)
- Mostra nomi prodotti reali grazie a metadata_lookup (ASIN → dettagli prodotto).
- **Chiamate LLM**: 1 (solo traduzione per anchor search)
- **Approcci futuri**: Visual similarity con Trendyol DinoV2.

### 4. Nodi semplici (`agents/simple.py`)
- `nodo_saluti`: risposta statica di benvenuto.
- `nodo_default`: rimanda al supporto umano.

---

## Stack Tecnologico

| Componente | Tecnologia |
|---|---|
| Orchestrazione agenti | LangGraph (estensione di LangChain) |
| LLM | OpenAI GPT-4o-mini (`gpt-4o-mini`) |
| Embeddings | `all-MiniLM-L6-v2` (sentence-transformers) |
| Vector DB | FAISS (locale) |
| Recommender | LightFM (ibrido: collaborative + content-based) |
| Visual recommending | Trendyol DinoV2 (da implementare) |
| Ambiente | Python 3.9, venv `.venv`, macOS M1 |

---

## Struttura Progetto

```
~/Desktop/tesi/
├── .env                       # API keys (GOOGLE_API_KEY, OPENAI_API_KEY)
├── CLAUDE.md                  # Questo file
├── requirements.txt           # Dipendenze Python
├── main.py                    # Entry point (loop interattivo)
├── graph.py                   # Definizione grafo LangGraph + caricamento risorse
│
├── agents/
│   ├── __init__.py            # Re-export di tutti i nodi
│   ├── router.py              # Router Agent (RouteDecision + factory)
│   ├── search.py              # Search Agent (traduzione + FAISS + risposta)
│   ├── recommender.py         # Recommender Agent (LightFM item-to-item)
│   └── simple.py              # Nodi saluti + default
│
├── utils/
│   ├── __init__.py
│   ├── config.py              # Costanti, path assoluti, parametri
│   ├── llm.py                 # Inizializzazione LLM (OpenAI GPT-4o-mini)
│   └── loaders.py             # Caricamento artefatti (FAISS, LightFM, metadata)
│
├── artifacts/                 # Artefatti generati dalla pipeline
│   ├── faiss_products/        # Indice FAISS prodotti (203MB)
│   ├── faiss_router/          # Indice FAISS router (4.5MB)
│   ├── recommender_model.pkl  # Modello LightFM (23MB)
│   ├── metadata_cleaned.json  # ~100k prodotti puliti (37MB)
│   └── reviews_cleaned.json   # Recensioni filtrate per catalogo (11MB)
│
├── raw/                       # File originali (NON usati a runtime)
│   ├── Clothing_Shoes_and_Jewelry.jsonl        # 26GB recensioni
│   └── meta_Clothing_Shoes_and_Jewelry.jsonl   # 17GB metadati
│
├── pipeline/                  # Script ETL (numerati in ordine di esecuzione)
│   ├── 1_extract_metadata.py         # raw metadata → artifacts/metadata_cleaned.json
│   ├── 2_build_faiss_index.py        # metadata → artifacts/faiss_products/
│   ├── 3_filter_reviews.py           # raw reviews → artifacts/reviews_cleaned.json
│   ├── 4_train_recommender.py        # reviews + metadata features → artifacts/recommender_model.pkl
│   └── 5_build_router_index.py       # HuggingFace dataset → artifacts/faiss_router/
│
├── tests/                     # Script di test/tutorial (storici)
│   ├── test_grafo.py
│   ├── test_router.py
│   ├── test_router_gemini.py
│   └── check_models.py
│
├── lightfm/                   # Repo clonato per installazione manuale
└── main_thesis_bot_ORIGINAL.py  # Backup del vecchio monolite
```

---

## Dataset

| Dataset | Uso | Fonte |
|---|---|---|
| ecommerce-intent-routing | Few-shot examples per il Router | [HuggingFace](https://huggingface.co/datasets/vansh-khaneja/ecommerce-intent-routing) |
| Amazon Product Data — Fashion (metadata) | Catalogo prodotti per il Search Agent | [Amazon](http://jmcauley.ucsd.edu/data/amazon/) |
| Amazon Product Data — Fashion (reviews) | Interazioni utente-prodotto per il Recommender | [Amazon](http://jmcauley.ucsd.edu/data/amazon/) |
| Immagini prodotto | Embeddings con Trendyol DinoV2 (futuro) | Estratte dal dataset Amazon |

---

## Stato Attuale

### Completato
- Struttura progetto riorganizzata in moduli (`agents/`, `utils/`, `pipeline/`, `artifacts/`)
- Migrazione LLM: Gemini → OpenAI GPT-4o-mini (completata)
- Router Agent con FAISS few-shot + LLM structured output
- **Risoluzione contesto conversazionale** (Router):
  - Estrazione keyword pulite dal contesto (rimozione "consigliami", "simili", etc.)
  - Output: solo sostantivi/aggettivi ("scarpe rosse donna" vs "consigliami scarpe rosse simili")
  - Salvato in `resolved_query` condiviso con Search e Recommender
- Search Agent:
  - Traduzione IT→EN con LLM prima di FAISS
  - Formato output fisso senza markdown (`1. Nome (Brand) - Prezzo: $XX.XX`)
  - 2 chiamate LLM (traduzione + risposta)
- Recommender Agent ibrido (content-based + collaborative):
  - Item features: 408 categorie + 5 fasce prezzo passate a LightFM
  - **Anchor contestuale con traduzione**: `resolved_query` IT → traduzione EN → FAISS → anchor LightFM
  - Dot product vettorizzato per top 5 simili
  - Formato output fisso senza LLM per risposta
  - 1 chiamata LLM (solo traduzione anchor)
  - Metadata lookup per nomi prodotti reali
- Pipeline ETL completa e numerata (5 step)
- Esclusione gioielli/orologi dal catalogo
- Filtraggio recensioni per ASIN presenti nel catalogo

### Da fare
1. **Visual Recommending**: Integrare Trendyol DinoV2 per similarity search su immagini.
2. **Valutazione metriche**: Script per calcolare accuratezza Router, Precision@5, nDCG@10.

### Limiti noti e migliorie future
1. **Colore non modellato**: LightFM usa categorie e fasce prezzo come feature, ma non il colore. Se l'utente chiede "scarpe rosse simili", l'anchor sara' una scarpa rossa (grazie a FAISS testuale), ma il dot product LightFM non filtra per colore — i risultati saranno scarpe della stessa categoria/prezzo, non necessariamente rosse. Soluzione futura: aggiungere il colore come feature (richiede estrazione NLP dal titolo, il dataset Amazon non ha un campo colore strutturato) oppure post-filtro testuale sui risultati.
2. **Abbinamento cross-categoria**: se l'utente chiede "consigliami una gonna da abbinare a queste scarpe", il sistema trova gonne simili tra loro ma non modella la compatibilita' stilistica tra scarpe e gonne. Servirebbe un modello di "outfit recommendation" (es. graph neural network su outfit completi). Fuori scope tesi.
3. **Riferimento a prodotti mostrati ("simili a queste")**: se l'utente chiede "consigliami scarpe simili a queste" dopo aver visto dei risultati, il sistema non sa esattamente *quali* prodotti sono stati mostrati — la resolved_query conterra' una descrizione generica (es. "scarpe rosse simili"), non un prodotto specifico tra quelli visti. L'anchor FAISS trovera' una scarpa rossa dal catalogo, ma non necessariamente una di quelle mostrate prima. Soluzione futura: salvare gli ASIN dei prodotti mostrati nello state (`last_shown_asins`) e usarli direttamente come anchor nel Recommender, bypassando FAISS.
4. **Conversazioni molto lunghe**: la risoluzione contesto funziona bene su 2-4 turni. Su catene molto lunghe (6+ turni) con contesto frammentato, la qualita' della risoluzione dipende dalla capacita' dell'LLM di ricostruire il senso.
5. **Visual similarity**: Trendyol DinoV2 per ricerca per immagine (da implementare).

---

## Note per Claude Code

### Pattern architetturale
Gli agenti usano il **factory pattern** con dependency injection: `create_nodo_router(llm, retriever)`. Le dipendenze (LLM, retriever) vengono iniettate da `graph.py`, non importate come globali. Nessuna dipendenza circolare.

### Flusso import
```
main.py → graph.py → agents/* + utils/*
                      agents/* → utils/config.py
                      utils/llm.py → utils/config.py
                      utils/loaders.py → utils/config.py
```

### Approcci di Raccomandazione (concordati con la professoressa)

| Approccio | Stato | Come Funziona | Tecnologia |
|---|---|---|---|
| Content-based | Implementato | Raccomanda prodotti con attributi simili (categoria, fascia prezzo) | LightFM (item features: 408 categorie + 5 fasce prezzo) |
| Collaborative filtering | Implementato | Raccomanda prodotti apprezzati da utenti con gusti simili | LightFM (131k interazioni user-item) |
| Visual similarity | Da fare | Dato un'immagine, trova prodotti visivamente simili | Trendyol DinoV2 (embedding 256d + cosine similarity) |

I primi due sono combinati in un unico modello LightFM ibrido. L'anchor contestuale (FAISS) collega la query dell'utente al modello.

### Scope del chatbot
Il chatbot si occupa **esclusivamente** di ricerca e raccomandazione prodotti fashion. NON gestisce: spedizioni, resi, account, bug tecnici.

### Trendyol DinoV2 — Riferimento per implementazione futura
```python
from transformers import AutoModel, AutoImageProcessor
import torch
from PIL import Image

processor = AutoImageProcessor.from_pretrained(
    "Trendyol/trendyol-dino-v2-ecommerce-256d", trust_remote_code=True
)
model = AutoModel.from_pretrained(
    "Trendyol/trendyol-dino-v2-ecommerce-256d", trust_remote_code=True
)

image = Image.open("product_image.jpg").convert("RGB")
inputs = processor(images=image, return_tensors="pt")

with torch.no_grad():
    outputs = model(**inputs)
    embedding = outputs.last_hidden_state  # Shape: [1, 256]
```

---

## Metriche di Valutazione

| Componente | Metrica | Target |
|---|---|---|
| Intent Classification | Accuracy, F1-score | > 90% |
| Product Search | Precision@5 | > 0.7 |
| Recommender (hybrid) | nDCG@10, MAP | nDCG > 0.5 |
| Visual Similarity | Cosine Similarity | > 0.85 |
