File size: 8,203 Bytes
6f053b0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Chatbot Basado en Reglas - Sesión 1
Curso: Diseño e Implementación de Chatbots

Este chatbot funciona mediante coincidencia de patrones y reglas predefinidas.
Los mensajes y flujos están en el archivo flujos_conversacion.json
"""

import json
import re
import difflib
import random
import unicodedata
from pathlib import Path


class ChatbotReglas:
    """Chatbot basado en reglas con dos flujos de conversación"""
    def __init__(self, archivo_flujos='instructions.json'):
        """
        Inicializa el chatbot cargando los flujos desde JSON

        Args:
            archivo_flujos: Ruta al archivo JSON con los flujos
        """
        self.archivo_flujos = archivo_flujos
        self.cargar_flujos()
        self.contexto = {
            'nombre_usuario': None,
            'flujo_actual': None,
            'ultima_intencion': None,
            'historial': []
        }

    def cargar_flujos(self):
        """Carga los flujos de conversación desde el archivo JSON"""
        try:
            ruta = Path(__file__).parent / self.archivo_flujos
            with open(ruta, 'r', encoding='utf-8') as f:
                data = json.load(f)

            self.config = data['configuracion']
            self.flujos = data['flujos']
            print("✓ Flujos de conversación cargados correctamente\n")

        except FileNotFoundError:
            print(f"❌ Error: No se encontró el archivo {self.archivo_flujos}")
            exit(1)
        except json.JSONDecodeError:
            print(f"❌ Error: El archivo {self.archivo_flujos} no es un JSON válido")
            exit(1)

    def normalizar_texto(self, texto):
        """
        Normaliza el texto del usuario para mejorar coincidencias

        Args:
            texto: Texto a normalizar

        Returns:
            Texto normalizado (minúsculas, sin tildes, sin puntuación extra)
        """
        # Convertir a minúsculas
        texto = texto.lower()

        # Eliminar tildes
        texto = ''.join(
            c for c in unicodedata.normalize('NFD', texto)
            if unicodedata.category(c) != 'Mn'
        )

        # Remover puntuación excesiva pero mantener espacios
        texto = re.sub(r'[^\w\s]', '', texto)

        # Normalizar espacios
        texto = ' '.join(texto.split())

        return texto

    def calcular_similitud(self, texto1, texto2):
        """
        Calcula la similitud entre dos textos usando difflib

        Args:
            texto1: Primer texto
            texto2: Segundo texto

        Returns:
            Score de similitud entre 0.0 y 1.0
        """
        return difflib.SequenceMatcher(None, texto1, texto2).ratio()

    def buscar_mejor_intencion(self, mensaje_usuario):
        """
        Busca la mejor intención que coincida con el mensaje del usuario

        Args:
            mensaje_usuario: Mensaje del usuario (ya normalizado)

        Returns:
            Tupla (intencion, score, flujo_nombre) o (None, 0, None)
        """
        mejor_intencion = None
        mejor_score = 0
        mejor_flujo = None

        # Buscar en todos los flujos
        for nombre_flujo, flujo in self.flujos.items():
            for intencion in flujo['intenciones']:
                # Calcular similitud con cada patrón
                for patron in intencion['patrones']:
                    patron_normalizado = self.normalizar_texto(patron)

                    # Similitud general
                    score = self.calcular_similitud(mensaje_usuario, patron_normalizado)

                    # Bonus si la palabra clave está contenida exactamente
                    if patron_normalizado in mensaje_usuario:
                        score = max(score, 0.8)

                    # Bonus si todas las palabras del patrón están en el mensaje
                    palabras_patron = patron_normalizado.split()
                    palabras_mensaje = mensaje_usuario.split()
                    if all(palabra in palabras_mensaje for palabra in palabras_patron):
                        score = max(score, 0.85)

                    if score > mejor_score:
                        mejor_score = score
                        mejor_intencion = intencion
                        mejor_flujo = nombre_flujo

        return mejor_intencion, mejor_score, mejor_flujo

    def seleccionar_respuesta(self, intencion):
        """
        Selecciona una respuesta aleatoria de la intención

        Args:
            intencion: Diccionario con la intención

        Returns:
            String con la respuesta
        """
        respuestas = intencion['respuestas']
        respuesta = random.choice(respuestas)

        # Agregar sugerencia si existe
        if 'siguiente_sugerencia' in intencion:
            respuesta += f"\n\n💡 {intencion['siguiente_sugerencia']}"

        return respuesta

    def procesar_mensaje(self, mensaje_usuario):
        """
        Procesa el mensaje del usuario y genera una respuesta

        Args:
            mensaje_usuario: Mensaje del usuario

        Returns:
            Respuesta del bot, o None si debe terminar
        """
        # Normalizar mensaje
        mensaje_normalizado = self.normalizar_texto(mensaje_usuario)

        # Guardar en historial
        self.contexto['historial'].append(mensaje_usuario)

        # Buscar mejor intención
        intencion, score, flujo = self.buscar_mejor_intencion(mensaje_normalizado)

        # Decidir respuesta
        if score >= self.config['umbral_similitud']:
            # Actualizar contexto
            self.contexto['ultima_intencion'] = intencion['id']
            self.contexto['flujo_actual'] = flujo

            # Verificar acciones especiales
            if 'accion_especial' in intencion:
                if intencion['accion_especial'] == 'terminar':
                    return self.seleccionar_respuesta(intencion), True

            return self.seleccionar_respuesta(intencion), False
        else:
            # No se entendió el mensaje
            return self.config['mensaje_no_entendido'], False

    def ejecutar(self):
        """Ejecuta el loop principal del chatbot"""
        print("=" * 70)
        print("CHATBOT BASADO EN REGLAS - CURSO DE CHATBOTS")
        print("=" * 70)
        print(self.config['mensaje_bienvenida'])
        print("=" * 70)

        # Loop principal
        while True:
            try:
                # Capturar input del usuario
                mensaje = input("\n🧑 Tú: ").strip()

                # Validar input vacío
                if not mensaje:
                    continue

                # Verificar comando de salida directo
                if mensaje.lower() in ['salir', 'exit', 'quit']:
                    print(f"\n🤖 Bot: {self.config['mensaje_despedida']}")
                    break

                # Procesar mensaje
                respuesta, debe_terminar = self.procesar_mensaje(mensaje)

                # Mostrar respuesta
                print(f"\n🤖 Bot: {respuesta}")

                # Terminar si es necesario
                if debe_terminar:
                    break

            except KeyboardInterrupt:
                print(f"\n\n🤖 Bot: {self.config['mensaje_despedida']}")
                break
            except Exception as e:
                print(f"\n❌ Error interno: {e}")
                print("Por favor, intenta de nuevo.")

        print("\n" + "=" * 70)
        print("Conversación terminada. ¡Gracias por usar el chatbot!")
        print("=" * 70)

    def mostrar_estadisticas(self):
        """Muestra estadísticas de la conversación"""
        print("\n📊 Estadísticas de la conversación:")
        print(f"  - Mensajes del usuario: {len(self.contexto['historial'])}")
        print(f"  - Último flujo usado: {self.contexto['flujo_actual']}")
        print(f"  - Última intención: {self.contexto['ultima_intencion']}")


def main():
    """Función principal"""
    # Crear instancia del chatbot
    bot = ChatbotReglas()

    # Ejecutar
    bot.ejecutar()

    # Mostrar estadísticas (opcional)
    # bot.mostrar_estadisticas()


if __name__ == "__main__":
    main()