update workflow error
Browse files- app.py +68 -45
- requirements.txt +4 -57
- tools.py +2 -1
app.py
CHANGED
|
@@ -182,8 +182,8 @@ class GAIAAgent:
|
|
| 182 |
|
| 183 |
# Import tools from our tools.py file
|
| 184 |
try:
|
| 185 |
-
from tools import
|
| 186 |
-
tool_list =
|
| 187 |
tools.extend(tool_list)
|
| 188 |
logger.info(f"✅ Loaded {len(tool_list)} tools from tools.py")
|
| 189 |
except ImportError as e:
|
|
@@ -191,18 +191,6 @@ class GAIAAgent:
|
|
| 191 |
except Exception as e:
|
| 192 |
logger.warning(f"⚠️ Error loading tools from tools.py: {e}")
|
| 193 |
|
| 194 |
-
# Import RAG tool from our retriever.py file
|
| 195 |
-
try:
|
| 196 |
-
from retriever import create_persona_tool
|
| 197 |
-
persona_tool = create_persona_tool()
|
| 198 |
-
if persona_tool:
|
| 199 |
-
tools.append(persona_tool)
|
| 200 |
-
logger.info("✅ Loaded persona RAG tool from retriever.py")
|
| 201 |
-
except ImportError as e:
|
| 202 |
-
logger.error(f"❌ Could not import retriever.py: {e}")
|
| 203 |
-
except Exception as e:
|
| 204 |
-
logger.warning(f"⚠️ Error loading RAG tool from retriever.py: {e}")
|
| 205 |
-
|
| 206 |
# Check if we have any tools
|
| 207 |
if not tools:
|
| 208 |
error_msg = "❌ No tools available! Check tools.py and retriever.py"
|
|
@@ -213,18 +201,18 @@ class GAIAAgent:
|
|
| 213 |
for tool in tools:
|
| 214 |
logger.info(f" - {tool.metadata.name}: {tool.metadata.description[:50]}...")
|
| 215 |
|
| 216 |
-
# Step 3: Create the agent
|
| 217 |
try:
|
| 218 |
-
from llama_index.core.agent.workflow import AgentWorkflow
|
| 219 |
|
| 220 |
-
# Create the agent with a GAIA-optimized system prompt
|
| 221 |
self.agent = AgentWorkflow.from_tools_or_functions(
|
| 222 |
tools_or_functions=tools,
|
| 223 |
llm=self.llm,
|
| 224 |
system_prompt=self._create_system_prompt()
|
| 225 |
)
|
| 226 |
|
| 227 |
-
logger.info("✅
|
| 228 |
|
| 229 |
except ImportError as e:
|
| 230 |
error_msg = f"❌ Could not import AgentWorkflow: {e}"
|
|
@@ -285,14 +273,13 @@ class GAIAAgent:
|
|
| 285 |
|
| 286 |
def __call__(self, question: str) -> str:
|
| 287 |
"""
|
| 288 |
-
Process a GAIA question and return an answer.
|
| 289 |
|
| 290 |
-
This
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
4. Returns a properly formatted answer
|
| 296 |
|
| 297 |
Args:
|
| 298 |
question (str): The GAIA question to answer
|
|
@@ -303,16 +290,35 @@ class GAIAAgent:
|
|
| 303 |
logger.info(f"📝 Processing GAIA question: {question[:100]}...")
|
| 304 |
|
| 305 |
try:
|
| 306 |
-
#
|
| 307 |
-
|
|
|
|
|
|
|
| 308 |
loop = asyncio.new_event_loop()
|
| 309 |
asyncio.set_event_loop(loop)
|
| 310 |
|
| 311 |
try:
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
self.agent.run(user_msg=question)
|
| 315 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 316 |
|
| 317 |
# Extract the response from the result object
|
| 318 |
answer = self._extract_response(result)
|
|
@@ -335,10 +341,10 @@ class GAIAAgent:
|
|
| 335 |
|
| 336 |
def _extract_response(self, result: Any) -> str:
|
| 337 |
"""
|
| 338 |
-
Extract the text response from the
|
| 339 |
|
| 340 |
-
|
| 341 |
-
|
| 342 |
|
| 343 |
Args:
|
| 344 |
result: The result object from the agent workflow
|
|
@@ -346,18 +352,35 @@ class GAIAAgent:
|
|
| 346 |
Returns:
|
| 347 |
str: Extracted response text
|
| 348 |
"""
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 357 |
else:
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
|
|
|
|
|
|
|
|
|
| 361 |
return str(result)
|
| 362 |
|
| 363 |
def _clean_answer(self, answer: str) -> str:
|
|
|
|
| 182 |
|
| 183 |
# Import tools from our tools.py file
|
| 184 |
try:
|
| 185 |
+
from tools import get_all_tools
|
| 186 |
+
tool_list = get_all_tools()
|
| 187 |
tools.extend(tool_list)
|
| 188 |
logger.info(f"✅ Loaded {len(tool_list)} tools from tools.py")
|
| 189 |
except ImportError as e:
|
|
|
|
| 191 |
except Exception as e:
|
| 192 |
logger.warning(f"⚠️ Error loading tools from tools.py: {e}")
|
| 193 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
# Check if we have any tools
|
| 195 |
if not tools:
|
| 196 |
error_msg = "❌ No tools available! Check tools.py and retriever.py"
|
|
|
|
| 201 |
for tool in tools:
|
| 202 |
logger.info(f" - {tool.metadata.name}: {tool.metadata.description[:50]}...")
|
| 203 |
|
| 204 |
+
# Step 3: Create the agent using AgentWorkflow (course approach)
|
| 205 |
try:
|
| 206 |
+
from llama_index.core.agent.workflow import AgentWorkflow, ToolCallResult, AgentStream
|
| 207 |
|
| 208 |
+
# Create the agent with a GAIA-optimized system prompt (exactly like course)
|
| 209 |
self.agent = AgentWorkflow.from_tools_or_functions(
|
| 210 |
tools_or_functions=tools,
|
| 211 |
llm=self.llm,
|
| 212 |
system_prompt=self._create_system_prompt()
|
| 213 |
)
|
| 214 |
|
| 215 |
+
logger.info("✅ AgentWorkflow created successfully")
|
| 216 |
|
| 217 |
except ImportError as e:
|
| 218 |
error_msg = f"❌ Could not import AgentWorkflow: {e}"
|
|
|
|
| 273 |
|
| 274 |
def __call__(self, question: str) -> str:
|
| 275 |
"""
|
| 276 |
+
Process a GAIA question and return an answer using course approach.
|
| 277 |
|
| 278 |
+
This follows the exact pattern from the course notebook:
|
| 279 |
+
1. Run the agent to get a handler
|
| 280 |
+
2. Stream events asynchronously
|
| 281 |
+
3. Extract the final response
|
| 282 |
+
4. Clean and return the answer
|
|
|
|
| 283 |
|
| 284 |
Args:
|
| 285 |
question (str): The GAIA question to answer
|
|
|
|
| 290 |
logger.info(f"📝 Processing GAIA question: {question[:100]}...")
|
| 291 |
|
| 292 |
try:
|
| 293 |
+
# Import event types for processing
|
| 294 |
+
from llama_index.core.agent.workflow import ToolCallResult, AgentStream
|
| 295 |
+
|
| 296 |
+
# Run the agent asynchronously following course pattern
|
| 297 |
loop = asyncio.new_event_loop()
|
| 298 |
asyncio.set_event_loop(loop)
|
| 299 |
|
| 300 |
try:
|
| 301 |
+
async def run_agent():
|
| 302 |
+
# Start the agent run (course pattern)
|
| 303 |
+
handler = self.agent.run(user_msg=question)
|
| 304 |
+
|
| 305 |
+
# Stream events and collect reasoning (optional for debugging)
|
| 306 |
+
reasoning_steps = []
|
| 307 |
+
async for ev in handler.stream_events():
|
| 308 |
+
if isinstance(ev, ToolCallResult):
|
| 309 |
+
step = f"Tool: {ev.tool_name}({ev.tool_kwargs}) => {ev.tool_output}"
|
| 310 |
+
reasoning_steps.append(step)
|
| 311 |
+
logger.info(f"🔧 {step}")
|
| 312 |
+
elif isinstance(ev, AgentStream):
|
| 313 |
+
# This is the agent's thought process
|
| 314 |
+
pass # We could log this for debugging
|
| 315 |
+
|
| 316 |
+
# Get the final response
|
| 317 |
+
resp = await handler
|
| 318 |
+
return resp
|
| 319 |
+
|
| 320 |
+
# Execute the agent
|
| 321 |
+
result = loop.run_until_complete(run_agent())
|
| 322 |
|
| 323 |
# Extract the response from the result object
|
| 324 |
answer = self._extract_response(result)
|
|
|
|
| 341 |
|
| 342 |
def _extract_response(self, result: Any) -> str:
|
| 343 |
"""
|
| 344 |
+
Extract the text response from the AgentWorkflow result.
|
| 345 |
|
| 346 |
+
Based on the course notebook, AgentWorkflow returns an AgentOutput with this structure:
|
| 347 |
+
AgentOutput(response=ChatMessage(...), tool_calls=[], raw={...})
|
| 348 |
|
| 349 |
Args:
|
| 350 |
result: The result object from the agent workflow
|
|
|
|
| 352 |
Returns:
|
| 353 |
str: Extracted response text
|
| 354 |
"""
|
| 355 |
+
try:
|
| 356 |
+
# Handle AgentOutput format from course (most likely)
|
| 357 |
+
if hasattr(result, 'response'):
|
| 358 |
+
chat_message = result.response
|
| 359 |
+
if hasattr(chat_message, 'blocks'):
|
| 360 |
+
# Extract text from TextBlock(s)
|
| 361 |
+
for block in chat_message.blocks:
|
| 362 |
+
if hasattr(block, 'text'):
|
| 363 |
+
return str(block.text)
|
| 364 |
+
elif hasattr(chat_message, 'content'):
|
| 365 |
+
return str(chat_message.content)
|
| 366 |
+
else:
|
| 367 |
+
return str(chat_message)
|
| 368 |
+
|
| 369 |
+
# Fallback to other common formats
|
| 370 |
+
elif hasattr(result, 'content'):
|
| 371 |
+
return str(result.content)
|
| 372 |
+
elif hasattr(result, 'message'):
|
| 373 |
+
if hasattr(result.message, 'content'):
|
| 374 |
+
return str(result.message.content)
|
| 375 |
+
else:
|
| 376 |
+
return str(result.message)
|
| 377 |
else:
|
| 378 |
+
# Final fallback: convert whatever we got to string
|
| 379 |
+
return str(result)
|
| 380 |
+
|
| 381 |
+
except Exception as e:
|
| 382 |
+
logger.warning(f"⚠️ Error extracting response: {e}")
|
| 383 |
+
# If extraction fails, try simple string conversion
|
| 384 |
return str(result)
|
| 385 |
|
| 386 |
def _clean_answer(self, answer: str) -> str:
|
requirements.txt
CHANGED
|
@@ -45,13 +45,10 @@ llama-index-llms-huggingface-api
|
|
| 45 |
# Requires HF_TOKEN in your Space secrets
|
| 46 |
|
| 47 |
# ============================================================================
|
| 48 |
-
# AGENT
|
| 49 |
# ============================================================================
|
| 50 |
-
#
|
| 51 |
-
|
| 52 |
-
llama-index-agent-workflow
|
| 53 |
-
# Agent workflow system - allows creating agents that can use multiple tools
|
| 54 |
-
# This is what orchestrates the web search, calculator, and RAG tools
|
| 55 |
|
| 56 |
# ============================================================================
|
| 57 |
# RETRIEVAL SYSTEMS (RAG) - Enhanced with Vector Embeddings
|
|
@@ -104,54 +101,4 @@ python-dotenv
|
|
| 104 |
nest-asyncio
|
| 105 |
# Allows running async code in environments that already have an event loop
|
| 106 |
# Required for running LlamaIndex query engines in Jupyter/Gradio
|
| 107 |
-
# Fixes "RuntimeError: This event loop is already running" errors
|
| 108 |
-
|
| 109 |
-
# ============================================================================
|
| 110 |
-
# OPTIONAL: ADDITIONAL USEFUL PACKAGES
|
| 111 |
-
# ============================================================================
|
| 112 |
-
# These might be helpful for specific GAIA questions but aren't required
|
| 113 |
-
|
| 114 |
-
# numpy
|
| 115 |
-
# For numerical computations if needed for advanced math questions
|
| 116 |
-
|
| 117 |
-
# matplotlib
|
| 118 |
-
# For creating charts/graphs if GAIA questions require visualizations
|
| 119 |
-
|
| 120 |
-
# beautifulsoup4
|
| 121 |
-
# For parsing HTML if web search results need detailed extraction
|
| 122 |
-
|
| 123 |
-
# ============================================================================
|
| 124 |
-
# DEVELOPMENT AND DEBUGGING (Optional)
|
| 125 |
-
# ============================================================================
|
| 126 |
-
# Uncomment these if you want enhanced debugging capabilities
|
| 127 |
-
|
| 128 |
-
# jupyter
|
| 129 |
-
# For interactive development and testing
|
| 130 |
-
|
| 131 |
-
# ipywidgets
|
| 132 |
-
# For enhanced Jupyter notebook widgets
|
| 133 |
-
|
| 134 |
-
# rich
|
| 135 |
-
# For beautiful terminal output and better logging
|
| 136 |
-
|
| 137 |
-
# ============================================================================
|
| 138 |
-
# INSTALLATION NOTES
|
| 139 |
-
# ============================================================================
|
| 140 |
-
#
|
| 141 |
-
# To install all dependencies:
|
| 142 |
-
# pip install -r requirements.txt
|
| 143 |
-
#
|
| 144 |
-
# For HuggingFace Spaces:
|
| 145 |
-
# - This file should be in your Space root directory
|
| 146 |
-
# - Dependencies are automatically installed when you deploy
|
| 147 |
-
# - No manual installation needed
|
| 148 |
-
#
|
| 149 |
-
# API Keys Setup:
|
| 150 |
-
# - Go to your Space Settings → Repository secrets
|
| 151 |
-
# - Add OPENAI_API_KEY (for OpenAI) or HF_TOKEN (for HuggingFace)
|
| 152 |
-
# - At least one is required for the agent to work
|
| 153 |
-
#
|
| 154 |
-
# Troubleshooting:
|
| 155 |
-
# - If imports fail, check that all packages installed correctly
|
| 156 |
-
# - Some packages may require specific versions for compatibility
|
| 157 |
-
# - Check the Space logs for detailed error messages
|
|
|
|
| 45 |
# Requires HF_TOKEN in your Space secrets
|
| 46 |
|
| 47 |
# ============================================================================
|
| 48 |
+
# AGENT SYSTEM - Course Approach
|
| 49 |
# ============================================================================
|
| 50 |
+
# AgentWorkflow is part of llama-index-core, no separate package needed
|
| 51 |
+
# This matches exactly what the course notebook uses
|
|
|
|
|
|
|
|
|
|
| 52 |
|
| 53 |
# ============================================================================
|
| 54 |
# RETRIEVAL SYSTEMS (RAG) - Enhanced with Vector Embeddings
|
|
|
|
| 101 |
nest-asyncio
|
| 102 |
# Allows running async code in environments that already have an event loop
|
| 103 |
# Required for running LlamaIndex query engines in Jupyter/Gradio
|
| 104 |
+
# Fixes "RuntimeError: This event loop is already running" errors
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tools.py
CHANGED
|
@@ -653,4 +653,5 @@ if __name__ == "__main__":
|
|
| 653 |
print("\n=== Tools Testing Complete ===")
|
| 654 |
print("\nTo use these tools in your agent:")
|
| 655 |
print("from tools import get_all_tools")
|
| 656 |
-
print("tools = get_all_tools()")
|
|
|
|
|
|
| 653 |
print("\n=== Tools Testing Complete ===")
|
| 654 |
print("\nTo use these tools in your agent:")
|
| 655 |
print("from tools import get_all_tools")
|
| 656 |
+
print("tools = get_all_tools()")
|
| 657 |
+
|