Compare commits
3 Commits
Agent_V2
...
23e18d6a88
| Author | SHA1 | Date | |
|---|---|---|---|
|
23e18d6a88
|
|||
|
f1caea0323
|
|||
|
d575fdb511
|
48
AgentReact/skills.md
Normal file
48
AgentReact/skills.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# Fichier des skills et compétences
|
||||||
|
Ce fichier vise à t'expliquer comment réaliser certaines étapes de la génération du rapport de stage.
|
||||||
|
|
||||||
|
---
|
||||||
|
## Creation_plan
|
||||||
|
|
||||||
|
Voici un plan que tu peux utiliser pour générer un rapport de stage:
|
||||||
|
```
|
||||||
|
# Rapport de stage au sein de l'entreprise [NOM]
|
||||||
|
|
||||||
|
## Sommaire
|
||||||
|
|
||||||
|
- Introduction
|
||||||
|
- L'entreprise [NOM]
|
||||||
|
- État de l'art
|
||||||
|
- Le stage
|
||||||
|
- Semaine 1
|
||||||
|
- Semaine 2
|
||||||
|
- ...
|
||||||
|
- Semaine n
|
||||||
|
- Conclusions et résultats
|
||||||
|
- Sources
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
Ecris une courte intro, de deux ou trois paragraphes.
|
||||||
|
|
||||||
|
## L'entreprise [NOM]
|
||||||
|
Décris l'entreprise, ce qu'elle fait, ses clients, son secteur, ...
|
||||||
|
Fais deux ou trois paragraphes.
|
||||||
|
|
||||||
|
## État de l'art
|
||||||
|
Regarde quels outils, techniques, supports ont été utilisés, décris-les et explique. Décris aussi les dernières avancées dans le domaine du rapport de stage et les outils utilisés pour le mener à bien, avec un paragraphe par outil, technique ou support.
|
||||||
|
|
||||||
|
## Le stage
|
||||||
|
Décris globalement le stage et introduis le en un paragraphe
|
||||||
|
|
||||||
|
|
||||||
|
### Semaine 1
|
||||||
|
Fais deux ou trois paragraphes par semaine pour expliquer ce qui a été fait, ce qui devra suivre, comment ça a été fait, ect...
|
||||||
|
|
||||||
|
### Semaine n
|
||||||
|
|
||||||
|
## Conclusion et résultats
|
||||||
|
Reprends ici les découvertes faites pendant le stage, fais ressortir les résultats et les enseignements du stage, en deux à quatre paragraphes
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
Liste ici les sources que tu as utilisé pour rédiger l'ensemble du document
|
||||||
|
```
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
# Classes utilisées pour représenter des données
|
# Classes utilisées pour représenter des données
|
||||||
class TodoElement():
|
class TodoElement():
|
||||||
STATE_NOT_STARTED = 0 # Sorte d'enum qui représente l'état d'une tâche
|
STATE_NOT_STARTED = 0 # Sorte d'enum qui représente l'état d'une tâche
|
||||||
@@ -7,10 +9,10 @@ class TodoElement():
|
|||||||
name: str
|
name: str
|
||||||
state: int
|
state: int
|
||||||
|
|
||||||
def __init__(self, name:str, description:str=None):
|
def __init__(self, name:str, description:str=None, state:int=0):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.description = description
|
self.description = description
|
||||||
self.state = TodoElement.STATE_NOT_STARTED
|
self.state = state
|
||||||
|
|
||||||
def __str__(self)->str:
|
def __str__(self)->str:
|
||||||
"""
|
"""
|
||||||
@@ -33,8 +35,39 @@ class TodoElement():
|
|||||||
else:
|
else:
|
||||||
return "Inconnu"
|
return "Inconnu"
|
||||||
|
|
||||||
|
def toJSON(self, indent:int=None)->str: # Vient de https://github.com/LJ5O/Assistant/blob/main/modules/Brain/src/Json/Types.py
|
||||||
|
"""
|
||||||
|
Exporter cet objet vers une String JSON. Permet de le passer dans le State
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: String sérialisable via la méthode statique TodoElement.strImport(string)
|
||||||
|
"""
|
||||||
|
return '{"name":"'+ str(self.name) +'", "desc": "'+str(self.description)+'", "state": ' + str(self.state) +'}'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def fromJSON(json_str: str|dict) -> 'InterruptPayload':
|
||||||
|
"""
|
||||||
|
Parse a JSON string to create a TodoElement instance
|
||||||
|
|
||||||
|
Args:
|
||||||
|
json_str (str|dict): JSON string to parse, or JSON shaped dict
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
TodoElement: instance created from JSON data
|
||||||
|
"""
|
||||||
|
data = json.loads(json_str) if type(json_str) is str else json_str
|
||||||
|
|
||||||
|
nom_ = data.get("name", "undefined")
|
||||||
|
desc_ = data.get("desc", "undefined")
|
||||||
|
state_ = data.get("state", TodoElement.STATE_NOT_STARTED)
|
||||||
|
|
||||||
|
return TodoElement(nom_, desc_, state_)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
test = TodoElement("TEST tâche", "OUI")
|
test = TodoElement("TEST tâche", "OUI")
|
||||||
test.state = TodoElement.STATE_STARTED
|
test.state = TodoElement.STATE_STARTED
|
||||||
print(test)
|
print(test)
|
||||||
print([str(test)])
|
print([str(test)])
|
||||||
|
print(test.toJSON())
|
||||||
|
|
||||||
|
print(TodoElement.fromJSON(test.toJSON()))
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import json
|
|||||||
from .tools import getTools, getWeeklyReportTools
|
from .tools import getTools, getWeeklyReportTools
|
||||||
from .state import CustomState
|
from .state import CustomState
|
||||||
from .InterruptPayload import InterruptPayload
|
from .InterruptPayload import InterruptPayload
|
||||||
|
from .StateElements.TodoElement import TodoElement
|
||||||
|
|
||||||
# Variables principales
|
# Variables principales
|
||||||
TAILLE_CONTEXTE_MAX = 20000 #charactères
|
TAILLE_CONTEXTE_MAX = 20000 #charactères
|
||||||
@@ -66,10 +67,17 @@ def user_prompt(state: CustomState):
|
|||||||
return {'messages': messages}# Je passe unen liste, devrait écraser tous les messages précédent au lieu d'ajouter à la liste du State
|
return {'messages': messages}# Je passe unen liste, devrait écraser tous les messages précédent au lieu d'ajouter à la liste du State
|
||||||
|
|
||||||
|
|
||||||
def LLM_central(state: MessagesState):
|
def LLM_central(state: CustomState):
|
||||||
"""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
|
||||||
model = llm.bind_tools(getTools())
|
model = llm.bind_tools(getTools())
|
||||||
|
#print(state)
|
||||||
|
|
||||||
|
if "todo" in state.keys(): # S'il y a des TODO, je l'ajoute avant le prompt au LLM
|
||||||
|
if len(state['todo'])>0:
|
||||||
|
sysmsg = SystemMessage(f"Voici la liste des tâches en cours : {str([f"{i}: {str(TodoElement.fromJSON(todo))}\n" for i,todo in enumerate(state['todo'])])}")
|
||||||
|
print(sysmsg.content)
|
||||||
|
return {"messages": [model.invoke(state["messages"] + [AIMessage('.'), sysmsg])]} # AIMessage pour que Msitrail ne refuse pas la requête avec un 400
|
||||||
|
|
||||||
# Appel du LLM
|
# Appel du LLM
|
||||||
return {"messages": [model.invoke(state["messages"])]}
|
return {"messages": [model.invoke(state["messages"])]}
|
||||||
@@ -142,7 +150,7 @@ def should_shorten(state: CustomState)->str:
|
|||||||
return 'réduire contexte'
|
return 'réduire contexte'
|
||||||
|
|
||||||
# fonction de routage : Après reponse_question, si le LLM veut appeler un outil, on va au tool_node
|
# fonction de routage : Après reponse_question, si le LLM veut appeler un outil, on va au tool_node
|
||||||
def should_continue(state: MessagesState):
|
def should_continue(state: CustomState):
|
||||||
"""
|
"""
|
||||||
Vérifier s'il y a un appel aux outils dans le dernier message
|
Vérifier s'il y a un appel aux outils dans le dernier message
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,11 +1,9 @@
|
|||||||
from langgraph.graph import StateGraph, MessagesState
|
from langgraph.graph import StateGraph, MessagesState
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from .StateElements.TodoElement import TodoElement
|
|
||||||
|
|
||||||
|
|
||||||
class CustomState(MessagesState):
|
class CustomState(MessagesState):
|
||||||
todo: List[TodoElement] # Les tâches en cours
|
todo: List[str] # 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
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
from langchain.tools import tool
|
from langchain.tools import tool
|
||||||
from langgraph.prebuilt import InjectedState
|
from langgraph.prebuilt import InjectedState
|
||||||
|
from langchain_core.tools import InjectedToolCallId
|
||||||
|
from langchain_core.messages import ToolMessage
|
||||||
|
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
|
||||||
@@ -60,21 +63,20 @@ def write_file(file_path:str, content: str, append:bool=True) -> str:
|
|||||||
return f"Erreur lors de l'écriture: {str(e)}"
|
return f"Erreur lors de l'écriture: {str(e)}"
|
||||||
|
|
||||||
@tool
|
@tool
|
||||||
def editTodo(index:int, todoState:int, state: Annotated[dict, InjectedState])->bool: # https://stackoverflow.com/a/79525434
|
def editTodo(index:int, todoState:int, state: Annotated[dict, InjectedState], tool_call_id: Annotated[str, InjectedToolCallId])->Command: # https://stackoverflow.com/a/79525434
|
||||||
"""
|
"""
|
||||||
Modifier l'état d'une tâche (TODO)
|
Modifier l'état d'une tâche (TODO)
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
index (int): Index de la tâche à modifier, en commançant à 0 pour la première tâche.
|
index (int): Index de la tâche à modifier, en commançant à 0 pour la première tâche.
|
||||||
todoState (int): Nouvel état. 0 pour "non commencé, 1 pour "en cours", 2 pour "complété"
|
todoState (int): Nouvel état. 0 pour "non commencé, 1 pour "en cours", 2 pour "complété"
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: Réussite de l'opération, ou non.
|
|
||||||
"""
|
"""
|
||||||
|
if "todo" not in state.keys(): return Command(update={"messages": [ToolMessage(content="Echec!", tool_call_id=tool_call_id)]})
|
||||||
if len(state["todo"]) <= index:
|
if len(state["todo"]) <= index:
|
||||||
# Erreur, l'index est trop grand
|
# Erreur, l'index est trop grand
|
||||||
return False
|
return Command(update={"messages": [ToolMessage(content="Index en dehors de la liste, echec!", tool_call_id=tool_call_id)]})
|
||||||
|
|
||||||
|
state["todo"] = [TodoElement.fromJSON(e) for e in state["todo"]] # Convertion vers de vraies instances
|
||||||
state["todo"][index].state = todoState # Modification de l'état de cette tâche
|
state["todo"][index].state = todoState # Modification de l'état de cette tâche
|
||||||
|
|
||||||
# Toutes les tâches complétées ?
|
# Toutes les tâches complétées ?
|
||||||
@@ -85,42 +87,47 @@ def editTodo(index:int, todoState:int, state: Annotated[dict, InjectedState])->b
|
|||||||
break
|
break
|
||||||
|
|
||||||
if not found: state["todo"] = [] # Toutes les tâches terminées, je peux clear la TODO list du state
|
if not found: state["todo"] = [] # Toutes les tâches terminées, je peux clear la TODO list du state
|
||||||
return True
|
return Command(update={
|
||||||
|
"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
|
||||||
|
})
|
||||||
|
|
||||||
@tool
|
@tool
|
||||||
def addTodo(name:str, description:str, state: Annotated[dict, InjectedState])->bool:
|
def addTodo(name:str, description:str, state: Annotated[dict, InjectedState], tool_call_id: Annotated[str, InjectedToolCallId])->Command:
|
||||||
"""
|
"""
|
||||||
Ajouter une nouvelle tâche/TODO
|
Ajouter une nouvelle tâche/TODO
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
name (str): Nom de cette tâche
|
name (str): Nom de cette tâche
|
||||||
description (str): Une ou deux phrases pour décrire le travail à effectuer dans ce TODO
|
description (str): Une ou deux phrases pour décrire le travail à effectuer dans ce TODO
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: Réussite de l'opération, ou non
|
|
||||||
"""
|
"""
|
||||||
if state["todo"] is None: state["todo"] = []
|
if "todo" not in state.keys(): state["todo"] = []
|
||||||
|
|
||||||
|
state["todo"] = [TodoElement.fromJSON(e) for e in state["todo"]] # Convertion vers de vraies instances
|
||||||
state["todo"].append(TodoElement(name, description))
|
state["todo"].append(TodoElement(name, description))
|
||||||
return True
|
return Command(update={
|
||||||
|
"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
|
||||||
|
})
|
||||||
|
|
||||||
@tool
|
@tool
|
||||||
def removeTodo(index:int, state: Annotated[dict, InjectedState])->bool:
|
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
|
Retirer une tâche/TODO de la liste des tâches
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
index (int): Position de la tâche dans la liste, commence à 0 pour le premier TODO
|
index (int): Position de la tâche dans la liste, commence à 0 pour le premier TODO
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: Réussite de l'opération, ou non
|
|
||||||
"""
|
"""
|
||||||
|
if "todo" not in state.keys(): return Command(update={"messages": [ToolMessage(content="Echec!", tool_call_id=tool_call_id)]})
|
||||||
if len(state["todo"]) <= index:
|
if len(state["todo"]) <= index:
|
||||||
# Erreur, l'index est trop grand
|
# Erreur, l'index est trop grand
|
||||||
return False
|
return Command(update={"messages": [ToolMessage(content="Index en dehors de la liste, echec!", tool_call_id=tool_call_id)]})
|
||||||
|
|
||||||
state['todo'].pop(index)
|
state['todo'].pop(index)
|
||||||
return True
|
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
|
||||||
def read_file(file_path: str) -> str:
|
def read_file(file_path: str) -> str:
|
||||||
@@ -145,6 +152,43 @@ def read_file(file_path: str) -> str:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return f"Erreur lors de la lecture : {str(e)}"
|
return f"Erreur lors de la lecture : {str(e)}"
|
||||||
|
|
||||||
|
@tool
|
||||||
|
def get_skill(skill_name:str=None)->str:
|
||||||
|
"""
|
||||||
|
Obtenir un skill, la description de comment faire quelque chose.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
skill_name (str, optional): Nom du skill recherché. Si ce n'est pas donné, listera les skills disponibles.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Sans nom de skill, la liste de ceux disponibles. Si un nom de skill est donné, l'ensemble de ce skill.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
base_dir:str = Path(sys.argv[0]).resolve().parent.as_posix() # Récupérer le chemin vers le point d'entrée du programme
|
||||||
|
full_path:str = base_dir + "/skills.md" # Puis générer le chemin vers le fichier
|
||||||
|
|
||||||
|
with open(full_path, "r", encoding="utf-8") as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
if skill_name is None:
|
||||||
|
# Liste des skills
|
||||||
|
names = []
|
||||||
|
for part in content.split("---")[1:]: # Pas besoin de la première partie
|
||||||
|
names.append(part.splitlines()[1].split(' ')[1]) # Récupérer le nom du skill à la seconde ligne
|
||||||
|
return str(names)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Récupérer un skill
|
||||||
|
for part in content.split("---")[1:]:
|
||||||
|
if skill_name.lower() in part.lower(): # Dégueulasse pour l'opti mais c'est rapide à implémenter
|
||||||
|
# Si c'est ce skill qui est recherché
|
||||||
|
return f"{content.split("---")[0]}\n\n{part}"
|
||||||
|
|
||||||
|
return "Ce skill n'existe pas ! Regarde la liste des skills en rappelant cet outil sans arguments !"
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return f"Erreur lors de la lecture : {str(e)}"
|
||||||
|
|
||||||
@tool
|
@tool
|
||||||
def ask_human(request:str)->str:
|
def ask_human(request:str)->str:
|
||||||
"""
|
"""
|
||||||
@@ -257,7 +301,7 @@ 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, get_skill]
|
||||||
|
|
||||||
def getWeeklyReportTools()->List['Tools']:
|
def getWeeklyReportTools()->List['Tools']:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
## Amélioration de l'agent
|
## Amélioration de l'agent
|
||||||
- [ ] Cross-encoding sur la sortie du **RAG**
|
- [ ] Cross-encoding sur la sortie du **RAG**
|
||||||
- [ ] Sauvegarde de l'état de l'agent
|
- [ ] Sauvegarde de l'état de l'agent
|
||||||
- [ ] Lecture d'un `skills.md`
|
- [X] Lecture d'un `skills.md`
|
||||||
- [ ] Système de redémarrage après un arrêt
|
- [ ] Système de redémarrage après un arrêt
|
||||||
- [ ] Détection de *prompt injection*
|
- [ ] Détection de *prompt injection*
|
||||||
- [ ] Génération d'un PDF en sortie du système
|
- [ ] Génération d'un PDF en sortie du système
|
||||||
|
|||||||
Reference in New Issue
Block a user