|
|
|
|
@@ -8,11 +8,21 @@ from pathlib import Path
|
|
|
|
|
from typing import List, Dict, Annotated, Tuple
|
|
|
|
|
import sys
|
|
|
|
|
import os
|
|
|
|
|
from sentence_transformers import CrossEncoder
|
|
|
|
|
from langgraph.types import interrupt
|
|
|
|
|
from langchain_core.documents import Document
|
|
|
|
|
|
|
|
|
|
from .StateElements.TodoElement import TodoElement
|
|
|
|
|
from .VectorDatabase import VectorDatabase
|
|
|
|
|
from .InterruptPayload import InterruptPayload
|
|
|
|
|
from langchain_mistralai import ChatMistralAI # LLM définit dans le fichier agent
|
|
|
|
|
|
|
|
|
|
CROSS_ENCODEUR = CrossEncoder('jinaai/jina-reranker-v2-base-multilingual', trust_remote_code=True)
|
|
|
|
|
CROSS_ENCODEUR_MIN_SIM_SCORE = 0.5
|
|
|
|
|
|
|
|
|
|
llm = ChatMistralAI(model="ministral-3b-2512", # Petit modèle, pour aller vite sur des tâches simples
|
|
|
|
|
temperature=0,
|
|
|
|
|
max_retries=2)
|
|
|
|
|
|
|
|
|
|
@tool
|
|
|
|
|
def append_part_to_report(contenu:str)->str:
|
|
|
|
|
@@ -219,15 +229,16 @@ def search_in_files(query:str, state: Annotated[dict, InjectedState])->str:
|
|
|
|
|
|
|
|
|
|
retrieved_docs = bdd.similarity_search(query, k=5) # 5 documents
|
|
|
|
|
|
|
|
|
|
reprompt = cross_encodeur(query, retrieved_docs) # Cross-encodeur en charge de regarder si la recherche a été efficace ou non
|
|
|
|
|
if reprompt is not None:
|
|
|
|
|
# Il y a un nouveau prompt, il faut recommencer la recherche
|
|
|
|
|
retrieved_docs = bdd.similarity_search(reprompt, k=5)
|
|
|
|
|
|
|
|
|
|
# Conversion des documents en texte
|
|
|
|
|
docs_content = "\n".join(
|
|
|
|
|
[f"Document {i+1}:\n{doc.page_content}" for i,doc in enumerate(retrieved_docs)]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Sauvegarde des données dans le State
|
|
|
|
|
state["ragQuery"] = query
|
|
|
|
|
state["ragDocuments"] = retrieved_docs
|
|
|
|
|
|
|
|
|
|
return docs_content # Retourne la liste de documents trouvés
|
|
|
|
|
|
|
|
|
|
@tool
|
|
|
|
|
@@ -299,3 +310,31 @@ 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]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# CROSS-ENCODEUR
|
|
|
|
|
# Selon https://app.ailog.fr/fr/blog/guides/cross-encoder-reranking
|
|
|
|
|
def cross_encodeur(query:str, docs:List[Document])->str|None:
|
|
|
|
|
"""
|
|
|
|
|
Fonction que j'utilise pour faire tourner le cross-encodeur.
|
|
|
|
|
Il vérifie la sortie de la recherche des documents, et reformule la question si besoin.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
query (str): Requête originale
|
|
|
|
|
docs (List[Document]): documents retrouvés par la première recherche
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
str|None: None si le résultat est valide, une reformulation de la requête sinon.
|
|
|
|
|
"""
|
|
|
|
|
pairs = [[query, doc.page_content] for doc in docs]
|
|
|
|
|
scores = CROSS_ENCODEUR.predict(pairs) # Scores de similarité de la recherche
|
|
|
|
|
|
|
|
|
|
sum = 0
|
|
|
|
|
for s in scores: sum+= s
|
|
|
|
|
if sum / len(docs) < CROSS_ENCODEUR_MIN_SIM_SCORE:
|
|
|
|
|
# Si en dessous d'un certain score de qualité
|
|
|
|
|
return llm.invoke("Voici une recherche de documents locale.\
|
|
|
|
|
Essaie de la réecrire de façon à améliorer le score de la recherche.\
|
|
|
|
|
Ne retourne QUE la nouvelle question, rien d'autre. Voici la question originale: "+query).content
|
|
|
|
|
return None # Recherche valide, pas besoin de retenter
|