IAREST / app.py
Oxyb's picture
Rename app (31).py to app.py
950d0b3 verified
Raw
History Blame Contribute Delete
7.47 kB
"""
Avis'IA Resto : application de gestion des réponses aux avis clients
Génération assistée par IA (Mistral) avec validation humaine (Human-in-the-Loop)
Déploiement : Hugging Face Spaces (SDK Gradio)
Prérequis : secret MISTRAL_API_KEY défini dans Settings > Variables and secrets
"""
import os
import gradio as gr
from mistralai import Mistral
# ---------------------------------------------------------------
# 1. Configuration et client Mistral
# ---------------------------------------------------------------
MISTRAL_API_KEY = os.environ.get("MISTRAL_API_KEY")
MODEL = "mistral-small-latest"
client = Mistral(api_key=MISTRAL_API_KEY) if MISTRAL_API_KEY else None
# ---------------------------------------------------------------
# 2. Templates de secours (fallback) par note
# Utilisés uniquement si l'API est indisponible
# ---------------------------------------------------------------
TEMPLATES_FALLBACK = {
1: ("Nous sommes sincèrement désolés que votre expérience n'ait pas été "
"à la hauteur. Vos remarques sont précieuses et nous allons agir. "
"N'hésitez pas à nous contacter directement pour en discuter."),
2: ("Merci pour votre retour honnête. Nous regrettons que votre visite "
"n'ait pas été pleinement satisfaisante et nous prenons vos remarques "
"très au sérieux. Nous espérons pouvoir vous reconquérir."),
3: ("Merci pour votre avis. Nous notons vos points d'amélioration et "
"travaillons chaque jour à offrir une meilleure expérience. "
"Au plaisir de vous accueillir à nouveau."),
4: ("Merci beaucoup pour ce retour positif ! Nous sommes ravis que votre "
"expérience vous ait plu et prenons note de vos suggestions. "
"À très bientôt !"),
5: ("Un immense merci pour ce superbe avis ! Toute l'équipe est touchée. "
"Nous avons hâte de vous retrouver très vite !"),
}
# ---------------------------------------------------------------
# 3. Construction du prompt à partir du contenu réel de l'avis
# ---------------------------------------------------------------
def construire_prompt(avis_texte: str, note: int, nom_restaurant: str) -> str:
if note <= 2:
consigne_ton = (
"Le client est mécontent. Adopte un ton empathique et professionnel. "
"Présente des excuses sincères, reprends explicitement le ou les "
"problèmes concrets mentionnés dans l'avis, et propose un contact "
"direct ou un geste commercial pour rétablir la confiance."
)
elif note == 3:
consigne_ton = (
"Le client est mitigé. Remercie-le, reconnais les points négatifs "
"qu'il mentionne, valorise les points positifs, et montre que ses "
"remarques seront prises en compte."
)
else:
consigne_ton = (
"Le client est satisfait. Adopte un ton chaleureux et reconnaissant. "
"Reprends un ou deux éléments précis qu'il a appréciés et invite-le "
"à revenir."
)
return f"""Tu es le gérant du restaurant {nom_restaurant}.
Rédige une réponse publique à l'avis client ci-dessous.
Avis du client ({note}/5 étoiles) :
\"\"\"{avis_texte}\"\"\"
Consignes :
- {consigne_ton}
- Réponds en français, en 3 à 5 phrases maximum.
- Personnalise la réponse avec les éléments concrets de l'avis (plats, service, ambiance...). Ne reste jamais générique.
- Ne mentionne jamais que tu es une IA.
- Pas de formule pompeuse, un ton naturel de restaurateur qui répond à ses clients.
- Termine par une signature simple : "L'équipe de {nom_restaurant}".
Réponds uniquement avec le texte de la réponse, sans commentaire ni guillemets."""
# ---------------------------------------------------------------
# 4. Génération de la réponse avec bascule automatique sur fallback
# ---------------------------------------------------------------
def generer_reponse(avis_texte: str, note: int, nom_restaurant: str):
"""Retourne (reponse, source) où source vaut 'llm' ou 'fallback'."""
note = int(note)
avis_texte = (avis_texte or "").strip()
nom_restaurant = (nom_restaurant or "notre restaurant").strip()
if not avis_texte:
return TEMPLATES_FALLBACK.get(note, TEMPLATES_FALLBACK[3]), "fallback"
if client is None:
return TEMPLATES_FALLBACK.get(note, TEMPLATES_FALLBACK[3]), "fallback"
try:
reponse_api = client.chat.complete(
model=MODEL,
messages=[
{
"role": "user",
"content": construire_prompt(avis_texte, note, nom_restaurant),
}
],
temperature=0.7,
max_tokens=300,
)
texte = reponse_api.choices[0].message.content.strip()
if texte:
return texte, "llm"
return TEMPLATES_FALLBACK.get(note, TEMPLATES_FALLBACK[3]), "fallback"
except Exception as e:
print(f"[Avis'IA Resto] Erreur API Mistral, bascule sur template : {e}")
return TEMPLATES_FALLBACK.get(note, TEMPLATES_FALLBACK[3]), "fallback"
# ---------------------------------------------------------------
# 5. Fonction branchée sur l'interface Gradio
# ---------------------------------------------------------------
def repondre_avis(avis_texte, note, nom_restaurant):
reponse, source = generer_reponse(avis_texte, note, nom_restaurant)
if source == "fallback":
info = "ℹ️ Réponse générée en mode standard (modèle IA indisponible)."
else:
info = "✅ Réponse générée par IA (Mistral). Relisez et ajustez avant publication."
return reponse, info
# ---------------------------------------------------------------
# 6. Interface Gradio
# ---------------------------------------------------------------
with gr.Blocks(title="Avis'IA Resto") as demo:
gr.Markdown(
"""
# 🍽️ Avis'IA Resto
### Réponses aux avis clients assistées par IA, validées par l'humain
Générez une proposition de réponse personnalisée, puis relisez et
ajustez avant publication. L'humain garde toujours la main.
"""
)
with gr.Row():
with gr.Column():
nom = gr.Textbox(
label="Nom du restaurant",
value="Chez Coralie",
)
avis = gr.Textbox(
label="Texte de l'avis client",
lines=6,
placeholder="Collez ici l'avis du client...",
)
etoiles = gr.Slider(
minimum=1,
maximum=5,
step=1,
value=3,
label="Note (étoiles)",
)
bouton = gr.Button("Générer la réponse", variant="primary")
with gr.Column():
sortie = gr.Textbox(
label="Réponse proposée (modifiable avant publication)",
lines=8,
interactive=True,
)
statut = gr.Markdown()
regenerer = gr.Button("🔄 Régénérer une autre version")
bouton.click(
fn=repondre_avis,
inputs=[avis, etoiles, nom],
outputs=[sortie, statut],
)
regenerer.click(
fn=repondre_avis,
inputs=[avis, etoiles, nom],
outputs=[sortie, statut],
)
if __name__ == "__main__":
demo.launch()