226 lines
7.1 KiB
TypeScript
226 lines
7.1 KiB
TypeScript
import OpenAI from 'openai';
|
|
|
|
const openai = new OpenAI({
|
|
apiKey: process.env.OPENAI_API_KEY || '',
|
|
});
|
|
|
|
interface ParsedBookingData {
|
|
customerName: string;
|
|
customerEmail: string;
|
|
customerPhone: string;
|
|
customerAddress?: string;
|
|
customerCity?: string;
|
|
customerZip?: string;
|
|
companyName?: string;
|
|
invoiceType: 'PRIVATE' | 'BUSINESS';
|
|
eventDate: string;
|
|
eventAddress: string;
|
|
eventCity: string;
|
|
eventZip: string;
|
|
eventLocation?: string;
|
|
setupTimeStart: string;
|
|
setupTimeLatest?: string;
|
|
photoboxModel?: 'VINTAGE_SMILE' | 'VINTAGE_PHOTOS' | 'NOSTALGIE' | 'MAGIC_MIRROR';
|
|
specialRequests?: string;
|
|
}
|
|
|
|
export class AIService {
|
|
async parseBookingEmail(emailContent: string, subject: string): Promise<{
|
|
parsed: ParsedBookingData;
|
|
responseDraft: string;
|
|
confidence: number;
|
|
}> {
|
|
const prompt = `Du bist ein KI-Assistent für ein Fotobox-Vermietungsunternehmen.
|
|
Analysiere die folgende E-Mail-Anfrage und extrahiere alle relevanten Buchungsinformationen.
|
|
|
|
E-Mail-Betreff: ${subject}
|
|
|
|
E-Mail-Inhalt:
|
|
${emailContent}
|
|
|
|
Extrahiere folgende Informationen (falls vorhanden):
|
|
- Kundenname
|
|
- E-Mail-Adresse
|
|
- Telefonnummer
|
|
- Adresse (Straße, PLZ, Stadt)
|
|
- Firmenname (falls Geschäftskunde)
|
|
- Event-Datum (Format: YYYY-MM-DD)
|
|
- Event-Uhrzeit (wann soll die Fotobox aufgebaut sein?)
|
|
- Event-Adresse (Straße, PLZ, Stadt)
|
|
- Event-Location-Name
|
|
- Gewünschtes Fotobox-Modell
|
|
- Besondere Wünsche
|
|
|
|
Antworte im folgenden JSON-Format:
|
|
{
|
|
"customerData": {
|
|
"name": "string",
|
|
"email": "string",
|
|
"phone": "string",
|
|
"address": "string",
|
|
"city": "string",
|
|
"zip": "string",
|
|
"companyName": "string oder null",
|
|
"invoiceType": "PRIVATE" oder "BUSINESS"
|
|
},
|
|
"eventData": {
|
|
"date": "YYYY-MM-DD",
|
|
"time": "HH:MM",
|
|
"address": "string",
|
|
"city": "string",
|
|
"zip": "string",
|
|
"locationName": "string",
|
|
"photoboxModel": "VINTAGE_SMILE" | "VINTAGE_PHOTOS" | "NOSTALGIE" | "MAGIC_MIRROR" oder null,
|
|
"specialRequests": "string"
|
|
},
|
|
"confidence": 0.0 bis 1.0 (wie sicher bist du bei der Extraktion?)
|
|
}`;
|
|
|
|
try {
|
|
const completion = await openai.chat.completions.create({
|
|
model: 'gpt-4-turbo-preview',
|
|
messages: [
|
|
{
|
|
role: 'system',
|
|
content: 'Du bist ein Experte für die Analyse von Buchungsanfragen. Antworte immer nur mit validem JSON.',
|
|
},
|
|
{
|
|
role: 'user',
|
|
content: prompt,
|
|
},
|
|
],
|
|
response_format: { type: 'json_object' },
|
|
temperature: 0.3,
|
|
});
|
|
|
|
const result = JSON.parse(completion.choices[0].message.content || '{}');
|
|
|
|
const parsed: ParsedBookingData = {
|
|
customerName: result.customerData.name,
|
|
customerEmail: result.customerData.email,
|
|
customerPhone: result.customerData.phone,
|
|
customerAddress: result.customerData.address,
|
|
customerCity: result.customerData.city,
|
|
customerZip: result.customerData.zip,
|
|
companyName: result.customerData.companyName,
|
|
invoiceType: result.customerData.invoiceType,
|
|
eventDate: `${result.eventData.date}T${result.eventData.time || '00:00'}:00.000Z`,
|
|
eventAddress: result.eventData.address,
|
|
eventCity: result.eventData.city,
|
|
eventZip: result.eventData.zip,
|
|
eventLocation: result.eventData.locationName,
|
|
setupTimeStart: `${result.eventData.date}T${result.eventData.time || '00:00'}:00.000Z`,
|
|
photoboxModel: result.eventData.photoboxModel,
|
|
specialRequests: result.eventData.specialRequests,
|
|
};
|
|
|
|
const responseDraft = await this.generateResponseDraft(parsed, emailContent);
|
|
|
|
return {
|
|
parsed,
|
|
responseDraft,
|
|
confidence: result.confidence,
|
|
};
|
|
} catch (error) {
|
|
console.error('AI Email Parsing Error:', error);
|
|
throw new Error('KI-Analyse fehlgeschlagen');
|
|
}
|
|
}
|
|
|
|
async generateResponseDraft(bookingData: ParsedBookingData, originalEmail: string): Promise<string> {
|
|
const prompt = `Du bist Kundenservice-Mitarbeiter bei "Save the Moment" - einem Premium-Fotobox-Vermietungsservice.
|
|
|
|
Erstelle eine professionelle, freundliche Antwort-E-Mail für diese Buchungsanfrage:
|
|
|
|
Kundendaten:
|
|
- Name: ${bookingData.customerName}
|
|
- Event-Datum: ${new Date(bookingData.eventDate).toLocaleDateString('de-DE')}
|
|
- Event-Ort: ${bookingData.eventCity}
|
|
${bookingData.specialRequests ? `- Besondere Wünsche: ${bookingData.specialRequests}` : ''}
|
|
|
|
Die E-Mail soll:
|
|
1. Freundlich und professionell sein
|
|
2. Die Anfrage bestätigen
|
|
3. Die wichtigsten Details zusammenfassen
|
|
4. Erwähnen, dass ein Angebot und Mietvertrag im Anhang sind
|
|
5. Nächste Schritte erklären (Vertrag digital unterschreiben)
|
|
6. Kontaktdaten für Rückfragen nennen
|
|
|
|
Schreibe die E-Mail auf Deutsch, ohne Betreff-Zeile (nur Body).`;
|
|
|
|
try {
|
|
const completion = await openai.chat.completions.create({
|
|
model: 'gpt-4-turbo-preview',
|
|
messages: [
|
|
{
|
|
role: 'system',
|
|
content: 'Du bist ein freundlicher, professioneller Kundenservice-Mitarbeiter.',
|
|
},
|
|
{
|
|
role: 'user',
|
|
content: prompt,
|
|
},
|
|
],
|
|
temperature: 0.7,
|
|
max_tokens: 500,
|
|
});
|
|
|
|
return completion.choices[0].message.content || '';
|
|
} catch (error) {
|
|
console.error('AI Response Generation Error:', error);
|
|
return `Sehr geehrte/r ${bookingData.customerName},
|
|
|
|
vielen Dank für Ihre Anfrage für den ${new Date(bookingData.eventDate).toLocaleDateString('de-DE')} in ${bookingData.eventCity}.
|
|
|
|
Wir haben Ihre Anfrage erhalten und freuen uns, Ihnen unser Angebot sowie den Mietvertrag im Anhang zu senden.
|
|
|
|
Bitte prüfen Sie die Unterlagen und unterschreiben Sie den Vertrag digital über den Link im Anhang.
|
|
|
|
Bei Fragen stehen wir Ihnen gerne zur Verfügung.
|
|
|
|
Mit freundlichen Grüßen
|
|
Ihr Save the Moment Team`;
|
|
}
|
|
}
|
|
|
|
async improveContractText(standardContract: string, bookingData: any): Promise<string> {
|
|
const prompt = `Personalisiere den folgenden Standard-Mietvertrag mit den spezifischen Buchungsdaten:
|
|
|
|
STANDARD-VERTRAG:
|
|
${standardContract}
|
|
|
|
BUCHUNGSDATEN:
|
|
- Kunde: ${bookingData.customerName}
|
|
- Event-Datum: ${new Date(bookingData.eventDate).toLocaleDateString('de-DE')}
|
|
- Event-Ort: ${bookingData.eventAddress}, ${bookingData.eventZip} ${bookingData.eventCity}
|
|
- Aufbauzeit: ${new Date(bookingData.setupTimeStart).toLocaleTimeString('de-DE')}
|
|
|
|
Füge die Daten in die Platzhalter ein und passe die Formulierungen an. Der Vertrag soll rechtlich bindend bleiben.`;
|
|
|
|
try {
|
|
const completion = await openai.chat.completions.create({
|
|
model: 'gpt-4-turbo-preview',
|
|
messages: [
|
|
{
|
|
role: 'system',
|
|
content: 'Du bist ein Experte für Vertragsformulierungen. Achte auf korrekte Rechtschreibung und Grammatik.',
|
|
},
|
|
{
|
|
role: 'user',
|
|
content: prompt,
|
|
},
|
|
],
|
|
temperature: 0.2,
|
|
max_tokens: 2000,
|
|
});
|
|
|
|
return completion.choices[0].message.content || standardContract;
|
|
} catch (error) {
|
|
console.error('AI Contract Generation Error:', error);
|
|
return standardContract;
|
|
}
|
|
}
|
|
}
|
|
|
|
export const aiService = new AIService();
|