Nancy1906 commited on
Commit
119c10a
·
verified ·
1 Parent(s): 4f974de
Files changed (1) hide show
  1. my_tools.py +86 -63
my_tools.py CHANGED
@@ -1,5 +1,3 @@
1
- # my_tools.py
2
-
3
  import os
4
  import math
5
  import time
@@ -7,7 +5,7 @@ import asyncio
7
  import subprocess
8
  import requests
9
  import pandas as pd
10
- from io import BytesIO
11
  from bs4 import BeautifulSoup
12
  from duckduckgo_search import DDGS
13
  import wikipedia
@@ -40,20 +38,19 @@ class GeminiLLM(LLM):
40
 
41
  def __init__(self, **kwargs):
42
  super().__init__(**kwargs)
43
- # Resolver FieldInfo si es necesario
44
  actual_model_name = self.model_name
45
  if not isinstance(actual_model_name, str):
46
- model_field_definition = self.__fields__.get("model_name")
47
- if model_field_definition and hasattr(model_field_definition, 'default'):
48
- actual_model_name = model_field_definition.default
49
  if not isinstance(actual_model_name, str):
50
  actual_model_name = "models/gemini-1.5-flash-latest"
51
 
52
  actual_temperature = self.temperature
53
  if not isinstance(actual_temperature, (float, int)):
54
- temp_field_definition = self.__fields__.get("temperature")
55
- if temp_field_definition and hasattr(temp_field_definition, 'default'):
56
- actual_temperature = temp_field_definition.default
57
  if not isinstance(actual_temperature, (float, int)):
58
  actual_temperature = 0.0
59
 
@@ -78,9 +75,9 @@ class GeminiLLM(LLM):
78
  def metadata(self):
79
  actual_model_name_meta = self.model_name
80
  if not isinstance(actual_model_name_meta, str):
81
- model_field_def_meta = self.__fields__.get("model_name")
82
- if model_field_def_meta and hasattr(model_field_def_meta, 'default'):
83
- actual_model_name_meta = model_field_def_meta.default
84
  if not isinstance(actual_model_name_meta, str):
85
  actual_model_name_meta = "models/gemini-1.5-flash-latest"
86
  return LLMMetadata(
@@ -201,7 +198,6 @@ def analyze_table(table_md: str, question: str) -> str:
201
  verifica la conmutatividad de la matriz; en otro caso, devuelve el CSV equivalente.
202
  """
203
  try:
204
- # Quitar líneas de separación y vacías
205
  lines = [l for l in table_md.splitlines() if l.strip() and '---' not in l]
206
  rows = [[c.strip() for c in l.strip().strip('|').split('|')] for l in lines]
207
  if len(rows) < 2:
@@ -265,16 +261,26 @@ def classify_botanical(items_list_str: str) -> str:
265
  """
266
  Clasifica botánicamente una lista de alimentos (en inglés o español) en Verduras, Frutas u Otros.
267
  """
268
- # Mapeo inglés → español
269
  mapping = {
270
  "tomato": "tomate", "pepper": "pimiento", "bell pepper": "pimiento",
271
- "cucumber": "pepino", "eggplant": "berenjena", "zucchini": "calabacín",
272
- "avocado": "aguacate", "squash": "calabaza", "pea": "guisante", "corn": "maíz",
273
- "bean": "judía", "beans": "judía", "green beans": "judía verde", "sweet potato": "batata",
274
- "whole bean coffee": "café", "rice": "arroz", "oregano": "orégano"
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  }
276
- fruits = {"tomate", "pepino", "calabacín", "berenjena", "pimiento", "aguacate", "calabaza", "guisante", "judía verde", "maíz"}
277
- vegetables = {"zanahoria", "patata", "batata", "cebolla", "ajo", "puerro", "apio", "lechuga", "espinaca", "brócoli", "pepino", "pepino"}
278
 
279
  items = []
280
  for raw in items_list_str.split(','):
@@ -282,13 +288,18 @@ def classify_botanical(items_list_str: str) -> str:
282
  itm_es = mapping.get(itm, itm)
283
  items.append(itm_es)
284
 
285
- vegs = [i for i in items if i in vegetables]
286
- fruits_found = [i for i in items if i in fruits]
287
- others = [i for i in items if i not in fruits and i not in vegetables]
 
 
 
 
 
288
  return (
289
- f"Verduras: {', '.join(sorted(set(vegs)))}\n"
290
- f"Frutas: {', '.join(sorted(set(fruits_found)))}\n"
291
- f"Otros: {', '.join(sorted(set(others)))}"
292
  )
293
 
294
  def scrape_wikipedia_table(page_title: str, section: str, table_index: int = 0) -> str:
@@ -380,11 +391,11 @@ all_tools = [
380
  tool_descriptions = "\n".join([
381
  f"{t.metadata.name}: {t.metadata.description} "
382
  + {
383
- "classify_botanical_foods": "(Ej: classify_botanical_foods('zanahoria, pepino, tomate'))",
384
  "read_excel_data": "(Ej: read_excel_data('ventas.xlsx', sheet_name=0))",
385
  "analyze_markdown_table": "(Ej: analyze_markdown_table('| A | B |\\n|---|---|\\n|1|2|', '¿Es conmut?'))",
386
- "web_search": "(Ej: web_search('¿Quién ganó la Champions 2025?'))",
387
- "scrape_wiki_table": "(Ej: scrape_wiki_table('Lionel Messi', 'Carrera', 0))",
388
  "reverse_text": "(Ej: reverse_text('Hola'))",
389
  "execute_code": "(Ej: execute_code('5*7'))",
390
  }.get(t.metadata.name, "")
@@ -392,30 +403,50 @@ tool_descriptions = "\n".join([
392
  ])
393
 
394
  # -------------------------------------------------------------------
395
- # 6) PROMPT DE SISTEMA MEJORADO
396
  # -------------------------------------------------------------------
397
  system_prompt = f"""
398
  Eres Alfred, un agente ReAct eficiente y preciso. Tu objetivo es responder correctamente usando las herramientas disponibles.
399
- Sigue este flujo en cada pregunta:
400
- 1. LEE la pregunta y analiza palabras clave:
401
- - Si menciona “lista” de “alimentos” o “categorizar” botánicamente, llama:
402
- classify_botanical_foods(<lista_coma_sep>).
403
- - Si menciona “archivo Excel” o “Excel adjunto”, llama:
404
- read_excel_data(<ruta_o_URL>).
405
- - Si ves una “tabla Markdown”, llama:
406
- analyze_markdown_table(<tabla_md>, <pregunta>).
407
- - Si necesitas información general de la web, llama:
408
- web_search(<consulta>).
409
- - Si necesitas raspar tablas de Wikipedia, llama:
410
- scrape_wiki_table(<título>, <sección>, <índice_tabla>).
411
- - Si hay que invertir texto, llama:
412
- reverse_text(<texto>).
413
- - Si hay que ejecutar código Python, llama:
414
- execute_code(<código>).
415
- 2. GENERA el “TOOL CALL” con la entrada correcta.
416
- 3. EJECUTA la herramienta y observa el resultado.
417
- 4. VERIFICA que el resultado responda bien la pregunta. Si no, intenta otro paso.
418
- 5. RESPONDE de forma clara y concisa usando la salida obtenida.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
419
 
420
  Herramientas disponibles (USAR EXÁCTAMENTE estos nombres):
421
  {tool_descriptions}
@@ -430,31 +461,23 @@ alfred_agent = ReActAgent.from_tools(
430
  llm=llm,
431
  system_prompt=system_prompt,
432
  verbose=True,
433
- max_iterations=25, # Más iteraciones para razonamiento multi-paso
434
  callback_manager=llm.callback_manager,
435
- handle_parsing_errors=True # Para que reintente si la llamada a herramienta sale malformada
436
  )
437
 
438
  def basic_agent_response(question: str) -> str:
439
  """
440
- Si detecta “Excel adjunto”, asume que SAIA inyecta el path y fuerza read_excel_data.
441
- De lo contrario, usa ReActAgent.query().
442
  """
443
  try:
444
- # Forzar uso de read_excel_data si aparece Excel en la pregunta
445
  if "attached excel" in question.lower() or "archivo excel" in question.lower():
446
- # En el entorno SAIA normalmente inyectan la ruta real; aquí usamos un placeholder.
447
  return read_excel_data("data/attached.xlsx")
448
  resp = alfred_agent.query(question)
449
  if hasattr(resp, 'response') and resp.response is not None:
450
  return str(resp.response)
451
  elif resp is not None:
452
  return str(resp)
453
- else:
454
- return "No se generó una respuesta válida."
455
  except Exception as e:
456
  return f"Error crítico del agente: {e}"
457
-
458
- # --- FIN DE my_tools.py ---
459
-
460
-
 
 
 
1
  import os
2
  import math
3
  import time
 
5
  import subprocess
6
  import requests
7
  import pandas as pd
8
+ from io import BytesIO, StringIO
9
  from bs4 import BeautifulSoup
10
  from duckduckgo_search import DDGS
11
  import wikipedia
 
38
 
39
  def __init__(self, **kwargs):
40
  super().__init__(**kwargs)
 
41
  actual_model_name = self.model_name
42
  if not isinstance(actual_model_name, str):
43
+ field_def = self.__fields__.get("model_name")
44
+ if field_def and hasattr(field_def, 'default'):
45
+ actual_model_name = field_def.default
46
  if not isinstance(actual_model_name, str):
47
  actual_model_name = "models/gemini-1.5-flash-latest"
48
 
49
  actual_temperature = self.temperature
50
  if not isinstance(actual_temperature, (float, int)):
51
+ temp_field_def = self.__fields__.get("temperature")
52
+ if temp_field_def and hasattr(temp_field_def, 'default'):
53
+ actual_temperature = temp_field_def.default
54
  if not isinstance(actual_temperature, (float, int)):
55
  actual_temperature = 0.0
56
 
 
75
  def metadata(self):
76
  actual_model_name_meta = self.model_name
77
  if not isinstance(actual_model_name_meta, str):
78
+ field_meta = self.__fields__.get("model_name")
79
+ if field_meta and hasattr(field_meta, 'default'):
80
+ actual_model_name_meta = field_meta.default
81
  if not isinstance(actual_model_name_meta, str):
82
  actual_model_name_meta = "models/gemini-1.5-flash-latest"
83
  return LLMMetadata(
 
198
  verifica la conmutatividad de la matriz; en otro caso, devuelve el CSV equivalente.
199
  """
200
  try:
 
201
  lines = [l for l in table_md.splitlines() if l.strip() and '---' not in l]
202
  rows = [[c.strip() for c in l.strip().strip('|').split('|')] for l in lines]
203
  if len(rows) < 2:
 
261
  """
262
  Clasifica botánicamente una lista de alimentos (en inglés o español) en Verduras, Frutas u Otros.
263
  """
 
264
  mapping = {
265
  "tomato": "tomate", "pepper": "pimiento", "bell pepper": "pimiento",
266
+ "green beans": "judía verde", "beans": "judía verde",
267
+ "zucchini": "calabacín", "eggplant": "berenjena", "cucumber": "pepino",
268
+ "broccoli": "brócoli", "celery": "apio", "lettuce": "lechuga",
269
+ "corn": "maíz", "peas": "guisante", "pea": "guisante",
270
+ "spinach": "espinaca", "kale": "col rizada",
271
+ "sweet potatoes": "batata", "sweet potato": "batata", "potato": "patata",
272
+ "onion": "cebolla", "garlic": "ajo", "carrot": "zanahoria",
273
+ "okra": "okra", "cabbage": "col", "cauliflower": "coliflor"
274
+ }
275
+ vegetables_es = {
276
+ "zanahoria","patata","batata","cebolla","ajo","puerro","apio",
277
+ "lechuga","espinaca","brócoli","calabacín","berenjena","pepino",
278
+ "judía verde","maíz","okra","col rizada","col","coliflor"
279
+ }
280
+ fruits_es = {
281
+ "tomate","pepino","calabacín","berenjena","pimiento","aguacate",
282
+ "calabaza","guisante","judía verde","maíz"
283
  }
 
 
284
 
285
  items = []
286
  for raw in items_list_str.split(','):
 
288
  itm_es = mapping.get(itm, itm)
289
  items.append(itm_es)
290
 
291
+ vegs = [i for i in items if i in vegetables_es and i not in fruits_es]
292
+ fruits_found = [i for i in items if i in fruits_es]
293
+ others = [i for i in items if i not in vegetables_es and i not in fruits_es]
294
+
295
+ vegs_sorted = sorted(set(vegs))
296
+ fruits_sorted = sorted(set(fruits_found))
297
+ others_sorted = sorted(set(others))
298
+
299
  return (
300
+ f"Verduras: {', '.join(vegs_sorted)}\n"
301
+ f"Frutas: {', '.join(fruits_sorted)}\n"
302
+ f"Otros: {', '.join(others_sorted)}"
303
  )
304
 
305
  def scrape_wikipedia_table(page_title: str, section: str, table_index: int = 0) -> str:
 
391
  tool_descriptions = "\n".join([
392
  f"{t.metadata.name}: {t.metadata.description} "
393
  + {
394
+ "classify_botanical_foods": "(Ej: classify_botanical_foods('milk, eggs, broccoli, celery, lettuce'))",
395
  "read_excel_data": "(Ej: read_excel_data('ventas.xlsx', sheet_name=0))",
396
  "analyze_markdown_table": "(Ej: analyze_markdown_table('| A | B |\\n|---|---|\\n|1|2|', '¿Es conmut?'))",
397
+ "web_search": "(Ej: web_search('Hokkaido Nippon-Ham Fighters roster'))",
398
+ "scrape_wiki_table": "(Ej: scrape_wiki_table('Malko Competition', 'Winners', 0))",
399
  "reverse_text": "(Ej: reverse_text('Hola'))",
400
  "execute_code": "(Ej: execute_code('5*7'))",
401
  }.get(t.metadata.name, "")
 
403
  ])
404
 
405
  # -------------------------------------------------------------------
406
+ # 6) PROMPT DE SISTEMA MEJORADO with few-shot examples
407
  # -------------------------------------------------------------------
408
  system_prompt = f"""
409
  Eres Alfred, un agente ReAct eficiente y preciso. Tu objetivo es responder correctamente usando las herramientas disponibles.
410
+ A continuación tienes ejemplos de cómo usar cada herramienta:
411
+
412
+ ### EJEMPLO 1: Clasificación botánica
413
+ Usuario: "I have a grocery list: milk, eggs, broccoli, celery, lettuce. Please give me only the vegetables, alphabetized, comma-separated."
414
+ Agente (pensando): "La pregunta menciona 'grocery list' y 'vegetables' -> usar classify_botanical_foods"
415
+ Agente (acción):
416
+ {{"tool": "classify_botanical_foods", "input": "milk, eggs, broccoli, celery, lettuce"}}
417
+ Observación: "Verduras: broccoli, celery, lettuce\nFrutas: \nOtros: eggs, milk"
418
+ Agente (respuesta final): "Verduras: broccoli, celery, lettuce"
419
+
420
+ ### EJEMPLO 2: Excel ventas de comida
421
+ Usuario: "Attached is an Excel file with sales data. What were the total sales from food only?"
422
+ Agente (pensando): "Menciona 'Excel' y 'food' -> usar read_excel_data"
423
+ Agente (acción):
424
+ {{"tool": "read_excel_data", "input": "data/attached.xlsx"}}
425
+ Observación (CSV): "item,type,sales\nBurger,food,1000\nSoda,drink,200\nPizza,food,1500"
426
+ Agente (respuesta final): "Total food sales: $2500.00"
427
+
428
+ ### EJEMPLO 3: Roster de jugadores
429
+ Usuario: "Who are the pitchers with the numbers before and after Taishō Tamai as of July 2023? Use last names only, Roman characters."
430
+ Agente (pensando): "La pregunta menciona 'Taishō Tamai' y 'pitchers' -> usar scrape_wiki_table o web_search"
431
+ Agente (acción):
432
+ {{"tool": "scrape_wiki_table", "input": ["Hokkaido Nippon-Ham Fighters roster", "Players", 0]}}
433
+ Observación (CSV): "Number,Name,Position\n... ,Tamai Taishō,Pitcher\n45,Uehara,Pitcher\n47,Kakui,Pitcher, ..."
434
+ Agente (procesar): "Tamai tiene número 46. El pitcher 45 es Uehara, el 47 es Kakui."
435
+ Agente (respuesta final): "Uehara, Kakui"
436
+
437
+ ### FLUJO GENERAL:
438
+ 1. LEE la pregunta y detecta palabras clave:
439
+ - "grocery list", "vegetables" -> classify_botanical_foods
440
+ - "attached Excel", "sales" -> read_excel_data
441
+ - "roster", "pitchers", "number" -> scrape_wiki_table o web_search
442
+ - "Malko Competition" -> scrape_wiki_table
443
+ - "tabla Markdown" -> analyze_markdown_table
444
+ - "invertir texto" -> reverse_text
445
+ - "ejecutar código" -> execute_code
446
+ 2. PRODUCE el "TOOL CALL" en formato JSON con el nombre exacto de la herramienta.
447
+ 3. EJECUTA la herramienta y recibe la salida.
448
+ 4. PROCESA la salida (filter, sumar, ordenar) en Python si es necesario.
449
+ 5. RESPONDE con el formato exacto que SAIA espera (solo la parte solicitada, sin texto extra).
450
 
451
  Herramientas disponibles (USAR EXÁCTAMENTE estos nombres):
452
  {tool_descriptions}
 
461
  llm=llm,
462
  system_prompt=system_prompt,
463
  verbose=True,
464
+ max_iterations=25,
465
  callback_manager=llm.callback_manager,
466
+ handle_parsing_errors=True
467
  )
468
 
469
  def basic_agent_response(question: str) -> str:
470
  """
471
+ Detecta "Excel adjunto" o usa ReActAgent.query para el resto.
 
472
  """
473
  try:
 
474
  if "attached excel" in question.lower() or "archivo excel" in question.lower():
 
475
  return read_excel_data("data/attached.xlsx")
476
  resp = alfred_agent.query(question)
477
  if hasattr(resp, 'response') and resp.response is not None:
478
  return str(resp.response)
479
  elif resp is not None:
480
  return str(resp)
481
+ return "No se generó una respuesta válida."
 
482
  except Exception as e:
483
  return f"Error crítico del agente: {e}"