|
@@ -38,7 +38,7 @@ class AgentState(TypedDict):
|
|
|
|
|
|
|
|
#model_gpt = ChatOpenAI(model="gpt-5.3-codex" )
|
|
#model_gpt = ChatOpenAI(model="gpt-5.3-codex" )
|
|
|
|
|
|
|
|
-model_gpt_5 = ChatOpenAI(model="o4-mini" )
|
|
|
|
|
|
|
+#model_gpt_5 = ChatOpenAI(model="o4-mini" )
|
|
|
tools = [ search_tool , excel_code_interpreter, inspect_data ]
|
|
tools = [ search_tool , excel_code_interpreter, inspect_data ]
|
|
|
|
|
|
|
|
|
|
|
|
@@ -49,16 +49,16 @@ from langchain_experimental.llms.ollama_functions import OllamaFunctions
|
|
|
from langchain_community.chat_models import ChatOllama
|
|
from langchain_community.chat_models import ChatOllama
|
|
|
|
|
|
|
|
# On crée une instance standard pour l'Analyseur
|
|
# On crée une instance standard pour l'Analyseur
|
|
|
-analyseur_llm = ChatOllama(
|
|
|
|
|
|
|
+local_llm_chat = ChatOllama(
|
|
|
model="deepseek-coder-v2:16b-lite-instruct-q4_K_M",
|
|
model="deepseek-coder-v2:16b-lite-instruct-q4_K_M",
|
|
|
temperature=0,
|
|
temperature=0,
|
|
|
format="json" # On garde le format JSON ici
|
|
format="json" # On garde le format JSON ici
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
-local_llm = OllamaFunctions(
|
|
|
|
|
- model="deepseek-coder-v2:16b-lite-instruct-q4_K_M",
|
|
|
|
|
- #model="llama3.1:8b",
|
|
|
|
|
|
|
+local_llm_fonction = OllamaFunctions(
|
|
|
|
|
+ #model="deepseek-coder-v2:16b-lite-instruct-q4_K_M",
|
|
|
|
|
+ model="llama3.1:8b",
|
|
|
temperature=0,
|
|
temperature=0,
|
|
|
format="json",
|
|
format="json",
|
|
|
num_ctx=4096
|
|
num_ctx=4096
|
|
@@ -67,7 +67,7 @@ local_llm = OllamaFunctions(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-model_with_tools = local_llm.bind_tools(tools)
|
|
|
|
|
|
|
+model_with_tools = local_llm_fonction.bind_tools(tools)
|
|
|
|
|
|
|
|
def agent_analyseur(state: AgentState):
|
|
def agent_analyseur(state: AgentState):
|
|
|
import os
|
|
import os
|
|
@@ -128,7 +128,7 @@ def agent_analyseur(state: AgentState):
|
|
|
|
|
|
|
|
# 5. APPEL DU MODÈLE
|
|
# 5. APPEL DU MODÈLE
|
|
|
# Note : On utilise local_llm (ou analyseur_llm si tu as séparé)
|
|
# Note : On utilise local_llm (ou analyseur_llm si tu as séparé)
|
|
|
- response = local_llm.invoke(final_messages, config=config_analyseur)
|
|
|
|
|
|
|
+ response = local_llm_chat.invoke(final_messages, config=config_analyseur)
|
|
|
|
|
|
|
|
return {"messages": [response]}
|
|
return {"messages": [response]}
|
|
|
|
|
|
|
@@ -144,28 +144,36 @@ def agent_executor(state: AgentState):
|
|
|
# On construit le prompt en injectant le nom dynamiquement
|
|
# On construit le prompt en injectant le nom dynamiquement
|
|
|
prompt = (
|
|
prompt = (
|
|
|
"### SYSTEM ROLE ###\n"
|
|
"### SYSTEM ROLE ###\n"
|
|
|
- "Tu es un moteur d'exécution Python. Réponds EXCLUSIVEMENT en JSON.\n\n"
|
|
|
|
|
|
|
+ "Tu es un moteur d'exécution Python. Réponds EXCLUSIVEMENT au format JSON.\n\n"
|
|
|
|
|
+
|
|
|
|
|
+ "### FICHIER ACTUEL ###\n"
|
|
|
|
|
+ f"Fichier : '{file_name}' | Chemin : 'data/{file_name}'\n\n"
|
|
|
|
|
+
|
|
|
|
|
+ "### INSTRUCTIONS DE CODE & SÉCURITÉ ###\n"
|
|
|
|
|
+ "1. Imports : Commence par les imports (pandas, matplotlib.pyplot, etc.).\n"
|
|
|
|
|
+ f"2. Chargement : df = pd.read_csv('data/{file_name}')\n"
|
|
|
|
|
+
|
|
|
|
|
+ "3. SYNTAXE (Apostrophes) : Utilise TOUJOURS des doubles guillemets (\"\") pour les labels et titres.\n"
|
|
|
|
|
+ " INTERDIT : plt.title('L'analyse') | VALIDE : plt.title(\"L'analyse\")\n\n"
|
|
|
|
|
|
|
|
- "### FICHIER ACTUEL (IMPORTANT) ###\n"
|
|
|
|
|
- f"Tu dois travailler UNIQUEMENT sur le fichier : '{file_name}'\n"
|
|
|
|
|
- f"Le chemin à utiliser dans ton code est : 'data/{file_name}'\n\n"
|
|
|
|
|
|
|
+ "4. GRAPHIQUES (SAUVEGARDE OBLIGATOIRE) :\n"
|
|
|
|
|
+ " - Ne JAMAIS utiliser plt.show().\n"
|
|
|
|
|
+ " - Utilise TOUJOURS : plt.savefig('outputs/nom_du_graphe.png')\n"
|
|
|
|
|
+ " - Assure-toi que le dossier 'outputs/' est utilisé pour la sauvegarde.\n\n"
|
|
|
|
|
|
|
|
- "### INSTRUCTIONS DE CODE ###\n"
|
|
|
|
|
- "1. Commence ton code par l'import des librairies.\n"
|
|
|
|
|
- f"2. Charge le fichier avec : df = pd.read_csv('data/{file_name}') (ou read_excel).\n"
|
|
|
|
|
- "3. Termine TOUJOURS par une variable 'result' contenant ton analyse.\n\n"
|
|
|
|
|
|
|
+ "5. SORTIE : Termine ton code par une variable 'result' contenant le résumé texte des données "
|
|
|
|
|
+ "ou la confirmation du fichier généré (ex: result = \"Graphique sauvegardé : ventes.png\").\n\n"
|
|
|
|
|
|
|
|
"### EXEMPLE DE FORMAT ATTENDU ###\n"
|
|
"### EXEMPLE DE FORMAT ATTENDU ###\n"
|
|
|
"{\n"
|
|
"{\n"
|
|
|
" \"tool\": \"excel_code_interpreter\",\n"
|
|
" \"tool\": \"excel_code_interpreter\",\n"
|
|
|
" \"tool_input\": {\n"
|
|
" \"tool_input\": {\n"
|
|
|
f" \"file_path\": \"data/{file_name}\",\n"
|
|
f" \"file_path\": \"data/{file_name}\",\n"
|
|
|
- " \"code\": \"import pandas as pd\\n# Ton code ici...\"\n"
|
|
|
|
|
|
|
+ " \"code\": \"import pandas as pd\\nimport matplotlib.pyplot as plt\\n# ... code ...\\nplt.savefig('outputs/chart.png')\\nresult = 'Analyse terminée'\"\n"
|
|
|
" }\n"
|
|
" }\n"
|
|
|
"}"
|
|
"}"
|
|
|
)
|
|
)
|
|
|
-
|
|
|
|
|
- # Nettoyage des messages pour Ollama (comme avant)
|
|
|
|
|
|
|
+ # Nettoyage des messages pour Ollama (comme avant)
|
|
|
cleaned_messages = []
|
|
cleaned_messages = []
|
|
|
for m in state["messages"]:
|
|
for m in state["messages"]:
|
|
|
if m.type == "tool":
|
|
if m.type == "tool":
|
|
@@ -210,7 +218,17 @@ def agent_reporter(state : AgentState) :
|
|
|
" '📥 Fichier disponible : [nom_du_fichier.csv/xlsx]'\n"
|
|
" '📥 Fichier disponible : [nom_du_fichier.csv/xlsx]'\n"
|
|
|
"3. SANS CHEMIN : Ne mentionne JAMAIS le dossier 'outputs/' ou 'data/'. Utilise uniquement le nom final du fichier.\n\n"
|
|
"3. SANS CHEMIN : Ne mentionne JAMAIS le dossier 'outputs/' ou 'data/'. Utilise uniquement le nom final du fichier.\n\n"
|
|
|
)
|
|
)
|
|
|
- messages = [SystemMessage(content=prompt_reporter)] + state["messages"]
|
|
|
|
|
|
|
|
|
|
- response = local_llm.invoke(messages )
|
|
|
|
|
- return{"messages" : [response]}
|
|
|
|
|
|
|
+ # Nettoyage des messages pour Ollama (comme avant)
|
|
|
|
|
+ cleaned_messages = []
|
|
|
|
|
+ for m in state["messages"]:
|
|
|
|
|
+ if m.type == "tool":
|
|
|
|
|
+ cleaned_messages.append(HumanMessage(content=f"Résultat : {m.content}"))
|
|
|
|
|
+ else:
|
|
|
|
|
+ cleaned_messages.append(m)
|
|
|
|
|
+
|
|
|
|
|
+ # On place le prompt dynamique en premier
|
|
|
|
|
+ final_messages = [HumanMessage(content=prompt_reporter)] + cleaned_messages
|
|
|
|
|
+
|
|
|
|
|
+ return {"messages": [local_llm_chat.invoke(final_messages)]}
|
|
|
|
|
+
|