Capture d'écran de l'interface de l'agent de voyage Kit Travel
|

Intégration de la reconnaissance vocale dans Kit Travel avec l’API Web Speech et l’IA

La saisie de formulaires complexes (comme la planification de vacances) peut s’avérer fastidieuse pour les utilisateurs. Dans le cadre de notre projet de gestion de voyages Kit Travel, nous avons intégré un assistant vocal intelligent. L’utilisateur clique sur un bouton de dictée, énonce son projet de voyage en langage naturel, et notre intelligence artificielle extrait automatiquement toutes les informations structurées pour pré-remplir le formulaire en temps réel.

Voici comment fonctionne cette intégration technique, du navigateur (Frontend) jusqu’au serveur (Backend C#).

Interface de l'agent de voyage Kit Travel


Le flux de fonctionnement (Workflow)

L’intégration repose sur un flux en trois étapes :

  1. Capture Vocale : Le navigateur enregistre la voix de l’utilisateur et la convertit localement en texte via l’API native Web Speech API.
  2. Extraction IA : Le texte transcrit est envoyé en arrière-plan à notre serveur ASP.NET Core, qui interroge un modèle d’IA (Gemini / OpenAI) avec un prompt d’extraction de données.
  3. Remplissage dynamique : L’IA renvoie un objet structuré au format JSON. Le script JavaScript frontend parse ce JSON et met à jour instantanément les différents contrôles du formulaire (champs texte, dates, listes déroulantes).

1. Le Frontend : Capture audio via la Web Speech API

La Web Speech API est une API JavaScript native supportée par la plupart des navigateurs modernes (Chrome, Safari, Edge). Elle évite d’avoir à uploader des fichiers audio lourds vers le serveur, car la reconnaissance vocale est effectuée directement par le moteur du navigateur.

Voici le code JavaScript intégré à notre page Razor Page (Agent.cshtml) pour gérer le cycle de vie de la reconnaissance :

var SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;

if (SpeechRecognition) {
    var recognition = new SpeechRecognition();
    recognition.lang = 'fr-FR'; // Langue d'écoute

    // Démarrage lors du clic sur le bouton micro
    document.getElementById('btn-global-voice').addEventListener('click', function () { 
        recognition.start(); 
    });

    recognition.onstart = function() {
        // Retour visuel : bouton rouge et spinner de chargement
        statusV.style.display = 'inline';
        statusV.innerHTML = 'Écoute en cours...';
        btnGlobal.classList.replace('btn-outline-primary', 'btn-danger');
    };

    recognition.onend = function() {
        // Remise à l'état initial du bouton
        btnGlobal.classList.replace('btn-danger', 'btn-outline-primary');
    };

    recognition.onresult = function (event) {
        var transcript = event.results[0][0].transcript;
        
        // Affichage de l'état d'analyse par l'IA
        statusV.innerHTML = 'Analyse de votre demande par l'IA...';

        // Envoi de la transcription brute au serveur en POST
        fetch('/Agent?handler=ExtraireChamps', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'RequestVerificationToken': token
            },
            body: JSON.stringify(transcript)
        })
        .then(r => r.json())
        .then(data => {
            // Remplissage dynamique des contrôles Syncfusion
            if (data.Nom) document.getElementById('Nom').ej2_instances[0].value = data.Nom;
            if (data.Prenom) document.getElementById('Prenom').ej2_instances[0].value = data.Prenom;
            if (data.NbAdultes) document.getElementById('NbAdultes').ej2_instances[0].value = data.NbAdultes;
            if (data.Budget) document.getElementById('Budget').ej2_instances[0].value = data.Budget;
            if (data.DestinationId) document.getElementById('DestinationId').ej2_instances[0].value = data.DestinationId;
            
            if (data.DateDebut && data.DateFin) {
                var picker = document.getElementById('daterangepicker').ej2_instances[0];
                picker.value = [new Date(data.DateDebut), new Date(data.DateFin)];
            }
        });
    };
}

2. Le Backend C# : Structurer le langage naturel avec l’IA

Sur notre serveur (Agent.cshtml.cs), la méthode handler OnPostExtraireChampsAsync reçoit la chaîne de texte transcrite. L’objectif est de la transformer en un modèle structuré C# ExtractionResult.

Le point crucial de cette étape est de **fournir le contexte de la base de données à l’IA** (comme la liste des pays et destinations actives avec leurs IDs respectifs). Cela permet à l’IA d’associer un nom de destination dicté vocalement (ex: « Tahiti » ou « Polynésie ») à son identifiant numérique unique (`DestinationId`) requis par notre système.

Voici l’implémentation du contrôleur C# :

public async Task<IActionResult> OnPostExtraireChampsAsync([FromBody] string texteDicté)
{
    // 1. Récupération de la liste des destinations actives avec leurs IDs pour le RAG / contexte
    var destinations = _context.DestinationText
        .Where(dt => dt.LangageId == 1)
        .Select(dt => new { dt.DestinationId, dt.Titre })
        .ToList();

    // 2. Conception du System Prompt guidant l'IA
    string systemPrompt = $@"Tu es un assistant qui extrait des paramètres de voyage. 
        Analyse la phrase de l'utilisateur et renvoie UNIQUEMENT un JSON.
        Si l'utilisateur se présente (ex: 'Je suis Jean Dupont'), extrais le prénom et le nom.

        Format attendu :
        {{
          ""Nom"": ""Dupont"",
          ""Prenom"": ""Jean"",
          ""DestinationId"": 123,
          ""NbAdultes"": 2,
          ""NbEnfants"": 0,
          ""Budget"": 5000,
          ""DateDebut"": ""2026-10-01"",
          ""DateFin"": ""2026-10-15"",
          ""Activites"": ""...""
        }}
        Voici la liste des destinations : {JsonSerializer.Serialize(destinations)}.
        Si une info manque, laisse à null. Ne réponds rien d'autre que le JSON.";

    // 3. Appel de notre service d'IA (Gemini / OpenAI)
    string jsonIA = await _aiService.ObtenirReponseAsync(systemPrompt, texteDicté, null, null);

    // Nettoyage des balises Markdown de l'IA (```json ... ```)
    jsonIA = jsonIA.Replace("```json", "").Replace("```", "").Trim();

    return Content(jsonIA, "application/json");
}

Bilan : Une expérience utilisateur fluidifiée

En combinant la reconnaissance vocale locale du navigateur et la puissance de raisonnement linguistique d’un grand modèle de langage, nous avons transformé la saisie d’un devis de voyage en une simple phrase énoncée à voix haute. Cette fonctionnalité, rapide à mettre en œuvre et entièrement sécurisée (les flux vocaux ne transitent pas par notre serveur, seule la transcription y parvient), améliore significativement l’accessibilité et le confort de notre application métier Kit Travel.

A lire également