import os import gradio as gr from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline from dotenv import load_dotenv # Sistema de prompt mejorado SYSTEM_PROMPT = """Tu tarea es proporcionar información precisa sobre delitos según el Código Penal español. Has sido entrenado con conocimientos jurídicos especializados y debes responder siguiendo este formato: 1. ARTÍCULOS APLICABLES: [Enumera los artículos relevantes del Código Penal] 2. TIPO BÁSICO DEL DELITO: [Describe el tipo básico] 3. SUBTIPOS Y MODALIDADES: [Describe los subtipos agravados o atenuados] 4. JURISPRUDENCIA RELEVANTE: [Menciona sentencias recientes del Tribunal Supremo] 5. REFORMAS LEGISLATIVAS: [Indica si ha habido reformas recientes] A continuación te daré una consulta sobre un delito específico y debes responder según el formato anterior.""" # Definir ejemplos de pares pregunta-respuesta para few-shot learning EJEMPLOS = """ Consulta: Delito de robo Respuesta: 1. ARTÍCULOS APLICABLES: Arts. 237-242 del Código Penal español. 2. TIPO BÁSICO DEL DELITO: El robo se define en el artículo 237 como la apropiación de cosas muebles ajenas empleando fuerza en las cosas o violencia/intimidación en las personas. 3. SUBTIPOS Y MODALIDADES: El artículo 240 establece que el robo con fuerza se castiga con pena de prisión de 1 a 3 años. El robo con violencia/intimidación (art. 242) se castiga con pena de 2 a 5 años. 4. JURISPRUDENCIA RELEVANTE: STS 382/2023 estableció criterios para diferenciar entre robo y hurto. 5. REFORMAS LEGISLATIVAS: La última modificación significativa fue con la LO 1/2015, que modificó las penas. Consulta: Delito de estafa Respuesta: 1. ARTÍCULOS APLICABLES: Arts. 248-251 bis del Código Penal español. 2. TIPO BÁSICO DEL DELITO: La estafa se define en el artículo 248 como la utilización de engaño bastante para producir error en otro, induciéndolo a realizar un acto de disposición patrimonial en perjuicio propio o ajeno. 3. SUBTIPOS Y MODALIDADES: Las estafas agravadas (art. 250) prevén penas de 1 a 6 años cuando el valor supera los 50.000€ o afecta a vivienda habitual. 4. JURISPRUDENCIA RELEVANTE: STS 420/2022 clarificó los requisitos del engaño en estafas digitales. 5. REFORMAS LEGISLATIVAS: La LO 1/2019 introdujo nuevas modalidades de estafa informática. """ # Cargar modelo y tokenizador # Cambio a un modelo con mejor rendimiento para tareas jurídicas MODEL_NAME = "EleutherAI/gpt-neo-2.7B" # Mantenemos este por compatibilidad, pero ideal cambiarlo si es posible try: tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME) model = AutoModelForCausalLM.from_pretrained(MODEL_NAME, device_map="auto") # Crear pipeline de generación de texto generator = pipeline( "text-generation", model=model, tokenizer=tokenizer, device_map="auto" # Usar GPU si está disponible ) modelo_cargado = True error_modelo = None except Exception as e: modelo_cargado = False error_modelo = str(e) # Historial global de chat chat_history = [] def construir_prompt(mensaje): """Construye un prompt efectivo con few-shot learning para mejorar la respuesta.""" return f"{SYSTEM_PROMPT}\n\n{EJEMPLOS}\n\nConsulta: {mensaje}\n\nRespuesta:\n1. ARTÍCULOS APLICABLES:" def consulta_penalista(mensaje, tipo_consulta): """Procesa una consulta jurídica y devuelve la respuesta del modelo.""" global chat_history if not modelo_cargado: return f"Error al cargar el modelo: {error_modelo}", "El modelo no está disponible." # Añadir mensaje del usuario al historial chat_history.append({"role": "user", "content": f"[{tipo_consulta}] {mensaje}"}) try: # Construir prompt con few-shot learning prompt = construir_prompt(mensaje) # Generar respuesta con el modelo respuesta = generator( prompt, max_length=1500, # Aumentado para permitir respuestas más completas num_return_sequences=1, temperature=0.3, # Reducido para mayor coherencia top_p=0.85, top_k=40, repetition_penalty=1.2, # Evita repeticiones do_sample=True, truncation=True, pad_token_id=tokenizer.eos_token_id ) respuesta_texto = respuesta[0]["generated_text"] # Eliminar el prompt de la respuesta generada if respuesta_texto.startswith(prompt): respuesta_texto = respuesta_texto[len(prompt):].strip() # Limpiar respuesta para evitar repeticiones if "1. ARTÍCULOS APLICABLES:" not in respuesta_texto: respuesta_texto = "1. ARTÍCULOS APLICABLES:" + respuesta_texto # Limitar la respuesta a 5 secciones si se repite secciones = [] for i, seccion in enumerate(["1. ARTÍCULOS APLICABLES:", "2. TIPO BÁSICO DEL DELITO:", "3. SUBTIPOS Y MODALIDADES:", "4. JURISPRUDENCIA RELEVANTE:", "5. REFORMAS LEGISLATIVAS:"]): if seccion in respuesta_texto: inicio = respuesta_texto.find(seccion) fin = len(respuesta_texto) if i < 4: # Si no es la última sección siguiente = respuesta_texto.find(f"{i+2}. ", inicio) if siguiente != -1: fin = siguiente secciones.append(respuesta_texto[inicio:fin]) if secciones: respuesta_texto = "\n".join(secciones) # Añadir la respuesta al historial chat_history.append({"role": "assistant", "content": respuesta_texto}) # Formatear el historial para mostrarlo en la interfaz historial_formateado = "\n\n".join([ f"{'Usuario' if msg['role'] == 'user' else 'Asistente'}: {msg['content']}" for msg in chat_history ]) return respuesta_texto, historial_formateado except Exception as e: error_msg = f"Error al procesar la solicitud: {str(e)}" chat_history.append({"role": "assistant", "content": error_msg}) return error_msg, "\n\n".join([f"{'Usuario' if msg['role'] == 'user' else 'Asistente'}: {msg['content']}" for msg in chat_history]) def limpiar_historial(): """Limpia el historial de conversación.""" global chat_history chat_history = [] return "", "Historial limpiado." def iniciar_interfaz(): """Inicia la interfaz de usuario con Gradio.""" interfaz = gr.Blocks(theme=gr.themes.Soft()) with interfaz: gr.Markdown("# ⚖️ Asistente Jurídico Penal Español") gr.Markdown("Realiza consultas sobre derecho penal y recibe respuestas fundamentadas en la legislación española.") if not modelo_cargado: gr.Markdown(f"⚠️ **Error al cargar el modelo**: {error_modelo}") gr.Markdown("Intenta ejecutar el script con un entorno que tenga más memoria o usar un modelo más pequeño.") tipo_consulta = gr.Radio( choices=["Delito específico", "Jurisprudencia", "Reforma legislativa"], label="Tipo de Consulta", value="Delito específico" ) with gr.Row(): entrada_texto = gr.Textbox( label="Consulta Jurídica", placeholder="Ejemplo: Consultar sobre el delito de robo con violencia", lines=2 ) with gr.Row(): boton_enviar = gr.Button("📝 Consultar", variant="primary") boton_limpiar = gr.Button("🗑️ Limpiar Historial") with gr.Accordion("Consejos para mejores resultados", open=False): gr.Markdown(""" - Sé específico en tu consulta (ej: "Delito de estafa informática" es mejor que "Estafa") - Puedes preguntar sobre aspectos específicos como "Agravantes del delito de lesiones" - Menciona si buscas información sobre alguna reforma reciente """) salida_texto = gr.Textbox( label="Respuesta del Asistente", lines=12 ) historial_texto = gr.Textbox( label="Historial de Conversación", interactive=False, lines=10, visible=True ) boton_enviar.click( consulta_penalista, inputs=[entrada_texto, tipo_consulta], outputs=[salida_texto, historial_texto] ) entrada_texto.submit( consulta_penalista, inputs=[entrada_texto, tipo_consulta], outputs=[salida_texto, historial_texto] ) boton_limpiar.click( limpiar_historial, outputs=[salida_texto, historial_texto] ) gr.Markdown("### ℹ️ Información importante") gr.Markdown(""" - Este asistente proporciona información general sobre legislación penal española. - No constituye asesoramiento jurídico profesional. - Para casos reales, consulte siempre con un abogado. """) return interfaz # Ejecutar la aplicación si este script es el principal if __name__ == "__main__": interfaz = iniciar_interfaz() interfaz.launch(share=True) # Usar share=True para crear un enlace público temporal