$l->ID ?? null, 'name' => $l->name ?? null, 'fullname' => $l->fullname ?? null, 'address' => $l->address ?? null, 'address1' => $l->address1 ?? null, 'address2' => $l->address2 ?? null, 'mapslink' => $l->mapslink ?? null, ]; } if (!empty($locations)) { return $locations; } } catch (Exception $e) { // fallback below } } try { $db = ensure_efisio_db(); $stmt = $db->query("SELECT * FROM efisio_locations ORDER BY `order`, ID"); $rows = $stmt ? $stmt->fetchAll(PDO::FETCH_ASSOC) : []; foreach ($rows as $r) { $locations[] = [ 'id' => isset($r['ID']) ? intval($r['ID']) : null, 'name' => $r['name'] ?? null, 'fullname' => $r['fullname'] ?? null, 'address' => $r['address'] ?? null, 'address1' => $r['address1'] ?? null, 'address2' => $r['address2'] ?? null, 'mapslink' => $r['mapslink'] ?? null, ]; } } catch (Exception $e) { return []; } return $locations; } function get_location_details($clinica_id = null, $clinica_name = null) { if ($clinica_id !== null) { $clinica_id = intval($clinica_id); } if (class_exists('App_Location')) { $loc = null; if ($clinica_id !== null && $clinica_id >= 0) { $loc = App_Location::get($clinica_id); } else if (!empty($clinica_name)) { $loc = App_Location::get($clinica_name); } if ($loc) { return [ 'id' => $loc->ID ?? null, 'name' => $loc->name ?? null, 'fullname' => $loc->fullname ?? null, 'address' => $loc->address ?? null, 'address1' => $loc->address1 ?? null, 'address2' => $loc->address2 ?? null, 'mapslink' => $loc->mapslink ?? null, 'address_note' => $loc->address_note ?? null, ]; } } try { $db = ensure_efisio_db(); if ($clinica_id !== null && $clinica_id >= 0) { $stmt = $db->prepare("SELECT * FROM efisio_locations WHERE ID = ? LIMIT 1"); $stmt->execute([$clinica_id]); } else if (!empty($clinica_name)) { $stmt = $db->prepare("SELECT * FROM efisio_locations WHERE name LIKE ? OR fullname LIKE ? LIMIT 1"); $like = '%' . $clinica_name . '%'; $stmt->execute([$like, $like]); } else { return null; } $r = $stmt->fetch(PDO::FETCH_ASSOC); if ($r) { return [ 'id' => isset($r['ID']) ? intval($r['ID']) : null, 'name' => $r['name'] ?? null, 'fullname' => $r['fullname'] ?? null, 'address' => $r['address'] ?? null, 'address1' => $r['address1'] ?? null, 'address2' => $r['address2'] ?? null, 'mapslink' => $r['mapslink'] ?? null, 'address_note' => $r['address_note'] ?? null, ]; } } catch (Exception $e) { return null; } return null; } function recommend_workers_for_symptoms($symptoms, $especialidad = null, $clinica_id = null, $limit = 5) { $symptoms = trim((string)$symptoms); $especialidad = trim((string)($especialidad ?? '')); if ($symptoms === '' && $especialidad === '') return []; try { require_once __DIR__ . '/../workers/expertise.php'; $db = ensure_efisio_db(); $query = $symptoms; if ($especialidad !== '') { $query = trim($query . ' ' . $especialidad); } $results = recommend_workers($db, $query, $limit); if ($clinica_id !== null) { $clinica_id = intval($clinica_id); $results = array_values(array_filter($results, function($r) use ($clinica_id) { return isset($r['location_id']) && intval($r['location_id']) === $clinica_id; })); } return $results; } catch (Exception $e) { return []; } } function get_favorite_worker_info($user_id) { $user_id = intval($user_id); if ($user_id <= 0) return null; try { require_once __DIR__ . '/../appointments/favorites.php'; require_once __DIR__ . '/../appointments/worker.php'; require_once __DIR__ . '/../../libwp/appointments/includes/model/class_app.php'; $fav = get_favorite_worker($user_id); if ($fav > 0 && class_exists('App_Worker')) { $worker = App_Worker::get($fav); if ($worker) { return [ 'worker_id' => $worker->ID ?? $fav, 'worker_name' => $worker->name ?? '', 'worker_fullname' => method_exists($worker, 'getFullName') ? $worker->getFullName() : '', 'location_id' => $worker->location ?? null, 'location_name' => $worker->location_name ?? '', 'role' => $worker->role ?? '', ]; } } } catch (Exception $e) { return null; } return null; } function get_user_future_appointments($user_id, $limit = 5) { $user_id = intval($user_id); if ($user_id <= 0) return []; try { $db = ensure_efisio_db(); $stmt = $db->prepare(" SELECT ID, start, end, status, service, worker, location FROM wp_app_appointments WHERE user = ? AND start >= NOW() AND status != 'removed' ORDER BY start ASC LIMIT $limit "); $stmt->execute([$user_id]); return $stmt->fetchAll(PDO::FETCH_ASSOC) ?: []; } catch (Exception $e) { return []; } } function get_user_past_appointments($user_id, $limit = 5) { $user_id = intval($user_id); if ($user_id <= 0) return []; try { $db = ensure_efisio_db(); $stmt = $db->prepare(" SELECT ID, start, end, status, service, worker, location FROM wp_app_appointments WHERE user = ? AND start < NOW() AND status != 'removed' ORDER BY start DESC LIMIT $limit "); $stmt->execute([$user_id]); return $stmt->fetchAll(PDO::FETCH_ASSOC) ?: []; } catch (Exception $e) { return []; } } function get_chat_system_prompt($user_context = []) { $fecha = date("Y-m-d"); if (class_exists('IntlDateFormatter')) { setlocale(LC_TIME, "es_ES.UTF-8", "es_ES", "spanish"); $formatter = new IntlDateFormatter("es_ES", IntlDateFormatter::FULL, IntlDateFormatter::NONE, null, null, "EEEE"); $dia_semana = $formatter->format(strtotime($fecha)); } else { $dias_semana = ['domingo','lunes','martes','miércoles','jueves','viernes','sábado']; $dia_semana = $dias_semana[intval(date('w'))]; } // Ubicaciones desde App_Location / DB $locations = get_locations_for_prompt(); // Servicios principales $services = [ ['id' => 545, 'name' => 'Fisioterapia', 'price' => 50, 'duration' => 50], ['id' => 2509, 'name' => 'Drenaje Linfático', 'price' => 55, 'duration' => 60], ['id' => 29811, 'name' => 'Suelo Pélvico', 'price' => 60, 'duration' => 60], ['id' => 2082, 'name' => 'Osteopatía', 'price' => 60, 'duration' => 60], ]; $user_info = ""; if (!empty($user_context['name'])) { $user_info = "El usuario se llama {$user_context['name']}."; } if (!empty($user_context['phone'])) { $user_info .= " Su teléfono es {$user_context['phone']}."; } if (!empty($user_context['favfisio_name'])) { $user_info .= " Fisio habitual: {$user_context['favfisio_name']}."; } if (!empty($user_context['upcoming_appointments'])) { $user_info .= " Citas futuras: " . json_encode($user_context['upcoming_appointments']) . "."; } if (!empty($user_context['recent_appointments'])) { $user_info .= " Historial reciente: " . json_encode($user_context['recent_appointments']) . "."; } return "Eres el asistente virtual de eFISIO, una red de clínicas de fisioterapia en Madrid. PERSONALIDAD: - Cercano y profesional (tutea al usuario) - Conciso (respuestas cortas y claras) - Empático con quienes tienen dolor - Proactivo para ayudar a reservar cita FECHA ACTUAL: $fecha ($dia_semana) CLÍNICAS: " . json_encode($locations) . " SERVICIOS PRINCIPALES: " . json_encode($services) . " BONOS: - 5 sesiones: 225€ (45€/sesión) - 10 sesiones: 400€ (40€/sesión) CONTACTO: - Teléfono: 910 052 363 - WhatsApp: 644 054 800 $user_info REGLAS: - NO des diagnósticos médicos - Si el usuario quiere reservar, guíale preguntando: servicio, clínica preferida, y disponibilidad - Ofrece siempre opciones claras - Cuando muestres fechas, incluye el día de la semana - Responde en español - Si el usuario es existente (hay user_id) y pide cita o cancelación, primero consulta \"citas_futuras\" y, si no tiene, consulta \"fisio_habitual\" para recomendar con su fisio de confianza. - Si el usuario pide CAMBIAR la cita y aporta una fecha/hora (o franja), debes consultar disponibilidad real con la herramienta \"ver_disponibilidad\" usando: - \"clinica_id\" y \"servicio_id\" de la próxima cita (si existen) - \"worker_id\" del fisio de esa cita (si existe) o su fisio habitual - \"desde\" y \"hasta\" alrededor de la fecha sugerida Responde directamente con opciones de horas libres (máx 3) o indica que no hay y ofrece alternativa. FORMATO DE RESPUESTA: Responde SIEMPRE en este formato JSON: { \"respuesta\": \"tu mensaje aquí\", \"acciones\": [\"opción 1\", \"opción 2\"] } Las acciones son botones que el usuario puede pulsar para continuar la conversación."; } function ejecutar_agente_v3($mensaje, $history = [], $user_context = []) { $system_prompt = get_chat_system_prompt($user_context); $messages = [ ["role" => "system", "content" => $system_prompt] ]; // Añadir historial (máximo 6 mensajes) foreach (array_slice($history, -6) as $h) { $messages[] = [ "role" => $h["role"], "content" => $h["content"] ]; } // Añadir mensaje actual $messages[] = ["role" => "user", "content" => $mensaje]; // Usar Claude Sonnet via OpenRouter (rápido y económico) $response = ask_ia($messages, "google/gemini-2.0-flash-001"); if (!$response) { return [ "respuesta" => "Disculpa, estoy teniendo problemas técnicos. ¿Puedes intentarlo de nuevo o llamarnos al 910 052 363?", "acciones" => ["Llamar al 910 052 363", "Intentar de nuevo"] ]; } // Intentar parsear JSON $parsed = extract_json_from_agent_response($response); if ($parsed && isset($parsed['respuesta'])) { return $parsed; } // Si no es JSON válido, devolver como texto return [ "respuesta" => $response, "acciones" => [] ]; } /** * Versión con herramientas para consultas de disponibilidad */ function ejecutar_agente_v3_tools($mensaje, $history = [], $user_context = []) { $system_prompt = get_chat_system_prompt($user_context); // Añadir instrucciones de herramientas $system_prompt .= " HERRAMIENTAS DISPONIBLES: Si necesitas consultar disponibilidad real, responde con: { \"herramienta\": \"ver_disponibilidad\", \"parametros\": { \"clinica_id\": 2, \"desde\": \"2024-01-20\", \"hasta\": \"2024-01-27\", \"servicio_id\": 545, \"worker_id\": 123 } } Si necesitas citas futuras del usuario, responde con: { \"herramienta\": \"citas_futuras\", \"parametros\": { \"user_id\": 12345 } } Si necesitas historial de citas del usuario, responde con: { \"herramienta\": \"citas_historial\", \"parametros\": { \"user_id\": 12345 } } Si necesitas detalles de una clínica, responde con: { \"herramienta\": \"detalle_clinica\", \"parametros\": { \"clinica_id\": 2, \"clinica_nombre\": \"Chamberí\" } } Si necesitas recomendar un fisio por patología/síntomas y clínica, responde con: { \"herramienta\": \"recomendar_fisio\", \"parametros\": { \"sintomas\": \"dolor lumbar, ciática\", \"especialidad\": \"suelo pélvico\", \"clinica_id\": 2 } } Si necesitas el fisio habitual del usuario, responde con: { \"herramienta\": \"fisio_habitual\", \"parametros\": { \"user_id\": 12345 } } PAUSA Te responderé con los resultados de la herramienta."; $messages = [ ["role" => "system", "content" => $system_prompt] ]; foreach (array_slice($history, -6) as $h) { $messages[] = ["role" => $h["role"], "content" => $h["content"]]; } $messages[] = ["role" => "user", "content" => $mensaje]; $max_turnos = 3; $turno = 0; $tool_trace = []; while ($turno < $max_turnos) { $response = ask_ia($messages, "google/gemini-2.0-flash-001"); if (!$response) { return [ "respuesta" => "Disculpa, estoy teniendo problemas. Llámanos al 910 052 363.", "acciones" => ["Llamar"] ]; } $parsed = extract_json_from_agent_response($response); // Si tiene respuesta final, devolverla if ($parsed && isset($parsed['respuesta'])) { if (!empty($user_context['debug_tools'])) { $parsed['debug_tools'] = $tool_trace; } return $parsed; } // Si pide herramienta if ($parsed && isset($parsed['herramienta'])) { $herramienta = $parsed['herramienta']; $params = $parsed['parametros'] ?? []; $result = null; if ($herramienta == 'ver_disponibilidad') { $location_id = $params['clinica_id'] ?? null; $service_id = $params['servicio_id'] ?? 545; $worker_id = $params['worker_id'] ?? 0; $desde = $params['desde'] ?? date('Y-m-d'); $hasta = $params['hasta'] ?? date('Y-m-d', strtotime('+7 days')); $result = null; if (class_exists('App_WeeklyAdm')) { $raw = App_WeeklyAdm::apps_free($location_id, strtotime($desde), strtotime($hasta), $service_id, $worker_id); $result = $raw; } else { $query = http_build_query([ 'from' => $desde, 'to' => $hasta, 'l' => $location_id, 's' => $service_id, 'w' => $worker_id ]); $url = "https://www.efisio.es/api/appsfree?" . $query; $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 15); $resp = curl_exec($ch); $http = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($resp && $http >= 200 && $http < 300) { $json = json_decode($resp, true); if (is_array($json) && isset($json['apps'])) { $result = $json['apps']; } } } if (!$result) { $result = ['error' => 'tool_unavailable']; } else { $simplified = []; if (!is_array($result) && !is_object($result)) $result = []; foreach ($result as $key => $slots) { $date = is_numeric($key) ? date('Y-m-d', intval($key)) : $key; if (!isset($simplified[$date])) $simplified[$date] = []; foreach ($slots as $s) { $status = $s['status'] ?? ($s->status ?? ''); if ($status && $status !== 'available') continue; $wkr = $s['worker'] ?? ($s->worker ?? null); if ($worker_id && intval($wkr) !== intval($worker_id)) continue; $start = $s['start'] ?? ($s->start ?? null); if (!$start) continue; $simplified[$date][] = [ 'hora' => date('H:i', strtotime($start)), 'inicio' => $start, 'fisio' => $s['worker_name'] ?? ($s->worker_name ?? ''), 'fisio_id' => $wkr, 'clinica' => $s['location'] ?? ($s->location ?? $location_id) ]; if (count($simplified[$date]) >= 5) break; } } $result = $simplified; } } else if ($herramienta == 'citas_futuras') { $user_id = $params['user_id'] ?? ($user_context['user_id'] ?? null); $result = get_user_future_appointments($user_id, 5); } else if ($herramienta == 'citas_historial') { $user_id = $params['user_id'] ?? ($user_context['user_id'] ?? null); $result = get_user_past_appointments($user_id, 5); } else if ($herramienta == 'detalle_clinica') { $clinica_id = $params['clinica_id'] ?? null; $clinica_name = $params['clinica_nombre'] ?? null; $result = get_location_details($clinica_id, $clinica_name); } else if ($herramienta == 'recomendar_fisio') { $sintomas = $params['sintomas'] ?? ''; $especialidad = $params['especialidad'] ?? ''; $clinica_id = $params['clinica_id'] ?? null; $result = recommend_workers_for_symptoms($sintomas, $especialidad, $clinica_id, 5); } else if ($herramienta == 'fisio_habitual') { $user_id = $params['user_id'] ?? ($user_context['user_id'] ?? null); $result = get_favorite_worker_info($user_id); } if ($result) { if (!empty($user_context['debug_tools'])) { $tool_trace[] = [ 'tool' => $herramienta, 'params' => $params, 'result' => $result ]; } $messages[] = ["role" => "assistant", "content" => $response]; $messages[] = ["role" => "user", "content" => "Resultado: " . json_encode($result, JSON_PRETTY_PRINT)]; } } else { // Respuesta en texto plano $res = [ "respuesta" => $response, "acciones" => [] ]; if (!empty($user_context['debug_tools'])) { $res['debug_tools'] = $tool_trace; } return $res; } $turno++; } $res = [ "respuesta" => "No pude completar tu consulta. ¿Prefieres que te llamemos?", "acciones" => ["Sí, llamadme", "Intentar de nuevo"] ]; if (!empty($user_context['debug_tools'])) { $res['debug_tools'] = $tool_trace; } return $res; } /** * Helper para extraer JSON de texto (local a este archivo) */ function extract_json_from_agent_response($text) { if (!$text) return null; // Buscar JSON en code blocks if (preg_match('/```(?:json)?\s*(\{.*?\})\s*```/s', $text, $matches)) { $json = json_decode($matches[1], true); if ($json) return $json; } // Buscar JSON directo if (preg_match('/\{[^{}]*"respuesta"[^{}]*\}/s', $text, $matches)) { $json = json_decode($matches[0], true); if ($json) return $json; } // Intentar parsear todo el texto $json = json_decode($text, true); if ($json) return $json; return null; }
Si tienes alguna duda o necesitas ayuda, no dudes en contactar con nosotros.
Aquí tienes algunas reseñas escritas en google por nuestros pacientes
Muy bien. Me dieron cita rápidamente y la atención fue fenomenal.
Genial atención
Gracias a Sharon estoy recuperando la movilidad de mi mano tras una operación, que creía complicada recuperar. Es fantástico el trato y la atención recibida en eFisio en el Barrio del Pilar
Increíble la atención desde el minuto 1. Y las indicaciones recibidas además del tratamiento que me sirven de rescate estos días. Vine buscando un "milagro" al dolor antes de un viaje de trabajo y, aunque Amanda no tiene el poder de hacerlos, el resultado es tremendo. Tenían razón aquellos que me recomendaron esta clínica, sin duda.
Ya es la tercera sesión que tengo con Wayra y no puedo decir más que cosas positivas. Muy amable y con muy buena atención. Y gran profesional, localizó rápidamente mi lesión y ha conseguido que vuelva a creer que me recuperaré completamente. Un gran acierto. Muchas gracias!!
Excelente atención y trato personal. Jorge es un gran profesional!
Muy buena atención, trato y procedimientos realizados
Felicito a Soraya por su atención y amabilidad durante el tiempo que recibí fisioterapia , es una gran persona y muy profesional. Quiteria Un gran saludo para todos los fisios
Soy una persona mayor que he visitado este centro y he salido encantada no solo con la atención recibida sino con la profesionalidad de Marcos y Javi . Son unos profesionales como la copa de un pino.. muchas felicidades por vuestro gran trabajo.. Quiteria
Excelente atención del fisioterapeuta Jorge . Es muy profesional, paciente y explica cada ejercicio con claridad. Gracias a su tratamiento he tenido una gran mejoría. ¡Totalmente recomendado!
Fui por dolor en hombro y me Alba me atendió. Me explicó lo que iba haciendo en cada momento y me recomendó ejercicios para seguir en casa. Muy satisfecha por su atención tanto profesional como personal.
Béatrice brindó una atención excepcional para mi dolor muscular en el cuello tras una lesión practicando esquí acuático, utilizando punción seca, terapia de radiofrecuencia y masaje. Fue increíblemente atenta y cálida, y explicó todo con claridad. He recuperado la movilidad y estoy durmiendo mucho mejor! La recomiendo sin ninguna duda!
Muy contento con el trato y la atención, especialmente con Javi. Me ha ayudado mucho con mi dolencia, aún teniendo algo bastante complejo y atípico. Siempre me ha explicado y me ha respondido a cualquier pregunta que tuviera y gracias a él he recuperado vida normal. Sin duda lo recomendaría.
Muy buena atención, me explicó lo que me pasaba de forma que lo entendía y el trabajo excelente, muy recomendable 😊
Feliz con la atención y la profesionalidad de Belen.
Me atendió Max y además de solucionarme el problema (en mi caso una contractura con mucho dolor en la zona de la espalda), la atención fue muy buena. En todo momento me indicaba los procesos que iba a seguir y se adaptó a mi ritmo cuando era necesario, sintiéndome muy cómodo con él en todo momento.
Durante muchos años he disfrutado de una muy buena atención y alta calidad de diagnóstico y posterior fisio y cuidado en todos los centros de efisio. Ir a efisio es la “par excellence” de la salud privada.
Durante muchos años he disfrutado de una muy buena atención y alta calidad de diagnóstico y posterior fisio en los centros de efisio. Igualmente con Lina en Calle Carranza que es una profesional estupenda. Lo único en Calle Carranza es que me suelta un fisio el comentario “pórtate bien” en plan sarcástico antes de acudir a mi sesión de fisio con Lina, sin conocer mi historial en efisio y de forma poco respetuoso. Así dado su actitud callejera me pregunto como se hablan de él en los informes de sus pacientes en efisio?
Muy buena atención ,y masaje muy bien
Maravilloso trato y atención. Álba es muy humana y muy buena profesional
Su atención muy profesional ,me he sentido mejor después de la sesión y me sugirió ejercicios y hábitos que me ayudan a evitar más dolor.
La atención y como me explica el tratamiento que está llevando a cabo es genial. Me encuentro a gusto con Genesis
Excelente atención por parte de Noelia.
Me encantó la sesión. Jorge Garcia fue muy amable y me explicó todo bien. Me gustó que desde el inicio, mostró una actitud cercana y empática. Hice una valoración inicial que le hay permitido adaptar las tecnicas de manera personalizada. Además, se ofrecieron recomendaciones prácticas para continuar el cuidado en casa, mostrando un enfoque preventivo y educativo muy valioso. El lugar es limpio y tranquilo. Estoy contenta con la atención y tendré que volver otra vez.
Muy buena atención, especial mención a María Luisa que ha sido la encargada de tratarme sobre el suelo pélvico y además me ha explicado muy bien los ejercicios/respiraciones que debo de practicar para prepararme lo mejor posible para el parto, además de ayudarme con el masaje perineal. Muchas gracias
Gran atención y cuidado al paciente. Relación calidad-precio inmejorable
Muy contento con la atención y profesionalidad de Amanda, haciendo todo lo que puede en detectar las lesiones y darles una solución que ayude a mejor mi calidad de vida.
La atención de Ainhoa es de 10. Es una súper profesional, te escucha, te aconseja y es súper maja. Sin duda volveré, mil gracias.
Muy buena atención y técnica. Muy satisfecho con el tratamiento
Muy buena la atención una gran profesional .volveré seguro .muchas gracias
Estoy muy agradecida con Lina, mi fisioterapeuta neurológica. Está siendo un proceso duro para mí y ella me está ayudando muchísimo. Lo que más valoro es que me escucha con atención, se implica de verdad y me hace sentir acompañada y apoyada en todo momento. Además, no sé cómo lo hace, pero siempre consigue sacarme más de una sonrisa. Su profesionalidad y cercanía me dan mucha confianza para seguir avanzando. La recomiendo muchísimo!!
Super buena atención y profesionalismo. De las pocas visitas al fisio que han sido entretenidas.
El centro está muy bien y son muy profesionales, la atención estuvo genial porque sirvió a lo que iba y me lo solucionó, saben lo que hacen.mil gracias ya estoy muchísimo mejor
Muy buena la atención de beatrice desde el minuto 1 hasta el final, me dio muchos consejos y recomendaciones para llevar una mejor calidad de vida. la recomendaría siempre. Saludos ☺️🫶 Paso 2.
Estoy muy conforme con la atención recibida. Fueron 5 sesiones a cargo de Jhordan y en cada una de ellas volvía a casa sintiéndome mejor. Es un gran profesional. Lo recomendaré a él y a efisio. Volveré cuando sea necesario.
Experiencia de cliente excelente. Información constante y actualizada, trato estupendo y atención muy profesional. Repetiré seguro. ¡Experiencia 5 estrellas!
Encuentra la clínica eFISIO más cercana a tu ubicación.
¿Es tu primera vez en eFISIO?
Ahora tienes 10€ de descuento en tu primera reserva
Exclusivo para tienda online. Un único cupón por paciente. Válido para la primera cita del paciente. No compatible con otras ofertas de fisioterapia. Oferta válida hasta el 30 de abril de 2026