🎯 Ce que tu as ici : un standard téléphonique IA qui décroche à la place du restaurant pendant le coup de feu, prend la réservation (couverts, service, heure, allergies), l'écrit dans Google Agenda et envoie la confirmation par e-mail. Hébergé sur ton VPS, pour environ 4 centimes la minute.
📦 Le code est sur GitHub : NautyZ-coder/agent-vocal-ia
Tout est dedans : le serveur vocal, l'assistant de configuration, les 2 workflows n8n et un script qui vérifie toute la chaîne. Le métier restaurant est prêt à l'emploi (un restaurant de démo, « Le Comptoir des Halles », avec Camille à l'accueil). Le même repo sert aussi pour un garage ou un cabinet dentaire.
✅ Ce que tu obtiens
- Un agent qui annonce qu'il est une IA dès la première phrase (obligatoire, voir plus bas), puis demande pour combien de personnes, quel jour, midi ou soir
- L'heure demandée d'abord : « 20 heures » est libre, il la confirme. Sinon il le dit et propose les heures libres les plus proches (19 heures 30, 20 heures 30…), calculées à partir des horaires, du nombre de tables et de l'agenda réel
- Les groupes : au-delà de 8 personnes (réglable), il ne réserve pas et prend un message pour que le restaurant rappelle
- Allergies et demandes particulières (chaise haute, anniversaire) notées sur la réservation
- Récap (nom, couverts, jour et heure, e-mail relu lettre par lettre), puis réservation seulement après un « oui »
- Réservation écrite dans Google Agenda (« Table au restaurant – nom », avec le nombre de couverts et les allergies dans la description), mail de confirmation au client, mail « Nouveau RDV » au restaurant
- Privatisation, réclamation, objet oublié : il prend un message, envoyé par mail au restaurant
- Il répond aux questions de la FAQ (terrasse, parking, végétarien, accessibilité…) et n'invente rien en dehors
- Rapport de fin d'appel avec la transcription complète
- Une page d'appel dans le navigateur pour tester gratuitement, sans numéro de téléphone
- Multi-clients : un fichier de config par restaurant, donc revendable
Le circuit d'un appel
- Le client appelle le numéro Twilio ou ouvre la page /appel.
- L'audio arrive en temps réel sur ton VPS : Deepgram (oreilles) → Gemini ou Claude (cerveau) → Cartesia (voix).
- L'agent appelle n8n par webhooks.
- n8n écrit dans Google Agenda et envoie les mails par Gmail.
🧰 Prérequis
| Quoi | Rôle | Prix pour tester |
|---|---|---|
| Un VPS Linux avec Docker (2 Go de RAM suffisent) | Héberge l'agent | Celui que tu as déjà |
| Un nom de domaine | Sous-domaine du type voice.tondomaine.com | Celui que tu as déjà |
| n8n (sur le même VPS, c'est l'idéal) | Agenda + mails | Gratuit en self-hosted |
| Deepgram | Les oreilles (transcription) | Crédit offert à l'inscription |
| Cartesia | La voix | Gratuit jusqu'à 20 000 caractères/mois |
| Google AI Studio (Gemini) ou Anthropic (Claude Haiku) | Le cerveau | Gemini : quota gratuit |
| Un compte Google | Agenda + Gmail | Gratuit |
| Twilio (facultatif) | Un vrai numéro de téléphone | Payant dès que tu veux ton propre numéro |
⚠️ Piège : Mistral. Les clés API Mistral demandent maintenant un plan payant. Prends Gemini pour tester, ou Claude Haiku si tu veux le plus fiable sur la prise de réservation.
1. Récupère tes clés
- Deepgram → API Keys → Create a New API Key.
- Cartesia → API Keys → crée une clé. Puis va dans Voices, filtre French, choisis une voix et copie son ID (tu en auras besoin à l'étape 4).
- Gemini → aistudio.google.com/apikey → Create API key. (ou une clé Anthropic si tu préfères Claude)
- Garde-les dans un gestionnaire de mots de passe. Ne les colle jamais dans un chat ou une capture.
2. Fais pointer ton sous-domaine vers le VPS
Chez ton registrar, ajoute un enregistrement A : voice → l'IP de ton VPS. Vérifie qu'il répond avant d'aller plus loin :
dig +short voice.tondomaine.com
⚠️ Piège : DNS avant HTTPS. Si le domaine ne pointe pas encore sur le VPS, le certificat HTTPS échoue et tu prends une erreur 502. Attends que dig renvoie la bonne IP.
3. Dépose le code sur le VPS
Sur le VPS :
cd /opt && git clone https://github.com/NautyZ-coder/agent-vocal-ia.git agent-vocal && cd agent-vocal
💡 Astuce : tu avais déjà installé la version garage ou cabinet ?cd /opt/agent-vocal && git pullsuffit pour récupérer le restaurant. Tonserver/.envn'est pas suivi par git, il reste en place.
4. Lance l'assistant de configuration
bash server/configure.sh
Il te demande, dans l'ordre : ton métier (tape r = restaurant), ton sous-domaine, les clés Deepgram et Cartesia, l'ID de la voix, le modèle (g = Gemini, c = Claude), l'e-mail qui recevra les réservations, l'URL de ton n8n, et Twilio (Entrée pour passer).
À la fin, il affiche deux choses à noter :
- 🔑 le secret à coller dans n8n (étape 6)
- 📞 le lien de ta page d'appel, code d'accès compris (étape 9)
⚠️ Piège : le code d'accès perdu. La page d'appel demande un code avant le bouton « Appeler ». Il est généré une fois et rangé dansserver/.env(pas dans un.envà la racine), sousWEB_CALL_CODE. Pour le retrouver sur le VPS :grep '^WEB_CALL_CODE=' /opt/agent-vocal/server/.env. Ne le publie pas : avec lui, n'importe qui consomme tes crédits.
💡 Pas encore de n8n prêt ? Appuie sur Entrée à la question de l'URL : l'agent tourne avec un agenda simulé. Tu relanceras l'assistant plus tard.
5. Démarre l'agent (HTTPS compris)
Deux cas, choisis le tien :
Cas A : rien ne tourne sur les ports 80/443 (VPS neuf). Caddy est inclus et gère le certificat tout seul.
docker compose --profile caddy up -d --build
Cas B : tu as déjà un reverse proxy dans Docker (Caddy, Traefik, Nginx Proxy Manager…). Trouve le nom du réseau Docker de ton proxy avec docker network ls, ajoute-le dans server/.env (PROXY_NETWORK=nom_du_reseau), puis :
docker compose --env-file server/.env -f docker-compose.yml -f docker-compose.proxy.yml up -d --build
Ensuite, dans ton proxy, route voice.tondomaine.com vers voice-agent:8765. Exemple avec Caddy :
voice.tondomaine.com {
reverse_proxy voice-agent:8765
}
⚠️ Piège : Caddy. Valide toujours avant de recharger, sinon une faute de frappe coupe tous tes sites :docker exec caddy caddy validate --config /etc/caddy/Caddyfile && docker exec caddy caddy reload --config /etc/caddy/Caddyfile
Test : https://voice.tondomaine.com/health doit afficher {"ok":true,...} avec restaurant-demo dans la liste.
💡 L'agent est plafonné à 1 Go de RAM et 1 CPU dans le docker-compose.yml, pour ne jamais faire tomber le reste de ton VPS (n8n, base de données…).
6. Branche n8n (agenda + mails)
a. Importe les 2 workflows du dossier n8n/ : Workflows → Import from File → agent-vocal-agenda.json, puis agent-vocal-notifications.json.
b. Crée le credential de sécurité : Credentials → New → Header Auth.
- Name :
X-Voice-Secret - Value : le secret affiché par l'assistant à l'étape 4
Sélectionne ce même credential dans tous les nœuds Webhook des 2 workflows.
🚨 Le piège n°1 (erreur 403). Le champ Name n'est pas le nom de ton credential : c'est le nom de l'en-tête HTTP. Il doit être exactementX-Voice-Secret. Crée un seul credential et réutilise-le partout. Si tu changes le secret, change-le aux deux endroits (n8n etserver/.env).
c. Connecte Google (une seule fois, sert pour l'agenda et Gmail) :
- console.cloud.google.com → crée un projet.
- APIs & Services → Library → active Google Calendar API et Gmail API.
- OAuth consent screen → type External → ajoute ton adresse Gmail dans Test users.
- Credentials → Create credentials → OAuth client ID → Web application. Dans Authorized redirect URIs, colle l'URL de redirection que n8n t'affiche (du type
https://n8n.tondomaine.com/rest/oauth2-credential/callback). - Dans n8n, crée un credential Google Calendar OAuth2 API et un Gmail OAuth2 API avec le même Client ID / Client Secret, puis clique « Sign in with Google ».
- Sélectionne-les dans les nœuds Google Agenda (HTTP Request) et Gmail.
d. Crée l'agenda du restaurant et indique-le à n8n.
- Dans Google Agenda, avec le compte connecté à n8n : « Autres agendas » → + → « Créer un agenda ». Nom :
Restaurant, fuseau : Paris. - Paramètres de cet agenda → « Intégrer l'agenda » → copie l'ID de l'agenda (du type
…@group.calendar.google.com). - Dans n8n, le bloc
GARAGESen haut des nœuds Code contient une entrée par client (le nom date du garage, il sert à tous les métiers). Dans l'entréerestaurant-demo, remplaceID_AGENDA_RESTAURANT@group.calendar.google.compar ton ID, ettoi@exemple.compar l'e-mail qui reçoit les réservations. - Fais-le dans les 5 nœuds Code qui contiennent ce bloc : « Préparer la recherche » et « Préparer l'événement + SMS » (workflow agenda), « Formater le message », « Formater le nouveau RDV » et « Formater le rapport » (workflow notifications).
⚠️ Piège : le faux ID. Tant que ID_AGENDA_RESTAURANT@group.calendar.google.com est en place, l'agent ne peut ni lire les disponibilités ni écrire les réservations. Un agenda dédié au restaurant évite aussi de mélanger les tables avec tes RDV perso.
e. Active les 2 workflows (bouton en haut à droite).
🚨 Piège : enregistrer n'est pas publier. Sur les versions récentes de n8n, quand tu modifies un workflow déjà actif, Ctrl+S n'enregistre qu'un brouillon. La version qui répond aux appels reste l'ancienne tant que tu ne cliques pas sur Publish. Symptôme : tes modifs sont bien visibles dans l'éditeur, mais n8n répond « Garage inconnu » au moment de réserver. Après chaque modif, clique Publish dans les 2 workflows.
💡 Si n8n et l'agent sont sur le même réseau Docker, tu peux mettreN8N_WEBHOOK_BASE=http://n8n:5678/webhookdansserver/.env: plus rapide, et ça ne passe pas par Internet.
💡 Le nœud SMS est désactivé par défaut : envoyer des SMS demande un numéro Twilio payant. L'e-mail suffit pour commencer.
7. Configure ton restaurant
Tout se passe dans un seul fichier : server/tenants/restaurant-demo.yaml. Les champs importants :
name,address,timezone(ex.Europe/Paris)profile: restaurant: c'est lui qui choisit le script de l'agent (couverts, service, allergies). Ne le change pas.agent: prénom, voix (voice_id), phrase d'accueil et phrase d'au revoir (goodbye). Accorde le prénom avec la voix (voix d'homme → prénom masculin).opening_hours: une plage par service, de la première arrivée jusqu'à l'heure où la table doit être libérée. Avec des tables de 90 minutes,"19:00-23:00"donne une dernière arrivée à 21 heures 30. Un jour fermé =[].booking.capacity: le nombre de tables réservables en même temps (garde-en quelques-unes pour les clients sans réservation)services: une seule entrée, la table, avec sa durée (duration_min: 90)extra.max_party_size: la taille maximale d'un groupe réservable par l'agent (8 par défaut). Au-delà, il prend un message.faq: ce que l'agent peut répondre (carte, terrasse, parking, végétarien, délai d'annulation…). Il n'invente rien en dehors.transfer_number: un numéro pour passer la main à un humain (vide = il prend un message et propose d'être rappelé)
Puis :
docker compose restart voice-agent
Les réglages du restaurant de démo
| Réglage | Valeur |
|---|---|
| Services | Midi 12 h – 15 h (dernière arrivée 13 h 30), soir 19 h – 23 h (dernière arrivée 21 h 30). Fermé le lundi, pas de service le dimanche soir. |
| Durée d'une table | 90 min |
| Tables en parallèle | 10 |
| Pas des horaires | Toutes les 30 min (19 h, 19 h 30, 20 h…) |
| Délai minimum / horizon | 1 h / 30 jours |
| Groupe max | 8 personnes (au-delà : message pour rappel) |
💡 Pas de réservation en terrasse dans la démo : la FAQ le dit, et l'agent le répète au client. Adapte cette ligne à ton restaurant.
8. Vérifie toute la chaîne
docker exec -i voice-agent python - < server/check.py
Neuf vérifications : config, clés, appel d'outil par l'IA, transcription, voix, lecture et écriture dans l'agenda via n8n, Twilio. Tout doit être vert avant de passer un appel. Chaque ligne rouge te dit quoi corriger.
💡 Les tests du check utilisent des exemples de garage (une vidange, « Garage Démo Auto »), même en mode restaurant. C'est normal : ils vérifient que la chaîne fonctionne, pas le métier. Le restaurant se teste à l'étape 9.
9. Passe ton premier appel
Sans numéro (gratuit) : ouvre le lien de ta page d'appel (https://voice.tondomaine.com/appel?g=restaurant-demo&code=...) et appuie sur le bouton vert.
⚠️ Piège iPhone. Ouvre la page dans Safari. Si tu l'ajoutes à l'écran d'accueil et que tu la lances depuis l'icône, le micro reste muet.
Petit scénario de test, en trois appels :
- La réservation normale : « une table pour 2 demain soir à 20 heures ». Si 20 heures est libre, il la confirme. Donne ton nom, dis « pas d'allergie », donne ton e-mail, dis « oui » au récap, puis « non merci, c'est tout ».
- L'allergie : réserve pour 4 en précisant « une personne allergique aux fruits à coque ». L'allergie doit apparaître dans la description de l'événement.
- Le groupe : « une table pour 12 samedi midi ». Il ne doit pas réserver, mais prendre un message pour que le restaurant rappelle.
Vérifie ensuite l'agenda du restaurant et ta boîte mail.
Avec un vrai numéro (Twilio) : relance bash server/configure.sh pour ajouter l'Account SID et l'Auth Token, puis sur ton numéro Twilio → Voice → A call comes in → Webhook https://voice.tondomaine.com/twilio/voice (POST). Ajoute ce numéro dans phone_numbers du fichier YAML du restaurant : c'est ce numéro qui dit à l'agent pour quel client il répond. Le restaurant n'a plus qu'à renvoyer sa ligne vers ce numéro (en cas de non-réponse ou d'occupation, pendant le service).
💡 Un compte Twilio d'essai, sans crédit, ne peut pas acheter de numéro et ajoute un message d'avertissement en début d'appel. Pour une démo, la page web fait le même travail.
10. Suis les appels en direct
docker compose logs -f voice-agent | grep -E --line-buffered "CLIENT :|AGENT :|ERROR|WARNING"
Tu vois chaque phrase du client et de l'agent. Ctrl+C pour sortir.
🔁 Au quotidien
- Modifier le restaurant (horaires, FAQ, accueil, nombre de tables) → édite le YAML, puis
docker compose restart voice-agent - Ajouter un restaurant client → copie
restaurant-demo.yamlen<id-client>.yaml, changeid,name, l'accueil etphone_numbers. Dans n8n, ajoute l'entrée<id-client>(avec soncalendarId) dans le blocGARAGESdes 5 nœuds Code, puis Publish les 2 workflows. Redémarre et teste avec/appel?g=<id-client>&code=... - Changer de modèle ou de métier par défaut → relance
bash server/configure.sh, puisdocker compose up -d(avec les mêmes options qu'à l'étape 5) : un simple restart ne relit passerver/.env - Après une mise à jour du code →
git pull,docker compose up -d --build(avec les mêmes options qu'à l'étape 5), puis relance le check
🛠️ Ça ne marche pas ?
Erreur 502 sur le sous-domaine
Le proxy ne joint pas l'agent. Vérifie que le conteneur tourne (docker ps), que le DNS pointe bien sur le VPS, et en cas B que l'agent est sur le même réseau Docker que ton proxy (docker network inspect nom_du_reseau).
n8n répond 403
Le secret ne correspond pas. Vérifie que le Name du credential Header Auth est exactement X-Voice-Secret, que la Value est identique à N8N_WEBHOOK_SECRET dans server/.env, et que tous les nœuds Webhook utilisent ce même credential.
n8n répond 404
Les workflows ne sont pas activés, ou N8N_WEBHOOK_BASE ne se termine pas par /webhook.
« Garage inconnu » au moment de réserver
L'id du YAML (restaurant-demo) n'a pas d'entrée dans le bloc GARAGES de la version publiée du workflow. Vérifie les 5 nœuds Code, puis clique Publish (voir le piège de l'étape 6e).
Il annonce une heure prise alors que l'agenda est vide
Vérifie le calendarId (faux ID encore en place ?) et que le compte Google connecté à n8n a bien accès à l'agenda du restaurant en modification. Vérifie aussi opening_hours : 22 heures est refusé si la table doit être libérée à 23 heures avec des tables de 90 minutes.
L'agent ne propose jamais d'heure / erreur d'outil avec Gemini
Garde LLM_PROVIDER=google. L'ancienne méthode « compatible OpenAI » de Gemini casse les appels d'outils (erreur « Name cannot be empty »). Le code utilise l'API native de Google.
Pas de mail de confirmation
Le workflow agent-vocal-notifications est-il importé, activé et publié ? Le credential Gmail est-il connecté ? Le client a-t-il donné un e-mail valide ? (les logs montrent l'adresse comprise). Pense aux spams.
Le son coupe ou arrive par morceaux
Souvent un Wi-Fi faible. Teste en 4G ou rapproche-toi de la box.
💶 Combien ça coûte
Environ 0,04 $ la minute en production (transcription + IA + voix), plus le numéro de téléphone si tu en prends un. Pour tester : quasiment gratuit avec les offres de Deepgram, Cartesia et Gemini. Un appel de réservation dure 1 à 2 minutes.
⚖️ Côté légal
- AI Act, article 50 : la personne doit savoir qu'elle parle à une IA. L'accueil le dit dès la première phrase, ne l'enlève pas.
- RGPD : l'agent collecte des données personnelles (nom, téléphone, e-mail). Mentionne-le dans la politique de confidentialité du restaurant et ne garde que ce qui sert à la réservation.
- Allergies : une allergie rattachée à un nom peut être considérée comme une donnée de santé (RGPD, article 9). L'agent la note en une phrase courte, uniquement pour la cuisine. Ne la réutilise pas pour autre chose (fichier client, marketing) et supprime les réservations passées au bout d'un délai raisonnable.
- Si tu le revends : le restaurant reste responsable de ses données, toi de l'hébergement. Écris-le dans ton contrat.