Update agent_collaboratif_avid.py
Browse files- agent_collaboratif_avid.py +80 -3
agent_collaboratif_avid.py
CHANGED
|
@@ -36,6 +36,7 @@ from langgraph.prebuilt import ToolNode
|
|
| 36 |
|
| 37 |
from pinecone import Pinecone
|
| 38 |
import asyncio
|
|
|
|
| 39 |
|
| 40 |
# =============================================================================
|
| 41 |
# CONFIGURATION GLOBALE
|
|
@@ -43,6 +44,7 @@ import asyncio
|
|
| 43 |
|
| 44 |
# Configuration API
|
| 45 |
PINECONE_API_KEY = os.environ.get("PINECONE_API_KEY")
|
|
|
|
| 46 |
#OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
|
| 47 |
#OPENAI_BASE_URL = os.environ.get("OPENAI_BASE_URL")
|
| 48 |
#OPENAI_MODEL_NAME = os.environ.get("OPENAI_MODEL_NAME")
|
|
@@ -64,6 +66,8 @@ if not PINECONE_API_KEY:
|
|
| 64 |
raise ValueError("❌ PINECONE_API_KEY non définie. Exécutez: export PINECONE_API_KEY='votre-clé'")
|
| 65 |
if not OPENAI_API_KEY:
|
| 66 |
raise ValueError("❌ OPENAI_API_KEY non définie. Exécutez: export OPENAI_API_KEY='votre-clé'")
|
|
|
|
|
|
|
| 67 |
|
| 68 |
# =============================================================================
|
| 69 |
# EMBEDDINGS HUGGINGFACE
|
|
@@ -131,6 +135,7 @@ class AgentState(TypedDict):
|
|
| 131 |
errors: List[str]
|
| 132 |
additional_information: List[Dict[str, Any]] # Nouvelles infos similaires
|
| 133 |
similar_info_response: str # Réponse LLM basée sur les informations similaires
|
|
|
|
| 134 |
|
| 135 |
# =============================================================================
|
| 136 |
# INITIALISATION DES RETRIEVERS PINECONE
|
|
@@ -707,6 +712,61 @@ def collect_similar_information_node(state: AgentState) -> AgentState:
|
|
| 707 |
|
| 708 |
return state
|
| 709 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 710 |
# =============================================================================
|
| 711 |
# FONCTIONS DE ROUTAGE
|
| 712 |
# =============================================================================
|
|
@@ -763,6 +823,7 @@ def create_agent_workflow() -> StateGraph:
|
|
| 763 |
workflow.add_node("validate_response", validate_response_node)
|
| 764 |
workflow.add_node("refine_response", refine_response_node)
|
| 765 |
workflow.add_node("collect_similar_information", collect_similar_information_node)
|
|
|
|
| 766 |
|
| 767 |
workflow.set_entry_point("analyze_query")
|
| 768 |
|
|
@@ -803,7 +864,8 @@ def create_agent_workflow() -> StateGraph:
|
|
| 803 |
)
|
| 804 |
|
| 805 |
workflow.add_edge("refine_response", "validate_response")
|
| 806 |
-
workflow.add_edge("collect_similar_information",
|
|
|
|
| 807 |
|
| 808 |
#memory = MemorySaver()
|
| 809 |
#app = workflow.compile(checkpointer=memory)
|
|
@@ -836,7 +898,8 @@ async def run_collaborative_agent(user_query: str) -> Dict[str, Any]:
|
|
| 836 |
"final_response": "",
|
| 837 |
"iteration_count": 0,
|
| 838 |
"errors": [],
|
| 839 |
-
"additional_information": []
|
|
|
|
| 840 |
}
|
| 841 |
|
| 842 |
print(f"{'='*80}")
|
|
@@ -859,8 +922,9 @@ async def run_collaborative_agent(user_query: str) -> Dict[str, Any]:
|
|
| 859 |
"iteration_count": final_state.get("iteration_count", 0),
|
| 860 |
"errors": final_state.get("errors", []),
|
| 861 |
"additional_information": final_state.get("additional_information", []),
|
|
|
|
| 862 |
"sources_used": [
|
| 863 |
-
info["database"]
|
| 864 |
for info in final_state.get("collected_information", [])
|
| 865 |
],
|
| 866 |
"pinecone_index": PINECONE_INDEX_NAME
|
|
@@ -1055,6 +1119,19 @@ def display_results(result: Dict[str, Any]) -> None:
|
|
| 1055 |
print(result['similar_info_response'])
|
| 1056 |
print()
|
| 1057 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1058 |
async def main():
|
| 1059 |
"""Fonction principale de l'application."""
|
| 1060 |
|
|
|
|
| 36 |
|
| 37 |
from pinecone import Pinecone
|
| 38 |
import asyncio
|
| 39 |
+
from tavily import TavilyClient
|
| 40 |
|
| 41 |
# =============================================================================
|
| 42 |
# CONFIGURATION GLOBALE
|
|
|
|
| 44 |
|
| 45 |
# Configuration API
|
| 46 |
PINECONE_API_KEY = os.environ.get("PINECONE_API_KEY")
|
| 47 |
+
TAVILY_API_KEY = os.environ.get("TAVILY_API_KEY")
|
| 48 |
#OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
|
| 49 |
#OPENAI_BASE_URL = os.environ.get("OPENAI_BASE_URL")
|
| 50 |
#OPENAI_MODEL_NAME = os.environ.get("OPENAI_MODEL_NAME")
|
|
|
|
| 66 |
raise ValueError("❌ PINECONE_API_KEY non définie. Exécutez: export PINECONE_API_KEY='votre-clé'")
|
| 67 |
if not OPENAI_API_KEY:
|
| 68 |
raise ValueError("❌ OPENAI_API_KEY non définie. Exécutez: export OPENAI_API_KEY='votre-clé'")
|
| 69 |
+
if not TAVILY_API_KEY:
|
| 70 |
+
raise ValueError("❌ TAVILY_API_KEY non définie. Exécutez: export TAVILY_API_KEY='votre-clé'")
|
| 71 |
|
| 72 |
# =============================================================================
|
| 73 |
# EMBEDDINGS HUGGINGFACE
|
|
|
|
| 135 |
errors: List[str]
|
| 136 |
additional_information: List[Dict[str, Any]] # Nouvelles infos similaires
|
| 137 |
similar_info_response: str # Réponse LLM basée sur les informations similaires
|
| 138 |
+
web_search_results: List[Dict[str, Any]] # Résultats de la recherche web
|
| 139 |
|
| 140 |
# =============================================================================
|
| 141 |
# INITIALISATION DES RETRIEVERS PINECONE
|
|
|
|
| 712 |
|
| 713 |
return state
|
| 714 |
|
| 715 |
+
# =============================================================================
|
| 716 |
+
# NODE 7: RECHERCHE WEB (TAVILY)
|
| 717 |
+
# =============================================================================
|
| 718 |
+
|
| 719 |
+
def web_search_node(state: AgentState) -> AgentState:
|
| 720 |
+
"""
|
| 721 |
+
Node de recherche web utilisant Tavily Search.
|
| 722 |
+
Recherche sur le web en fonction de la requête utilisateur analysée.
|
| 723 |
+
"""
|
| 724 |
+
print(f"\n{'='*80}")
|
| 725 |
+
print(f"🌐 NODE 7: RECHERCHE WEB (TAVILY)")
|
| 726 |
+
print(f"{'='*80}\n")
|
| 727 |
+
|
| 728 |
+
# Initialiser le client Tavily
|
| 729 |
+
tavily = TavilyClient(api_key=TAVILY_API_KEY)
|
| 730 |
+
|
| 731 |
+
user_query = state["user_query"]
|
| 732 |
+
web_search_results = []
|
| 733 |
+
|
| 734 |
+
try:
|
| 735 |
+
print(f"🔍 Exécution de la recherche web pour: '{user_query}'...")
|
| 736 |
+
# Effectuer la recherche web, obtenir les 5 meilleurs résultats
|
| 737 |
+
response = tavily.search(query="Université Gustave Eiffel + " + user_query, search_depth="advanced", max_results=5, include_raw_content=False)
|
| 738 |
+
|
| 739 |
+
if response and response['results']:
|
| 740 |
+
print(f"✅ {len(response['results'])} résultats trouvés.")
|
| 741 |
+
for i, result in enumerate(response['results']):
|
| 742 |
+
title = result.get('title', 'Titre inconnu')
|
| 743 |
+
url = result.get('url', '#')
|
| 744 |
+
content = result.get('content', 'Contenu non disponible')
|
| 745 |
+
|
| 746 |
+
# Résumé en 2 phrases
|
| 747 |
+
sentences = content.split('.')
|
| 748 |
+
summary = ". ".join(sentences[:2]) + ("." if len(sentences) > 2 else "")
|
| 749 |
+
|
| 750 |
+
web_search_results.append({
|
| 751 |
+
"title": title,
|
| 752 |
+
"url": url,
|
| 753 |
+
"summary": summary,
|
| 754 |
+
"markdown_link": f"[{title}]({url})"
|
| 755 |
+
})
|
| 756 |
+
print(f" - {i+1}. {title} ({url})")
|
| 757 |
+
else:
|
| 758 |
+
print("ℹ️ Aucun résultat de recherche web trouvé.")
|
| 759 |
+
|
| 760 |
+
except Exception as e:
|
| 761 |
+
error_msg = f"Erreur lors de la recherche web avec Tavily: {str(e)}"
|
| 762 |
+
print(f"❌ {error_msg}")
|
| 763 |
+
state["errors"].append(error_msg)
|
| 764 |
+
|
| 765 |
+
state["web_search_results"] = web_search_results
|
| 766 |
+
state["messages"].append(AIMessage(content=f"Recherche web terminée avec {len(web_search_results)} résultats."))
|
| 767 |
+
|
| 768 |
+
return state
|
| 769 |
+
|
| 770 |
# =============================================================================
|
| 771 |
# FONCTIONS DE ROUTAGE
|
| 772 |
# =============================================================================
|
|
|
|
| 823 |
workflow.add_node("validate_response", validate_response_node)
|
| 824 |
workflow.add_node("refine_response", refine_response_node)
|
| 825 |
workflow.add_node("collect_similar_information", collect_similar_information_node)
|
| 826 |
+
workflow.add_node("web_search", web_search_node) # Ajout du nouveau node de recherche web
|
| 827 |
|
| 828 |
workflow.set_entry_point("analyze_query")
|
| 829 |
|
|
|
|
| 864 |
)
|
| 865 |
|
| 866 |
workflow.add_edge("refine_response", "validate_response")
|
| 867 |
+
workflow.add_edge("collect_similar_information", "web_search") # Ajouter un edge vers la recherche web
|
| 868 |
+
workflow.add_edge("web_search", END) # La recherche web est le point final après les infos similaires
|
| 869 |
|
| 870 |
#memory = MemorySaver()
|
| 871 |
#app = workflow.compile(checkpointer=memory)
|
|
|
|
| 898 |
"final_response": "",
|
| 899 |
"iteration_count": 0,
|
| 900 |
"errors": [],
|
| 901 |
+
"additional_information": [],
|
| 902 |
+
"web_search_results": []
|
| 903 |
}
|
| 904 |
|
| 905 |
print(f"{'='*80}")
|
|
|
|
| 922 |
"iteration_count": final_state.get("iteration_count", 0),
|
| 923 |
"errors": final_state.get("errors", []),
|
| 924 |
"additional_information": final_state.get("additional_information", []),
|
| 925 |
+
"web_search_results": final_state.get("web_search_results", []),
|
| 926 |
"sources_used": [
|
| 927 |
+
info["database"]
|
| 928 |
for info in final_state.get("collected_information", [])
|
| 929 |
],
|
| 930 |
"pinecone_index": PINECONE_INDEX_NAME
|
|
|
|
| 1119 |
print(result['similar_info_response'])
|
| 1120 |
print()
|
| 1121 |
|
| 1122 |
+
# Nouvelle section : Résultats de la recherche web
|
| 1123 |
+
if result.get('web_search_results') and len(result['web_search_results']) > 0:
|
| 1124 |
+
print(f"\n{'='*80}")
|
| 1125 |
+
print(f"🌐 RÉSULTATS DE LA RECHERCHE WEB (TAVILY)")
|
| 1126 |
+
print(f"{'='*80}")
|
| 1127 |
+
print(f"\nInformations trouvées sur le web via Tavily Search:\n")
|
| 1128 |
+
|
| 1129 |
+
for idx, item in enumerate(result['web_search_results'], 1):
|
| 1130 |
+
print(f" Résultat {idx}:")
|
| 1131 |
+
print(f" ├─ Titre: {item['title']}")
|
| 1132 |
+
print(f" ├─ Lien: {item['markdown_link']}")
|
| 1133 |
+
print(f" └─ Résumé: {item['summary']}\n")
|
| 1134 |
+
|
| 1135 |
async def main():
|
| 1136 |
"""Fonction principale de l'application."""
|
| 1137 |
|