Kalli
RAG Agent

Pipeline RAG

Traitement d'une question dans rag-agent, de la résolution du rôle au flux SSE.

Pipeline RAG

Pour chaque message, POST /chat exécute les étapes suivantes. Les paramètres cités sont décrits dans Configuration.

1. Résolution du rôle

Le service lit les noms de rôles dans les métadonnées de l'utilisateur envoyées par le chat (user.metadata.roles, ou user.metadata.role à défaut). Il les retrouve dans la table roles et retient le plus élevé, c'est-à-dire celui dont display_order est le plus petit.

Le rôle retenu détermine deux choses : l'index utilisé à l'étape 2, et le droit de voir les sources (seuls les rôles is_super_admin les voient).

2. Index du rôle

Il existe un index FAISS (IndexFlatL2) par rôle. Il est construit par l'Index API à partir des chunks et des paires question/réponse :

  • de catégorie dynamic ou static uniquement ;
  • visibles par le rôle selon les tags : un élément sans tag est visible de tous les rôles, un élément avec tags n'est visible que des rôles qui possèdent au moins un de ces tags, et un rôle sans tag ne voit que les éléments sans tag. Le super-admin voit tout.

La régénération construit aussi un index sans rôle, qui ne contient que les éléments sans tag. Un utilisateur sans rôle, ou dont le rôle n'a pas d'index, reçoit cet index, jamais celui d'un autre rôle. Un rôle qui n'a plus aucun élément à indexer est retiré : son ancien index n'est plus servi. Si l'index sans rôle n'existe pas non plus, la requête est refusée jusqu'à la prochaine régénération.

L'index est gardé en mémoire par rôle. Il est rechargé dès qu'un index plus récent existe en base pour ce rôle.

3. Question exacte dans les paires Q/R

Avant la recherche vectorielle, la question est comparée aux questions des paires Q/R de l'index. La comparaison porte sur le texte normalisé (minuscules, ponctuation et espaces retirés) :

  1. égalité stricte ;
  2. à défaut, la question normalisée est la fin d'une question de paire.

Si une paire correspond, sa réponse est affichée en tête de la réponse, sous un titre dédié. Le modèle répond ensuite quand même, à la suite.

4. Embedding et recherche FAISS

La question est vectorisée avec RAG_EMBEDDING_MODEL_ID, puis l'index retourne RAG_K_CHUNKS × RERANKER_FETCH_MULTIPLIER candidats (15 par défaut).

5. Reranking

Un CrossEncoder (RERANKER_MODEL_ID, CPU) note chaque couple (question, candidat). Seuls les RAG_K_CHUNKS meilleurs sont gardés. Si le reranker est indisponible, les RAG_K_CHUNKS premiers candidats de FAISS sont gardés à la place.

Si aucun candidat n'est trouvé, ou si l'embedding de la question échoue, le service répond par un événement error (« Could not prepare RAG context »).

6. Construction du prompt

Les chunks retenus sont joints par une ligne ---------------------, puis insérés dans BOT_SYSTEM_PROMPT :

  • {context} : les chunks ;
  • {question} : la question de l'utilisateur.

Ce sont les seuls marqueurs reconnus. Un autre marqueur, par exemple {exact_match}, fait échouer la requête (l'erreur remonte en événement error).

7. Génération

Le prompt est envoyé à LiteLLM (RAG_QUERY_MODEL_ID, température RAG_TEMP) avec l'historique de la conversation. L'historique contient les messages précédents, sans le dernier message de l'utilisateur. Le prompt RAG est ajouté comme dernier message user. La réponse est relayée en flux.

8. Sources

Une fois la réponse terminée, si le rôle est is_super_admin et qu'au moins un chunk a été utilisé, une liste Sources est ajoutée en Markdown. Chaque entrée pointe vers le chunk ou la paire dans l'interface web (WEB_APP_BASE_URL). Pour les autres rôles, rien n'est ajouté.

Flux SSE

POST /chat répond en text/event-stream. Les événements sont :

ÉvénementDonnéesContenu
progress{"text": "...", "done": false} puis {"text": "...", "done": true}Étape « Recherche d'informations » (traduite selon la langue demandée)
token{"text": "..."}Fragment de la réponse, y compris la liste des sources
error{"text": "..."}Erreur de traitement ; le flux peut continuer
done{}Fin de la réponse

Les sources ne forment pas un événement distinct : elles arrivent comme des token.

Rôle non résolu

Si aucun rôle n'est résolu (métadonnées absentes, ou noms absents de la table roles), ou si le rôle n'a pas encore d'index, la requête est refusée (Error: Could not prepare RAG context). Le service ne charge jamais l'index d'un autre rôle.