JairoCesar commited on
Commit
ea0be12
·
verified ·
1 Parent(s): fee9869

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +36 -53
app.py CHANGED
@@ -1,5 +1,5 @@
1
- # ==================== Buscador de Intolerancias Alimentarias (Versión Híbrida) =====================================
2
- # Implementa un motor de búsqueda híbrido que combina la extracción de entidades y la inferencia de la IA.
3
 
4
  import streamlit as st
5
  import google.generativeai as genai
@@ -10,7 +10,7 @@ import logging
10
  import re
11
 
12
  st.set_page_config(
13
- page_title="Asistente de Bienestar Digestivo",
14
  page_icon="🍎",
15
  layout="wide"
16
  )
@@ -60,14 +60,13 @@ def load_data():
60
  with open(path_alimentos, 'r', encoding='utf-8') as f:
61
  data_alimentos = json.load(f)
62
 
63
- # Extraer una lista única de todas las condiciones posibles para guiar a la IA
64
  lista_condiciones = sorted(list(set(item['condicion_asociada'] for item in data_alimentos)))
65
 
66
  logger.info(f"✅ Base de datos enriquecida cargada con {len(data_alimentos)} registros.")
67
  return data_alimentos, lista_condiciones
68
 
69
  except FileNotFoundError:
70
- st.error("❌ Error: No se encontró el archivo 'alimentos_enriquecido.json'. Asegúrate de haber ejecutado el script de enriquecimiento.")
71
  return None, None
72
  except json.JSONDecodeError:
73
  st.error("❌ Error: El archivo 'alimentos_enriquecido.json' tiene un formato incorrecto.")
@@ -81,7 +80,8 @@ FOOD_TO_COMPOUND_MAP = {
81
  "leche": ["lácteos", "caseína", "lactosa"], "queso": ["lácteos", "caseína", "lactosa", "histamina", "tiramina"], "yogur": ["lácteos", "caseína", "lactosa"], "mantequilla": ["lácteos", "caseína", "lactosa"],
82
  "manzana": ["salicilatos", "fructosa"], "almendras": ["salicilatos"], "uvas": ["salicilatos"], "vino tinto": ["histamina", "tiramina", "sulfitos"], "vino rojo": ["histamina", "tiramina", "sulfitos"],
83
  "azucar": ["azúcar", "fructosa"], "dulces": ["azúcar"], "refrescos": ["azúcar", "fructosa"], "gaseosas": ["azúcar", "fructosa"],
84
- "embutidos": ["histamina", "tiramina", "nitritos"], "carne": ["alfa-gal", "proteínas"]
 
85
  }
86
 
87
  # --- LÓGICA DE BÚSQUEDA Y ANÁLISIS (ENFOQUE HÍBRIDO) ---
@@ -104,23 +104,14 @@ def extract_and_infer_with_gemini(query, condiciones):
104
  Devuelve la respuesta ÚNICAMENTE en formato JSON. Si no puedes inferir una condición, deja el campo como un string vacío "".
105
 
106
  Ejemplo 1:
107
- Consulta: "Cuando como mucho pan con harina de trigo se me hincha el estomago y siento fatiga."
108
  Respuesta:
109
  {{
110
- "alimentos": ["pan", "harina de trigo"],
111
- "sintomas": ["hinchazón abdominal", "fatiga"],
112
- "condicion_probable": "Enfermedad Celíaca (Clásica)."
113
  }}
114
 
115
- Ejemplo 2:
116
- Consulta: "me duele la cabeza cada vez que tomo vino rojo"
117
- Respuesta:
118
- {{
119
- "alimentos": ["vino rojo"],
120
- "sintomas": ["dolor de cabeza"],
121
- "condicion_probable": "Intolerancia a la Histamina (Histaminosis)."
122
- }}
123
-
124
  Ahora, analiza la siguiente consulta:
125
  Consulta: "{query}"
126
  """
@@ -138,36 +129,35 @@ def extract_and_infer_with_gemini(query, condiciones):
138
  return None
139
 
140
  def find_best_matches_hybrid(entities, data):
141
- """Motor de búsqueda híbrido con puntuación ponderada."""
142
  if not entities or not data: return []
143
 
144
  user_symptoms = set(s.lower().strip() for s in entities.get("sintomas", []))
145
  user_foods = set(f.lower().strip() for f in entities.get("alimentos", []))
146
  inferred_condition = entities.get("condicion_probable", "").lower().strip()
147
 
148
- candidate_compounds = set()
149
  for food in user_foods:
150
  if food in FOOD_TO_COMPOUND_MAP:
151
- candidate_compounds.update(c.lower() for c in FOOD_TO_COMPOUND_MAP[food])
152
 
153
  scores = {}
154
  for index, entry in enumerate(data):
155
  score = 0
156
 
157
- # Ponderación 1: Coincidencia de Condición (la más importante)
158
  entry_condition = entry.get("condicion_asociada", "").lower().strip()
159
  if inferred_condition and inferred_condition == entry_condition:
160
- score += 15
161
 
162
- # Ponderación 2: Coincidencia de Alimentos/Compuestos
163
  entry_compounds_text = entry.get("compuesto_alimento", "").lower()
164
- if any(food in entry_compounds_text for food in user_foods) or any(comp in entry_compounds_text for comp in candidate_compounds):
165
- score += 8
166
 
167
- # Ponderación 3: Coincidencia de Síntomas
168
  entry_symptoms_keys = set(s.lower().strip() for s in entry.get("sintomas_clave", []))
169
- matching_symptoms = user_symptoms.intersection(entry_symptoms_keys)
170
- score += len(matching_symptoms) * 4 # Cada síntoma coincidente suma
 
 
171
 
172
  if score > 0:
173
  scores[index] = score
@@ -178,11 +168,10 @@ def find_best_matches_hybrid(entities, data):
178
  return [data[i] for i in sorted_matches_indices]
179
 
180
  def generate_detailed_analysis(query, match):
181
- """Genera la explicación final para el usuario (sin cambios necesarios aquí)."""
182
- # Esta función ya es robusta y no necesita cambios.
183
  if not model: return "Error: El modelo de IA no está disponible."
184
  prompt = f"""
185
- Eres un asistente de IA experto en nutrición y bienestar, diseñado para ayudar a las personas a entender posibles intolerancias alimentarias. Tu tono debe ser claro, empático y educativo.
186
  Un usuario ha descrito el siguiente caso: "{query}"
187
  Basado en esto, nuestro sistema ha encontrado una posible coincidencia con:
188
  - **Compuesto Intolerante:** {match.get("compuesto_alimento")}
@@ -194,7 +183,7 @@ def generate_detailed_analysis(query, match):
194
  **Tu Tarea:** Redacta una respuesta completa y estructurada para el usuario usando Markdown:
195
  1. **Posible Causa:** Resume por qué sus síntomas podrían estar relacionados con esta condición.
196
  2. **¿Qué podría estar pasando en tu cuerpo?:** Explica el "Mecanismo Fisiológico" en términos sencillos.
197
- 3. **Pasos a Seguir y Recomendaciones:** Presenta las "Recomendaciones" como sugerencias, enfatizando la supervisión profesional. Menciona otros alimentos a tener en cuenta del campo "compuesto_alimento".
198
  4. **Descargo de Responsabilidad Médico:** Incluye OBLIGATORIAMENTE un párrafo final claro y visible que indique que NO es un diagnóstico médico y que debe consultar a un profesional.
199
  """
200
  try:
@@ -204,29 +193,17 @@ def generate_detailed_analysis(query, match):
204
  logger.error(f"Error generando análisis detallado con Gemini: {e}")
205
  return "No se pudo generar el análisis detallado."
206
 
207
-
208
  # --- INTERFAZ DE USUARIO (UI) ---
209
- # Se crea un layout con columnas para la cabecera
210
  col_img, col_text = st.columns([1, 4], gap="medium")
211
-
212
  with col_img:
213
- # Asegúrate de que el archivo 'imagen.png' esté en la misma carpeta que el script
214
- st.image("imagen.png", width=150)
215
-
216
  with col_text:
217
  st.title("El Detective de Alimentos")
218
  st.markdown("##### Describe lo que sientes y lo que comiste para descubrir posibles intolerancias.")
219
-
220
- # Una línea divisoria para separar la cabecera del formulario
221
  st.markdown("---")
222
 
223
-
224
-
225
-
226
-
227
-
228
-
229
-
230
  if 'search_results' not in st.session_state: st.session_state.search_results = None
231
  if 'user_query' not in st.session_state: st.session_state.user_query = ""
232
 
@@ -235,14 +212,14 @@ def clear_search_state():
235
  st.session_state.user_query = ""
236
 
237
  with st.form(key="search_form"):
238
- query = st.text_area("Describe tu caso aquí:", height=150, placeholder="Ej: Cuando como mucho pan se me hincha el estómago, me da niebla mental y siento fatiga, ¿qué puede ser?")
239
  submitted = st.form_submit_button("Analizar mi caso", type="primary")
240
 
241
  if submitted:
242
  if not query:
243
  st.warning("Por favor, describe lo que sientes y lo que comiste.")
244
  elif alimentos_data is None:
245
- st.error("La base de datos de alimentos no está disponible.")
246
  else:
247
  st.session_state.user_query = query
248
  with st.spinner("🧠 Interpretando tu caso y buscando pistas con IA..."):
@@ -260,15 +237,21 @@ if submitted:
260
  st.session_state.search_results = results
261
  else:
262
  st.error("No se pudieron identificar alimentos o síntomas claros en tu descripción. Intenta ser un poco más específico.")
263
- st.session_state.search_results = []
264
 
265
  if st.session_state.search_results is not None:
266
  st.button("Realizar nueva consulta", on_click=clear_search_state)
267
  st.markdown("---")
268
 
269
  results = st.session_state.search_results
 
 
270
  if not results:
271
- st.warning(f"No se encontraron coincidencias claras en nuestra base de datos para tu caso: '{st.session_state.user_query}'.")
 
 
 
 
272
  else:
273
  st.success(f"Hemos encontrado {len(results)} posible(s) causa(s) relacionada(s) con tu caso. Aquí está el análisis de la más probable:")
274
 
 
1
+ # ==================== Buscador de Intolerancias Alimentarias (Versión Híbrida 2.1) =====================================
2
+ # Incluye manejo de casos donde no se encuentran coincidencias.
3
 
4
  import streamlit as st
5
  import google.generativeai as genai
 
10
  import re
11
 
12
  st.set_page_config(
13
+ page_title="El Detective de Alimentos",
14
  page_icon="🍎",
15
  layout="wide"
16
  )
 
60
  with open(path_alimentos, 'r', encoding='utf-8') as f:
61
  data_alimentos = json.load(f)
62
 
 
63
  lista_condiciones = sorted(list(set(item['condicion_asociada'] for item in data_alimentos)))
64
 
65
  logger.info(f"✅ Base de datos enriquecida cargada con {len(data_alimentos)} registros.")
66
  return data_alimentos, lista_condiciones
67
 
68
  except FileNotFoundError:
69
+ st.error("❌ Error: No se encontró el archivo 'alimentos_enriquecido.json'. Asegúrate de que existe en la carpeta 'DATOS'.")
70
  return None, None
71
  except json.JSONDecodeError:
72
  st.error("❌ Error: El archivo 'alimentos_enriquecido.json' tiene un formato incorrecto.")
 
80
  "leche": ["lácteos", "caseína", "lactosa"], "queso": ["lácteos", "caseína", "lactosa", "histamina", "tiramina"], "yogur": ["lácteos", "caseína", "lactosa"], "mantequilla": ["lácteos", "caseína", "lactosa"],
81
  "manzana": ["salicilatos", "fructosa"], "almendras": ["salicilatos"], "uvas": ["salicilatos"], "vino tinto": ["histamina", "tiramina", "sulfitos"], "vino rojo": ["histamina", "tiramina", "sulfitos"],
82
  "azucar": ["azúcar", "fructosa"], "dulces": ["azúcar"], "refrescos": ["azúcar", "fructosa"], "gaseosas": ["azúcar", "fructosa"],
83
+ "embutidos": ["histamina", "tiramina", "nitritos"], "carne": ["alfa-gal", "proteínas", "purinas"], "carnes rojas": ["purinas", "alfa-gal"],
84
+ "mariscos": ["purinas", "sulfitos", "alérgenos"]
85
  }
86
 
87
  # --- LÓGICA DE BÚSQUEDA Y ANÁLISIS (ENFOQUE HÍBRIDO) ---
 
104
  Devuelve la respuesta ÚNICAMENTE en formato JSON. Si no puedes inferir una condición, deja el campo como un string vacío "".
105
 
106
  Ejemplo 1:
107
+ Consulta: "Cuando como mucha carne me duele el dedo gordo del pie."
108
  Respuesta:
109
  {{
110
+ "alimentos": ["carne"],
111
+ "sintomas": ["dolor en el dedo gordo del pie"],
112
+ "condicion_probable": "Gota / Hiperuricemia."
113
  }}
114
 
 
 
 
 
 
 
 
 
 
115
  Ahora, analiza la siguiente consulta:
116
  Consulta: "{query}"
117
  """
 
129
  return None
130
 
131
  def find_best_matches_hybrid(entities, data):
132
+ """Motor de búsqueda híbrido (v2.1) con puntuación ponderada y mayor autoridad para la inferencia de la IA."""
133
  if not entities or not data: return []
134
 
135
  user_symptoms = set(s.lower().strip() for s in entities.get("sintomas", []))
136
  user_foods = set(f.lower().strip() for f in entities.get("alimentos", []))
137
  inferred_condition = entities.get("condicion_probable", "").lower().strip()
138
 
139
+ candidate_terms = set(user_foods)
140
  for food in user_foods:
141
  if food in FOOD_TO_COMPOUND_MAP:
142
+ candidate_terms.update(c.lower() for c in FOOD_TO_COMPOUND_MAP[food])
143
 
144
  scores = {}
145
  for index, entry in enumerate(data):
146
  score = 0
147
 
 
148
  entry_condition = entry.get("condicion_asociada", "").lower().strip()
149
  if inferred_condition and inferred_condition == entry_condition:
150
+ score += 100
151
 
 
152
  entry_compounds_text = entry.get("compuesto_alimento", "").lower()
153
+ if any(term in entry_compounds_text for term in candidate_terms):
154
+ score += 20
155
 
 
156
  entry_symptoms_keys = set(s.lower().strip() for s in entry.get("sintomas_clave", []))
157
+ # Para hacer la coincidencia más flexible
158
+ for user_symptom in user_symptoms:
159
+ if any(key in user_symptom for key in entry_symptoms_keys) or any(user_symptom in key for key in entry_symptoms_keys):
160
+ score += 5
161
 
162
  if score > 0:
163
  scores[index] = score
 
168
  return [data[i] for i in sorted_matches_indices]
169
 
170
  def generate_detailed_analysis(query, match):
171
+ """Genera la explicación final para el usuario."""
 
172
  if not model: return "Error: El modelo de IA no está disponible."
173
  prompt = f"""
174
+ Eres un asistente de IA experto en nutrición y bienestar, con un tono claro, empático y educativo.
175
  Un usuario ha descrito el siguiente caso: "{query}"
176
  Basado en esto, nuestro sistema ha encontrado una posible coincidencia con:
177
  - **Compuesto Intolerante:** {match.get("compuesto_alimento")}
 
183
  **Tu Tarea:** Redacta una respuesta completa y estructurada para el usuario usando Markdown:
184
  1. **Posible Causa:** Resume por qué sus síntomas podrían estar relacionados con esta condición.
185
  2. **¿Qué podría estar pasando en tu cuerpo?:** Explica el "Mecanismo Fisiológico" en términos sencillos.
186
+ 3. **Pasos a Seguir y Recomendaciones:** Presenta las "Recomendaciones" como sugerencias, mencionando otros alimentos a tener en cuenta del campo "compuesto_alimento".
187
  4. **Descargo de Responsabilidad Médico:** Incluye OBLIGATORIAMENTE un párrafo final claro y visible que indique que NO es un diagnóstico médico y que debe consultar a un profesional.
188
  """
189
  try:
 
193
  logger.error(f"Error generando análisis detallado con Gemini: {e}")
194
  return "No se pudo generar el análisis detallado."
195
 
 
196
  # --- INTERFAZ DE USUARIO (UI) ---
 
197
  col_img, col_text = st.columns([1, 4], gap="medium")
 
198
  with col_img:
199
+ if os.path.exists("imagen.png"):
200
+ st.image("imagen.png", width=150)
 
201
  with col_text:
202
  st.title("El Detective de Alimentos")
203
  st.markdown("##### Describe lo que sientes y lo que comiste para descubrir posibles intolerancias.")
 
 
204
  st.markdown("---")
205
 
206
+ # --- LÓGICA PRINCIPAL DE LA APLICACIÓN ---
 
 
 
 
 
 
207
  if 'search_results' not in st.session_state: st.session_state.search_results = None
208
  if 'user_query' not in st.session_state: st.session_state.user_query = ""
209
 
 
212
  st.session_state.user_query = ""
213
 
214
  with st.form(key="search_form"):
215
+ query = st.text_area("Describe tu caso aquí:", height=150, placeholder="Ej: Cuando como mucha carne me duele el dedo gordo del pie...")
216
  submitted = st.form_submit_button("Analizar mi caso", type="primary")
217
 
218
  if submitted:
219
  if not query:
220
  st.warning("Por favor, describe lo que sientes y lo que comiste.")
221
  elif alimentos_data is None:
222
+ st.error("La base de datos de alimentos no está disponible. No se puede continuar.")
223
  else:
224
  st.session_state.user_query = query
225
  with st.spinner("🧠 Interpretando tu caso y buscando pistas con IA..."):
 
237
  st.session_state.search_results = results
238
  else:
239
  st.error("No se pudieron identificar alimentos o síntomas claros en tu descripción. Intenta ser un poco más específico.")
240
+ st.session_state.search_results = [] # Resetear a lista vacía
241
 
242
  if st.session_state.search_results is not None:
243
  st.button("Realizar nueva consulta", on_click=clear_search_state)
244
  st.markdown("---")
245
 
246
  results = st.session_state.search_results
247
+
248
+ # --- AQUÍ ESTÁ LA NUEVA LÓGICA DE MANEJO DE "NO ENCONTRADO" ---
249
  if not results:
250
+ st.warning(f"No se encontraron coincidencias claras en nuestra base de datos para tu caso: '{st.session_state.user_query}'.\n\n"
251
+ "**Sugerencias:**\n"
252
+ "- Intenta ser más específico con los síntomas.\n"
253
+ "- Asegúrate de mencionar al menos un alimento o bebida.\n"
254
+ "- Reformula tu consulta con otras palabras.")
255
  else:
256
  st.success(f"Hemos encontrado {len(results)} posible(s) causa(s) relacionada(s) con tu caso. Aquí está el análisis de la más probable:")
257