# Implémentation du Système de Retrievers Dual ## Vue d'ensemble Le système a été modifié pour supporter deux types de retrievers distincts : - **Retriever Formations** : Pour les documents de type `formation` - **Retriever Prestations** : Pour les documents de type `prestation` ## Modifications apportées ### 1. État du Graph (`AgentState`) ```python class AgentState(TypedDict, total=False): messages: Annotated[Sequence[BaseMessage], add_messages] query: Optional[str] formation_docs: List[Document] # Nouveau prestation_docs: List[Document] # Nouveau formation_context: str # Nouveau prestation_context: str # Nouveau ``` ### 2. Fonction de Récupération des Retrievers ```python @lru_cache(maxsize=2) def _get_retriever(doc_type: str, k: int = int(os.getenv("RAG_TOP_K", "5"))): """Get retriever for specific document type (formation or prestation).""" # ... configuration Supabase ... return vector_store.as_retriever(search_kwargs={"k": k, "filter": {"type": doc_type}}) ``` **Changements clés :** - Cache augmenté à 2 (pour les deux types) - Paramètre `doc_type` pour filtrer par type - Filtre Supabase par type de document ### 3. Nœud de Récupération (`retrieve`) ```python def retrieve(state: AgentState) -> AgentState: # Récupération des deux retrievers formation_retriever = _get_retriever("formation") prestation_retriever = _get_retriever("prestation") # Récupération des documents pour les deux types formation_docs = formation_retriever.invoke(q or "") prestation_docs = prestation_retriever.invoke(q or "") # Formatage des contextes séparés formation_context = _format_docs(formation_docs, "formation") prestation_context = _format_docs(prestation_docs, "prestation") return { "formation_docs": formation_docs, "prestation_docs": prestation_docs, "formation_context": formation_context, "prestation_context": prestation_context } ``` ### 4. Nœud de Génération (`call_model_with_history`) ```python def call_model_with_history(state: AgentState) -> AgentState: # Récupération des contextes séparés formation_context = state.get("formation_context", "") prestation_context = state.get("prestation_context", "") # Ajout du contexte formations if formation_context: sys_msgs.append(SystemMessage(content=( "CONTEXTE FORMATIONS (extraits du catalogue formations; n'utilise rien d'autre):\n\n" f"{formation_context}\n\n" "Consignes formations: Utilise exclusivement ce contexte pour recommander les formations. " "Cite la page et la source pour chaque recommandation. " "Une formation = un document." ))) # Ajout du contexte prestations if prestation_context: sys_msgs.append(SystemMessage(content=( "CONTEXTE PRESTATIONS (extraits du catalogue services; n'utilise rien d'autre):\n\n" f"{prestation_context}\n\n" "Consignes prestations: Utilise exclusivement ce contexte pour recommander les prestations. " "Cite la page et la source pour chaque recommandation. " "Un document peut contenir plusieurs prestations." ))) ``` ## Workflow du Graph ``` Entry Point ↓ [retrieve] → Récupère docs formations + prestations ↓ [agent] → Génère réponse avec contextes séparés ↓ END ``` ## Différences entre Formations et Prestations ### Formations - **Type de document** : `formation` - **Structure** : 1 formation par document - **Contexte** : Catalogue formations - **Instructions** : Recommandations spécifiques aux formations ### Prestations - **Type de document** : `prestation` - **Structure** : Plusieurs prestations par document possible - **Contexte** : Catalogue services - **Instructions** : Recommandations spécifiques aux prestations ## Configuration requise ### Variables d'environnement ```bash SUPABASE_URL=your_supabase_url SUPABASE_KEY=your_supabase_key OPENAI_API_KEY=your_openai_key RAG_TOP_K=5 # Nombre de documents à récupérer par type ``` ### Structure de la base de données Les documents doivent avoir un champ `type` dans leurs métadonnées : - `type: "formation"` pour les formations - `type: "prestation"` pour les prestations ## Test de l'implémentation Un script de test est disponible : `test_dual_retriever.py` ```bash python test_dual_retriever.py ``` ## Avantages de cette approche 1. **Séparation claire** : Formations et prestations sont traitées séparément 2. **Filtrage efficace** : Récupération ciblée par type de document 3. **Contextes spécialisés** : Instructions spécifiques pour chaque type 4. **Flexibilité** : Possibilité d'ajuster les paramètres par type 5. **Performance** : Cache optimisé pour les deux retrievers ## Utilisation Le graph modifié peut être utilisé exactement comme avant : ```python from graphs.base_graph import create_simple_graph_with_history from langchain_openai import ChatOpenAI llm = ChatOpenAI(model="gpt-3.5-turbo") graph = create_simple_graph_with_history(llm) # Utilisation normale result = graph.invoke({ "messages": [{"type": "human", "content": "Je cherche des formations et prestations"}] }) ``` Le système récupérera automatiquement les documents pertinents pour les deux types et générera une réponse appropriée.