File size: 9,783 Bytes
10f2497 c6aeefd |
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 |
# 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()
|