Skip to content

Achraf8EL/OrderLingo

Repository files navigation

OrderLingo — Système de Prise de Commande Vocale Intelligent

Prise de commande téléphonique automatisée par IA pour la restauration

Python FastAPI Twilio Next.js PostgreSQL


Résumé

OrderLingo est un système de prise de commande vocale par téléphone conçu pour la restauration. Il permet à un client d'appeler un numéro de téléphone et d'interagir avec un assistant vocal intelligent qui comprend le langage naturel, reconnaît les produits du menu, gère les options et variantes, vérifie les disponibilités en temps réel, et confirme la commande avant de l'enregistrer. Le système s'appuie sur Twilio pour la téléphonie, FastAPI pour le backend, et des modèles LLM (Groq, Gemini, Ollama) pour l'interprétation du langage. Une machine à états structure la conversation et un mécanisme de fast-path traite les interactions simples sans appel LLM, réduisant la latence. Un tableau de bord Next.js permet au restaurateur de gérer son menu, ses disponibilités et ses commandes. OrderLingo répond à un besoin réel de disponibilité, de réduction des erreurs et de gain de productivité pour les établissements de restauration.


Table des matières

  1. Introduction
  2. Contexte et problématique
  3. Objectifs du projet
  4. Présentation détaillée
  5. Fonctionnalités principales
  6. Interface — Tableau de bord
  7. Technologies utilisées
  8. Architecture du système
  9. Fonctionnement détaillé
  10. Modélisation des données
  11. Choix techniques
  12. Difficultés rencontrées
  13. Solutions mises en place
  14. Sécurité et robustesse
  15. Résultats et apport
  16. Limites
  17. Perspectives d'amélioration
  18. Installation et démarrage
  19. Conclusion
  20. Annexes


1. Introduction

Le rôle de l'intelligence artificielle dans la restauration moderne

L'industrie de la restauration est l'un des secteurs économiques les plus dynamiques et les plus soumis à la pression opérationnelle. Entre les pics d'affluence, la gestion des stocks, la coordination en salle et en cuisine, et la relation client, les restaurateurs font face à une charge de travail considérable. La prise de commande, qu'elle soit sur place, en ligne ou par téléphone, constitue le point de contact le plus direct avec le client, et représente, à ce titre, un maillon critique de toute la chaîne de service.

Au cours des dernières années, l'essor des interfaces conversationnelles et des modèles de traitement du langage naturel (NLP) a ouvert de nouvelles perspectives pour automatiser, enrichir et fluidifier cette interaction. Dans ce contexte, la prise de commande téléphonique constitue un cas d'usage particulièrement pertinent : une part significative des commandes dans la restauration rapide et les pizzerias est encore passée par téléphone.

OrderLingo : une réponse concrète à un besoin réel

OrderLingo est un système de prise de commande vocale intelligent, développé pour la restauration. Un client appelle le numéro de téléphone du restaurant, interagit avec un assistant vocal capable de comprendre sa demande en langage naturel, de reconnaître les produits du menu, de gérer les options et variantes, de confirmer la commande, et de la transmettre au système de gestion du restaurant de façon structurée et exploitable.


2. Contexte et problématique

Le contexte métier

La restauration rapide et la pizzeria artisanale partagent un défi commun : gérer un volume d'interactions client élevé avec un effectif souvent réduit. Pendant les heures de pointe, le personnel doit simultanément accueillir les clients en salle, gérer les commandes en caisse, superviser la production, et répondre aux appels entrants.

Les erreurs liées à la prise de commande téléphonique sont fréquentes : une option oubliée, une quantité mal comprise, ou une mauvaise interprétation d'une demande multilingue peuvent conduire à une commande incorrecte, un client mécontent, et une perte de rentabilité.

Pourquoi une solution vocale intelligente ?

L'automatisation classique — menus téléphoniques à touches, SVI rigides — a montré ses limites depuis longtemps. Ces systèmes contraignent l'utilisateur à s'adapter à la machine. L'IA conversationnelle moderne offre une alternative radicalement différente : c'est la machine qui s'adapte à l'utilisateur, non l'inverse.

Problématiques centrales

# Problématique
1 Comment automatiser une prise de commande téléphonique tout en maintenant une interaction naturelle et agréable ?
2 Comment comprendre un langage oral imprécis, familier ou mélangé (français/arabe/dialecte) ?
3 Comment transformer une expression vocale non structurée en commande exploitable par le système ?
4 Comment gérer les ambiguïtés, les oublis d'options et les produits indisponibles sans bloquer l'interaction ?
5 Comment assurer la cohérence entre ce que dit le client et ce qui existe réellement dans le menu, en temps réel ?

3. Objectifs du projet

Objectif général

Fournir aux restaurateurs un assistant vocal intelligent, déployable et opérationnel, capable de remplacer ou de compléter la prise de commande téléphonique humaine.

Objectifs spécifiques

Fonctionnels :

  • Permettre à un client de passer une commande complète par appel téléphonique, sans intervention humaine
  • Comprendre les expressions naturelles du client, y compris les formulations familières ou imprécises
  • Reconnaître les articles du menu, leurs variantes, tailles et options
  • Gérer les options obligatoires/facultatives et demander les précisions nécessaires
  • Confirmer la commande avant validation pour éviter toute erreur
  • Permettre au client de suivre sa commande par téléphone via un numéro de suivi
  • Fournir des informations sur le restaurant (horaires, adresse, menu)
  • Gérer les ruptures de stock en temps réel

Techniques :

  • Produire une commande structurée en JSON, exploitable directement par le système de gestion
  • Maintenir l'état de la conversation entre les échanges (sessions persistantes)
  • Assurer une faible latence entre la parole du client et la réponse vocale
  • Garantir la robustesse face aux erreurs de transcription ou aux demandes hors périmètre

4. Présentation détaillée du projet

Qu'est-ce qu'OrderLingo ?

OrderLingo est un système logiciel complet de prise de commande vocale par téléphone. Il associe :

  • La téléphonie cloud (Twilio) pour recevoir et gérer les appels
  • Un backend API moderne (FastAPI / Python) pour la logique métier
  • Des modèles IA conversationnels (LLM) pour comprendre le langage naturel
  • Une base de données structurée (PostgreSQL) pour gérer menus, options et commandes
  • Un tableau de bord web (Next.js) pour que le manager pilote son établissement

À qui s'adresse OrderLingo ?

  • Aux restaurateurs (pizzerias, restauration rapide) souhaitant automatiser la prise de commande téléphonique
  • Aux petits et moyens établissements qui ne peuvent maintenir une ligne occupée en permanence
  • Aux établissements à fort volume d'appels pendant les heures de pointe
  • Aux clients finaux qui bénéficient d'une prise de commande rapide et disponible

Valeur apportée

Pour le restaurateur : augmentation de la capacité de traitement, réduction des coûts liés à la réception téléphonique, amélioration de la qualité de service.

Pour le client : prise en charge immédiate, précise et agréable, quel que soit le moment de l'appel, avec un numéro de suivi identique entre le canal vocal et le tableau de bord.


5. Fonctionnalités principales

5.1 Réception et gestion des appels entrants

Twilio détecte l'appel, décroche et engage la conversation. Le système est disponible 24h/24, 7j/7, sans temps d'attente ni signal « occupé ». Il peut traiter plusieurs appels simultanément.

5.2 Machine à états — flux de conversation guidé

La conversation est structurée par une machine à états qui guide l'assistant à travers les étapes successives, garantissant qu'aucune étape n'est sautée.

États du flux pizzeria :

idle
 ├─► waiting_pizza_type
 │    ├─► waiting_pizza_size ──► waiting_pizza_garniture ──► waiting_boisson
 │    └─► waiting_formule_size ──► waiting_pizza_garniture ──► waiting_formule_boisson
 │                                                                      │
 │                                                               waiting_more
 │                                                                      │
 │                                                              waiting_confirm
 │                                                                      │
 │                                                             waiting_new_order
 │
 ├─► waiting_tracking_code (suivi commande)
 └─► idle (informations restaurant)

5.3 Fast-path par règles (sans LLM)

Pour les interactions simples et prévisibles (oui/non, sélection dans une liste connue), le système prend la décision sans appeler le LLM, réduisant la latence à quelques millisecondes. Le LLM n'est invoqué que pour les expressions complexes ou ambiguës.

Signaux fast-path : __PIZZA_ORDER__ · __FORMULE_ORDER__ · __BOISSON__ · __FORMULE_BOISSON__ · __RECAP__ · __CONFIRM__ · __END_CALL__ · __INFO__ · __TRACKING__

5.4 Compréhension du langage naturel (LLM)

Le LLM reçoit un prompt système contenant le menu, les règles métier et le contexte de la conversation. Il génère une sortie JSON structurée indiquant les actions à effectuer (créer commande, ajouter article, confirmer, etc.).

5.5 Détection des produits avec matching souple

La fonction _find_menu_item effectue une recherche en plusieurs passes :

  1. Correspondance exacte par UUID
  2. Correspondance par sous-chaîne (label contains ref)
  3. Correspondance par mots-clés triés par longueur décroissante

Cela permet de reconnaître « marguerite » pour « Margherita », « 4 froms » pour « 4 fromages », etc.

5.6 Gestion des options et suppléments

Validation des options obligatoires (_validate_required_options) avant tout ajout à la commande. Des messages d'erreur précis sont générés en cas d'option manquante : « Il faut choisir une garniture. Laquelle souhaitez-vous ? »

5.7 Ruptures de stock en temps réel

Le champ out_of_stock sur MenuItem est vérifié à chaque proposition. Lorsque le manager marque un produit en rupture depuis le tableau de bord, le cache est invalidé immédiatement — pas de délai de 60 secondes.

Manager marque "Fanta" en rupture
         ↓
invalidate_pizza_menu_cache(restaurant_id) appelé
         ↓
Prochain appel : "Nous n'avons plus de Fanta.
                  Souhaitez-vous une autre boisson ?"

5.8 Format vocal naturel des prix

_prix_vocal(9.50)  → "neuf euros et cinquante centimes"
_prix_vocal(13.00) → "treize euros"
_prix_vocal(0.50)  → "cinquante centimes"
_prix_vocal(1.00)  → "un euro"

5.9 Récapitulatif et confirmation

Avant validation, la commande est reformulée intégralement :

« Voici votre commande : 1 Formule Medium (Garniture : 4 fromages) à treize euros. Boisson incluse : Sprite. Total : treize euros. C'est correct ? »

5.10 Numéro de suivi unifié

Après confirmation, un code de 6 caractères hexadécimaux est généré (secrets.token_hex(3).upper()). Ce même code est :

  • Annoncé vocalement au client (« Votre numéro de suivi est A1B2C3 »)
  • Affiché dans le tableau de bord manager (« Commande #A1B2C3 »)

5.11 Suivi de commande par téléphone

Le client peut rappeler et dire « je veux suivre ma commande ». L'assistant demande le numéro de suivi, interroge la base de données, et répond :

« Votre commande numéro A1B2C3 est en cours de préparation. Votre pizzaïolo s'en occupe ! Puis-je faire autre chose pour vous ? »

5.12 Informations restaurant

Sur demande, l'assistant fournit le nom, l'adresse et les horaires d'ouverture du restaurant, directement depuis la base de données.


6. Interface — Tableau de bord Manager

Le tableau de bord est une application Next.js 14 (App Router, TypeScript, Tailwind CSS, composants Radix UI). Il offre au manager une interface moderne et réactive pour piloter son établissement en temps réel.

6.1 Authentification

La connexion s'effectue via Keycloak (OAuth2 / Direct Access Grant). Trois niveaux d'accès sont définis :

Rôle Accès
platform_admin Gestion complète de tous les restaurants, managers et staff
restaurant_manager Gestion complète de son restaurant (menu, stock, commandes, staff)
staff Lecture seule (commandes uniquement)

6.2 Page Restaurants

La page d'accueil du dashboard liste tous les restaurants accessibles à l'utilisateur connecté. Le platform admin peut créer de nouveaux établissements depuis un dialogue dédié. Chaque restaurant est cliquable et ouvre son tableau de bord détaillé.

6.3 Onglet Informations (/dashboard/restaurants/[id])

Permet de configurer les informations de base du restaurant :

  • Nom et slug de l'établissement
  • Description
  • Numéro Twilio (format E.164, ex : +33974994861) — associe le restaurant au numéro vocal
  • Statut actif/inactif
  • Managers assignés (platform admin uniquement) — sélecteur de comptes Keycloak
  • Staff assigné (manager ou platform admin) — sélecteur de comptes Keycloak

Le numéro Twilio est la clé de routage : quand un client appelle ce numéro, OrderLingo identifie automatiquement le restaurant et charge son menu.

6.4 Onglet Menu

Interface complète de gestion du catalogue produit, organisée en trois sous-onglets :

Produits

Liste tous les articles du menu avec filtre par catégorie. Pour chaque produit :

  • Nom, description, prix en rouge terracotta
  • Badges de catégorie et groupes d'options associés
  • Indicateur "Inactif" si le produit est désactivé
  • Actions : modifier (dialogue) · supprimer (confirmation)

Formulaire de création/modification d'un produit :

  • Nom, description, prix
  • Catégorie (sélecteur)
  • Groupes de suppléments associés (cases à cocher multiples)
  • Ingrédients liés au stock (pour la disponibilité automatique)
  • Statut actif/inactif

Catégories

Organise les produits par familles (Pizzas, Formules, Boissons, Desserts…). Affiche le nombre de produits par catégorie. CRUD complet.

Groupes de suppléments

Gère les options associables aux produits (Choix de garniture, Tailles, Sauces…). Chaque groupe est extensible :

  • Nom et contraintes de sélection (min_select, max_select)
  • Options individuelles avec supplément de prix (ou "Gratuit")
  • Ajout d'options depuis le groupe (bouton +)

Exemple : groupe « Choix de garniture » (min: 1, max: 1) → options : Margherita (gratuit), Reine (gratuit), Orientale (gratuit), 4 fromages (gratuit).

6.5 Onglet Stock (Inventaire)

Gestion des ingrédients en stock :

  • Liste des ingrédients avec leur unité (kg, litre, unité…)
  • Ajout/modification d'ingrédients
  • Mise à jour des niveaux de stock (quantité + statut en_stock/rupture)

Les ingrédients peuvent être liés aux produits du menu. Si un ingrédient passe en rupture, tous les produits qui en dépendent deviennent automatiquement indisponibles.

6.6 Onglet Disponibilité

Vue synthétique de la disponibilité de chaque produit du menu :

┌─────────────────────────────────────────────────────────┐
│ Margherita                              ✅ Disponible    │
│ 4 fromages                             ✅ Disponible    │
│ Fanta            Rupture manuelle      ❌ Indisponible  [Rétablir]│
│ Sprite                                 ✅ Disponible    │
└─────────────────────────────────────────────────────────┘

Pour chaque produit, le manager peut :

  • Marquer en rupture : le produit est immédiatement exclu des propositions de l'assistant vocal (cache invalidé en temps réel)
  • Rétablir : le produit redevient disponible instantanément
  • Voir la raison de l'indisponibilité (rupture manuelle ou ingrédient manquant)

Impact direct sur la voix : dès que le manager clique sur "Marquer rupture", l'assistant vocal l'annoncera lors du prochain appel — sans redémarrage.

6.7 Onglet Commandes

Suivi en temps réel de toutes les commandes passées vocalement (ou manuellement) :

Liste des commandes :

  • Référence #TRACKING_CODE — même code que celui annoncé au client par téléphone
  • Nombre d'articles, date et heure
  • Badge de statut coloré : Brouillon · Confirmée · En préparation · Prête · Livrée · Annulée
  • Rafraîchissement automatique toutes les 15 secondes
  • Filtre par statut (tous / brouillon / confirmée / en préparation / prête / livrée / annulée)
  • Bouton "Nouvelle commande" pour créer une commande manuellement

Dialogue de détail / changement de statut :

En cliquant sur une commande, un dialogue s'ouvre avec :

  • Stepper visuel des étapes (Brouillon → Confirmée → En préparation → Prête → Livrée) ou bandeau rouge si annulée
  • Statut actuel en badge
  • Détail des articles : quantité × libellé, options choisies entre parenthèses, prix unitaire
  • Sélecteur pour passer au statut suivant (transitions valides uniquement)
  • Mise à jour optimiste immédiate (l'affichage change avant la réponse API)
Commande #A1B2C3
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Étapes : [1]━━━[2]━━━[3]━━━(4)━━━[5]
          Draft Conf  Prep  Prête Livr.

Statut actuel : En préparation

Articles :
  1× Formule Medium (Garniture: 4 fromages)  — 13.00 €

Passer à : [Prête ▼]          [Fermer] [Mettre à jour]

7. Technologies utilisées

Backend

Technologie Rôle Pourquoi
Python 3.11 Langage principal Écosystème IA/ML riche, lisibilité, rapidité de développement
FastAPI Framework API async Performances élevées, validation Pydantic native, documentation OpenAPI auto
SQLAlchemy 2.0 ORM async Abstraction DB compatible async, requêtes lisibles et maintenables
Alembic Migrations DB Versionnage du schéma reproductible et traçable
PostgreSQL Base de données Robustesse, support JSON natif, transactions ACID
Pydantic Validation des données Schémas stricts, sérialisation automatique, erreurs claires

IA / LLM

Technologie Rôle Avantage
Groq Inférence LLM ultra-rapide Latence < 500ms, critique pour la voix temps réel
Gemini (Google) LLM multimodal Compréhension avancée, multi-langues, expressions familières
Ollama LLM local Zéro dépendance cloud, confidentialité des données, coût nul

Téléphonie

Technologie Rôle Pourquoi
Twilio Voice Réception appels + STT + TTS API simple, fiable, documentée ; abstrait toute la complexité téléphonique
TwiML Contrôle du flux d'appel Markup déclaratif pour piloter la conversation depuis le backend

Frontend

Technologie Rôle
Next.js 14 (App Router) Framework React, routing, SSR
TypeScript Typage statique, sécurité à la compilation
Tailwind CSS Styling utilitaire, thème terracotta/sage/cream
Radix UI / shadcn Composants accessibles et composables
Keycloak Authentification OAuth2/OIDC

Infrastructure

Technologie Rôle
Docker + Docker Compose Orchestration multi-services (Postgres, Keycloak, Redis, API)
Redis Cache optionnel, sessions
Cache TTL in-process Cache du menu (60s) avec invalidation immédiate sur rupture

8. Architecture du système

┌────────────────────────────────────────────────────────────────┐
│                      CLIENT (téléphone)                         │
└────────────────────────────┬───────────────────────────────────┘
                             │ Appel téléphonique
                             ▼
┌────────────────────────────────────────────────────────────────┐
│                 COUCHE TÉLÉPHONIE — Twilio                      │
│  • Réception de l'appel entrant                                 │
│  • Speech-to-Text (transcription vocale)                        │
│  • Text-to-Speech (synthèse vocale)                             │
│  • TwiML pour le contrôle du flux d'appel                      │
│  • Webhooks HTTP POST vers le backend                           │
└────────────────────────────┬───────────────────────────────────┘
                             │ POST /twilio/voice/step
                             ▼
┌────────────────────────────────────────────────────────────────┐
│                   COUCHE API — FastAPI                          │
│  • /twilio/voice/incoming  (premier webhook)                    │
│  • /twilio/voice/step      (tours de conversation)             │
│  • /restaurants, /menu, /orders  (dashboard)                   │
│  • Gestion des sessions CallSession                             │
└───────────────┬────────────────────────────────────────────────┘
                │
                ▼
┌────────────────────────────────────────────────────────────────┐
│              COUCHE LOGIQUE MÉTIER — Services                   │
│                                                                  │
│  pizza_assistant_service.py                                      │
│    ├── Machine à états (idle → confirm → new_order)             │
│    ├── Fast-path par règles (sans LLM)                          │
│    └── Orchestration LLM + actions                              │
│                                                                  │
│  order_actions.py                                               │
│    ├── execute_create_order  (création + tracking_code)        │
│    ├── execute_add_item      (ajout article + validation)      │
│    ├── execute_confirm_order (draft → confirmed)               │
│    └── execute_track_order  (lookup par tracking_code)         │
│                                                                  │
│  assistant_service.py                                           │
│    ├── _build_recap_message  (résumé vocal)                    │
│    └── _prix_vocal()         (format naturel des prix)         │
└───────────┬──────────────────────────┬─────────────────────────┘
            │                          │
            ▼                          ▼
┌────────────────────┐     ┌──────────────────────────────────────┐
│   COUCHE IA/NLP    │     │          COUCHE DONNÉES              │
│                    │     │                                        │
│  LLM (Groq /       │     │  PostgreSQL + SQLAlchemy async        │
│  Gemini / Ollama)  │     │  • Restaurant (name, address, hours)  │
│                    │     │  • MenuItem (label, price, out_of_stock│
│  Prompt system :   │     │  • OptionGroup, OptionItem            │
│  • menu structuré  │     │  • Order (tracking_code, status)      │
│  • règles métier   │     │  • OrderItem (qty, unit_price, opts)  │
│  • contexte convo  │     │  • CallSession (flow_state, pending)  │
│                    │     │                                        │
│  Sortie : JSON     │     │  Cache TTL : menu options (60s)       │
│  validé Pydantic   │     │  Invalidation immédiate sur rupture   │
└────────────────────┘     └──────────────────────────────────────┘

Circulation des données de bout en bout

1. Client appelle → Twilio reçoit
2. Twilio POST /incoming → Backend crée CallSession(state=idle)
3. Backend répond TwiML : message d'accueil + <Gather>
4. Client parle → Twilio transcrit (STT)
5. Twilio POST /step → Backend lit CallSession
6. Fast-path : signal détecté sans LLM ? → action directe
   Sinon : LLM appelé avec prompt + menu + contexte → JSON
7. JSON validé Pydantic → action métier exécutée (order_actions)
8. CallSession mise à jour (nouvel état, pending_item, order_id)
9. Backend répond TwiML : réponse vocale + <Gather> suivant
10. Boucle jusqu'à __CONFIRM__ ou __END_CALL__

9. Fonctionnement détaillé — Pas à pas

Scénario : client commande une formule avec boisson

Client : "Je voudrais commander."
  → Fast-path détecte "commander" → signal __PIZZA_ORDER__
  → État : waiting_pizza_type

Assistant : "Très bien ! Souhaitez-vous une pizza seule ou une formule ?"

Client : "Une formule."
  → Fast-path détecte "formule" → signal __FORMULE_ORDER__
  → État : waiting_formule_size

Assistant : "Quelle taille ? Nous avons Medium à treize euros et Large à quinze euros."

Client : "Medium."
  → Fast-path détecte "medium" → stocké dans pending_item
  → État : waiting_pizza_garniture

Assistant : "Quelle garniture ? 4 fromages, Margherita, Orientale, Reine."

Client : "4 fromages."
  → Fast-path détecte la garniture → MenuItem trouvé en DB
  → create_order + add_item exécutés
  → État : waiting_formule_boisson

Assistant : "Votre formule inclut une boisson. Laquelle ?
             Nous avons Coca-Cola, Sprite, Eau minérale."

Client : "Sprite."
  → _detect_boisson() trouve "Sprite" dans la liste
  → signal __FORMULE_BOISSON__ → stocké dans pending_item SANS add_item DB
  → État : waiting_more

Assistant : "Sprite, parfait. Autre chose ?"

Client : "Non."
  → signal __RECAP__
  → _build_recap_message() construit le résumé
  → formule_boisson ajouté depuis pending_item

Assistant : "Voici votre commande : 1 Formule Medium (Garniture : 4 fromages)
             à treize euros. Boisson incluse : Sprite.
             Total : treize euros. C'est correct ?"

Client : "Oui."
  → signal __CONFIRM__ → execute_confirm_order()
  → Order status : draft → confirmed
  → tracking_code = "A1B2C3" généré

Assistant : "Commande enregistrée. Le montant total est de treize euros.
             Votre numéro de suivi est A1B2C3.
             Elle sera prête dans environ 20 minutes.
             Souhaitez-vous passer une autre commande ?"

Client : "Non."
  → signal __END_CALL__

Assistant : "Merci et à bientôt !" → Twilio raccroche

Dans le tableau de bord : La commande apparaît sous #A1B2C3 — même référence que celle entendue au téléphone.


10. Modélisation des données

Restaurant
  ├── name, slug, description
  ├── address, opening_hours
  ├── phone_number (numéro Twilio associé)
  ├── is_active
  │
  ├──< Category (Pizzas / Formules / Boissons…)
  │     └──< MenuItem
  │           ├── label, description, price
  │           ├── out_of_stock (bool, géré par le manager)
  │           ├── option_group_ids []
  │           └── ingredients [] (lien stock)
  │
  ├──< OptionGroup (Choix de garniture / Taille…)
  │     ├── min_select, max_select
  │     └──< OptionItem (Margherita, Reine… / price_extra)
  │
  ├──< Order
  │     ├── tracking_code (6 chars hex, ex: "A1B2C3")
  │     ├── status (draft→confirmed→preparing→ready→delivered|cancelled)
  │     ├── customer_phone
  │     └──< OrderItem
  │           ├── menu_item_id, quantity, unit_price
  │           └── options (JSON: {group_id: [option_id]})
  │
  └──< CallSession
        ├── call_sid (Twilio)
        ├── flow_state (machine à états)
        ├── current_order_id
        ├── pending_item_json (item en cours, formule_boisson…)
        └── repeat_count (anti-boucle infinie)

Transitions de statut des commandes

draft ──► confirmed ──► preparing ──► ready ──► delivered
  │           │              │           │
  └───────────┴──────────────┴───────────┴──────► cancelled

11. Choix techniques et justification

FastAPI plutôt que Flask ou Django

FastAPI gère nativement l'asynchronisme (asyncio), indispensable quand chaque tour de conversation implique des appels concurrents à la DB et à un LLM externe. Flask (synchrone) aurait introduit des blocages. Django est trop lourd pour un projet API-first.

Séparation fast-path / LLM

Un appel LLM introduit 500ms à 2s de latence — perceptible au téléphone. Le fast-path traite la majorité des interactions simples (oui/non, sélections connues) sans LLM, réduisant la latence à quelques millisecondes.

Machine à états plutôt que LLM libre

La machine à états offre une prévisibilité et une robustesse supérieures à un LLM qui piloterait librement la conversation. Chaque état définit précisément ce qui est attendu et valide. Cela élimine les dérives conversationnelles.

Sortie JSON stricte + validation Pydantic

Le LLM est contraint à produire exclusivement du JSON valide. Pydantic valide chaque réponse avant exécution : aucune sortie malformée ne peut atteindre la couche données.

Session persistée en base de données

Chaque webhook Twilio est une requête HTTP stateless indépendante. La session ne peut pas être stockée en mémoire. La persistance DB garantit la cohérence même en cas de redémarrage ou de déploiement multi-instances.

Numéro de suivi unifié (tracking_code)

Le tracking_code (ex : A1B2C3) est le même identifiant entre le canal vocal et le tableau de bord. Cette unicité élimine toute confusion entre l'UUID interne et la référence communiquée au client.


12. Difficultés et problématiques rencontrées

# Problème Impact
1 Langage oral imprécis (« marguerite » → Margherita) Commandes mal détectées
2 Expressions familières ou mélange de langues LLM mal orienté
3 Options obligatoires manquantes (garniture non précisée) Commande incomplète
4 Hallucinations du LLM (produits inventés) Données incorrectes en DB
5 Latence LLM perceptible au téléphone (1-3s) Mauvaise expérience client
6 Rupture de stock non reflétée en temps réel (cache) Produit indispo proposé quand même
7 Boisson formule ajoutée comme article payant séparé Facturation incorrecte
8 Faux positifs dans la détection de boisson Mauvaise boisson assignée
9 Dashboard affichait UUID ≠ tracking_code annoncé au tel Référence introuvable
10 « je veux suivre » routé vers commande Mauvais flux déclenché
11 Prix annoncé « neuf virgule cinq euros » Incompréhensible à l'oral

13. Solutions mises en place

# Solution
1 _find_menu_item : matching multi-niveaux + normalisation sans accents
2 LLM avec prompt enrichi d'exemples familiers ; fast-path pour les cas fréquents
3 _validate_required_options côté backend, indépendante du LLM
4 Sortie JSON stricte + validation Pydantic + re-validation métier avant exécution
5 Fast-path prioritaire pour toutes les interactions prévisibles
6 invalidate_pizza_menu_cache(restaurant_id) appelé immédiatement sur update manager
7 Signal __FORMULE_BOISSON__ : boisson dans pending_item, non créée en DB
8 _detect_boisson retourne uniquement si item réel de la liste correspond (pas de fallback générique)
9 tracking_code ajouté à OrderRead + utilisé dans le dashboard à la place de id.slice(0,8)
10 Vérifications suivre / info exécutées avant la liste intent_words dans l'état idle
11 Fonction _prix_vocal() : conversion décimale → expression naturelle française

14. Sécurité, robustesse et fiabilité

Validation à trois niveaux

Requête entrante
      │
      ▼ Pydantic (schéma API)
      │
      ▼ Validation sortie LLM (Pydantic)
      │
      ▼ Validation métier (options, disponibilité, transitions)
      │
      ▼ Exécution DB

Gestion des erreurs

Chaque opération critique (appel LLM, requête SQL, parsing JSON) est encadrée par un bloc de gestion d'erreur. En cas d'échec, un message de fallback générique est retourné, maintenant la conversation active.

Anti-boucle infinie

Un compteur repeat_count par session détecte les interactions sans progression. Après N répétitions, le système propose une sortie alternative ou invite à contacter directement le restaurant.

Confirmation obligatoire

La commande n'est jamais enregistrée sans confirmation explicite du client — filet de sécurité humain dans la chaîne automatisée.

Logs structurés

Tous les événements critiques sont journalisés (réception webhook, état session, signal fast-path, appel LLM, action exécutée, erreurs). Ces logs permettent de diagnostiquer les dysfonctionnements rapidement.


15. Résultats attendus et apport du projet

Apport Détail
Disponibilité 24/7 Aucun appel sans réponse, aucun signal occupé, appels simultanés
Réduction des erreurs Reformulation + confirmation systématique
Libération du personnel Le personnel se concentre sur la production et le service en salle
Expérience client améliorée Réponse immédiate, fluide, avec numéro de suivi
Traçabilité complète Chaque commande vocale est enregistrée, horodatée, référencée
Base évolutive Architecture extensible vers paiement, fidélité, statistiques

16. Limites du projet

  • Qualité audio : la transcription dépend du bruit ambiant et de la qualité réseau
  • Accents marqués : certains accents régionaux ou prononciations atypiques peuvent dégrader la STT
  • Menu bien structuré requis : des libellés imprécis ou des contraintes mal définies génèrent des erreurs non compensables
  • Coût des services cloud : Twilio (à la minute), LLM cloud (au token) — volume élevé = coût significatif
  • Latence résiduelle : 1 à 3s en contexte LLM, perceptible pour un utilisateur impatient
  • Couverture fonctionnelle partielle : pas de paiement, pas de modification après confirmation, pas de fidélité

17. Perspectives d'amélioration

Priorité Amélioration
Haute Paiement intégré : lien SMS Stripe ou IVR payment Twilio
Haute Intégration caisse (POS) : Lightspeed, Zelty, Addition
Moyenne Statistiques dashboard : produits les plus commandés, panier moyen, heures de pointe
Moyenne Multilingue avancé : détection auto de la langue, flux adapté
Moyenne Clients réguliers : reconnaissance par numéro, commandes habituelles, fidélité
Basse Modèle STT spécialisé : entraîné sur le vocabulaire restauration
Basse Personnalisation par restaurant : ton de l'assistant, règles spécifiques
Basse Amélioration continue : analyse des transcriptions pour enrichir le matching

18. Installation et démarrage

Prérequis

  • Docker + Docker Compose
  • Node.js 18+
  • Python 3.11+ (développement local)

Démarrage rapide

# 1. Lancer l'infrastructure (Postgres, Keycloak, Redis, API)
docker compose up -d

# 2. Vérifier que l'API répond
curl -s http://localhost:8000/health
# => {"status":"ok"}

# 3. Documentation OpenAPI
open http://localhost:8000/docs

Variables d'environnement backend

DATABASE_URL=postgresql+asyncpg://orderlingo:orderlingo_secret@postgres:5432/orderlingo
KEYCLOAK_ISSUER=http://localhost:8081/realms/food
KEYCLOAK_AUDIENCE=food-api

# Twilio
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_AUTH_TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_PHONE_NUMBER=+33xxxxxxxxx

# LLM (choisir un fournisseur)
GROQ_API_KEY=gsk_xxxxxxxxxxxx
GEMINI_API_KEY=AIzaxxxxxxxxxx
OLLAMA_BASE_URL=http://localhost:11434   # LLM local

Démarrage du frontend

cd frontend
cp .env.example .env.local
# Éditer .env.local : KEYCLOAK_CLIENT_SECRET = secret du client food-api

npm install
npm run dev
# → http://localhost:3000

Comptes de démonstration (créés automatiquement)

Username Mot de passe Rôle
admin-food admin123 platform_admin
manager1 manager123 restaurant_manager
staff1 staff123 staff

Migrations Alembic

cd backend
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

export DATABASE_URL="postgresql+asyncpg://orderlingo:orderlingo_secret@localhost:5432/orderlingo"

alembic upgrade head          # appliquer toutes les migrations
alembic revision --autogenerate -m "description"  # créer une révision

Endpoints API principaux

Méthode Endpoint Rôle Accès
POST /restaurants Créer un restaurant platform_admin
GET /restaurants Lister les restaurants platform_admin
PATCH /restaurants/{id} Modifier un restaurant platform_admin / manager
POST /restaurants/{id}/menu/items Ajouter un produit manager
PATCH /restaurants/{id}/menu/items/{item_id} Modifier un produit manager
GET /restaurants/{id}/availability Disponibilité des produits staff / manager
POST /restaurants/{id}/orders Créer une commande staff / manager
GET /restaurants/{id}/orders Lister les commandes staff / manager
PATCH /restaurants/{id}/orders/{id}/status Changer le statut staff / manager
POST /twilio/voice/incoming Webhook appel entrant Twilio
POST /twilio/voice/step Webhook tour de conversation Twilio

19. Conclusion

OrderLingo représente une réponse concrète, techniquement fondée et commercialement pertinente au défi de l'automatisation de la prise de commande téléphonique dans la restauration. En combinant la puissance de la téléphonie cloud (Twilio), la flexibilité d'un backend moderne asynchrone (FastAPI / Python), les capacités d'interprétation des LLM contemporains, et un tableau de bord intuitif (Next.js), il propose une solution capable de comprendre un client, de l'accompagner dans sa commande, de valider les détails, et de produire un résultat exploitable par le système d'information du restaurant.

Le projet illustre comment l'intelligence artificielle conversationnelle peut être intégrée dans un processus métier réel, avec toutes les contraintes que cela implique : fiabilité des données, latence acceptable, robustesse face aux erreurs, et cohérence de l'expérience utilisateur. Il démontre aussi qu'une architecture en couches bien pensée — séparation claire entre dialogue, logique métier, IA et données — est indispensable pour construire un système de ce type de façon maintenable et évolutive.

Les défis rencontrés ont conduit à des solutions techniques précises : fast-path par règles, invalidation de cache immédiate, format vocal naturel, numéro de suivi unifié entre le canal vocal et le tableau de bord. Ces solutions illustrent la réalité du développement logiciel en contexte IA : les modèles sont des outils puissants, mais ils doivent être encadrés par une logique métier solide pour être fiables en production.


20. Annexes

Mots-clés

intelligence artificielle vocale · prise de commande téléphonique · NLP · LLM · Twilio · FastAPI · Python · machine à états · webhook · Speech-to-Text · Text-to-Speech · TwiML · restauration automatisée · assistant conversationnel · PostgreSQL · SQLAlchemy · Pydantic · cache TTL · tracking_code · fast-path · prompt engineering · JSON structuré · multilingue · rupture de stock · confirmation vocale · API REST · Groq · Gemini · Ollama · Next.js · Keycloak · tableau de bord · multi-tenant


Tableau Problèmes / Solutions

Problématique Solution mise en place
Langage oral imprécis (« marguerite ») Matching multi-niveaux + normalisation sans accents (_normalize_for_match)
Expressions familières ou multilingues LLM avec exemples dans le prompt ; fast-path pour les cas fréquents
Confusion entre produits similaires Tri par longueur décroissante ; demande de précision si ambiguïté
Options obligatoires manquantes _validate_required_options côté backend, indépendante du LLM
Hallucinations du LLM Sortie JSON stricte + validation Pydantic + validation métier avant exécution
Latence LLM perceptible Fast-path prioritaire pour toutes les interactions simples
Rupture stock non reflétée en temps réel invalidate_pizza_menu_cache() appelé immédiatement sur action manager
Boisson formule ajoutée comme article payant Signal __FORMULE_BOISSON__ : boisson dans pending_item uniquement, sans add_item
Faux positifs détection boisson _detect_boisson retourne uniquement si item réel de la liste correspond
Dashboard UUID ≠ tracking_code vocal tracking_code ajouté à OrderRead ; dashboard utilise tracking_code ?? id.slice(0,8)
« je veux suivre » routé vers commande Vérifications suivre/info exécutées avant intent_words dans l'état idle
Prix annoncé « neuf virgule cinq » _prix_vocal() : « neuf euros et cinquante centimes »
Boucle infinie sur incompréhension Compteur repeat_count par session ; fallback après N répétitions
Session perdue entre webhooks HTTP CallSession persistée en DB avec état, pending_item, order_id courant

Projet OrderLingo — Mars 2026

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors