|
|
|
|
|
|
|
import os |
|
import re |
|
import random |
|
from collections import Counter |
|
|
|
|
|
|
|
|
|
TOPICO = "entrenamiento en el gimnasio" |
|
PALABRAS_CLAVE = { |
|
|
|
"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_FUERA_DE_TEMA = ( |
|
"lo siento. solo puedo hablar sobre " + TOPICO + ". " |
|
"por favor hacé una pregunta relacionada." |
|
) |
|
|
|
|
|
|
|
|
|
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()] |
|
|
|
seen = set() |
|
vocab = [] |
|
for p in palabras: |
|
if p not in seen: |
|
seen.add(p) |
|
vocab.append(p) |
|
|
|
if len(vocab) > objetivo: |
|
vocab = vocab[:objetivo] |
|
elif len(vocab) < objetivo: |
|
faltan = objetivo - len(vocab) |
|
|
|
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()) |
|
|
|
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()] |
|
|
|
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 |
|
|
|
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()) |
|
|
|
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) |
|
|
|
|
|
|
|
|
|
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: |
|
|
|
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." |
|
|
|
return ( |
|
"puedo hablar sobre rutina, tecnica, nutricion, series y progresion en el gimnasio. " |
|
"decime que queres y armamos algo simple." |
|
) |
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
if not es_en_tema(user, PALABRAS_CLAVE): |
|
print("chatbot:", limitar_a_vocab(MENSAJE_FUERA_DE_TEMA, vocab_set)) |
|
continue |
|
|
|
|
|
intent = detectar_intencion(user) |
|
respuesta = generar_respuesta_en_tema(intent) |
|
respuesta_limited = limitar_a_vocab(respuesta, vocab_set) |
|
|
|
|
|
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() |
|
|