JairoCesar commited on
Commit
625601a
·
verified ·
1 Parent(s): e3c4d39

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +70 -52
app.py CHANGED
@@ -1,5 +1,5 @@
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
@@ -17,7 +17,7 @@ st.set_page_config(
17
 
18
  # Configurar logging
19
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
20
- logger = logging.getLogger("food_intolerance_app")
21
 
22
  # --- CONFIGURACIÓN DE GEMINI ---
23
  try:
@@ -74,45 +74,42 @@ def load_data():
74
 
75
  alimentos_data, lista_condiciones = load_data()
76
 
77
- # Mapeo expandido de alimentos a compuestos.
78
  FOOD_TO_COMPOUND_MAP = {
79
- "pan": ["gluten"], "trigo": ["gluten"], "harina de trigo": ["gluten"], "cebada": ["gluten"], "centeno": ["gluten"], "pasta": ["gluten"], "galletas": ["gluten"], "avena": ["gluten"],
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) ---
88
 
89
  def extract_and_infer_with_gemini(query, condiciones):
90
- """Usa Gemini para extraer alimentos, síntomas E INFERIR una condición probable."""
91
  if not model: return None
92
-
93
  condiciones_str = "\n".join([f"- {c}" for c in condiciones])
94
-
95
  system_prompt = f"""
96
- Eres un asistente de triaje clínico experto. Tu tarea es analizar la consulta de un usuario y extraer tres tipos de información:
97
- 1. `alimentos`: Una lista de los alimentos que el usuario menciona haber consumido.
98
- 2. `sintomas`: Una lista de los síntomas que el usuario describe, normalizados a un término común (ej: "hinchazón abdominal", "dolor de cabeza").
99
- 3. `condicion_probable`: Tu mejor inferencia sobre cuál de las siguientes condiciones podría explicar los síntomas del usuario. Elige solo una de la lista.
100
 
101
  LISTA DE CONDICIONES POSIBLES:
102
  {condiciones_str}
103
 
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
  """
118
  try:
@@ -125,11 +122,11 @@ def extract_and_infer_with_gemini(query, condiciones):
125
  return json.loads(json_text)
126
  except Exception as e:
127
  logger.error(f"Error en la extracción/inferencia con Gemini: {e}")
128
- st.error(f"Hubo un problema al interpretar tu consulta con la IA. Error: {e}")
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", []))
@@ -145,19 +142,23 @@ def find_best_matches_hybrid(entities, data):
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,23 +169,41 @@ def find_best_matches_hybrid(entities, data):
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")}
178
- - **Síntomas Comunes:** {match.get("sintomas_clinicos")}
179
- - **Condición Asociada:** {match.get("condicion_asociada")}
180
- - **Mecanismo Fisiológico:** {match.get("mecanismo_fisiologico")}
181
- - **Recomendaciones Generales:** {match.get("recomendaciones_examenes")}
182
-
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:
190
  response = model.generate_content(prompt)
@@ -237,7 +256,7 @@ if submitted:
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)
@@ -245,7 +264,6 @@ if st.session_state.search_results is not None:
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"
@@ -258,7 +276,7 @@ if st.session_state.search_results is not None:
258
  best_match = results[0]
259
 
260
  with st.expander(f"**Principal Coincidencia: {best_match.get('condicion_asociada')}**", expanded=True):
261
- with st.spinner("✍️ Generando un análisis detallado con IA..."):
262
  detailed_analysis = generate_detailed_analysis(st.session_state.user_query, best_match)
263
  st.markdown(detailed_analysis)
264
 
 
1
+ # ==================== El Detective de Alimentos (Versión 3.0 - Refinada) =====================================
2
+ # Mejoras: Base de conocimiento de alimentos expandida, búsqueda de síntomas flexible y generación de respuestas mejorada.
3
 
4
  import streamlit as st
5
  import google.generativeai as genai
 
17
 
18
  # Configurar logging
19
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
20
+ logger = logging.getLogger("food_detective_app")
21
 
22
  # --- CONFIGURACIÓN DE GEMINI ---
23
  try:
 
74
 
75
  alimentos_data, lista_condiciones = load_data()
76
 
77
+ # DICCIONARIO DE TRADUCCIÓN AMPLIADO
78
  FOOD_TO_COMPOUND_MAP = {
79
+ # Gluten
80
+ "pan": ["gluten"], "trigo": ["gluten"], "harina de trigo": ["gluten"], "cebada": ["gluten"],
81
+ "centeno": ["gluten"], "pasta": ["gluten"], "galletas": ["gluten"], "avena": ["gluten"], "pizza": ["gluten"], "torta": ["gluten"],
82
+ # Lácteos
83
+ "leche": ["lácteos", "caseína", "lactosa"], "queso": ["lácteos", "caseína", "lactosa", "histamina", "tiramina"],
84
+ "yogur": ["lácteos", "caseína", "lactosa"], "mantequilla": ["lácteos", "caseína", "lactosa"], "crema": ["lácteos"], "helado": ["lácteos"],
85
+ # Fenoles y Salicilatos
86
+ "manzana": ["salicilatos", "fructosa"], "almendras": ["salicilatos"], "uvas": ["salicilatos"], "pasas": ["salicilatos"],
87
+ "naranja": ["salicilatos"], "brócoli": ["salicilatos", "goitrógenos"], "cúrcuma": ["salicilatos"],
88
+ # Azúcares y Fructosa
89
  "azucar": ["azúcar", "fructosa"], "dulces": ["azúcar"], "refrescos": ["azúcar", "fructosa"], "gaseosas": ["azúcar", "fructosa"],
90
+ "miel": ["fructosa"], "jarabe de maiz": ["fructosa"],
91
+ # Aminas (Histamina, Tiramina)
92
+ "vino tinto": ["histamina", "tiramina", "sulfitos"], "vino rojo": ["histamina", "tiramina", "sulfitos"],
93
+ "cerveza": ["histamina", "tiramina", "purinas"], "chocolate": ["cafeína", "tiramina", "níquel"],
94
+ "embutidos": ["histamina", "tiramina", "nitritos"], "pescado enlatado": ["histamina"], "tomate": ["histamina", "solaninas"],
95
+ # Otros
96
+ "carne": ["alfa-gal", "proteínas", "purinas", "hierro"], "carnes rojas": ["purinas", "alfa-gal", "hierro"],
97
+ "mariscos": ["purinas", "sulfitos", "alérgenos", "yodo"], "huevo": ["alérgenos"], "soya": ["alérgenos"],
98
+ "café": ["cafeína", "ácidos"]
99
  }
100
 
101
+ # --- LÓGICA DE BÚSQUEDA Y ANÁLISIS (ENFOQUE HÍBRIDO REFINADO) ---
102
 
103
  def extract_and_infer_with_gemini(query, condiciones):
104
+ """Extrae entidades e infiere una condición probable."""
105
  if not model: return None
 
106
  condiciones_str = "\n".join([f"- {c}" for c in condiciones])
 
107
  system_prompt = f"""
108
+ Eres un asistente de triaje clínico. Analiza la consulta, extrae 'alimentos', 'sintomas' (normalizados) y la 'condicion_probable' más relevante de la lista proporcionada. Devuelve solo un JSON. Si no puedes inferir una condición, déjalo como un string vacío.
 
 
 
109
 
110
  LISTA DE CONDICIONES POSIBLES:
111
  {condiciones_str}
112
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  Consulta: "{query}"
114
  """
115
  try:
 
122
  return json.loads(json_text)
123
  except Exception as e:
124
  logger.error(f"Error en la extracción/inferencia con Gemini: {e}")
125
+ st.error(f"Hubo un problema al interpretar tu consulta con la IA.")
126
  return None
127
 
128
  def find_best_matches_hybrid(entities, data):
129
+ """Motor de búsqueda híbrido (v3.0) con puntuación ponderada y búsqueda de síntomas flexible."""
130
  if not entities or not data: return []
131
 
132
  user_symptoms = set(s.lower().strip() for s in entities.get("sintomas", []))
 
142
  for index, entry in enumerate(data):
143
  score = 0
144
 
145
+ # Ponderación 1: Coincidencia de Condición
146
  entry_condition = entry.get("condicion_asociada", "").lower().strip()
147
  if inferred_condition and inferred_condition == entry_condition:
148
  score += 100
149
 
150
+ # Ponderación 2: Coincidencia de Alimentos/Compuestos
151
  entry_compounds_text = entry.get("compuesto_alimento", "").lower()
152
  if any(term in entry_compounds_text for term in candidate_terms):
153
  score += 20
154
 
155
+ # Ponderación 3: Coincidencia de Síntomas (BÚSQUEDA FLEXIBLE)
156
  entry_symptoms_keys = set(s.lower().strip() for s in entry.get("sintomas_clave", []))
 
157
  for user_symptom in user_symptoms:
158
+ for key in entry_symptoms_keys:
159
+ if key in user_symptom or user_symptom in key:
160
+ score += 5
161
+ break # Evita sumar puntos múltiples veces por el mismo síntoma de usuario
162
 
163
  if score > 0:
164
  scores[index] = score
 
169
  return [data[i] for i in sorted_matches_indices]
170
 
171
  def generate_detailed_analysis(query, match):
172
+ """Genera la explicación final para el usuario (PROMPT MEJORADO)."""
173
  if not model: return "Error: El modelo de IA no está disponible."
174
+
175
  prompt = f"""
176
+ Eres un asistente de IA experto en nutrición y bienestar. Tu tono es empático, claro y muy educativo. NO actúas como un médico, sino como un guía informativo.
177
+
178
+ El usuario te ha contado esto: "{query}"
179
+
180
+ Tu sistema ha encontrado la siguiente posible conexión:
181
+ - Condición: {match.get("condicion_asociada")}
182
+ - Mecanismo: {match.get("mecanismo_fisiologico")}
183
+ - Recomendaciones: {match.get("recomendaciones_examenes")}
184
+ - Alimentos Implicados: {match.get("compuesto_alimento")}
185
+
186
+ **Tu Tarea:** Redacta una respuesta excepcional para el usuario usando Markdown. Sigue esta estructura OBLIGATORIAMENTE:
187
+
188
+ ### Posible Causa: {match.get("condicion_asociada")}
189
+
190
+ Hola. Entiendo que te preocupa lo que sientes después de comer. Basado en los síntomas y alimentos que mencionaste, como **[menciona aquí los síntomas y alimentos clave del 'query' del usuario]**, existe una posible relación con una condición conocida como **{match.get("condicion_asociada")}**.
191
+
192
+ ### ¿Qué podría estar pasando en tu cuerpo?
193
+
194
+ *Explica el campo "mecanismo_fisiologico" en términos muy sencillos. Usa una analogía si es posible. Por ejemplo, si es sobre Gota, explica las purinas y los cristales.*
195
+
196
+ ### Pasos a Seguir y Recomendaciones
197
+
198
+ Aquí te dejo algunas recomendaciones generales que se suelen considerar para esta condición. Es fundamental que las converses con un profesional de la salud:
199
+
200
+ * **[Punto 1 de las recomendaciones, reformulado como un consejo práctico]**
201
+ * **[Punto 2 de las recomendaciones, enfocado en los exámenes si los hay]**
202
+ * **Atención a otros alimentos:** Ten en cuenta que, además de lo que mencionaste, otros alimentos implicados en esta condición son: **[menciona 2-3 ejemplos del campo "compuesto_alimento", sin la lista completa]**.
203
+
204
+ ### **IMPORTANTE: Descargo de Responsabilidad**
205
+
206
+ Este análisis es una herramienta informativa y de orientación basada en inteligencia artificial. **NO es un diagnóstico médico.** La información proporcionada no debe sustituir la consulta, el diagnóstico o el tratamiento de un médico o profesional de la salud cualificado. Consulta siempre a un experto para evaluar tu caso particular.
207
  """
208
  try:
209
  response = model.generate_content(prompt)
 
256
  st.session_state.search_results = results
257
  else:
258
  st.error("No se pudieron identificar alimentos o síntomas claros en tu descripción. Intenta ser un poco más específico.")
259
+ st.session_state.search_results = []
260
 
261
  if st.session_state.search_results is not None:
262
  st.button("Realizar nueva consulta", on_click=clear_search_state)
 
264
 
265
  results = st.session_state.search_results
266
 
 
267
  if not results:
268
  st.warning(f"No se encontraron coincidencias claras en nuestra base de datos para tu caso: '{st.session_state.user_query}'.\n\n"
269
  "**Sugerencias:**\n"
 
276
  best_match = results[0]
277
 
278
  with st.expander(f"**Principal Coincidencia: {best_match.get('condicion_asociada')}**", expanded=True):
279
+ with st.spinner("✍️ Generando un análisis detallado y personalizado con IA..."):
280
  detailed_analysis = generate_detailed_analysis(st.session_state.user_query, best_match)
281
  st.markdown(detailed_analysis)
282