Agents.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. import os
  2. from typing import Annotated, Sequence, TypedDict, Optional , List
  3. from dotenv import load_dotenv
  4. from langchain_openai import ChatOpenAI
  5. from langchain_core.messages import BaseMessage, HumanMessage, SystemMessage
  6. from langgraph.graph.message import add_messages
  7. from langchain_google_genai import ChatGoogleGenerativeAI
  8. from langchain_groq import ChatGroq
  9. from langfuse import get_client
  10. # Import de tes outils corrigés
  11. from tools import excel_code_interpreter, search_tool , inspect_data
  12. langfuse = get_client()
  13. load_dotenv()
  14. # 1. Définition du State
  15. class AgentState(TypedDict):
  16. messages: Annotated[Sequence[BaseMessage], add_messages]
  17. current_df_path: Optional[str]
  18. generated_charts : List[str]
  19. # 2. Configuration du Modèle et des Outils
  20. model =ChatGroq(model="llama-3.3-70b-versatile")
  21. #model_llama=ChatGoogleGenerativeAI(model="gemini-2.5-flash")
  22. model_gpt = ChatOpenAI(model="o3-mini")
  23. tools = [ search_tool , excel_code_interpreter, inspect_data ]
  24. model_with_tools = model.bind_tools(tools)
  25. # 3. Définition des Nœuds
  26. def agent_analyseur(state: AgentState):
  27. prompt = (
  28. "Tu es l'Analyseur Stratégique. Ton rôle est de décomposer la demande utilisateur en étapes techniques claires.\n"
  29. "RÈGLES CRITIQUES :\n"
  30. "1. Définis un plan d'action claire \n"
  31. "2. Ne pose JAMAIS de questions sur les données (prix, stock). L'Exécuteur doit les trouver lui-même via les outils.\n"
  32. "3. Sois concis : Ton message doit être une directive pour l'Exécuteur, pas une conversation avec l'utilisateur."
  33. )
  34. msg = [SystemMessage(content=prompt)] + state["messages"]
  35. response = model.invoke(msg)
  36. return {"messages": [response]}
  37. def agent_executor(state: AgentState):
  38. # 1. On récupère le chemin actuel depuis le State
  39. # Si current_df_path est None, on met data.xlsx par défaut
  40. file_path = state.get("current_df_path") or "data.xlsx"
  41. # 2. On construit un prompt qui contient le VRAI chemin
  42. prompt = (
  43. f"Tu es un Data Scientist expert spécialisé en automatisation. Le fichier cible se trouve dans le dossier 'data/' : '{file_path}'.\n"
  44. "PROTOCOLE D'ACTION IMPÉRATIF :\n"
  45. "1. INSPECTION PRÉALABLE : Avant de générer le moindre code Python, appelle TOUJOURS 'inspect_data'.\n Tu dois connaître les colonnes réelles avant de coder.\n"
  46. "2. ACTION D'ABORD : Si tu n'as pas les données externes, appelle 'search_tool' immédiatement.\n"
  47. "3. ZÉRO SIMULATION : Il est strictement interdit d'inventer des prix ou des stocks. Si l'outil 'search_tool' donne une plage de prix (ex: 30-40€), calcule la moyenne (35€).\n"
  48. "4. LOGIQUE PANDAS : Ne fais aucun calcul manuel. Utilise 'df' pour filtrer le produit exact (ex: df[df['Produit'] == 'Souris']).\n"
  49. "5. VARIABLE RESULT : Ton script Python DOIT etre complis avec tout les imports et se terminer par 'result = ...'. C'est cette variable qui sera transmise au système.\n"
  50. "6. PERSISTANCE : Si un graphique est demandé, utilise 'plt.savefig('outputs/nom_du_graphe.png')'.\n"
  51. "Tu es un moteur d'exécution froid et précis."
  52. "STRUCTURE DE TA RÉPONSE POST-OUTIL :\n"
  53. "- 'Statut : Code exécuté avec succès.'\n"
  54. "- 'Fichiers : [liste des .png générés]'\n"
  55. "- 'Validation : Les données de [Colonnes] ont été traitées.'\n"
  56. "PAS DE DISCOURS, PAS D'ÉTAPES."
  57. )
  58. # 3. Préparation des messages
  59. messages = [SystemMessage(content=prompt)] + state["messages"]
  60. # 4. Appel du modèle avec les outils
  61. response = model_with_tools.invoke(messages)
  62. return {"messages": [response]}
  63. def agent_reporter(state : AgentState) :
  64. prompt_reporter = (
  65. "Tu es l'Agent de Reporting.\n"
  66. "Ton rôle est de transformer les résultats techniques fournis par l'Exécuteur en un compte rendu court, clair et factuel.\n\n"
  67. "RÈGLES STRICTES :\n"
  68. "- N'invente aucune information.\n"
  69. "- Ne répète pas les mêmes informations.\n"
  70. "- Évite les phrases génériques ou marketing.\n"
  71. "- Sois concis et direct.\n"
  72. "- Utilise uniquement les données réellement calculées.\n\n"
  73. "FORMAT DE SORTIE OBLIGATOIRE :\n"
  74. "1) Résultats clés\n"
  75. "- Liste courte des résultats principaux (produits les plus vendus, top commerciaux, etc.)\n\n"
  76. "2) Observations\n"
  77. "- Interprétation courte basée uniquement sur les résultats.\n\n"
  78. "3) Livrables générés\n"
  79. "- Liste des graphiques et fichiers créés avec leurs chemins exacts.\n\n"
  80. "IMPORTANT :\n"
  81. "- Maximum 6 à 8 lignes au total.\n"
  82. "- Pas de conclusion inutile.\n"
  83. "- Pas de texte explicatif long."
  84. )
  85. messages = [SystemMessage(content=prompt_reporter)] + state["messages"]
  86. response = model.invoke(messages)
  87. return{"messages" : [response]}