Skip to main content

Pipeline RAG et système IA

Vue d’ensemble

Le système IA du support UnikSim est basé sur une architecture RAG (Retrieval-Augmented Generation). Le backend NestJS orchestre la conversation de support, délègue la recherche et la génération au serveur IA interne, puis décide si une réponse IA peut être retournée ou si la conversation doit être escaladée vers un humain.

Les providers de génération et d'embedding sont volontairement indépendants. Cela permet de changer le modèle de génération sans modifier l'espace vectoriel existant dans Chroma.

1. Architecture des composants

Ce diagramme montre les responsabilités principales et les frontières entre le support, le serveur IA et les providers.

flowchart LR
    U[Utilisateur] --> FE[Frontend Angular / App]
    FE --> SS[SupportService - NestJS]
    SS --> AIS[AIService - NestJS]
    AIS -->|POST /ai/query| AI[Serveur IA interne]

    AI --> CG[CostGuard]
    AI --> UM[UsageMeter]
    AI --> EP[Embedding Provider]
    EP --> CH[(Chroma)]
    AI --> GP[Generation Provider]
    GP --> LLM[Gemini / autre LLM]

    SS --> DB[(MySQL)]
    SS -->|answer| FE
    SS -->|insufficient_context| ESC[Suggestion d'escalade humaine]

Responsabilités

  • SupportService : persistance de la conversation, historique, contexte de récupération, pièces jointes et décision de réponse/escalade.
  • AIService : client NestJS du serveur IA interne, validation et gestion des erreurs/timeouts.
  • Serveur IA : recherche vectorielle, construction du contexte, génération, comptabilisation des coûts et garde-fous.
  • Embedding Provider : transforme une requête ou un chunk documentaire en vecteur.
  • Chroma : stocke et retourne les chunks sémantiquement proches.
  • Generation Provider : encapsule le SDK du fournisseur LLM et retourne une réponse normalisée.

2. Pipeline d’ingestion documentaire

Les documents de support sont préparés à l'avance par le script d'ingestion. Le même provider/modèle d'embedding doit être utilisé à l'ingestion et à la recherche.

flowchart TD
    DOC[Documents PDF / Markdown] --> META[Lecture des métadonnées héritées]
    META --> VALID[Validation game / scope / audience / language]
    VALID --> PARSE[Parsing du document]
    PARSE --> CHUNK[Découpage en chunks avec overlap]
    CHUNK --> EMB[Embedding de chaque chunk]
    EMB --> IDS[IDs stables SHA-256]
    IDS --> UPSERT[Upsert par batch dans Chroma]
    UPSERT --> CLEAN[Suppression des anciens chunks devenus obsolètes]
    CLEAN --> ORPHAN[Nettoyage des documents supprimés ou déplacés]

    UPSERT -->|échec| ROLLBACK[Rollback des nouveaux IDs]

Le chunking privilégie les paragraphes et titres, puis découpe les blocs trop longs en phrases et en mots. Un chevauchement est conservé entre les chunks afin de limiter la perte de contexte.

3. Séquence complète d’une requête RAG

sequenceDiagram
    actor U as Utilisateur
    participant S as SupportService
    participant A as AIService
    participant R as Serveur IA
    participant E as Embedding Provider
    participant C as Chroma
    participant G as Generation Provider

    U->>S: Envoie un message
    S->>S: Sauvegarde message utilisateur
    S->>S: Charge historique + langue + retrievalContexts
    S->>A: queryRAG(...)
    A->>R: POST /ai/query + token interne
    R->>R: Réserve budget (CostGuard)
    R->>R: Construit la requête de recherche
    R->>E: embed(searchQuery)
    E-->>R: vecteur
    R->>C: query(vector, filter, nResults)
    C-->>R: chunks + distances
    R->>R: Filtre selon MAX_DISTANCE et construit le contexte
    R->>G: generate(prefix, history, question, image?)
    G-->>R: texte normalisé
    R->>R: UsageMeter + règlement du budget
    R-->>A: answer / insufficient_context
    A-->>S: résultat RAG
    alt réponse exploitable
        S->>S: Sauvegarde message IA
        S-->>U: Réponse IA
    else contexte insuffisant
        S->>S: Sauvegarde suggestion d'escalade
        S-->>U: Propose le support humain
    end

Construction du contexte de récupération

Le navigateur ne décide pas directement du scope RAG. Le backend dérive les retrievalContexts à partir des produits possédés par l'utilisateur. Les filtres peuvent inclure le jeu, le scope, l'audience et d'autres métadonnées documentaires.

L'historique envoyé à l'IA contient uniquement les tours USER et AI, dans une fenêtre bornée. L'image, lorsqu'elle existe, est attachée uniquement au tour courant.

4. Décision de réponse ou d’escalade

flowchart TD
    Q[Requête utilisateur] --> SEARCH[Recherche Chroma]
    SEARCH --> REL{Assez de chunks pertinents ?}
    REL -->|Non et aucune image| IC[insufficient_context]
    REL -->|Oui| CTX[Construire le contexte]
    REL -->|Image présente| CTX
    CTX --> GEN[Génération LLM]
    GEN --> OUT{Réponse vide ou marqueur unsupported ?}
    OUT -->|Oui| IC
    OUT -->|Non| ANS[Retourner answer + confidence]
    IC --> SUG[SupportService propose l'escalade]
    ANS --> SAVE[Sauvegarder le message IA]
    SUG --> HUMAN[Utilisateur peut escalader vers un humain]

Contrôle des coûts et garde-fous

Chaque opération protégée du serveur IA passe par une réservation de budget avant l'appel au modèle. Un UsageMeter accumule ensuite l'usage réellement observé et permet de régler la réservation dans un bloc finally, même si l'opération échoue.

  • Plafonds d'entrée, de sortie et de taille de recherche.
  • Timeouts côté NestJS et côté providers.
  • Tarification associée au provider/modèle réellement utilisé.
  • Refus des modèles non tarifés ou inconnus.
  • Endpoint de santé séparant l'état du provider de génération, de l'embedding et de Chroma.

Fichiers principaux

  • uniksim-back-end/src/service/support.service.ts
  • uniksim-back-end/src/service/ai.service.ts
  • uniksim-back-end/src/node.js/server/ai/server.js
  • uniksim-back-end/src/embedding/ingest.js
  • server/ai/provider/* pour les providers de génération, embedding, tokenizer et pricing.

Points d’attention

  • Changer uniquement le provider de génération ne nécessite pas de réindexer Chroma.
  • Changer le modèle d'embedding modifie l'espace vectoriel et implique normalement une réindexation.
  • Les objets SDK spécifiques à un fournisseur ne doivent pas sortir de son provider.
  • La réponse IA ne doit être considérée valide que si le contexte est suffisamment pertinent et que le provider retourne une réponse supportée.