Spaces:
Sleeping
Sleeping
| 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.""" | |
| 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() | |