Moteur de Recommandation de Cocktails par Intelligence Artificielle Semantique
Application utilisant NLP semantique (SBERT) et generation GenAI (Google Gemini).
Depot GitHub : https://github.com/Adam-Blf/ia-pero-final
Auteurs : Adam Beloucif & Amina Medjdoub Tutrice : MALAEB Sarah Formation : EFREI Paris - Mastere Data Engineering et IA Annee : 2025-2026
flowchart TB
UI["src/app.py<br/>Streamlit · interface Speakeasy · questionnaire hybride"]
Backend["src/backend.py<br/>RAG · guardrail sémantique · génération"]
Embed["src/embeddings.py<br/>SBERT all-MiniLM-L6-v2 · matrice de similarité"]
Scoring["src/scoring.py<br/>coverage score pondéré · profil gustatif"]
Profiler["src/ingredient_profiler.py<br/>profil 7 dimensions"]
Kaggle["src/kaggle_integration.py<br/>enrichissement dataset Kaggle"]
Data["data/<br/>cocktails.csv · kaggle_enriched.csv · known_ingredients.json"]
Gemini["Google Gemini<br/>génération de recette"]
Cache["cache JSON<br/>recettes déjà générées"]
Radar["Plotly<br/>graphique radar gustatif"]
UI --> Backend
UI --> Embed
UI --> Scoring
UI --> Radar
Backend --> Embed
Backend --> Cache
Backend --> Gemini
Scoring --> Profiler
Embed --> Data
Kaggle --> Data
| Document | Description |
|---|---|
| Rapport PDF | Rapport RNCP Bloc 2 complet |
| Lien GitHub PDF | Document avec lien depot |
| RAPPORT_FINAL.md | Rapport source Markdown |
| SOUTENANCE_SLIDES.md | Slides de presentation |
| QUICK_START.md | Guide de demarrage rapide |
| KAGGLE_INTEGRATION.md | Documentation integration Kaggle |
- Interface Streamlit moderne
- Support de plusieurs modeles SBERT
- Matrice de similarite interactive
- Detection des paires les plus similaires
- Backend RAG avec Guardrail semantique
- Interface Speakeasy (theme bar clandestin annees 1920)
- Questionnaire Hybride (texte libre + dropdown Budget)
- Graphique Radar Plotly (profil gustatif 7 dimensions)
- Generation GenAI (Google Gemini)
- Cache JSON (optimisation des couts API)
- 600+ cocktails dans la base de donnees (extensible a 1600 via Kaggle)
- Integration Kaggle - 1600+ cocktails supplementaires disponibles
- Profilage ingredients - 61 ingredients avec profils gustatifs 7D
- Analyse semantique detaillee - Pipeline SBERT complet documente
- Rapport RNCP - Documentation complete 27 pages avec analyse semantique
- Scripts enrichissement - Outils d'import et transformation des donnees
- Performance 40-60x - Cache embeddings precomputes
- Fallback multi-modeles - 5 modeles Gemini en cascade
- Historique des recettes - Derniers 5 cocktails crees dans la sidebar
- Filtres avances - Type (Alcool/Sans alcool/Tous), Difficulte, Temps de preparation
- Recherche SBERT - Rechercher dans les 600 cocktails de la base CSV
- Metriques de performance - Temps de reponse, taux de cache hit, requetes totales
- Analytics - Logging JSON des requetes pour analyse
- Export recette - Telecharger la recette en format texte
- Bouton Surprise - "Surprends-moi!" genere un cocktail aleatoire
- Son ambient - Musique jazz optionnelle (annees 1920)
Cette section documente les decisions techniques prises pour valider les competences du Bloc 2 "Piloter et implementer des solutions d'IA".
| Critere | Justification |
|---|---|
| Cout zero | Pas d'appel API pour la similarite semantique - modele execute localement |
| Latence faible | ~50ms vs 200-500ms pour API cloud (OpenAI embeddings) |
| Confidentialite | Donnees utilisateur restent locales, pas de transfert vers serveurs tiers |
| Reproductibilite | Meme modele = memes embeddings, resultats deterministes |
| Offline capable | Fonctionne sans connexion internet apres telechargement initial |
Alternative rejetee : OpenAI text-embedding-3 (cout par token, latence reseau, dependance externe)
| Critere | Justification |
|---|---|
| Optimisation industrielle des couts API | Evite les appels redondants a Gemini (15 req/min gratuit) |
| Reduction latence | Reponse instantanee (~5ms) pour requetes deja vues vs ~2s pour API |
| Simplicite | Pas de dependance externe (Redis, Memcached, DynamoDB) |
| Persistance | Survit aux redemarrages de l'application |
| Auditabilite | Fichier JSON lisible pour debug et analyse |
Implementation : Cle MD5 de la requete normalisee → Lookup O(1)
Le seuil de pertinence du guardrail (similarite cosinus minimale) a ete calibre empiriquement :
| Seuil | Comportement |
|---|---|
| 0.20 | Trop permissif - accepte "pizza", "meteo" |
| 0.30 | Optimal - rejette hors-sujet, accepte variations cocktails |
| 0.50 | Trop restrictif - rejette "quelque chose de frais" |
Methode : Test sur 100+ requetes reelles, matrice de confusion, F1-score optimise.
| Critere | Justification |
|---|---|
| Robustesse | Evite les abus et injections de prompts hors-domaine |
| UX | Message explicite plutot que reponse incoherente ou erreur API |
| Evaluation modele | Metrique mesurable (taux de rejet, precision, recall) |
| Scalabilite | Filtre en amont avant appel API couteux |
| Critere | Justification |
|---|---|
| Tier gratuit genereux | 15 requetes/minute, suffisant pour demo et evaluation |
| Qualite generation | gemini-1.5-flash produit du JSON structure fiable |
| Latence | ~1-2s pour generation complete |
| Fallback | Mode offline avec recettes pre-generees si API indisponible |
L'interface combine texte libre et donnees structurees (critere RNCP obligatoire) :
| Element | Type | Justification |
|---|---|---|
| Envie | Texte libre | Capture la creativite et les nuances ("fruite", "rafraichissant") |
| Budget | Dropdown | Donnee structuree, filtrage efficace, UX guidee |
Options Budget :
- Economique (< 8€)
- Modere (8-15€)
- Premium (15-25€)
- Luxe (> 25€)
La requete enrichie {envie} (budget: {selection}) est envoyee au backend RAG.
# Clone the repository
git clone https://github.com/Adam-Blf/ia-pero-final.git
cd ia-pero
# Create virtual environment
python -m venv .venv
# Activate (Windows)
.venv\Scripts\activate
# Activate (Linux/Mac)
source .venv/bin/activate
# Install dependencies
pip install -r requirements.txt
# Configure API key (optionnel - mode fallback sans cle)
cp .env.example .env
# Editer .env et ajouter GOOGLE_API_KEY# Interface Similarite Semantique
streamlit run app.py
# Interface Speakeasy (Cocktails)
streamlit run src/app.pyL'application sera disponible sur http://localhost:8501
- Python 3.9+
- Streamlit >= 1.35 - Interface web interactive
- Sentence-Transformers >= 2.2.0 - Embeddings SBERT
- PyTorch >= 2.0.0 - Backend ML
- Transformers >= 4.34.0 - HuggingFace models
- Google Generative AI >= 0.3.0 - Gemini API
- Pandas - Data manipulation
- NumPy - Numerical operations
- Plotly >= 5.18.0 - Graphiques interactifs (radar chart)
| Modele | Dimensions | Description |
|---|---|---|
all-MiniLM-L6-v2 |
384 | Rapide et leger (default) |
all-mpnet-base-v2 |
768 | Meilleure qualite |
paraphrase-multilingual-MiniLM-L12-v2 |
384 | Support multilingue |
Le module src/backend.py fournit un systeme de guardrail semantique + generation GenAI :
from src.backend import check_relevance, generate_recipe
# Verification de pertinence (guardrail)
result = check_relevance("Je veux un mojito")
# {"status": "ok", "similarity": 0.72}
result = check_relevance("Quelle heure est-il ?")
# {"status": "error", "message": "Desole, le barman ne comprend que les commandes de boissons !"}
# Generation de recette avec cache JSON + Gemini
recipe = generate_recipe("mojito frais et tropical")
# {"status": "ok", "recipe": {...}, "cached": False}Pipeline de generation :
- Guardrail semantique (SBERT)
- Verification cache JSON
- Appel API Gemini (si pas en cache)
- Fallback local (si API indisponible)
- Mise en cache du resultat
Cette section démontre le fonctionnement du guardrail sémantique et les optimisations de performance.
# Activer l'environnement virtuel
.venv\Scripts\activate # Windows
source .venv/bin/activate # Linux/Mac
# Lancer l'interface Speakeasy
streamlit run src/app.pyL'application sera disponible sur http://localhost:8501
Note Performance: Le premier chargement prend ~3s (précompilation des embeddings), puis les requêtes sont très rapides (~50ms avec cache, ~1-2s sans cache).
Essayez ces requetes pour voir le message d'erreur :
| Requete | Resultat |
|---|---|
| "Comment reparer mon velo ?" | Message d'erreur |
| "Quelle est la capitale de la France ?" | Message d'erreur |
| "Quel temps fait-il ?" | Message d'erreur |
| "Raconte-moi une blague" | Message d'erreur |
Message affiche : "Desole, le barman ne comprend que les commandes de boissons !"
| Requete | Resultat |
|---|---|
| "Je veux un mojito" | Recette + Radar chart |
| "Un cocktail fruite et rafraichissant" | Recette + Radar chart |
| "Whisky sour" | Recette + Radar chart |
| "Quelque chose avec du rhum" | Recette + Radar chart |
Des tests Playwright verifient automatiquement le guardrail :
# Installer Playwright (premiere fois)
playwright install chromium
# Lancer les tests
pytest tests/test_guardrail.py -vCouverture des tests :
test_off_topic_query_shows_error: Verifie qu'une requete hors-sujet affiche l'erreurtest_cocktail_query_shows_recipe: Verifie qu'une requete cocktail affiche la recettetest_complete_flow: Scenario complet (erreur puis succes)
ia-pero/
├── app.py # Streamlit app (Similarite Semantique)
├── requirements.txt # Python dependencies
├── .env.example # Template configuration API
├── .gitignore
├── README.md
├── RAPPORT_FINAL.md # Rapport RNCP Bloc 2 (27 pages)
├── SOUTENANCE_SLIDES.md # Slides presentation (12 slides)
├── pdf-compact.json # Configuration generation PDF
├── src/
│ ├── __init__.py
│ ├── app.py # Streamlit app (Speakeasy Cocktails) - 1,243 lignes
│ ├── embeddings.py # SBERT logic (78 lignes)
│ ├── backend.py # RAG engine, guardrail & Gemini (436 lignes)
│ ├── generate_data.py # Generateur de 600 cocktails (645 lignes)
│ ├── ingredient_profiler.py # Profilage ingredients 4 niveaux (441 lignes)
│ ├── kaggle_integration.py # Parser Kaggle 1600+ cocktails (390 lignes)
│ └── utils.py # Utility functions
├── data/
│ ├── cocktails.csv # Base de 600 cocktails
│ ├── known_ingredients.json # 61 ingredients profiles
│ ├── recipe_cache.json # Recipe cache (auto-generated)
│ └── analytics.json # Analytics log (auto-generated)
├── scripts/
│ ├── download_kaggle.py # Telechargement dataset Kaggle
│ ├── enrich_kaggle.py # Enrichissement donnees
│ ├── export_known_ingredients.py # Export ingredients
│ └── test_integration.py # Tests integration
├── tests/
│ └── test_guardrail.py # Tests E2E Playwright
├── assets/
│ ├── logo.svg # Logo Art Deco
│ └── logo-efrei.png # Logo EFREI
└── .streamlit/
└── config.toml # Theme configuration
Total : ~3,279 lignes de code Python
Symptôme: L'application était très lente, surtout lors des recherches SBERT.
Cause racine: La fonction search_cocktails_sbert() encodait tous les 600 cocktails à chaque recherche utilisateur, prenant ~2-3 secondes par recherche.
✅ Mise en cache des embeddings via la nouvelle fonction _precompute_cocktail_embeddings():
- Les embeddings des 600 cocktails sont calculés une seule fois au démarrage
- Mise en cache avec
@st.cache_data(réutilisation instantanée) - Seule la requête utilisateur est encodée à chaque recherche (~20ms)
| Métrique | Avant | Après | Amélioration |
|---|---|---|---|
| Recherche SBERT | 2-3 secondes | ~50ms | 40-60x plus rapide 🚀 |
| Génération avec cache hit | ~2.5s | ~50ms | 50x plus rapide |
| Premier chargement | ~3s | ~3s | Identique (précomputation) |
| Mémoire utilisée | ~150 Mo | ~180 Mo | +20% (acceptable) |
┌─────────────────────────────────────────────────────────┐
│ USER QUERY: "mojito tropical" │
└────────────────────┬────────────────────────────────────┘
↓
┌────────────────────────┐
│ Guardrail Check │ ~50ms (SBERT local)
│ Pertinence: 0.72 ✓ │
└───────────┬────────────┘
↓
┌────────────────────────┐
│ Cache Lookup (MD5) │ ~5ms (JSON file)
│ Hit: False ✗ │
└───────────┬────────────┘
↓
┌────────────────────────┐
│ Gemini API Call │ ~1-2s (externe)
│ gemini-2.5-flash-lite │
└───────────┬────────────┘
↓
┌────────────────────────┐
│ Save to Cache │ ~10ms (JSON write)
└───────────┬────────────┘
↓
┌────────────────────────┐
│ Display Recipe │ <5ms (Streamlit)
└────────────────────────┘
TOTAL TIME: ~1.5-2.5s (nouvelle recette)
~50ms (recette en cache)
✅ Commentaires exhaustifs: Tout le code est maintenant documenté en français de manière humaine et accessible.
✅ Cache LRU pour le modèle SBERT: Le modèle (~91 Mo) est chargé une seule fois et réutilisé.
✅ Fallback automatique Gemini: Si un modèle est en rate limit, on passe automatiquement au suivant:
gemini-2.5-flash-lite(10 RPM)gemini-2.5-flash(5 RPM)gemini-3-flash(5 RPM)gemini-1.5-flash-latest(fallback)- Génération locale si tous échouent
✅ Analytics détaillées: Chaque requête est loguée avec timestamp, durée, cache hit, pour analyse ultérieure.
- Utilisez le cache: Les requêtes identiques sont instantanées (~50ms)
- Évitez les longues descriptions: Restez concis (< 50 mots)
- Configurez GOOGLE_API_KEY: Sans clé API, le fallback local est moins créatif
- Redémarrez l'app occasionnellement: Streamlit peut accumuler de la mémoire
L'onglet Stats affiche:
- Nombre de requêtes totales
- Taux de cache hit (%)
- Temps de réponse moyen
- Historique des 10 dernières créations
- Rapport RNCP Bloc 2 : Documentation complete 27 pages
- Analyse semantique detaillee avec exemples concrets
- Pipeline SBERT documente (embeddings 384D, similarite cosinus)
- Comparaison recherche mot-cle vs semantique
- Visualisation espace vectoriel (clusters t-SNE)
- Integration Kaggle : 1600+ cocktails supplementaires
- Parser automatique du dataset Kaggle
- Enrichissement avec profils gustatifs
- Scripts de transformation et export
- Profilage ingredients : 61 ingredients avec profils 7D
- 4 niveaux de profilage (connu, categorie, IA, defaut)
- Profils gustatifs (Douceur, Acidite, Amertume, Force, Fraicheur, Prix, Qualite)
- Documentation : QUICK_START, KAGGLE_INTEGRATION, SOUTENANCE_SLIDES
- Assets : Logo EFREI pour rapport officiel
- Performance Critique: Recherche SBERT 40-60x plus rapide (50ms vs 2-3s)
- Implémentation du cache des embeddings via
_precompute_cocktail_embeddings() - Seule la requête utilisateur est encodée dynamiquement
- Embeddings des 600 cocktails précompilés au démarrage
- Implémentation du cache des embeddings via
- Documentation: Commentaires exhaustifs en français sur tout le codebase
- Explication détaillée du fonctionnement de chaque fonction
- Documentation du workflow complet et des optimisations
- Architecture et diagrammes de flux ajoutés au README
- Fallback Gemini: Basculement automatique entre 5 modèles en cas de rate limit
- Monitoring: Analytics enrichies avec durées d'exécution et cache hits
- Sidebar Complete :
- Historique des 5 derniers cocktails crees (session state)
- Filtres avances : Type (Alcool/Sans alcool), Difficulte (1-5), Temps de preparation
- Recherche SBERT dans les 600 cocktails de la base CSV
- Metriques de performance en temps reel
- Bouton son ambient (jazz annees 1920)
- Analytics : Logging JSON des requetes (timestamp, query, budget, cached, temps)
- Export : Telecharger la recette en format texte stylise
- UX : Bouton "Surprends-moi!" pour generation aleatoire
- Performance : Affichage temps de reponse et taux de cache hit
- Google Gemini : Integration complete de l'API Gemini
- Prompt Engineering "Persona Barman Speakeasy"
- Parsing JSON robuste avec fallback
- Mode offline si API indisponible
- Documentation RNCP : Section justification des choix techniques
- Dataset : 600 cocktails avec descriptions semantiques riches
- Configuration : Support
.envpour cle API
- Documentation Professeurs : Section dediee pour tester le guardrail
- Instructions de lancement detaillees
- Exemples de requetes rejetees vs acceptees
- Guide des tests E2E Playwright
- Interface Speakeasy :
src/app.py- Theme bar clandestin annees 1920 (noir/or)
- CSS custom injecte via
st.markdown(unsafe_allow_html=True) - Graphique radar Plotly (profil gustatif)
- Gestion des etats (empty, loading, error, success)
- Backend RAG & Guardrail :
src/backend.pycheck_relevance(): Guardrail semantique (seuil 0.30)generate_recipe(): Generation avec cache JSON- Cache LRU pour le modele SBERT
- Initial project setup
- Streamlit interface with SBERT integration
- Similarity matrix visualization
- Multi-model support
MIT
Par Adam Beloucif · Data Engineer & Fullstack Developer · GitHub · LinkedIn