Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# OpenAI API Key
OPENAI_API_KEY=your_openai_api_key_here

# Email Configuration (pour l'envoi d'emails via Nodemailer)
# Option 1: Gmail (recommandé pour tester)
# 1. Créer un "App Password" dans votre compte Google: https://myaccount.google.com/apppasswords
# 2. Utiliser cet App Password (pas votre mot de passe Gmail normal)
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
[email protected]
SMTP_PASS=votre-app-password-gmail

# Option 2: Outlook/Office365
# SMTP_HOST=smtp-mail.outlook.com
# SMTP_PORT=587
# [email protected]
# SMTP_PASS=votre-mot-de-passe

# Option 3: Serveur SMTP personnalisé
# SMTP_HOST=smtp.votre-domaine.com
# SMTP_PORT=587
# [email protected]
# SMTP_PASS=votre-mot-de-passe
135 changes: 135 additions & 0 deletions SETUP_EMAIL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Configuration de l'envoi d'emails

Ce guide explique comment configurer l'envoi d'emails réels pour les demandes de rappel.

## 📦 Installation de Nodemailer

```bash
npm install nodemailer
npm install --save-dev @types/nodemailer
```

## 🔧 Configuration

### Option 1: Gmail (recommandé pour tester)

1. **Créer un App Password Gmail**:
- Allez sur https://myaccount.google.com/apppasswords
- Connectez-vous à votre compte Google
- Sélectionnez "Mail" et "Autre (nom personnalisé)"
- Entrez "Grand Chasseral Immo Assistant"
- Copiez le mot de passe généré (16 caractères)

2. **Configurer `.env`**:
```env
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
[email protected]
SMTP_PASS=xxxx xxxx xxxx xxxx # App Password de l'étape 1
```

### Option 2: Outlook/Office365

```env
SMTP_HOST=smtp-mail.outlook.com
SMTP_PORT=587
[email protected]
SMTP_PASS=votre-mot-de-passe
```

### Option 3: Serveur SMTP personnalisé

```env
SMTP_HOST=smtp.votre-domaine.com
SMTP_PORT=587
[email protected]
SMTP_PASS=votre-mot-de-passe
```

## 🚀 Utilisation

Une fois configuré, l'agent `contactHumanAgent` enverra automatiquement des emails réels quand:
- Un client demande à contacter un collaborateur
- Le collaborateur n'est pas disponible
- Le client accepte d'envoyer un email de rappel

### Exemple de flux

```
User: "Je voudrais parler à Sandy Bircher"
Agent: [vérifie disponibilité]
Agent: "Sandy Bircher n'est pas disponible actuellement.
Puis-je vous proposer de lui envoyer un email pour qu'elle vous rappelle ?"
User: "Oui, s'il vous plaît"
Agent: "Parfait ! J'ai besoin de votre nom, numéro de téléphone, email et un bref message."
User: "Je m'appelle Jean Dupont, mon numéro est 079 123 45 67,
email [email protected], je souhaite discuter d'un appartement à louer"
Agent: [appelle sendCallbackRequest]
→ Email envoyé à [email protected]
→ Copie envoyée à [email protected]
Agent: "Votre demande de rappel a été envoyée par email à Sandy Bircher.
Vous serez contacté au 079 123 45 67 dans les plus brefs délais."
```

## 📧 Format de l'email envoyé

**À**: [email protected]
**Cc**: [email protected]
**Sujet**: Demande de rappel - Jean Dupont

```
Bonjour Sandy Bircher,

Vous avez reçu une demande de rappel via l'assistant vocal Grand Chasseral Immo SA.

📋 Informations du client:
- Nom: Jean Dupont
- Téléphone: 079 123 45 67
- Email: [email protected]

💬 Message:
Je souhaite discuter d'un appartement à louer

Merci de contacter ce client dans les plus brefs délais.
```

## 🔒 Sécurité

- ✅ Ne commitez **JAMAIS** votre fichier `.env` (déjà dans `.gitignore`)
- ✅ Utilisez des App Passwords, pas vos mots de passe principaux
- ✅ Les emails sont envoyés côté serveur (Next.js API), jamais côté client
- ✅ Le client reçoit une copie de l'email pour confirmation

## 🧪 Test

Pour tester l'envoi d'email:

1. Installez les dépendances
2. Configurez `.env` avec vos identifiants SMTP
3. Lancez `npm run dev`
4. Connectez-vous au scénario `realEstate`
5. Demandez à contacter un collaborateur
6. Acceptez d'envoyer un email de rappel
7. Vérifiez votre boîte de réception

## ❌ Dépannage

### Erreur: "Invalid login"
- Vérifiez que vous utilisez un App Password (Gmail)
- Vérifiez que l'authentification à 2 facteurs est activée (Gmail)

### Erreur: "Connection timeout"
- Vérifiez votre pare-feu
- Vérifiez que le port 587 est ouvert

### Email non reçu
- Vérifiez les spams
- Vérifiez que l'email du collaborateur est correct dans `constants.ts`
- Vérifiez les logs de la console Next.js

## 🎯 Prochaines étapes

- [ ] Configurer un vrai service d'email professionnel (SendGrid, Mailgun, AWS SES)
- [ ] Ajouter des templates d'email plus sophistiqués
- [ ] Ajouter un système de suivi des demandes de rappel
- [ ] Intégrer avec un CRM
195 changes: 195 additions & 0 deletions doc/AGENT_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
# Guide interne: Ajouter un nouvel agent (OpenAI Realtime Agents SDK)

Ce guide explique la différence entre scénarios, agents et outils, et comment ajouter un nouvel agent/scénario dans ce repo. Il s’appuie sur le guide officiel: https://openai.github.io/openai-agents-js/guides/voice-agents/build/

## Définitions

- **Scénario**
- Un ensemble d’agents qui collaborent, avec des transferts définis entre eux.
- Exemple: `realEstateScenario` dans `src/app/agentConfigs/real-estate-agent/index.ts`.

- **Agent (RealtimeAgent)**
- Un agent vocal en temps réel, configuré avec des instructions (prompt), une voix, des **outils**, et des **handoffs** (transferts) vers d’autres agents.
- Exemple: `greeterAgent`, `sectorInfoAgent`, `appointmentAgent`, `complexTaskAgent`.

- **Outil (tool)**
- Une fonction exécutable côté serveur (Node/Next) que l’agent peut appeler pour effectuer des actions concrètes (ex. interroger des dispo, créer une tâche, envoyer un email).
- Déclaré via `tool({ name, description, parameters, execute })`.

## Contraintes clés (référence guide officiel)

- **Handoffs**
- Un handoff bascule la session vers un autre RealtimeAgent (même backend Realtime). On ne peut pas changer de voix/modèle dans le handoff.
- Pour utiliser un autre modèle (ex. reasoning), déléguer via un outil (pattern « delegation through tools »).

- **Audio / VAD / Interruptions**
- La gestion de l’audio, VAD et interruptions est déjà câblée dans `App.tsx` via `useRealtimeSession`.

- **Guardrails**
- Les sorties sont modérées via `createModerationGuardrail(companyName)` dans `App.tsx`.

## Structure du repo (extraits pertinents)

- `src/app/agentConfigs/` contient les scénarios et agents
- `index.ts` (registry de scénarios)
- `real-estate-agent/` (exemple complet immobilier)
- `greeter-agent.ts`
- `sector-info-agent.ts`
- `appointment-agent.ts`
- `complex-task-agent.ts`
- `constants.ts` (ex: `realEstateCompanyName`)
- `index.ts` (assemble les agents et définit les handoffs)
- `src/app/App.tsx`
- `sdkScenarioMap` (scénarios disponibles côté client)
- Mapping `companyName` → guardrails

## Ajouter un nouveau scénario + agents (pas à pas)

1. **Créer un dossier** `src/app/agentConfigs/<votre-scenario>/`.
2. **Créer des agents** (1 fichier par agent) avec:
- `name`, `voice`, `instructions`: prompt concis et orienté voix.
- `tools`: liste d’outils pertinents (voir section Outillage).
- `handoffDescription`: courte description (utile pour le transfert).
3. **Configurer les handoffs** dans `index.ts` du scénario:
```ts
(agentA.handoffs as any).push(agentB, agentC);
```
4. **Exporter le scénario** (tableau d’agents) dans `index.ts`:
```ts
export const myScenario = [agentA, agentB, agentC];
```
5. **Enregistrer le scénario dans le registre** `src/app/agentConfigs/index.ts`:
```ts
export const allAgentSets = { ... , myScenario };
```
6. **Ajouter le scénario au client** dans `src/app/App.tsx`:
- `sdkScenarioMap["myScenario"] = myScenario`
- Si besoin de guardrails personnalisés: ajouter un `companyName` au mapping.
7. **Éviter les dépendances circulaires**:
- Placez les constantes partagées (ex: nom d’entreprise) dans un `constants.ts` du scénario.
- Importez ces constantes **directement** dans `App.tsx` (évitez de les ré-exporter via `index.ts`).
8. **Tester**: `npm run dev` puis sélectionner votre scénario dans le menu "Scenario".

## Exemple: Scénario Immobilier (`realEstate`)

### Agents

- **`greeterAgent`** (`greeter-agent.ts`)
- Accueil + routage. Demande le besoin et transfère vers l’agent approprié.
- Transferts possibles: `sectorInfoAgent`, `appointmentAgent`, `complexTaskAgent`.

- **`sectorInfoAgent`** (`sector-info-agent.ts`)
- Répond aux questions quartiers/secteurs: prix moyen, commodités, transports, ambiance.
- Outils: `getSectorInfo`, `compareSectors` (exemples inclus).

- **`appointmentAgent`** (`appointment-agent.ts`)
- Collecte les infos (nom, téléphone, email, type de projet, préférences) puis propose des créneaux.
- Outils: `checkAvailability` (déjà implémenté), `bookAppointment` (exemple inclus).

- **`complexTaskAgent`** (`complex-task-agent.ts`)
- Gère les demandes hors périmètre. Crée une tâche interne et envoie un email récapitulatif.
- Outils: `createTask`, `sendEmail`.

### Outils (extraits)

- Définition générique d’un outil:
```ts
import { tool } from '@openai/agents/realtime';

const myTool = tool({
name: 'myToolName',
description: 'Ce que fait l’outil',
parameters: {
type: 'object',
properties: { /* schéma JSON */ },
required: [/* champs obligatoires */],
additionalProperties: false,
},
execute: async (input: any) => {
// logique serveur
return { /* résultat */ };
},
});
```

- Outil RDV: `checkAvailability` (dans `appointment-agent.ts`)
- Entrée: préférences (matin/après-midi, jours)
- Sortie: créneaux disponibles (mock)

- Outils ComplexTask: `createTask`, `sendEmail` (dans `complex-task-agent.ts`)
- `createTask(summary, details?, priority)` → `taskId`, `status`
- `sendEmail(to, subject, body)` → `sent`, `messageId`

### Handoffs

- Définis dans `real-estate-agent/index.ts`:
```ts
(greeterAgent.handoffs as any).push(sectorInfoAgent, appointmentAgent, complexTaskAgent);
(sectorInfoAgent.handoffs as any).push(greeterAgent, appointmentAgent, complexTaskAgent);
(appointmentAgent.handoffs as any).push(greeterAgent, sectorInfoAgent, complexTaskAgent);
(complexTaskAgent.handoffs as any).push(greeterAgent, sectorInfoAgent, appointmentAgent);
export const realEstateScenario = [greeterAgent, sectorInfoAgent, appointmentAgent, complexTaskAgent];
```

### Constantes & Guardrails

- `real-estate-agent/constants.ts` expose `realEstateCompanyName`.
- Dans `App.tsx` importez:
```ts
import { realEstateCompanyName } from '@/app/agentConfigs/real-estate-agent/constants';
```
- Ajoutez-le au mapping Guardrails:
```ts
const companyNameMap = {
customerServiceRetail: customerServiceRetailCompanyName,
chatSupervisor: chatSupervisorCompanyName,
realEstate: realEstateCompanyName,
};
```

## Bonnes pratiques

- **Instructions courtes & vocales**: style concis, naturel, éviter les listes trop longues.
- **Outils cohérents**: schémas JSON clairs, `additionalProperties: false`.
- **1 responsabilité par agent**: facilite le routage et la maintenance.
- **Tester les transferts**: vérifier que l’agent par défaut est celui désiré et que les handoffs couvrent les cas attendus.
- **Éviter les cycles d’import**: importer les constantes directement depuis `constants.ts`.

## Dépannage (FAQ)

- **Hydration error (classe ajoutée par extension)**
- Résolu en ajoutant `suppressHydrationWarning` sur `<html>` dans `src/app/layout.tsx`.

- **TDZ / Cannot access 'X' before initialization**
- Évitez de ré-exporter des constantes via `index.ts` si ce `index.ts` importe déjà des agents qui eux importent ces constantes. Importez directement depuis `constants.ts`.

- **Le scénario n’apparaît pas dans l’UI**
- Vérifier l’ajout dans `src/app/agentConfigs/index.ts` (registry) ET `sdkScenarioMap` dans `src/app/App.tsx`.

## Diagramme (exemple Immobilier)

```mermaid
flowchart LR
G[greeterAgent] --> S[sectorInfoAgent]
G --> A[appointmentAgent]
G --> C[complexTaskAgent]
S --> G
S --> A
S --> C
A --> G
A --> S
A --> C
C --> G
C --> S
C --> A
```

## Checklist d’ajout rapide

- [ ] Dossier `src/app/agentConfigs/<scenario>/` créé
- [ ] Agents créés avec instructions/outils/voix
- [ ] Handoffs configurés dans `<scenario>/index.ts`
- [ ] Scénario exporté et ajouté dans `src/app/agentConfigs/index.ts`
- [ ] Ajouté dans `sdkScenarioMap` de `src/app/App.tsx`
- [ ] Guardrails: companyName mappé si nécessaire
- [ ] Tests UI: menu Scenario → connexion → transferts OK
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,17 @@
"lint": "next lint"
},
"dependencies": {
"@openai/agents": "^0.0.5",
"@openai/agents": "^0.2.1",
"@radix-ui/react-icons": "^1.3.2",
"dotenv": "^16.4.7",
"next": "^15.3.1",
"nodemailer": "^7.0.10",
"openai": "^4.77.3",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-markdown": "^9.0.3",
"uuid": "^11.0.4",
"zod": "^3.24.1"
"zod": "^4.1.12"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
Expand Down
Loading