BELASRI's picture
Déploiement initial : app.py
72d9c23 verified
Raw
History Blame Contribute Delete
4.8 kB
import os
import sys
import streamlit as st
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src"))
st.set_page_config(page_title="Assistant Solvabilité II", page_icon="⚖️")
REFORMULATION_PROMPT = """Voici l'historique récent d'une conversation sur Solvabilité II, suivi
d'une nouvelle question qui peut faire référence au contexte précédent.
HISTORIQUE :
{historique}
NOUVELLE QUESTION : {question}
Reformule cette nouvelle question sous une forme autonome et complète, qui a
du sens sans le contexte de la conversation. Si elle est déjà autonome,
renvoie-la telle quelle. Réponds uniquement avec la question reformulée,
rien d'autre."""
@st.cache_resource(show_spinner="Chargement du modèle BGE-M3 et de l'index...")
def charger_rag():
import rag
rag._get_embedding_model() # force le chargement immédiat (pas paresseux) au démarrage
return rag
def reformuler_question(rag_module, historique, question):
if not historique:
return question
historique_texte = "\n".join(
f"{'Utilisateur' if m['role'] == 'user' else 'Assistant'} : {m['content']}"
for m in historique
)
prompt = REFORMULATION_PROMPT.format(historique=historique_texte, question=question)
# appel_llm gère seul le fallback Gemini -> Mistral (Décision 009) ; verbose=False
# pour ne rien afficher côté utilisateur, la bascule reste visible en mode dev.
return rag_module.appel_llm(prompt, taille="long", verbose=False).strip()
def message_erreur_pour(exception):
err = str(exception)
if "429" in err or "401" in err or "402" in err:
return "Les fournisseurs LLM (Gemini et Mistral) sont actuellement indisponibles. Réessayez dans quelques instants."
return f"Une erreur est survenue lors de la génération de la réponse : {err}"
def afficher_articles(articles):
if not articles:
return
with st.expander("Articles consultés"):
for a in articles:
st.markdown(f"- **[Article {a['numero_article']}]** — {a['titre']}")
def nouvelle_conversation():
st.session_state.messages = []
def main():
rag_module = charger_rag()
st.title("Assistant Solvabilité II")
st.caption("312 articles · directive 2009/138/CE · réponses citées")
st.info("Ne constitue pas un avis actuariel", icon="ℹ️")
with st.sidebar:
st.button("Nouvelle conversation", on_click=nouvelle_conversation, use_container_width=True)
if "messages" not in st.session_state:
st.session_state.messages = []
for msg in st.session_state.messages:
with st.chat_message(msg["role"]):
st.markdown(msg["content"])
afficher_articles(msg.get("articles"))
question = st.chat_input("Posez votre question sur Solvabilité II...")
if question:
st.session_state.messages.append({"role": "user", "content": question})
with st.chat_message("user"):
st.markdown(question)
with st.chat_message("assistant"):
with st.spinner("Recherche en cours..."):
# ne garde que les ~5 derniers échanges (10 messages) pour la reformulation,
# afin de ne pas saturer le contexte envoyé au LLM
historique_recent = st.session_state.messages[:-1][-10:]
try:
question_reformulee = reformuler_question(rag_module, historique_recent, question)
except Exception:
question_reformulee = question
try:
resultat = rag_module.poser_question(
question_reformulee, k=5, verbose=False, clarify=True
)
if isinstance(resultat, dict) and resultat.get("type") == "clarification":
reponse = resultat["message"]
articles = []
else:
reponse = resultat
articles = rag_module._rechercher(
rag_module.expand_query(question_reformulee), 5
)
st.markdown(reponse)
afficher_articles(articles)
st.session_state.messages.append(
{"role": "assistant", "content": reponse, "articles": articles}
)
except Exception as e:
erreur = message_erreur_pour(e)
st.error(erreur)
st.session_state.messages.append(
{"role": "assistant", "content": erreur, "articles": []}
)
st.markdown("---")
st.caption("Prototype — Mohamed Amine Belasri · Iconcilio · 2026")
if __name__ == "__main__":
main()