Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -3,63 +3,126 @@ import gradio as gr
|
|
3 |
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
|
4 |
from dotenv import load_dotenv
|
5 |
|
6 |
-
#
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
25 |
|
26 |
# Historial global de chat
|
27 |
chat_history = []
|
28 |
|
|
|
|
|
|
|
|
|
29 |
def consulta_penalista(mensaje, tipo_consulta):
|
30 |
"""Procesa una consulta jurídica y devuelve la respuesta del modelo."""
|
31 |
global chat_history
|
32 |
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
Respuesta sobre el delito consultado según la legislación española:
|
39 |
-
- Artículos del Código Penal español: """
|
40 |
|
41 |
try:
|
42 |
-
#
|
43 |
-
|
44 |
-
prompt = SYSTEM_PROMPT + mensaje
|
45 |
|
46 |
# Generar respuesta con el modelo
|
47 |
respuesta = generator(
|
48 |
prompt,
|
49 |
-
max_length=
|
50 |
-
num_return_sequences=1,
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
|
|
55 |
truncation=True,
|
56 |
-
pad_token_id=tokenizer.eos_token_id
|
57 |
-
do_sample=True
|
58 |
)
|
59 |
|
60 |
respuesta_texto = respuesta[0]["generated_text"]
|
61 |
|
62 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
63 |
chat_history.append({"role": "assistant", "content": respuesta_texto})
|
64 |
|
65 |
# Formatear el historial para mostrarlo en la interfaz
|
@@ -71,7 +134,9 @@ Respuesta sobre el delito consultado según la legislación española:
|
|
71 |
return respuesta_texto, historial_formateado
|
72 |
|
73 |
except Exception as e:
|
74 |
-
|
|
|
|
|
75 |
|
76 |
def limpiar_historial():
|
77 |
"""Limpia el historial de conversación."""
|
@@ -79,44 +144,81 @@ def limpiar_historial():
|
|
79 |
chat_history = []
|
80 |
return "", "Historial limpiado."
|
81 |
|
82 |
-
|
83 |
-
interfaz
|
84 |
-
|
85 |
-
with interfaz:
|
86 |
-
gr.Markdown("# ⚖️ Asistente Jurídico Penal")
|
87 |
-
gr.Markdown("Realiza consultas sobre derecho penal y recibe respuestas fundamentadas.")
|
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 |
-
limpiar_historial,
|
119 |
-
outputs=[salida_texto, historial_texto]
|
120 |
-
)
|
121 |
|
122 |
-
|
|
|
|
|
|
|
|
3 |
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
|
4 |
from dotenv import load_dotenv
|
5 |
|
6 |
+
# Sistema de prompt mejorado
|
7 |
+
SYSTEM_PROMPT = """Tu tarea es proporcionar información precisa sobre delitos según el Código Penal español.
|
8 |
+
|
9 |
+
Has sido entrenado con conocimientos jurídicos especializados y debes responder siguiendo este formato:
|
10 |
+
|
11 |
+
1. ARTÍCULOS APLICABLES: [Enumera los artículos relevantes del Código Penal]
|
12 |
+
2. TIPO BÁSICO DEL DELITO: [Describe el tipo básico]
|
13 |
+
3. SUBTIPOS Y MODALIDADES: [Describe los subtipos agravados o atenuados]
|
14 |
+
4. JURISPRUDENCIA RELEVANTE: [Menciona sentencias recientes del Tribunal Supremo]
|
15 |
+
5. REFORMAS LEGISLATIVAS: [Indica si ha habido reformas recientes]
|
16 |
+
|
17 |
+
A continuación te daré una consulta sobre un delito específico y debes responder según el formato anterior."""
|
18 |
+
|
19 |
+
# Definir ejemplos de pares pregunta-respuesta para few-shot learning
|
20 |
+
EJEMPLOS = """
|
21 |
+
Consulta: Delito de robo
|
22 |
+
|
23 |
+
Respuesta:
|
24 |
+
1. ARTÍCULOS APLICABLES: Arts. 237-242 del Código Penal español.
|
25 |
+
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.
|
26 |
+
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.
|
27 |
+
4. JURISPRUDENCIA RELEVANTE: STS 382/2023 estableció criterios para diferenciar entre robo y hurto.
|
28 |
+
5. REFORMAS LEGISLATIVAS: La última modificación significativa fue con la LO 1/2015, que modificó las penas.
|
29 |
+
|
30 |
+
Consulta: Delito de estafa
|
31 |
+
|
32 |
+
Respuesta:
|
33 |
+
1. ARTÍCULOS APLICABLES: Arts. 248-251 bis del Código Penal español.
|
34 |
+
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.
|
35 |
+
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.
|
36 |
+
4. JURISPRUDENCIA RELEVANTE: STS 420/2022 clarificó los requisitos del engaño en estafas digitales.
|
37 |
+
5. REFORMAS LEGISLATIVAS: La LO 1/2019 introdujo nuevas modalidades de estafa informática.
|
38 |
+
"""
|
39 |
+
|
40 |
+
# Cargar modelo y tokenizador
|
41 |
+
# Cambio a un modelo con mejor rendimiento para tareas jurídicas
|
42 |
+
MODEL_NAME = "EleutherAI/gpt-neo-2.7B" # Mantenemos este por compatibilidad, pero ideal cambiarlo si es posible
|
43 |
+
|
44 |
+
try:
|
45 |
+
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
|
46 |
+
model = AutoModelForCausalLM.from_pretrained(MODEL_NAME, device_map="auto")
|
47 |
+
|
48 |
+
# Crear pipeline de generación de texto
|
49 |
+
generator = pipeline(
|
50 |
+
"text-generation",
|
51 |
+
model=model,
|
52 |
+
tokenizer=tokenizer,
|
53 |
+
device_map="auto" # Usar GPU si está disponible
|
54 |
+
)
|
55 |
+
|
56 |
+
modelo_cargado = True
|
57 |
+
error_modelo = None
|
58 |
+
|
59 |
+
except Exception as e:
|
60 |
+
modelo_cargado = False
|
61 |
+
error_modelo = str(e)
|
62 |
|
63 |
# Historial global de chat
|
64 |
chat_history = []
|
65 |
|
66 |
+
def construir_prompt(mensaje):
|
67 |
+
"""Construye un prompt efectivo con few-shot learning para mejorar la respuesta."""
|
68 |
+
return f"{SYSTEM_PROMPT}\n\n{EJEMPLOS}\n\nConsulta: {mensaje}\n\nRespuesta:\n1. ARTÍCULOS APLICABLES:"
|
69 |
+
|
70 |
def consulta_penalista(mensaje, tipo_consulta):
|
71 |
"""Procesa una consulta jurídica y devuelve la respuesta del modelo."""
|
72 |
global chat_history
|
73 |
|
74 |
+
if not modelo_cargado:
|
75 |
+
return f"Error al cargar el modelo: {error_modelo}", "El modelo no está disponible."
|
76 |
+
|
77 |
+
# Añadir mensaje del usuario al historial
|
78 |
+
chat_history.append({"role": "user", "content": f"[{tipo_consulta}] {mensaje}"})
|
|
|
|
|
79 |
|
80 |
try:
|
81 |
+
# Construir prompt con few-shot learning
|
82 |
+
prompt = construir_prompt(mensaje)
|
|
|
83 |
|
84 |
# Generar respuesta con el modelo
|
85 |
respuesta = generator(
|
86 |
prompt,
|
87 |
+
max_length=1500, # Aumentado para permitir respuestas más completas
|
88 |
+
num_return_sequences=1,
|
89 |
+
temperature=0.3, # Reducido para mayor coherencia
|
90 |
+
top_p=0.85,
|
91 |
+
top_k=40,
|
92 |
+
repetition_penalty=1.2, # Evita repeticiones
|
93 |
+
do_sample=True,
|
94 |
truncation=True,
|
95 |
+
pad_token_id=tokenizer.eos_token_id
|
|
|
96 |
)
|
97 |
|
98 |
respuesta_texto = respuesta[0]["generated_text"]
|
99 |
|
100 |
+
# Eliminar el prompt de la respuesta generada
|
101 |
+
if respuesta_texto.startswith(prompt):
|
102 |
+
respuesta_texto = respuesta_texto[len(prompt):].strip()
|
103 |
+
|
104 |
+
# Limpiar respuesta para evitar repeticiones
|
105 |
+
if "1. ARTÍCULOS APLICABLES:" not in respuesta_texto:
|
106 |
+
respuesta_texto = "1. ARTÍCULOS APLICABLES:" + respuesta_texto
|
107 |
+
|
108 |
+
# Limitar la respuesta a 5 secciones si se repite
|
109 |
+
secciones = []
|
110 |
+
for i, seccion in enumerate(["1. ARTÍCULOS APLICABLES:", "2. TIPO BÁSICO DEL DELITO:",
|
111 |
+
"3. SUBTIPOS Y MODALIDADES:", "4. JURISPRUDENCIA RELEVANTE:",
|
112 |
+
"5. REFORMAS LEGISLATIVAS:"]):
|
113 |
+
if seccion in respuesta_texto:
|
114 |
+
inicio = respuesta_texto.find(seccion)
|
115 |
+
fin = len(respuesta_texto)
|
116 |
+
if i < 4: # Si no es la última sección
|
117 |
+
siguiente = respuesta_texto.find(f"{i+2}. ", inicio)
|
118 |
+
if siguiente != -1:
|
119 |
+
fin = siguiente
|
120 |
+
secciones.append(respuesta_texto[inicio:fin])
|
121 |
+
|
122 |
+
if secciones:
|
123 |
+
respuesta_texto = "\n".join(secciones)
|
124 |
+
|
125 |
+
# Añadir la respuesta al historial
|
126 |
chat_history.append({"role": "assistant", "content": respuesta_texto})
|
127 |
|
128 |
# Formatear el historial para mostrarlo en la interfaz
|
|
|
134 |
return respuesta_texto, historial_formateado
|
135 |
|
136 |
except Exception as e:
|
137 |
+
error_msg = f"Error al procesar la solicitud: {str(e)}"
|
138 |
+
chat_history.append({"role": "assistant", "content": error_msg})
|
139 |
+
return error_msg, "\n\n".join([f"{'Usuario' if msg['role'] == 'user' else 'Asistente'}: {msg['content']}" for msg in chat_history])
|
140 |
|
141 |
def limpiar_historial():
|
142 |
"""Limpia el historial de conversación."""
|
|
|
144 |
chat_history = []
|
145 |
return "", "Historial limpiado."
|
146 |
|
147 |
+
def iniciar_interfaz():
|
148 |
+
"""Inicia la interfaz de usuario con Gradio."""
|
149 |
+
interfaz = gr.Blocks(theme=gr.themes.Soft())
|
|
|
|
|
|
|
150 |
|
151 |
+
with interfaz:
|
152 |
+
gr.Markdown("# ⚖️ Asistente Jurídico Penal Español")
|
153 |
+
gr.Markdown("Realiza consultas sobre derecho penal y recibe respuestas fundamentadas en la legislación española.")
|
154 |
+
|
155 |
+
if not modelo_cargado:
|
156 |
+
gr.Markdown(f"⚠️ **Error al cargar el modelo**: {error_modelo}")
|
157 |
+
gr.Markdown("Intenta ejecutar el script con un entorno que tenga más memoria o usar un modelo más pequeño.")
|
158 |
+
|
159 |
+
tipo_consulta = gr.Radio(
|
160 |
+
choices=["Delito específico", "Jurisprudencia", "Reforma legislativa"],
|
161 |
+
label="Tipo de Consulta",
|
162 |
+
value="Delito específico"
|
163 |
+
)
|
164 |
+
|
165 |
+
with gr.Row():
|
166 |
+
entrada_texto = gr.Textbox(
|
167 |
+
label="Consulta Jurídica",
|
168 |
+
placeholder="Ejemplo: Consultar sobre el delito de robo con violencia",
|
169 |
+
lines=2
|
170 |
+
)
|
171 |
+
|
172 |
+
with gr.Row():
|
173 |
+
boton_enviar = gr.Button("📝 Consultar", variant="primary")
|
174 |
+
boton_limpiar = gr.Button("🗑️ Limpiar Historial")
|
175 |
+
|
176 |
+
with gr.Accordion("Consejos para mejores resultados", open=False):
|
177 |
+
gr.Markdown("""
|
178 |
+
- Sé específico en tu consulta (ej: "Delito de estafa informática" es mejor que "Estafa")
|
179 |
+
- Puedes preguntar sobre aspectos específicos como "Agravantes del delito de lesiones"
|
180 |
+
- Menciona si buscas información sobre alguna reforma reciente
|
181 |
+
""")
|
182 |
+
|
183 |
+
salida_texto = gr.Textbox(
|
184 |
+
label="Respuesta del Asistente",
|
185 |
+
lines=12
|
186 |
+
)
|
187 |
+
|
188 |
+
historial_texto = gr.Textbox(
|
189 |
+
label="Historial de Conversación",
|
190 |
+
interactive=False,
|
191 |
+
lines=10,
|
192 |
+
visible=True
|
193 |
+
)
|
194 |
+
|
195 |
+
boton_enviar.click(
|
196 |
+
consulta_penalista,
|
197 |
+
inputs=[entrada_texto, tipo_consulta],
|
198 |
+
outputs=[salida_texto, historial_texto]
|
199 |
+
)
|
200 |
+
|
201 |
+
entrada_texto.submit(
|
202 |
+
consulta_penalista,
|
203 |
+
inputs=[entrada_texto, tipo_consulta],
|
204 |
+
outputs=[salida_texto, historial_texto]
|
205 |
+
)
|
206 |
+
|
207 |
+
boton_limpiar.click(
|
208 |
+
limpiar_historial,
|
209 |
+
outputs=[salida_texto, historial_texto]
|
210 |
+
)
|
211 |
+
|
212 |
+
gr.Markdown("### ℹ️ Información importante")
|
213 |
+
gr.Markdown("""
|
214 |
+
- Este asistente proporciona información general sobre legislación penal española.
|
215 |
+
- No constituye asesoramiento jurídico profesional.
|
216 |
+
- Para casos reales, consulte siempre con un abogado.
|
217 |
+
""")
|
218 |
|
219 |
+
return interfaz
|
|
|
|
|
|
|
220 |
|
221 |
+
# Ejecutar la aplicación si este script es el principal
|
222 |
+
if __name__ == "__main__":
|
223 |
+
interfaz = iniciar_interfaz()
|
224 |
+
interfaz.launch(share=True) # Usar share=True para crear un enlace público temporal
|