156 lines
5.1 KiB
PHP
156 lines
5.1 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
class AiBot
|
|
{
|
|
public static function process(array $company, array $context, string $input): ?array
|
|
{
|
|
$botCtx = ConversationContext::getOrCreate((int)$company['id'], $context['from'], 'ai');
|
|
$ctxId = (int)$botCtx['id'];
|
|
|
|
$config = self::getConfig($company);
|
|
$systemPrompt = $config['ai_prompt'] ?? self::defaultPrompt($company);
|
|
|
|
ConversationContext::addAiMessage($ctxId, ['role' => 'user', 'content' => $input]);
|
|
|
|
$result = self::callLlm($systemPrompt, $ctxId, $company);
|
|
|
|
if ($result === null) {
|
|
return null;
|
|
}
|
|
|
|
ConversationContext::addAiMessage($ctxId, ['role' => 'assistant', 'content' => $result['content'] ?? '']);
|
|
|
|
return [
|
|
'action' => 'send',
|
|
'type' => 'text',
|
|
'to' => $context['from'],
|
|
'payload' => json_encode(['text' => $result['content'] ?? '']),
|
|
];
|
|
}
|
|
|
|
private static function callLlm(string $systemPrompt, int $ctxId, array $company): ?array
|
|
{
|
|
$provider = env('AI_PROVIDER', 'openai');
|
|
$history = ConversationContext::getAiHistory($ctxId);
|
|
|
|
return match ($provider) {
|
|
'openai' => self::callOpenAI($systemPrompt, $history, $company),
|
|
'mock' => self::mockResponse($history),
|
|
default => self::callOpenAI($systemPrompt, $history, $company),
|
|
};
|
|
}
|
|
|
|
private static function callOpenAI(string $systemPrompt, array $history, array $company): ?array
|
|
{
|
|
$apiKey = env('OPENAI_API_KEY', '');
|
|
if ($apiKey === '') {
|
|
return null;
|
|
}
|
|
|
|
$model = env('OPENAI_MODEL', 'gpt-4o-mini');
|
|
|
|
$messages = [
|
|
['role' => 'system', 'content' => $systemPrompt],
|
|
];
|
|
|
|
foreach ($history as $msg) {
|
|
$messages[] = [
|
|
'role' => $msg['role'] ?? 'user',
|
|
'content' => $msg['content'] ?? '',
|
|
];
|
|
}
|
|
|
|
$payload = json_encode([
|
|
'model' => $model,
|
|
'messages' => $messages,
|
|
'max_tokens' => (int)env('AI_MAX_TOKENS', '500'),
|
|
'temperature' => 0.7,
|
|
]);
|
|
|
|
$ch = curl_init('https://api.openai.com/v1/chat/completions');
|
|
curl_setopt_array($ch, [
|
|
CURLOPT_POST => true,
|
|
CURLOPT_POSTFIELDS => $payload,
|
|
CURLOPT_HTTPHEADER => [
|
|
'Content-Type: application/json',
|
|
'Authorization: Bearer ' . $apiKey,
|
|
],
|
|
CURLOPT_RETURNTRANSFER => true,
|
|
CURLOPT_TIMEOUT => 30,
|
|
]);
|
|
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
$error = curl_error($ch);
|
|
curl_close($ch);
|
|
|
|
if ($httpCode !== 200 || $response === false) {
|
|
WpWebhook::log('ERROR', "OpenAI API error: {$error} HTTP:{$httpCode}");
|
|
return [
|
|
'content' => 'Lo siento, tengo problemas para procesar tu mensaje. Por favor intenta de nuevo más tarde.',
|
|
];
|
|
}
|
|
|
|
$data = json_decode($response, true);
|
|
$text = $data['choices'][0]['message']['content'] ?? null;
|
|
|
|
if ($text === null) {
|
|
return [
|
|
'content' => 'No pude generar una respuesta adecuada. ¿Puedes reformular tu pregunta?',
|
|
];
|
|
}
|
|
|
|
return ['content' => $text];
|
|
}
|
|
|
|
private static function mockResponse(array $history): array
|
|
{
|
|
$last = end($history);
|
|
$input = $last['content'] ?? '';
|
|
|
|
$responses = [
|
|
'hola' => '¡Hola! ¿En qué puedo ayudarte hoy?',
|
|
'gracias' => '¡De nada! Si necesitas algo más, estoy aquí.',
|
|
'adios' => '¡Hasta luego! Que tengas un excelente día.',
|
|
'default' => "Gracias por tu mensaje. He recibido: \"{$input}\". Un asesor se pondrá en contacto contigo pronto.",
|
|
];
|
|
|
|
$normalized = mb_strtolower(trim($input));
|
|
$found = $responses['default'];
|
|
|
|
foreach ($responses as $keyword => $response) {
|
|
if (str_contains($normalized, $keyword)) {
|
|
$found = $response;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return ['content' => $found];
|
|
}
|
|
|
|
private static function defaultPrompt(array $company): string
|
|
{
|
|
$name = $company['display_name'] ?? $company['name'] ?? 'la empresa';
|
|
return <<<PROMPT
|
|
Eres un asistente virtual de {$name}. Tu rol es:
|
|
|
|
1. Responder preguntas sobre los servicios y productos de {$name}.
|
|
2. Ayudar a los clientes con información general.
|
|
3. Ser amable, profesional y responder siempre en español.
|
|
4. Si no sabes la respuesta, indica que un asesor se comunicará.
|
|
5. No inventes información. Si no sabes algo, dilo honestamente.
|
|
|
|
Mantén las respuestas concisas (máximo 3 párrafos).
|
|
PROMPT;
|
|
}
|
|
|
|
private static function getConfig(array $company): array
|
|
{
|
|
$json = $company['config_json'] ?? '';
|
|
if ($json === '') return [];
|
|
$config = json_decode($json, true);
|
|
return is_array($config) ? $config : [];
|
|
}
|
|
}
|