# chatbot_tema.py # Chatbot por tema específico + vocabulario limitado a 1000 palabras. import os import re import random from collections import Counter # ========================= # CONFIG # ========================= TOPICO = "entrenamiento en el gimnasio" # <— Cambiá el tema acá PALABRAS_CLAVE = { # Palabras que identifican el tema. Agregá o quitá según tu caso. "gimnasio","entrenar","rutina","pesas","mancuernas","series","repeticiones", "calentamiento","piernas","pecho","espalda","hombros","biceps","triceps", "cardio","fuerza","hipertrofia","descanso","nutricion","proteina","creatina", "tecnica","maquina","sentadilla","press","remar","dominadas","plan","progreso" } ARCHIVO_VOCAB = "vocab.txt" TAMANIO_VOCAB = 1000 # Mensaje de rechazo cuando está fuera de tema MENSAJE_FUERA_DE_TEMA = ( "lo siento. solo puedo hablar sobre " + TOPICO + ". " "por favor hacé una pregunta relacionada." ) # ========================= # UTILIDADES # ========================= def normalizar(texto: str) -> str: texto = texto.lower() texto = re.sub(r"[^\wáéíóúñü ]", " ", texto, flags=re.UNICODE) texto = re.sub(r"\s+", " ", texto).strip() return texto def cargar_o_crear_vocab(ruta: str, objetivo: int) -> list: """ - Si existe vocab.txt: lo carga y valida tamaño. - Si no existe: crea una lista de palabras base + tokens de relleno hasta llegar a 1000 y la guarda en el archivo para que la puedas editar. """ if os.path.exists(ruta): with open(ruta, "r", encoding="utf-8") as f: palabras = [normalizar(l) for l in f if l.strip()] # Quitar duplicados respetando orden seen = set() vocab = [] for p in palabras: if p not in seen: seen.add(p) vocab.append(p) # Ajustar a tamaño deseado (recortar o avisar) if len(vocab) > objetivo: vocab = vocab[:objetivo] elif len(vocab) < objetivo: faltan = objetivo - len(vocab) # Relleno controlado inicio = len(vocab) + 1 for i in range(inicio, inicio + faltan): vocab.append(f"token{i:03d}") return vocab else: base = set(""" yo tu vos usted ustedes nosotros hola chau gracias por favor si no tal vez puedo podes podesme decir contar explicar como cuando donde cual porque para hoy ayer manana semana mes anio ahora antes despues poco mucho bien mal mas menos igual tema topico relacionado ejemplo paso guia idea ayuda duda pregunta respuesta claro listo simple rapido seguro lento suave duro facil dificil cuerpo espalda pecho hombro pierna brazo biceps triceps abdomen gluteo calentar descanso energia fuerza peso repetir serie rutina plan objetivo progreso tecnica forma postura respiracion dolor cuidado prevenir riesgo maquina banco barra mancuerna polea cuerda banda remo press sentadilla dominada elevacion curl extension cardio cinta bici remadora tiempo minuto segundo intervalo dia nutricion comida proteina carbohidrato grasa agua creatina suplemento tomar comer cocinar batido leche avena huevo arroz pollo carne banana antes durante despues entrenar """.split()) # Asegurar que las plantillas puedan hablar base.update({ "hola","bienvenido","estoy","listo","para","ayudar","sobre","entrenamiento", "en","el","gimnasio","es","importante","usar","buena","tecnica","y","progresar", "de","a","poco","si","sos","principiante","empeza","con","poco","peso","y", "aumenta","cuando","la","forma","sea","solida","queres","rutina","para","hoy", "tenes","alguna","meta","fuerza","o","hipertrofia","recorda","descansar", "entre","series","y","dormir","bien" }) base = [normalizar(w) for w in base if w.strip()] # Completar hasta objetivo vocab = [] seen = set() for w in base: if w not in seen: seen.add(w) vocab.append(w) i = 1 while len(vocab) < objetivo: token = f"token{i:03d}" if token not in seen: seen.add(token) vocab.append(token) i += 1 # Guardar para que puedas editarlo with open(ruta, "w", encoding="utf-8") as f: f.write("\n".join(vocab)) return vocab def es_en_tema(texto_usuario: str, palabras_clave: set) -> bool: tu = set(normalizar(texto_usuario).split()) # Coincidencia si hay intersección con palabras clave o con el nombre del tópico if tu & palabras_clave: return True for w in normalizar(TOPICO).split(): if w in tu: return True return False def limitar_a_vocab(texto: str, vocab_set: set) -> str: """ Asegura que la salida solo use palabras del vocabulario. Palabras fuera del vocabulario se sustituyen por 'token001'. """ palabras = normalizar(texto).split() out = [] sustituto = "token001" if "token001" in vocab_set else next(iter(vocab_set)) for p in palabras: out.append(p if p in vocab_set else sustituto) return " ".join(out) # ========================= # NUCLEO DE RESPUESTAS # ========================= def detectar_intencion(texto_usuario: str) -> str: t = normalizar(texto_usuario) if any(x in t for x in ["hola","buenas","que tal","buen dia","buenas tardes","buenas noches"]): return "saludo" if any(x in t for x in ["rutina","plan","programa","entreno","entrenar","hoy que hago"]): return "rutina" if any(x in t for x in ["pecho","espalda","pierna","hombro","biceps","triceps","abdomen","core"]): return "musculo" if any(x in t for x in ["tecnica","forma","postura","como hago","como se hace"]): return "tecnica" if any(x in t for x in ["nutricion","comer","proteina","creatina","batido","dieta","comida"]): return "nutricion" if any(x in t for x in ["descanso","series","repeticiones","cuanto","cuantas","minutos","intervalo"]): return "parametros" if any(x in t for x in ["chau","adios","gracias","nos vemos","hasta luego"]): return "despedida" return "desconocida" def generar_respuesta_en_tema(intencion: str) -> str: # Plantillas sencillas y cortas; todas en vocabulario básico if intencion == "saludo": return ( "hola. estoy listo para ayudar sobre entrenamiento en el gimnasio. " "queres una rutina para hoy o una guia por objetivo." ) if intencion == "rutina": return ( "rutina base: cuerpo completo. hacé tres series por ejercicio y ocho a doce repeticiones. " "ejemplo: sentadilla, press pecho, remo, elevacion hombro, curl biceps, extension triceps, plancha. " "descanso uno a dos minutos entre series." ) if intencion == "musculo": return ( "para ese musculo, usa uno a tres ejercicios con tecnica solida. " "progresá poco a poco y evita dolor raro. " "si la forma falla, baja el peso y repetí." ) if intencion == "tecnica": return ( "usa postura neutral, abdomen activo y movimiento controlado. " "sin rebote y con rango que puedas sostener sin dolor. " "respira: baja tomando aire y sube soltando." ) if intencion == "nutricion": return ( "nutricion simple: prioriza proteina en cada comida, hidrata con agua y dormi bien. " "si usas creatina: cinco gramos al dia con comida. " "no es magia, es constancia." ) if intencion == "parametros": return ( "series: dos a cuatro por ejercicio. repeticiones: seis a doce para hipertrofia. " "descanso: uno a tres minutos segun esfuerzo. " "progreso: subí poco el peso cuando la tecnica sea solida." ) if intencion == "despedida": return "gracias por charlar. buen entrenamiento y buen descanso." # desconocida en tema return ( "puedo hablar sobre rutina, tecnica, nutricion, series y progresion en el gimnasio. " "decime que queres y armamos algo simple." ) # ========================= # LOOP PRINCIPAL # ========================= def main(): vocab = cargar_o_crear_vocab(ARCHIVO_VOCAB, TAMANIO_VOCAB) vocab_set = set(vocab) print("chatbot:", limitar_a_vocab( f"hola. soy un asistente sobre {TOPICO}. usa mensajes simples. escribi 'salir' para terminar.", vocab_set )) while True: try: user = input("vos: ") except (EOFError, KeyboardInterrupt): print("\nchatbot: chau.") break if not user: continue if normalizar(user) == "salir": print("chatbot:", limitar_a_vocab("chau. hasta luego.", vocab_set)) break # Verificar tema if not es_en_tema(user, PALABRAS_CLAVE): print("chatbot:", limitar_a_vocab(MENSAJE_FUERA_DE_TEMA, vocab_set)) continue # Generar respuesta en tema y limitar a vocab intent = detectar_intencion(user) respuesta = generar_respuesta_en_tema(intent) respuesta_limited = limitar_a_vocab(respuesta, vocab_set) # Asegurar que no quede vacía if not respuesta_limited.strip(): respuesta_limited = limitar_a_vocab( "lo siento. no tengo una respuesta. podes repetir con otras palabras.", vocab_set ) print("chatbot:", respuesta_limited) if __name__ == "__main__": main() import gradio as gr iface = gr.Interface(fn=chat, inputs="text", outputs="text") iface.launch()