Nancy1906 commited on
Commit
ed31b4e
·
verified ·
1 Parent(s): 3d04920
Files changed (1) hide show
  1. my_tools.py +37 -86
my_tools.py CHANGED
@@ -1,4 +1,4 @@
1
- # alfred_agent.py
2
 
3
  import os
4
  import math
@@ -9,19 +9,17 @@ import pandas as pd
9
  from io import StringIO
10
  from duckduckgo_search import DDGS
11
  import wikipedia
12
- from llama_index import ServiceContext, LLMPredictor
13
  from llama_index.core.llms import ChatMessage, LLMMetadata, LLM, CompletionResponse
14
  from llama_index.core.tools import FunctionTool
15
  from llama_index.core.agent import ReActAgent
16
  from llama_index.indices.object_index import ObjectIndex
17
  from llama_index.indices.vector_store import VectorStoreIndex
18
- from llama_index.core.callbacks import CallbackManager
19
  from llama_index.core.callbacks.llama_debug import LlamaDebugHandler
20
  from pydantic import Field
21
  import google.generativeai as genai
22
 
23
  # -------------------------------------------------------------------
24
- # 1) GeminiLLM: tu implementación personalizada
25
  # -------------------------------------------------------------------
26
  class GeminiLLM(LLM):
27
  model_name: str = Field(default="models/gemini-1.5-flash-latest")
@@ -117,13 +115,11 @@ class GeminiLLM(LLM):
117
 
118
 
119
  # -------------------------------------------------------------------
120
- # 2) Funciones de herramienta
121
  # -------------------------------------------------------------------
122
-
123
  HEADERS = {'User-Agent':'Mozilla/5.0'}
124
 
125
  def buscar_web(query: str, max_attempts: int = 2) -> str:
126
- """Búsqueda real en DuckDuckGo con reintentos."""
127
  for i in range(max_attempts):
128
  try:
129
  with DDGS(headers=HEADERS, timeout=25) as ddgs:
@@ -135,139 +131,94 @@ def buscar_web(query: str, max_attempts: int = 2) -> str:
135
  if i < max_attempts-1:
136
  time.sleep(5*(i+1))
137
  else:
138
- return f"Error al buscar_web tras {max_attempts} intentos: {e}"
139
 
140
  def reverse_text(text: str) -> str:
141
  return text[::-1]
142
 
143
  def analyze_table(table_md: str, question: str) -> str:
144
- """
145
- Parsea tabla Markdown y responde preguntas sencillas.
146
- Ejemplo específico: acciones no conmutativas.
147
- """
148
  try:
149
- # Convertir Markdown a CSV-like
150
  lines = [l.strip() for l in table_md.strip().splitlines() if l.strip()]
151
- # Quitar las líneas de separador '---'
152
  content = [l for l in lines if not set(l) <= set("|- ")]
153
- # Extraer filas
154
  rows = [ [c.strip() for c in r.split("|")[1:-1]] for r in content ]
155
  df = pd.DataFrame(rows[1:], columns=rows[0])
156
- # Si la pregunta menciona 'conmutativa', detectamos contrajemplos
157
  if "no conmut" in question.lower():
158
- S = list(df.columns)
159
  counter = set()
160
  for x in S:
161
  for y in S:
162
  a = df.loc[df[rows[0][0]]==x, y].values[0]
163
  b = df.loc[df[rows[0][0]]==y, x].values[0]
164
  if a != b:
165
- counter.add(x); counter.add(y)
166
  return ", ".join(sorted(counter)) or "No hay contraejemplos"
167
- # Para otras preguntas, devolvemos la tabla en texto
168
  return df.to_csv(index=False)
169
  except Exception as e:
170
  return f"Error analyze_table: {e}"
171
 
172
  def execute_code(code: str) -> str:
173
- """Ejecuta código Python en un subproceso seguro."""
174
  try:
175
- # Prevenir imports faltantes
176
- result = subprocess.run(["python","-c",code], capture_output=True, text=True, timeout=5)
177
- if result.stderr:
178
- return f"Error en código: {result.stderr.strip()}"
179
- return result.stdout.strip() or "(sin salida)"
180
  except Exception as e:
181
- return f"Error ejecución código: {e}"
182
 
183
  def no_tool_solution(query: str) -> str:
184
- return "Procedo a responder con mi conocimiento interno."
185
 
186
  # -------------------------------------------------------------------
187
- # 3) Envolver como FunctionTool
188
  # -------------------------------------------------------------------
189
-
190
- search_tool = FunctionTool.from_defaults(fn=buscar_web, name="web_search",
191
- description="Busca información en la web usando DuckDuckGo (3 resultados).")
192
- reverse_tool = FunctionTool.from_defaults(fn=reverse_text, name="reverse_text",
193
- description="Invierte el texto dado. Útil para decodificar cadenas al revés.")
194
- table_tool = FunctionTool.from_defaults(fn=analyze_table, name="analyze_table",
195
- description="Parsea tabla Markdown y responde preguntas sobre conmutatividad u otras sencillas.")
196
- code_tool = FunctionTool.from_defaults(fn=execute_code, name="execute_code",
197
- description="Ejecuta fragmentos de código Python para cálculos o procesamiento.")
198
- fallback_tool = FunctionTool.from_defaults(fn=no_tool_solution, name="no_tool_solution",
199
- description="Fallback: responde usando únicamente conocimiento interno si otras herramientas fallan.")
200
 
201
  # -------------------------------------------------------------------
202
- # 4) Índice de herramientas + ToolRetriever
203
  # -------------------------------------------------------------------
204
-
205
- all_tools = [search_tool, reverse_tool, table_tool, code_tool, fallback_tool]
206
- obj_index = ObjectIndex.from_objects(all_tools, index_cls=VectorStoreIndex)
207
  tool_retriever = obj_index.as_retriever(similarity_top_k=3)
208
 
209
  # -------------------------------------------------------------------
210
  # 5) Prompt de sistema
211
  # -------------------------------------------------------------------
212
-
213
  system_prompt = (
214
- "Eres Alfred, un agente ReAct que usa pasos de pensamiento claros y herramientas especializadas.\n"
215
- "Sigue este flujo:\n"
216
  "1) Analiza la pregunta.\n"
217
- "2) Piensa paso a paso (internamente), luego decide qué acción tomar.\n"
218
- "3) Si necesitas datos externos, usa web_search.\n"
219
- "4) Si hay texto invertido, usa reverse_text.\n"
220
- "5) Si hay tablas Markdown, usa analyze_table.\n"
221
- "6) Para cálculos complejos, usa execute_code.\n"
222
- "7) Si nada ayuda, llama no_tool_solution y responde con tu conocimiento.\n"
223
- "8) Si la pregunta involucra audio, video o imágenes no procesables, informa que no puedes acceder.\n"
224
- "9) Proporciona siempre una respuesta final clara al usuario.\n"
225
- "\nHerramientas disponibles:\n{tool_descriptions}\n"
226
- "No muestres tus pensamientos al usuario; solo la respuesta final."
227
  )
228
 
229
  # -------------------------------------------------------------------
230
- # 6) Inicializar Agent con GeminiLLM
231
  # -------------------------------------------------------------------
 
232
 
233
- # Contexto de LLM
234
- service_ctx = ServiceContext.from_defaults(
235
- llm=GeminiLLM(model_name="models/gemini-1.5-flash-latest", temperature=0.0)
236
- )
237
-
238
- # Crear el agente ReAct
239
  alfred_agent = ReActAgent.from_tools(
240
  tool_retriever=tool_retriever,
241
- service_context=service_ctx,
242
  system_prompt=system_prompt,
243
- verbose=True, # mostrar pasos para depuración
244
- max_iterations=20 # permitir suficiente iteraciones
245
  )
246
 
247
  # -------------------------------------------------------------------
248
- # 7) Función de invocación
249
  # -------------------------------------------------------------------
250
-
251
  def basic_agent_response(question: str) -> str:
252
- print(f"🤖 Alfred recibió: {question}")
253
  try:
254
- resp_obj = alfred_agent.query(question)
255
- return resp_obj.response or "No se generó respuesta."
256
  except Exception as e:
257
- return f"Error crítico en el agente: {e}"
258
 
259
- # -------------------------------------------------------------------
260
- # 8) Prueba rápida (descomentar para test local)
261
- # -------------------------------------------------------------------
262
- if __name__ == "__main__":
263
- tests = [
264
- "¿Cuánto es 37 por 19?",
265
- ".tfel",
266
- "|*|a|b|\n|---|---|---|\n|a|b|c|\n|b|c|a|\nprovide the subset involved in non-commutative",
267
- "I'm making a grocery list para mi mamá..., incluye milk, eggs, bell pepper, zucchini. Solo vegetales."
268
- ]
269
- for q in tests:
270
- print("Q:", q)
271
- print("A:", basic_agent_response(q))
272
- print("-"*40)
273
 
 
1
+ # my_tools.py
2
 
3
  import os
4
  import math
 
9
  from io import StringIO
10
  from duckduckgo_search import DDGS
11
  import wikipedia
 
12
  from llama_index.core.llms import ChatMessage, LLMMetadata, LLM, CompletionResponse
13
  from llama_index.core.tools import FunctionTool
14
  from llama_index.core.agent import ReActAgent
15
  from llama_index.indices.object_index import ObjectIndex
16
  from llama_index.indices.vector_store import VectorStoreIndex
 
17
  from llama_index.core.callbacks.llama_debug import LlamaDebugHandler
18
  from pydantic import Field
19
  import google.generativeai as genai
20
 
21
  # -------------------------------------------------------------------
22
+ # 1) GeminiLLM personalizado
23
  # -------------------------------------------------------------------
24
  class GeminiLLM(LLM):
25
  model_name: str = Field(default="models/gemini-1.5-flash-latest")
 
115
 
116
 
117
  # -------------------------------------------------------------------
118
+ # 2) Herramientas
119
  # -------------------------------------------------------------------
 
120
  HEADERS = {'User-Agent':'Mozilla/5.0'}
121
 
122
  def buscar_web(query: str, max_attempts: int = 2) -> str:
 
123
  for i in range(max_attempts):
124
  try:
125
  with DDGS(headers=HEADERS, timeout=25) as ddgs:
 
131
  if i < max_attempts-1:
132
  time.sleep(5*(i+1))
133
  else:
134
+ return f"Error buscar_web tras {max_attempts} intentos: {e}"
135
 
136
  def reverse_text(text: str) -> str:
137
  return text[::-1]
138
 
139
  def analyze_table(table_md: str, question: str) -> str:
 
 
 
 
140
  try:
 
141
  lines = [l.strip() for l in table_md.strip().splitlines() if l.strip()]
 
142
  content = [l for l in lines if not set(l) <= set("|- ")]
 
143
  rows = [ [c.strip() for c in r.split("|")[1:-1]] for r in content ]
144
  df = pd.DataFrame(rows[1:], columns=rows[0])
 
145
  if "no conmut" in question.lower():
146
+ S = df.columns.tolist()
147
  counter = set()
148
  for x in S:
149
  for y in S:
150
  a = df.loc[df[rows[0][0]]==x, y].values[0]
151
  b = df.loc[df[rows[0][0]]==y, x].values[0]
152
  if a != b:
153
+ counter.update([x,y])
154
  return ", ".join(sorted(counter)) or "No hay contraejemplos"
 
155
  return df.to_csv(index=False)
156
  except Exception as e:
157
  return f"Error analyze_table: {e}"
158
 
159
  def execute_code(code: str) -> str:
 
160
  try:
161
+ res = subprocess.run(["python","- << 'EOF'\n" + code + "\nEOF"], shell=True,
162
+ capture_output=True, text=True, timeout=5)
163
+ if res.stderr:
164
+ return f"Error código: {res.stderr.strip()}"
165
+ return res.stdout.strip() or "(sin salida)"
166
  except Exception as e:
167
+ return f"Error ejecutar código: {e}"
168
 
169
  def no_tool_solution(query: str) -> str:
170
+ return "Procedo con conocimiento interno."
171
 
172
  # -------------------------------------------------------------------
173
+ # 3) FunctionTools
174
  # -------------------------------------------------------------------
175
+ search_tool = FunctionTool.from_defaults(fn=buscar_web, name="web_search", description="Búsqueda DDG (3 resultados).")
176
+ reverse_tool = FunctionTool.from_defaults(fn=reverse_text, name="reverse_text", description="Invierte texto.")
177
+ table_tool = FunctionTool.from_defaults(fn=analyze_table, name="analyze_table", description="Parses Markdown tables y responde conmutatividad.")
178
+ code_tool = FunctionTool.from_defaults(fn=execute_code, name="execute_code", description="Ejecuta Python para cálculos.")
179
+ fallback_tool = FunctionTool.from_defaults(fn=no_tool_solution, name="no_tool_solution", description="Fallback: conocimiento interno.")
 
 
 
 
 
 
180
 
181
  # -------------------------------------------------------------------
182
+ # 4) ObjectIndex + Retriever
183
  # -------------------------------------------------------------------
184
+ all_tools = [search_tool, reverse_tool, table_tool, code_tool, fallback_tool]
185
+ obj_index = ObjectIndex.from_objects(all_tools, index_cls=VectorStoreIndex)
 
186
  tool_retriever = obj_index.as_retriever(similarity_top_k=3)
187
 
188
  # -------------------------------------------------------------------
189
  # 5) Prompt de sistema
190
  # -------------------------------------------------------------------
 
191
  system_prompt = (
192
+ "Eres Alfred, un agente ReAct paso a paso.\n"
 
193
  "1) Analiza la pregunta.\n"
194
+ "2) Decide la herramienta adecuada.\n"
195
+ "3) Usa web_search, reverse_text, analyze_table, execute_code o no_tool_solution.\n"
196
+ "4) Si no puedes procesar audio/video, indícalo.\n"
197
+ "5) Responde claramente.\n"
198
+ "Herramientas: {tool_descriptions}"
 
 
 
 
 
199
  )
200
 
201
  # -------------------------------------------------------------------
202
+ # 6) Inicializar agente
203
  # -------------------------------------------------------------------
204
+ llm = GeminiLLM(model_name="models/gemini-1.5-flash-latest", temperature=0.0)
205
 
 
 
 
 
 
 
206
  alfred_agent = ReActAgent.from_tools(
207
  tool_retriever=tool_retriever,
208
+ llm=llm,
209
  system_prompt=system_prompt,
210
+ verbose=True,
211
+ max_iterations=20
212
  )
213
 
214
  # -------------------------------------------------------------------
215
+ # 7) Función pública
216
  # -------------------------------------------------------------------
 
217
  def basic_agent_response(question: str) -> str:
 
218
  try:
219
+ resp = alfred_agent.query(question)
220
+ return resp.response or "No se generó respuesta."
221
  except Exception as e:
222
+ return f"Error crítico del agente: {e}"
223
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224