Ajustements et tentative de génération du rapport

This commit is contained in:
2026-02-12 11:59:55 +01:00
parent f1d0c7e342
commit 84a27ea6c7
6 changed files with 82 additions and 46 deletions

View File

@@ -22,4 +22,4 @@ initial_input = {
config={"configurable": {"thread_id": 'yes'}} config={"configurable": {"thread_id": 'yes'}}
# Et je lance ! # Et je lance !
streamGraph(initial_input, config, getGraph(), showSysMessages=False) streamGraph(initial_input, config, getGraph(), showSysMessages=True)

View File

@@ -57,6 +57,8 @@ class TodoElement():
""" """
data = json.loads(json_str) if type(json_str) is str else json_str data = json.loads(json_str) if type(json_str) is str else json_str
if isinstance(data, TodoElement): return data
nom_ = data.get("name", "undefined") nom_ = data.get("name", "undefined")
desc_ = data.get("desc", "undefined") desc_ = data.get("desc", "undefined")
state_ = data.get("state", TodoElement.STATE_NOT_STARTED) state_ = data.get("state", TodoElement.STATE_NOT_STARTED)

View File

@@ -111,7 +111,7 @@ def context_shortener(state: CustomState):
lastSummarizedMessage = state['lastSummarizedMessage'] # Récupérer l'index du dernier message qui a été résumé lastSummarizedMessage = state['lastSummarizedMessage'] # Récupérer l'index du dernier message qui a été résumé
else: else:
# Premier passage, je supprime les anciens outils si besoin # Premier passage, je supprime les anciens outils si besoin
rmtree(reports_dir.as_posix()) # Supprimer le dossier rmtree(reports_dir.as_posix(), ignore_errors=True) # Supprimer le dossier
reports_dir.mkdir(parents=True, exist_ok=False) # Créer le dossier reports_dir.mkdir(parents=True, exist_ok=False) # Créer le dossier
messages = [msg for msg in state['messages'][lastSummarizedMessage+1:]] # Récupérer tous les messages après lastSummarizedMessage sans l'inclure messages = [msg for msg in state['messages'][lastSummarizedMessage+1:]] # Récupérer tous les messages après lastSummarizedMessage sans l'inclure

View File

@@ -1,9 +1,10 @@
from langgraph.graph import StateGraph, MessagesState from langgraph.graph import StateGraph, MessagesState
from typing import List from typing import List, Annotated
import operator
class CustomState(MessagesState): class CustomState(MessagesState):
todo: List[str] # Les tâches en cours, au format JSON todo: Annotated[list, operator.add] # Les tâches en cours, au format JSON
ragQuery: str # Requête envoyée au RAG, pour le cross-encodeur ragQuery: str # Requête envoyée au RAG, pour le cross-encodeur
ragDocuments: List[str] # Documents retrouvés par le RAG, pour le cross-encodeur ragDocuments: List[str] # Documents retrouvés par le RAG, pour le cross-encodeur

View File

@@ -5,7 +5,7 @@ from langchain_core.messages import ToolMessage
from langgraph.types import Command from langgraph.types import Command
from tavily import TavilyClient from tavily import TavilyClient
from pathlib import Path from pathlib import Path
from typing import List, Dict, Annotated from typing import List, Dict, Annotated, Tuple
import sys import sys
import os import os
from langgraph.types import interrupt from langgraph.types import interrupt
@@ -25,26 +25,53 @@ def append_part_to_report(contenu:str)->str:
Returns: Returns:
str: Retour, une confirmation, ou un message d'erreur str: Retour, une confirmation, ou un message d'erreur
""" """
try:
# Récupérer le chemin vers le point d'entrée # Récupérer le chemin vers le point d'entrée
base_dir: Path = Path(sys.argv[0]).resolve().parent base_dir: Path = Path(sys.argv[0]).resolve().parent
full_path = reports_dir / "RAPPORT_STAGE.md" full_path = base_dir / "RAPPORT_STAGE.md"
query= interrupt(InterruptPayload({ query= interrupt(InterruptPayload({
content: contenu 'content': contenu
}).toJSON()) }).toJSON())
response = InterruptPayload.fromJSON(query) response = InterruptPayload.fromJSON(query)
if response.isAccepted(): if response.isAccepted():
with open(full_path, "a", encoding="utf-8") as f: # Écrire le contenu with open(full_path, "a", encoding="utf-8") as f: # Écrire le contenu
f.write(response.get("content")) f.write("\n"+response.get("content"))
return "Requête acceptée et validée ! Tu peux considérer cette tâche comme complétée." return "Requête acceptée et validée ! Tu peux considérer cette tâche comme complétée."
else: else:
return "ERREUR! L'utilisateur a refusé ta demande. Tu devrais lui demander pourquoi avoir refusé, et comment améliorer cette partie." return "ERREUR! L'utilisateur a refusé ta demande. Tu devrais lui demander pourquoi avoir refusé, et comment améliorer cette partie."
@tool
def list_files(folder:str)->str:
"""
Retrouver la liste des fichiers dans un dossier
Args:
folder (str): Le chemin relatif vers le dossier
Returns:
str: La liste de tous les fichiers dans ce dossier
"""
try:
base_dir: Path = Path(sys.argv[0]).resolve().parent
full_path: Path = base_dir / folder
if not full_path.exists():
return f"Le dossier '{folder}' n'existe pas."
if not full_path.is_dir():
return f"Le chemin '{folder}' n'est pas un dossier."
files = [f.name for f in full_path.iterdir()]
if not files:
return f"Le dossier '{folder}' est vide."
return "\n".join(files)
except Exception as e: except Exception as e:
return f"Erreur lors de l'écriture: {str(e)}" return f"Erreur lors de la lecture du dossier : {str(e)}"
@tool @tool
@@ -99,40 +126,21 @@ def editTodo(index:int, todoState:int, state: Annotated[dict, InjectedState], to
}) })
@tool @tool
def addTodo(name:str, description:str, state: Annotated[dict, InjectedState], tool_call_id: Annotated[str, InjectedToolCallId])->Command: def setTodo(todoList:List[Tuple[str, str]], state: Annotated[dict, InjectedState], tool_call_id: Annotated[str, InjectedToolCallId])->Command:
""" """
Ajouter une nouvelle tâche/TODO Définir la liste des tâches à faire / TODO.
Permet aussi de la supprimer en appelant avec une liste vide.
Args: Args:
name (str): Nom de cette tâche todoList (List[Tuple[str, str]]): Une liste de tuples (str, str), donc le premier str est le nom de la tâche, et le second sa description, le travail à effectuer dans ce TODO
description (str): Une ou deux phrases pour décrire le travail à effectuer dans ce TODO
""" """
if "todo" not in state.keys(): state["todo"] = [] todo = []
state["todo"] = [TodoElement.fromJSON(e) for e in state["todo"]] # Convertion vers de vraies instances for t in todoList:
state["todo"].append(TodoElement(name, description)) todo.append(TodoElement(t[0], t[1]))
return Command(update={ return Command(update={
"messages": [ToolMessage(content="Réussite!", tool_call_id=tool_call_id)], "messages": [ToolMessage(content="Réussite!", tool_call_id=tool_call_id)],
"todo": [x.toJSON() for x in state["todo"]] # Update du state, # medium.com/@o39joey/a-comprehensive-guide-to-langgraph-managing-agent-state-with-tools-ae932206c7d7 "todo": [x.toJSON() for x in todo] # Update du state, # medium.com/@o39joey/a-comprehensive-guide-to-langgraph-managing-agent-state-with-tools-ae932206c7d7
})
@tool
def removeTodo(index:int, state: Annotated[dict, InjectedState], tool_call_id: Annotated[str, InjectedToolCallId])->Command:
"""
Retirer une tâche/TODO de la liste des tâches
Args:
index (int): Position de la tâche dans la liste, commence à 0 pour le premier TODO
"""
if "todo" not in state.keys(): return Command(update={"messages": [ToolMessage(content="Echec!", tool_call_id=tool_call_id)]})
if len(state["todo"]) <= index:
# Erreur, l'index est trop grand
return Command(update={"messages": [ToolMessage(content="Index en dehors de la liste, echec!", tool_call_id=tool_call_id)]})
state['todo'].pop(index)
return Command(update={
"messages": [ToolMessage(content="Réussite!", tool_call_id=tool_call_id)],
"todo": [x for x in state["todo"]] # Update du state, # medium.com/@o39joey/a-comprehensive-guide-to-langgraph-managing-agent-state-with-tools-ae932206c7d7
}) })
@tool @tool
@@ -284,7 +292,7 @@ def getTools()->List['Tools']:
""" """
Récupérer la liste des tools Récupérer la liste des tools
""" """
return [internet_search, append_part_to_report, editTodo, read_file, search_in_files, addTodo, removeTodo, get_skill] return [internet_search, append_part_to_report, read_file, search_in_files, get_skill, list_files] # editTodo, setTodo
def getWeeklyReportTools()->List['Tools']: def getWeeklyReportTools()->List['Tools']:
""" """

View File

@@ -25,3 +25,28 @@ Une fois le dossier **documents_projet** ajouté à la racine, il est possible d
``` ```
python RAG/init.py python RAG/init.py
``` ```
Puis de lancer l'agent
```
python AgentReact/start.py
```
### Exemple de prompt initial
Il faut le coller comme une seule ligne dans l'input, produira des bugs lors de prompts sinon.
#### Sans TODO
```
Ton but est d'écrire un rapport de stage sur l'entreprise Diag'n Grow. Commence par préparer un plan avec ton skill "Creation_plan", tu peux rechercher des informations sur l'entreprise avec une recherche internet en utilisant "internet_search". Ensuite, rédige chacune des parties du plan, en utilisant l'outil "append_part_to_report".
Tu as aussi des rapports de chaque semaine de stage dans le dossier `rapports_resumes`, tu peux en lister les fichiers avec l'outil "list_files".
En plus de ces rapports, tu as une base de données de ce qui a été fait, en plus détaillé, avec l'outil "search_in_files".
Bon couraj
```
#### Avec TODO
```
Ton but est d'écrire un rapport de stage sur l'entreprise Diag'n Grow. Commence par préparer un plan avec ton skill "Creation_plan", tu peux rechercher des informations sur l'entreprise avec une recherche internet en utilisant "internet_search". Ensuite, rédige chacune des parties du plan, en utilisant l'outil "append_part_to_report". En faisant cela, n'oublie pas de créer une liste de tâches(TODO), et de les garder à jour. A chaque fois qu'une partie du rapport est validée, mets à jour ta liste de tâches pour garder une trace de ta progression.
Tu as aussi des rapports de chaque semaine de stage dans le dossier `rapports_resumes`, tu peux en lister les fichiers avec l'outil "list_files".
En plus de ces rapports, tu as une base de données de ce qui a été fait, en plus détaillé, avec l'outil "search_in_files".
Bon couraj
```