Compare commits
4 Commits
a9ff56c122
...
82a5491188
| Author | SHA1 | Date | |
|---|---|---|---|
|
82a5491188
|
|||
|
ea314e5c5c
|
|||
|
fc7f692ba3
|
|||
|
52bf2d5a82
|
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,6 +5,7 @@ mlflow.db
|
||||
# Par sécurité
|
||||
documents_projet/
|
||||
chroma_db/
|
||||
AgentReact/rapports_resumes/
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
@@ -2,7 +2,7 @@ from langgraph.graph import START, END
|
||||
from langgraph.graph.state import CompiledStateGraph
|
||||
from langgraph.checkpoint.memory import InMemorySaver
|
||||
|
||||
from utils.nodes import call_to_LLM, should_continue, task_ended, BasicToolNode, tool_node
|
||||
from utils.nodes import *
|
||||
from utils.state import getState
|
||||
from utils.tools import getTools
|
||||
|
||||
@@ -17,10 +17,21 @@ def getGraph()->CompiledStateGraph:
|
||||
|
||||
# Définition des sommets du graphe
|
||||
workflow.add_node(call_to_LLM)
|
||||
workflow.add_node(preparation_docs)
|
||||
workflow.add_node(inject_preparation_prompt)
|
||||
workflow.add_node("tool_node", tool_node)# BasicToolNode(tools=getTools())) # N'est pas une fonction, mais une classe instanciée, je dois précisier le nom du node
|
||||
workflow.add_node("weekly_report_tools", weekly_report_tools)
|
||||
|
||||
# Arrêtes
|
||||
workflow.set_entry_point("call_to_LLM")
|
||||
workflow.set_entry_point("inject_preparation_prompt")
|
||||
workflow.add_edge("inject_preparation_prompt", "preparation_docs")
|
||||
workflow.add_conditional_edges("preparation_docs", should_continue, {
|
||||
"tools":"weekly_report_tools",
|
||||
"no_tools":"call_to_LLM"
|
||||
})
|
||||
|
||||
#workflow.set_entry_point("call_to_LLM")
|
||||
workflow.add_edge("weekly_report_tools", "preparation_docs")
|
||||
workflow.add_edge("tool_node", "call_to_LLM")
|
||||
workflow.add_conditional_edges("call_to_LLM", should_continue, {
|
||||
"tools":"tool_node",
|
||||
@@ -31,4 +42,4 @@ def getGraph()->CompiledStateGraph:
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Affichage du graphe
|
||||
getGraph().get_graph().draw_mermaid_png(output_file_path="agent.png")
|
||||
getGraph().get_graph().draw_mermaid_png(output_file_path="imgs/agent.png")
|
||||
@@ -13,7 +13,7 @@ mlflow.set_experiment("TEST PROJET") # VOIR AVEC LA COMMANDE "MLFLOW SERVER"
|
||||
mlflow.langchain.autolog()
|
||||
|
||||
initial_input = {
|
||||
'messages':[HumanMessage("Recherche 'Recette de Monster' sur internet")]
|
||||
'messages':[SystemMessage("Salut")]
|
||||
}
|
||||
|
||||
config={"configurable": {"thread_id": 'yes'}}
|
||||
|
||||
@@ -5,7 +5,7 @@ from langgraph.types import Command
|
||||
from .InterruptPayload import InterruptPayload
|
||||
|
||||
# Une fonction pour stream et gérer proprement le graphe
|
||||
def streamGraph(initial_input:Dict, config:Dict, graphe:CompiledStateGraph):
|
||||
def streamGraph(initial_input:Dict, config:Dict, graphe:CompiledStateGraph, lastMsgIndex=0):
|
||||
# https://docs.langchain.com/oss/python/langgraph/interrupts#stream-with-human-in-the-loop-hitl-interrupts
|
||||
for mode, state in graphe.stream(
|
||||
initial_input,
|
||||
@@ -15,8 +15,11 @@ def streamGraph(initial_input:Dict, config:Dict, graphe:CompiledStateGraph):
|
||||
):
|
||||
if mode == "values":
|
||||
# Handle streaming message content
|
||||
msg = state['messages'][-1]
|
||||
msg.pretty_print()
|
||||
i=0
|
||||
for msg in state['messages'][lastMsgIndex:]: # Permet de gérer plusieurs nouveaux messages d'un coup
|
||||
msg.pretty_print()
|
||||
i+=1
|
||||
lastMsgIndex+=i
|
||||
|
||||
elif mode == "updates":
|
||||
# Check for interrupts
|
||||
@@ -25,7 +28,7 @@ def streamGraph(initial_input:Dict, config:Dict, graphe:CompiledStateGraph):
|
||||
|
||||
payload = InterruptPayload.fromJSON(payload) # Chargement de la requête depuis sa version JSON
|
||||
payload.humanDisplay() # L'utilisateur peut accepter/modifier/refuser ici
|
||||
streamGraph(Command(resume=payload.toJSON()), config, graphe) # Je renvois la chaîne JSON, qui sera reconvertie en objet dans l'outil, et je relance le stream récursivement
|
||||
streamGraph(Command(resume=payload.toJSON()), config, graphe, lastMsgIndex) # Je renvois la chaîne JSON, qui sera reconvertie en objet dans l'outil, et je relance le stream récursivement
|
||||
return # Fin de cette fonction récursive
|
||||
|
||||
else:
|
||||
|
||||
@@ -3,8 +3,12 @@ from langgraph.graph import MessagesState
|
||||
from langgraph.prebuilt import ToolNode
|
||||
from langchain.chat_models import init_chat_model
|
||||
from langgraph.graph import START, END
|
||||
from langchain.messages import HumanMessage, AIMessage, SystemMessage
|
||||
from langgraph.types import interrupt
|
||||
|
||||
from .tools import getTools
|
||||
from .tools import getTools, getWeeklyReportTools
|
||||
from .state import CustomState
|
||||
from .InterruptPayload import InterruptPayload
|
||||
|
||||
# LLM principal
|
||||
llm = ChatMistralAI( # LLM sans outils
|
||||
@@ -14,6 +18,21 @@ llm = ChatMistralAI( # LLM sans outils
|
||||
)
|
||||
|
||||
# NODES
|
||||
def inject_preparation_prompt(state: CustomState):
|
||||
""" Noeud qui vise juste à insérer le message indiquant au LLM comment travailler sur les résumés de comptes-rendus """
|
||||
return {'messages': HumanMessage(
|
||||
"Ton but est de lire les fichiers présents dans la base de données en utilisant l'outil 'search_in_files',\
|
||||
afin de générer des rapports sur chaque semaine du stage qui y est décrit. Pour enregistrer chaque semaine du stage, utilise l'outil 'write_week_report'.\
|
||||
Une fois terminé, fais une liste de tous les outils, logiciels, méthodes, entreprises, techniques, ect.. utilisés,\
|
||||
et fais en une liste avec quelques descriptions que tu devras enregistrer avec l'outil 'write_library_tools_details_on_internship'."
|
||||
)}
|
||||
|
||||
def preparation_docs(state: CustomState):
|
||||
"""Noeud en charge de préparer les résumés pour chaque semaine des rapports, et la liste des outils et méthodes utilisées"""
|
||||
model = llm.bind_tools(getWeeklyReportTools()) # LLM en charge de générer des rapports hebdomadaires sur le stage
|
||||
|
||||
return {'messages': model.invoke(state['messages'])}
|
||||
|
||||
def call_to_LLM(state: MessagesState):
|
||||
"""Noeud qui s'occupe de gérer les appels au LLM"""
|
||||
# Initialisation du LLM
|
||||
@@ -53,6 +72,7 @@ def task_ended(state: MessagesState):
|
||||
return END
|
||||
return "continue"
|
||||
|
||||
weekly_report_tools = ToolNode(tools=getWeeklyReportTools())
|
||||
tool_node = ToolNode(tools=getTools())
|
||||
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ from tavily import TavilyClient
|
||||
from pathlib import Path
|
||||
from typing import List, Dict, Annotated
|
||||
import sys
|
||||
import os
|
||||
from langgraph.types import interrupt
|
||||
|
||||
from .StateElements.TodoElement import TodoElement
|
||||
@@ -194,9 +195,72 @@ def search_in_files(query:str, state: Annotated[dict, InjectedState])->str:
|
||||
|
||||
return docs_content # Retourne la liste de documents trouvés
|
||||
|
||||
@tool
|
||||
def write_week_report(numero_semaine:int, contenu:str)->str:
|
||||
"""
|
||||
Écrire un rapport sur une semaine du stage. Sauvegardera ce rapport dans un fichier en mémoire pour un usage futur.
|
||||
|
||||
Args:
|
||||
numero_semaine (int): Semaine du stage. Commence à 1 pour la première semaine
|
||||
contenu (str): Ce qu'il faut écrire dans ce rapport
|
||||
|
||||
Returns:
|
||||
str: CHemin vers le fichier, ou une erreur en cas de problème
|
||||
"""
|
||||
try:
|
||||
# Récupérer le chemin vers le point d'entrée
|
||||
base_dir: Path = Path(sys.argv[0]).resolve().parent
|
||||
reports_dir = base_dir / "rapports_resumes" # Chemin du dossier des rapports
|
||||
reports_dir.mkdir(parents=True, exist_ok=True) # Créer le dossier
|
||||
|
||||
file_name = f"rapport_semaine_{numero_semaine}.txt"
|
||||
full_path = reports_dir / file_name
|
||||
|
||||
with open(full_path, "w", encoding="utf-8") as f: # Écrire le contenu
|
||||
f.write(contenu)
|
||||
|
||||
return str(full_path)
|
||||
|
||||
except Exception as e:
|
||||
return f"Erreur lors de l'écriture: {str(e)}"
|
||||
|
||||
@tool
|
||||
def write_library_tools_details_on_internship(contenu:str)->str:
|
||||
"""
|
||||
Enregistrer les détails sur le stage.
|
||||
Utilise cet outil pour enregistrer tous les outils, logiciels, programmes, entreprises, ect.. utilisés pendant le stage.
|
||||
|
||||
Args:
|
||||
contenu (str): Une liste de tous les éléments intéréssants, avec quelques détails sur chacun.
|
||||
|
||||
Returns:
|
||||
str: CHemin vers le fichier, ou une erreur en cas de problème
|
||||
"""
|
||||
try:
|
||||
# Récupérer le chemin vers le point d'entrée
|
||||
base_dir: Path = Path(sys.argv[0]).resolve().parent
|
||||
reports_dir = base_dir / "rapports_resumes" # Chemin du dossier des rapports
|
||||
reports_dir.mkdir(parents=True, exist_ok=True) # Créer le dossier
|
||||
|
||||
file_name = f"rapport_outils.txt"
|
||||
full_path = reports_dir / file_name
|
||||
|
||||
with open(full_path, "w", encoding="utf-8") as f: # Écrire le contenu
|
||||
f.write(contenu)
|
||||
|
||||
return str(full_path)
|
||||
|
||||
except Exception as e:
|
||||
return f"Erreur lors de l'écriture: {str(e)}"
|
||||
|
||||
def getTools()->List['Tools']:
|
||||
"""
|
||||
Récupérer la liste des tools
|
||||
"""
|
||||
return [internet_search, write_file, editTodo, read_file, ask_human, search_in_files, addTodo, removeTodo]
|
||||
|
||||
def getWeeklyReportTools()->List['Tools']:
|
||||
"""
|
||||
Récupérer la liste des tools, POUR LE LLM EN CHARGE DE FAIRE LES RAPPORTS DE CHAQUE SEMAINE
|
||||
"""
|
||||
return [write_week_report, write_library_tools_details_on_internship, internet_search, search_in_files]
|
||||
BIN
imgs/agent.png
Normal file
BIN
imgs/agent.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
Reference in New Issue
Block a user