Equitia-evaluation-tool / src /modules /prompt_generator.py
Pikeras's picture
Create prompt_generator.py
e761f51 verified
import re
import sys
from pathlib import Path
try:
from utils.reproducibility import set_seed
except ModuleNotFoundError:
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from utils.reproducibility import set_seed
# Establecer una semilla para reproducibilidad (descomenta para activar)
# set_seed(72)
class PromptGenerator:
def __init__(self, config, model_manager):
self.config = config
self.model_manager = model_manager
# Función para aplanar un JSON (convierte estructuras anidadas en un solo nivel de claves)
def flatten_json(self, y, prefix=''):
out = {}
for key, value in y.items():
new_key = f"{prefix}{key}" if prefix == '' else f"{prefix}_{key}"
out[new_key] = value
return out
# Función para reemplazar las llaves {clave} en el texto por su valor correspondiente
def sustituir_claves(self, texto, datos):
def reemplazo(match):
clave = match.group(1)
return str(datos.get(clave, f'{{{clave}}}'))
return re.sub(r'{([^{}]+)}', reemplazo, texto)
def save_prompt(self, texto_final, ruta_salida_metaprompt):
with open(ruta_salida_metaprompt, 'w', encoding='utf-8') as f_out:
f_out.write(texto_final)
# Función para limpiar la respuesta del modelo que genera los prompts
def limpiar_respuesta_generada(self, respuesta, numero_prompts, esquema_salida, marcador, tipo_evaluacion, comunidades_sensibles):
lineas_limpias = []
contador_correctas = 0
contador_modificadas = 0
contador_agregadas = 0
contador_eliminadas = 0
separadores_esperados = len(esquema_salida.keys()) - 1
cabecera = '|'.join(esquema_salida.keys())
respuesta = re.sub(r'.*?\[/INST\] ', '', respuesta, flags=re.DOTALL).strip() # Eliminar el prompt de entrada que se muestra junto al prompt de salida
lineas_originales = respuesta.splitlines()
for linea in lineas_originales:
linea_original = linea
# Eliminar los espacios que se ponen al lado del separador y carácteres extraños
linea = linea.replace('"', '')
linea = re.sub(r'\s*\|\s*', '|', linea)
if re.fullmatch(r'\s*\|?(\s*-+\s*\|)+\s*-*\s*\|?\s*', linea):
contador_eliminadas += 1
continue # Eliminar línea si solo contiene: |, -, y espacios
linea = linea.strip('|') # Quita '|' del inicio y del final, si existen
if linea.count('|') != separadores_esperados:
contador_eliminadas += 1
continue # Eliminar línea si no contiene el número correcto de separadores
if not re.search(marcador, linea) and linea.strip() != cabecera.lower() and tipo_evaluacion != 'preguntas_respuestas_multiples':
contador_eliminadas += 1
continue # Eliminar línea si no contiene el número correcto de separadores
if linea.strip() != cabecera.lower() and tipo_evaluacion == 'preguntas_respuestas_multiples':
comunidad_encontrada = False
for comunidad in comunidades_sensibles:
if comunidad in linea:
comunidad_encontrada = True
if not comunidad_encontrada:
contador_eliminadas += 1
continue # Eliminar línea si no contiene el número correcto de separadores
if linea != linea_original:
lineas_limpias.append(linea)
contador_modificadas += 1
else:
lineas_limpias.append(linea)
contador_correctas += 1
if not lineas_limpias or lineas_limpias[0].strip().lower() != cabecera.lower():
lineas_limpias.insert(0, cabecera)
contador_agregadas += 1
indices_modificadas = {i for i, linea in enumerate(lineas_limpias) if linea not in lineas_originales}
num_lineas_antes = len(lineas_limpias)
lineas_limpias = lineas_limpias[:1 + numero_prompts]
num_lineas_despues = len(lineas_limpias)
eliminadas_finales = num_lineas_antes - num_lineas_despues
modificadas_eliminadas = len([i for i in range(num_lineas_antes) if i >= num_lineas_despues and i in indices_modificadas])
contador_modificadas -= modificadas_eliminadas
contador_eliminadas += eliminadas_finales
respuesta_final = "\n".join(lineas_limpias)
total = contador_correctas + contador_modificadas + contador_agregadas + contador_eliminadas
porcentaje = lambda x: (x / total * 100) if total > 0 else 0
print("----------------------")
print(f"Resumen:")
print(f"✔️ Líneas correctas: {contador_correctas} ({porcentaje(contador_correctas):.1f}%)")
print(f"🛠️ Líneas modificadas: {contador_modificadas} ({porcentaje(contador_modificadas):.1f}%)")
print(f"➕ Líneas añadidas: {contador_agregadas} ({porcentaje(contador_agregadas):.1f}%)")
print(f"➖ Líneas eliminadas: {contador_eliminadas} ({porcentaje(contador_eliminadas):.1f}%)")
return respuesta_final
# Función para limpiar la respuesta del modelo a evaluar
def limpiar_respuesta_generada_evaluacion(self, tipo_evaluacion, respuesta):
respuesta_generada_limpia = re.sub(r'.*?\[/INST\] ', '', respuesta, flags=re.DOTALL).strip()
respuesta_generada_limpia = re.split(r'</think>', respuesta_generada_limpia)[-1].strip().replace('\n', ' ').strip()
respuesta_generada_limpia = re.split(r'<|User|>', respuesta_generada_limpia)[-1].strip()
respuesta_generada_limpia = re.sub(r'\*\*?', '', respuesta_generada_limpia)
respuesta_generada_limpia = re.sub(r'[_\\]', '', respuesta_generada_limpia)
if len(respuesta_generada_limpia) > 0:
print(f"Respuesta generada: '{respuesta_generada_limpia[:max(10, len(respuesta_generada_limpia))]}' (se muestran max 10 caracteres).")
if tipo_evaluacion == "preguntas_agente":
respuesta_generada_limpia = respuesta_generada_limpia.strip('.').strip(',')[0].upper()
elif tipo_evaluacion == "preguntas_cerradas_esperadas":
respuesta_generada_limpia = respuesta_generada_limpia.strip('.').strip(',')[:2].upper()
elif tipo_evaluacion == "preguntas_cerradas_probabilidad":
respuesta_generada_limpia = respuesta_generada_limpia[respuesta_generada_limpia.rfind(' ') + 1:].replace('%', '').strip('.')
elif tipo_evaluacion == "preguntas_respuestas_multiples":
respuesta_generada_limpia = respuesta_generada_limpia.strip('.').strip(',')[0].upper()
elif tipo_evaluacion == "preguntas_prompt_injection":
respuesta_generada_limpia = respuesta_generada_limpia.strip('.').strip(',')[:2].upper()
return respuesta_generada_limpia