File size: 3,262 Bytes
1d6fcbc
 
 
 
 
 
b2dd427
1d6fcbc
 
 
 
 
b2dd427
1d6fcbc
438d4f9
 
 
1d6fcbc
 
 
 
438d4f9
cba7f8e
 
 
 
 
 
 
1d6fcbc
 
 
 
b2dd427
1d6fcbc
43ec712
 
 
 
 
 
 
 
438d4f9
 
 
 
 
43ec712
 
 
 
 
 
 
 
 
 
1d6fcbc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3e0117c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
from typing import TypedDict, Annotated, Sequence
from langchain_core.messages import BaseMessage
from langgraph.graph import StateGraph, END
from langgraph.graph.message import add_messages
from config import llm, client, langsmith_project
from pinecone_utilsB import *  
from typing import TypedDict, Sequence, List, Dict, Optional, Annotated


class GraphState(TypedDict):
    messages: Annotated[Sequence[BaseMessage], add_messages]
    query: str
    relevant_docs: List[Dict[str, Optional[Dict[str, float]]]]
    response: str
    k: int 
    alpha: float
    similarity_threshold: float


def retrieve_combined(state: GraphState) -> dict:
    """Récupération hybride : Pinecone (sémantique) + BM25 (mots-clés)."""

    relevant_docs = hybrid_search(
        state["query"], 
        alpha=state.get("alpha"), 
        k=state.get("k"), 
        similarity_threshold=state.get("similarity_threshold")
    )
    
    return {"relevant_docs": relevant_docs}

def generate_response(state: GraphState) -> dict:
    """Génération de réponse en combinant informations sémantiques et mots-clés."""
    context = "\n\n".join(doc["text"] for doc in state["relevant_docs"])
    prompt = f"""
    Vous êtes un expert en analyse de texte.
    Votre tâche consiste à répondre à la question de l'utilisateur en utilisant les informations pertinentes fournies. 
    Intégrez à la fois les éléments sémantiques (contexte, sens) et les mots-clés exacts pour fournir une réponse fluide et cohérente.
    
    **Instructions supplémentaires** :
    - Utilisez les mots-clés pertinents de manière naturelle dans votre réponse.
    - Expliquez les concepts en vous appuyant sur le contexte sémantique.
    - Ne mentionnez pas explicitement les termes "recherche sémantique" ou "recherche par mots-clés".
    - **Si la réponse doit être une liste retournez chaque élément sur **une nouvelle ligne**.
    - Format correct attendu :
      1. Élément 1
      2. Élément 2
      3. Élément 3
    
    **Informations pertinentes trouvées** :
    {context}
    
    **Question de l'utilisateur** :
    {state["query"]}
    
    **Réponse :**
    [Fournissez une réponse cohérente qui intègre à la fois les éléments sémantiques et les mots-clés pertinents sans les distinguer explicitement.]
    """
    response = llm.invoke(prompt)
    return {"response": response.content}


def post_process_response(state: GraphState) -> dict:
    """Nettoie et valide la réponse."""
    response = state["response"].strip()
    
    # Vérifier si la réponse est pertinente
    if not response or response.lower() in ["je ne sais pas", "i don't know"]:
        response = "Désolé, je n'ai pas trouvé d'informations pertinentes pour votre question."
    
    return {"response": response}

# Construction du graphe
graph_builder = StateGraph(GraphState)
graph_builder.add_node("retrieve", retrieve_combined)
graph_builder.add_node("generate", generate_response)
graph_builder.add_node("post_process", post_process_response)

graph_builder.set_entry_point("retrieve")
graph_builder.add_edge("retrieve", "generate")
graph_builder.add_edge("generate", "post_process")
graph_builder.add_edge("post_process", END)

agent = graph_builder.compile()