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é
|
# Par sécurité
|
||||||
documents_projet/
|
documents_projet/
|
||||||
chroma_db/
|
chroma_db/
|
||||||
|
AgentReact/rapports_resumes/
|
||||||
|
|
||||||
# Python
|
# Python
|
||||||
__pycache__/
|
__pycache__/
|
||||||
@@ -2,7 +2,7 @@ from langgraph.graph import START, END
|
|||||||
from langgraph.graph.state import CompiledStateGraph
|
from langgraph.graph.state import CompiledStateGraph
|
||||||
from langgraph.checkpoint.memory import InMemorySaver
|
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.state import getState
|
||||||
from utils.tools import getTools
|
from utils.tools import getTools
|
||||||
|
|
||||||
@@ -17,10 +17,21 @@ def getGraph()->CompiledStateGraph:
|
|||||||
|
|
||||||
# Définition des sommets du graphe
|
# Définition des sommets du graphe
|
||||||
workflow.add_node(call_to_LLM)
|
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("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
|
# 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_edge("tool_node", "call_to_LLM")
|
||||||
workflow.add_conditional_edges("call_to_LLM", should_continue, {
|
workflow.add_conditional_edges("call_to_LLM", should_continue, {
|
||||||
"tools":"tool_node",
|
"tools":"tool_node",
|
||||||
@@ -31,4 +42,4 @@ def getGraph()->CompiledStateGraph:
|
|||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# Affichage du graphe
|
# 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()
|
mlflow.langchain.autolog()
|
||||||
|
|
||||||
initial_input = {
|
initial_input = {
|
||||||
'messages':[HumanMessage("Recherche 'Recette de Monster' sur internet")]
|
'messages':[SystemMessage("Salut")]
|
||||||
}
|
}
|
||||||
|
|
||||||
config={"configurable": {"thread_id": 'yes'}}
|
config={"configurable": {"thread_id": 'yes'}}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from langgraph.types import Command
|
|||||||
from .InterruptPayload import InterruptPayload
|
from .InterruptPayload import InterruptPayload
|
||||||
|
|
||||||
# Une fonction pour stream et gérer proprement le graphe
|
# 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
|
# https://docs.langchain.com/oss/python/langgraph/interrupts#stream-with-human-in-the-loop-hitl-interrupts
|
||||||
for mode, state in graphe.stream(
|
for mode, state in graphe.stream(
|
||||||
initial_input,
|
initial_input,
|
||||||
@@ -15,8 +15,11 @@ def streamGraph(initial_input:Dict, config:Dict, graphe:CompiledStateGraph):
|
|||||||
):
|
):
|
||||||
if mode == "values":
|
if mode == "values":
|
||||||
# Handle streaming message content
|
# Handle streaming message content
|
||||||
msg = state['messages'][-1]
|
i=0
|
||||||
msg.pretty_print()
|
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":
|
elif mode == "updates":
|
||||||
# Check for interrupts
|
# 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 = InterruptPayload.fromJSON(payload) # Chargement de la requête depuis sa version JSON
|
||||||
payload.humanDisplay() # L'utilisateur peut accepter/modifier/refuser ici
|
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
|
return # Fin de cette fonction récursive
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -3,8 +3,12 @@ from langgraph.graph import MessagesState
|
|||||||
from langgraph.prebuilt import ToolNode
|
from langgraph.prebuilt import ToolNode
|
||||||
from langchain.chat_models import init_chat_model
|
from langchain.chat_models import init_chat_model
|
||||||
from langgraph.graph import START, END
|
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 principal
|
||||||
llm = ChatMistralAI( # LLM sans outils
|
llm = ChatMistralAI( # LLM sans outils
|
||||||
@@ -14,6 +18,21 @@ llm = ChatMistralAI( # LLM sans outils
|
|||||||
)
|
)
|
||||||
|
|
||||||
# NODES
|
# 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):
|
def call_to_LLM(state: MessagesState):
|
||||||
"""Noeud qui s'occupe de gérer les appels au LLM"""
|
"""Noeud qui s'occupe de gérer les appels au LLM"""
|
||||||
# Initialisation du LLM
|
# Initialisation du LLM
|
||||||
@@ -53,6 +72,7 @@ def task_ended(state: MessagesState):
|
|||||||
return END
|
return END
|
||||||
return "continue"
|
return "continue"
|
||||||
|
|
||||||
|
weekly_report_tools = ToolNode(tools=getWeeklyReportTools())
|
||||||
tool_node = ToolNode(tools=getTools())
|
tool_node = ToolNode(tools=getTools())
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from tavily import TavilyClient
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Dict, Annotated
|
from typing import List, Dict, Annotated
|
||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
from langgraph.types import interrupt
|
from langgraph.types import interrupt
|
||||||
|
|
||||||
from .StateElements.TodoElement import TodoElement
|
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
|
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']:
|
def getTools()->List['Tools']:
|
||||||
"""
|
"""
|
||||||
Récupérer la liste des tools
|
Récupérer la liste des tools
|
||||||
"""
|
"""
|
||||||
return [internet_search, write_file, editTodo, read_file, ask_human, search_in_files, addTodo, removeTodo]
|
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