| |
| """ |
| Reasoning Agent for GAIA Agent System |
| Handles mathematical, logical, and analytical reasoning questions |
| """ |
|
|
| import re |
| import logging |
| from typing import Dict, List, Optional, Any, Union |
|
|
| from agents.state import GAIAAgentState, AgentRole, AgentResult, ToolResult |
| from models.qwen_client import QwenClient, ModelTier |
| from tools.calculator import CalculatorTool |
|
|
| logger = logging.getLogger(__name__) |
|
|
| class ReasoningAgent: |
| """ |
| Specialized agent for reasoning tasks |
| Handles mathematical calculations, logical deduction, and analytical problems |
| """ |
| |
| def __init__(self, llm_client: QwenClient): |
| self.llm_client = llm_client |
| self.calculator = CalculatorTool() |
| |
| def process(self, state: GAIAAgentState) -> GAIAAgentState: |
| """ |
| Process reasoning questions using mathematical and logical analysis |
| """ |
| logger.info(f"Reasoning agent processing: {state.question[:100]}...") |
| state.add_processing_step("Reasoning Agent: Starting analysis") |
| |
| try: |
| |
| strategy = self._determine_reasoning_strategy(state.question) |
| state.add_processing_step(f"Reasoning Agent: Strategy = {strategy}") |
| |
| |
| result = None |
| try: |
| |
| if strategy == "mathematical": |
| result = self._process_mathematical(state) |
| elif strategy == "statistical": |
| result = self._process_statistical(state) |
| elif strategy == "unit_conversion": |
| result = self._process_unit_conversion(state) |
| elif strategy == "logical_deduction": |
| result = self._process_logical_deduction(state) |
| elif strategy == "pattern_analysis": |
| result = self._process_pattern_analysis(state) |
| elif strategy == "step_by_step": |
| result = self._process_step_by_step(state) |
| elif strategy == "general_reasoning": |
| result = self._process_general_reasoning(state) |
| else: |
| result = self._process_general_reasoning(state) |
| |
| except Exception as strategy_error: |
| logger.warning(f"Strategy {strategy} failed: {strategy_error}, trying fallback") |
| |
| try: |
| result = self._process_fallback_reasoning(state, strategy, str(strategy_error)) |
| except Exception as fallback_error: |
| logger.error(f"Fallback reasoning also failed: {fallback_error}") |
| result = self._create_graceful_failure_result(state, f"Reasoning failed: {fallback_error}") |
| |
| |
| if not result or not isinstance(result, AgentResult): |
| result = self._create_graceful_failure_result(state, "No reasoning results available") |
| |
| |
| state.add_agent_result(result) |
| state.add_processing_step(f"Reasoning Agent: Completed with confidence {result.confidence:.2f}") |
| |
| return state |
| |
| except Exception as e: |
| error_msg = f"Reasoning failed: {str(e)}" |
| state.add_error(error_msg) |
| logger.error(error_msg) |
| |
| |
| failure_result = AgentResult( |
| agent_role=AgentRole.REASONING_AGENT, |
| success=False, |
| result=f"Processing encountered difficulties: Reasoning failed", |
| confidence=0.1, |
| reasoning=f"Exception during reasoning: {str(e)}", |
| tools_used=[], |
| model_used="error", |
| processing_time=0.0, |
| cost_estimate=0.0 |
| ) |
| state.add_agent_result(failure_result) |
| return state |
| |
| def _determine_reasoning_strategy(self, question: str) -> str: |
| """Determine the best reasoning strategy for the question""" |
| |
| question_lower = question.lower() |
| |
| |
| math_indicators = [ |
| 'calculate', 'compute', 'solve', 'equation', 'formula', |
| 'multiply', 'divide', 'add', 'subtract', 'sum', 'total', |
| 'percentage', 'percent', 'ratio', 'proportion' |
| ] |
| if any(indicator in question_lower for indicator in math_indicators): |
| return "mathematical" |
| |
| |
| stats_indicators = [ |
| 'average', 'mean', 'median', 'mode', 'standard deviation', |
| 'variance', 'correlation', 'distribution', 'sample' |
| ] |
| if any(indicator in question_lower for indicator in stats_indicators): |
| return "statistical" |
| |
| |
| unit_indicators = [ |
| 'convert', 'to', 'from', 'meter', 'feet', 'celsius', 'fahrenheit', |
| 'gram', 'pound', 'liter', 'gallon', 'hour', 'minute' |
| ] |
| conversion_pattern = r'\d+\s*\w+\s+to\s+\w+' |
| if (any(indicator in question_lower for indicator in unit_indicators) or |
| re.search(conversion_pattern, question_lower)): |
| return "unit_conversion" |
| |
| |
| logic_indicators = [ |
| 'if', 'then', 'therefore', 'because', 'since', 'given that', |
| 'prove', 'demonstrate', 'conclude', 'infer', 'deduce' |
| ] |
| if any(indicator in question_lower for indicator in logic_indicators): |
| return "logical_deduction" |
| |
| |
| pattern_indicators = [ |
| 'pattern', 'sequence', 'series', 'next', 'continues', |
| 'follows', 'trend', 'progression' |
| ] |
| if any(indicator in question_lower for indicator in pattern_indicators): |
| return "pattern_analysis" |
| |
| |
| step_indicators = [ |
| 'step', 'process', 'procedure', 'method', 'approach', |
| 'how to', 'explain how', 'show how' |
| ] |
| if any(indicator in question_lower for indicator in step_indicators): |
| return "step_by_step" |
| |
| |
| return "general_reasoning" |
| |
| def _process_mathematical(self, state: GAIAAgentState) -> AgentResult: |
| """Process mathematical calculation questions""" |
| |
| logger.info("Processing mathematical calculation") |
| |
| |
| expressions = self._extract_mathematical_expressions(state.question) |
| |
| if expressions: |
| |
| calc_results = [] |
| for expr in expressions: |
| calc_result = self.calculator.execute(expr) |
| calc_results.append(calc_result) |
| |
| |
| if calc_results and any(r.success for r in calc_results): |
| return self._analyze_calculation_results(state, calc_results) |
| else: |
| |
| return self._llm_mathematical_reasoning(state) |
| else: |
| |
| return self._llm_mathematical_reasoning(state) |
| |
| def _process_statistical(self, state: GAIAAgentState) -> AgentResult: |
| """Process statistical analysis questions""" |
| |
| logger.info("Processing statistical analysis") |
| |
| |
| numbers = self._extract_numbers(state.question) |
| |
| if len(numbers) >= 2: |
| |
| stats_data = {"operation": "statistics", "data": numbers} |
| calc_result = self.calculator.execute(stats_data) |
| |
| if calc_result.success: |
| return self._analyze_statistical_results(state, calc_result, numbers) |
| else: |
| return self._llm_statistical_reasoning(state, numbers) |
| else: |
| |
| return self._llm_statistical_reasoning(state, []) |
| |
| def _process_unit_conversion(self, state: GAIAAgentState) -> AgentResult: |
| """Process unit conversion questions""" |
| |
| logger.info("Processing unit conversion") |
| |
| |
| conversion_info = self._extract_conversion_info(state.question) |
| |
| if conversion_info: |
| value, from_unit, to_unit = conversion_info |
| conversion_data = { |
| "operation": "convert", |
| "value": value, |
| "from_unit": from_unit, |
| "to_unit": to_unit |
| } |
| |
| calc_result = self.calculator.execute(conversion_data) |
| |
| if calc_result.success: |
| return self._analyze_conversion_results(state, calc_result, conversion_info) |
| else: |
| return self._llm_conversion_reasoning(state, conversion_info) |
| else: |
| |
| return self._llm_conversion_reasoning(state, None) |
| |
| def _process_logical_deduction(self, state: GAIAAgentState) -> AgentResult: |
| """Process logical reasoning and deduction questions""" |
| |
| logger.info("Processing logical deduction") |
| |
| |
| reasoning_prompt = f""" |
| Please solve this logical reasoning problem step by step: |
| |
| Question: {state.question} |
| |
| Approach this systematically: |
| 1. Identify the given information |
| 2. Identify what needs to be determined |
| 3. Apply logical rules and deduction |
| 4. State your conclusion clearly |
| |
| Please provide a clear, logical answer. |
| """ |
| |
| model_tier = ModelTier.COMPLEX |
| llm_result = self.llm_client.generate(reasoning_prompt, tier=model_tier, max_tokens=600) |
| |
| if llm_result.success: |
| return AgentResult( |
| agent_role=AgentRole.REASONING_AGENT, |
| success=True, |
| result=llm_result.response, |
| confidence=0.80, |
| reasoning="Applied logical deduction and reasoning", |
| model_used=llm_result.model_used, |
| processing_time=llm_result.response_time, |
| cost_estimate=llm_result.cost_estimate |
| ) |
| else: |
| return self._create_failure_result("Logical reasoning failed") |
| |
| def _process_pattern_analysis(self, state: GAIAAgentState) -> AgentResult: |
| """Process pattern recognition and analysis questions""" |
| |
| logger.info("Processing pattern analysis") |
| |
| |
| numbers = self._extract_numbers(state.question) |
| |
| pattern_prompt = f""" |
| Analyze this pattern or sequence problem: |
| |
| Question: {state.question} |
| |
| {"Numbers found: " + str(numbers) if numbers else ""} |
| |
| Please: |
| 1. Identify the pattern or rule |
| 2. Explain the logic |
| 3. Provide the answer |
| |
| Be systematic and show your reasoning. |
| """ |
| |
| model_tier = ModelTier.COMPLEX |
| llm_result = self.llm_client.generate(pattern_prompt, tier=model_tier, max_tokens=500) |
| |
| if llm_result.success: |
| confidence = 0.85 if numbers else 0.75 |
| return AgentResult( |
| agent_role=AgentRole.REASONING_AGENT, |
| success=True, |
| result=llm_result.response, |
| confidence=confidence, |
| reasoning="Analyzed patterns and sequences with 72B model", |
| model_used=llm_result.model_used, |
| processing_time=llm_result.response_time, |
| cost_estimate=llm_result.cost_estimate |
| ) |
| else: |
| return self._create_failure_result("Pattern analysis failed") |
| |
| def _process_step_by_step(self, state: GAIAAgentState) -> AgentResult: |
| """Process questions requiring step-by-step explanation""" |
| |
| logger.info("Processing step-by-step reasoning") |
| |
| step_prompt = f""" |
| Please solve this problem with a clear step-by-step approach: |
| |
| Question: {state.question} |
| |
| Structure your response as: |
| Step 1: [First step and reasoning] |
| Step 2: [Second step and reasoning] |
| ... |
| Final Answer: [Clear conclusion] |
| |
| Be thorough and explain each step. |
| """ |
| |
| model_tier = ModelTier.COMPLEX |
| llm_result = self.llm_client.generate(step_prompt, tier=model_tier, max_tokens=600) |
| |
| if llm_result.success: |
| return AgentResult( |
| agent_role=AgentRole.REASONING_AGENT, |
| success=True, |
| result=llm_result.response, |
| confidence=0.85, |
| reasoning="Provided step-by-step solution with 72B model", |
| model_used=llm_result.model_used, |
| processing_time=llm_result.response_time, |
| cost_estimate=llm_result.cost_estimate |
| ) |
| else: |
| return self._create_failure_result("Step-by-step reasoning failed") |
| |
| def _process_general_reasoning(self, state: GAIAAgentState) -> AgentResult: |
| """Process general reasoning questions""" |
| |
| logger.info("Processing general reasoning") |
| |
| reasoning_prompt = f""" |
| Please analyze and answer this reasoning question: |
| |
| Question: {state.question} |
| |
| Think through this carefully and provide a well-reasoned answer. |
| Consider all aspects of the question and explain your reasoning. |
| """ |
| |
| model_tier = ModelTier.COMPLEX |
| llm_result = self.llm_client.generate(reasoning_prompt, tier=model_tier, max_tokens=500) |
| |
| if llm_result.success: |
| return AgentResult( |
| agent_role=AgentRole.REASONING_AGENT, |
| success=True, |
| result=llm_result.response, |
| confidence=0.80, |
| reasoning="Applied general reasoning and analysis with 72B model", |
| model_used=llm_result.model_used, |
| processing_time=llm_result.response_time, |
| cost_estimate=llm_result.cost_estimate |
| ) |
| else: |
| return self._create_failure_result("General reasoning failed") |
| |
| def _extract_mathematical_expressions(self, question: str) -> List[str]: |
| """Extract mathematical expressions from question text""" |
| expressions = [] |
| |
| |
| math_patterns = [ |
| r'\d+\s*[\+\-\*/]\s*\d+', |
| r'\d+\s*\^\s*\d+', |
| r'sqrt\(\d+\)', |
| r'\d+\s*%', |
| r'\d+\s*factorial', |
| ] |
| |
| for pattern in math_patterns: |
| matches = re.findall(pattern, question, re.IGNORECASE) |
| expressions.extend(matches) |
| |
| return expressions |
| |
| def _extract_numbers(self, question: str) -> List[float]: |
| """Extract numerical values from question text""" |
| numbers = [] |
| |
| |
| number_pattern = r'[-+]?\d*\.?\d+' |
| matches = re.findall(number_pattern, question) |
| |
| for match in matches: |
| try: |
| if '.' in match: |
| numbers.append(float(match)) |
| else: |
| numbers.append(float(int(match))) |
| except ValueError: |
| continue |
| |
| return numbers |
| |
| def _extract_conversion_info(self, question: str) -> Optional[tuple]: |
| """Extract unit conversion information from question""" |
| |
| |
| conversion_pattern = r'(\d+(?:\.\d+)?)\s*(\w+)\s+to\s+(\w+)' |
| match = re.search(conversion_pattern, question.lower()) |
| |
| if match: |
| value, from_unit, to_unit = match.groups() |
| return float(value), from_unit, to_unit |
| |
| return None |
| |
| def _analyze_calculation_results(self, state: GAIAAgentState, calc_results: List) -> AgentResult: |
| """Analyze calculator results and provide answer""" |
| |
| successful_results = [r for r in calc_results if r.success] |
| |
| if successful_results: |
| result_summaries = [] |
| total_cost = 0.0 |
| total_time = 0.0 |
| |
| for calc_result in successful_results: |
| if calc_result.result.get('success'): |
| calc_data = calc_result.result['calculation'] |
| result_summaries.append(f"{calc_data['expression']} = {calc_data['result']}") |
| total_cost += calc_result.result.get('cost_estimate', 0) |
| total_time += calc_result.execution_time |
| |
| analysis_prompt = f""" |
| Based on these calculations, please answer the original question: |
| |
| Question: {state.question} |
| |
| Calculation Results: |
| {chr(10).join(result_summaries)} |
| |
| Please provide a direct answer incorporating these calculations. |
| """ |
| |
| llm_result = self.llm_client.generate(analysis_prompt, tier=ModelTier.COMPLEX, max_tokens=400) |
| |
| if llm_result.success: |
| return AgentResult( |
| agent_role=AgentRole.REASONING_AGENT, |
| success=True, |
| result=llm_result.response, |
| confidence=0.85, |
| reasoning="Performed calculations and analyzed results", |
| tools_used=[ToolResult( |
| tool_name="calculator", |
| success=True, |
| result=result_summaries, |
| execution_time=total_time |
| )], |
| model_used=llm_result.model_used, |
| processing_time=total_time + llm_result.response_time, |
| cost_estimate=total_cost + llm_result.cost_estimate |
| ) |
| |
| return self._create_failure_result("Mathematical calculations failed") |
| |
| def _analyze_statistical_results(self, state: GAIAAgentState, calc_result, numbers: List[float]) -> AgentResult: |
| """Analyze statistical calculation results""" |
| |
| if calc_result.success and calc_result.result.get('success'): |
| stats = calc_result.result['statistics'] |
| |
| analysis_prompt = f""" |
| Based on this statistical analysis, please answer the question: |
| |
| Question: {state.question} |
| |
| Data: {numbers} |
| Statistical Results: |
| - Count: {stats.get('count')} |
| - Mean: {stats.get('mean')} |
| - Median: {stats.get('median')} |
| - Min: {stats.get('min')} |
| - Max: {stats.get('max')} |
| - Standard Deviation: {stats.get('stdev', 'N/A')} |
| |
| Please provide a direct answer based on this statistical analysis. |
| """ |
| |
| llm_result = self.llm_client.generate(analysis_prompt, tier=ModelTier.COMPLEX, max_tokens=400) |
| |
| if llm_result.success: |
| return AgentResult( |
| agent_role=AgentRole.REASONING_AGENT, |
| success=True, |
| result=llm_result.response, |
| confidence=0.85, |
| reasoning="Performed statistical analysis", |
| tools_used=[ToolResult( |
| tool_name="calculator", |
| success=True, |
| result=stats, |
| execution_time=calc_result.execution_time |
| )], |
| model_used=llm_result.model_used, |
| processing_time=calc_result.execution_time + llm_result.response_time, |
| cost_estimate=llm_result.cost_estimate |
| ) |
| |
| return self._create_failure_result("Statistical analysis failed") |
| |
| def _analyze_conversion_results(self, state: GAIAAgentState, calc_result, conversion_info: tuple) -> AgentResult: |
| """Analyze unit conversion results""" |
| |
| if calc_result.success and calc_result.result.get('success'): |
| conversion_data = calc_result.result['conversion'] |
| value, from_unit, to_unit = conversion_info |
| |
| analysis_prompt = f""" |
| Based on this unit conversion, please answer the question: |
| |
| Question: {state.question} |
| |
| Conversion: {value} {from_unit} = {conversion_data['result']} {conversion_data['units']} |
| |
| Please provide a direct answer incorporating this conversion. |
| """ |
| |
| llm_result = self.llm_client.generate(analysis_prompt, tier=ModelTier.COMPLEX, max_tokens=400) |
| |
| if llm_result.success: |
| return AgentResult( |
| agent_role=AgentRole.REASONING_AGENT, |
| success=True, |
| result=llm_result.response, |
| confidence=0.90, |
| reasoning="Performed unit conversion", |
| tools_used=[ToolResult( |
| tool_name="calculator", |
| success=True, |
| result=conversion_data, |
| execution_time=calc_result.execution_time |
| )], |
| model_used=llm_result.model_used, |
| processing_time=calc_result.execution_time + llm_result.response_time, |
| cost_estimate=llm_result.cost_estimate |
| ) |
| |
| return self._create_failure_result("Unit conversion failed") |
| |
| def _llm_mathematical_reasoning(self, state: GAIAAgentState) -> AgentResult: |
| """Fallback to LLM-only mathematical reasoning""" |
| |
| math_prompt = f""" |
| Please solve this mathematical problem: |
| |
| Question: {state.question} |
| |
| Show your mathematical reasoning and calculations step by step. |
| Provide a clear numerical answer. |
| """ |
| |
| model_tier = ModelTier.COMPLEX |
| llm_result = self.llm_client.generate(math_prompt, tier=model_tier, max_tokens=500) |
| |
| if llm_result.success: |
| return AgentResult( |
| agent_role=AgentRole.REASONING_AGENT, |
| success=True, |
| result=llm_result.response, |
| confidence=0.70, |
| reasoning="Applied mathematical reasoning (LLM-only)", |
| model_used=llm_result.model_used, |
| processing_time=llm_result.response_time, |
| cost_estimate=llm_result.cost_estimate |
| ) |
| else: |
| return self._create_failure_result("Mathematical reasoning failed") |
| |
| def _llm_statistical_reasoning(self, state: GAIAAgentState, numbers: List[float]) -> AgentResult: |
| """Fallback to LLM-only statistical reasoning""" |
| |
| stats_prompt = f""" |
| Please analyze this statistical problem: |
| |
| Question: {state.question} |
| |
| {"Numbers identified: " + str(numbers) if numbers else ""} |
| |
| Apply statistical reasoning and provide a clear answer. |
| """ |
| |
| model_tier = ModelTier.COMPLEX |
| llm_result = self.llm_client.generate(stats_prompt, tier=model_tier, max_tokens=400) |
| |
| if llm_result.success: |
| return AgentResult( |
| agent_role=AgentRole.REASONING_AGENT, |
| success=True, |
| result=llm_result.response, |
| confidence=0.65, |
| reasoning="Applied statistical reasoning (LLM-only)", |
| model_used=llm_result.model_used, |
| processing_time=llm_result.response_time, |
| cost_estimate=llm_result.cost_estimate |
| ) |
| else: |
| return self._create_failure_result("Statistical reasoning failed") |
| |
| def _llm_conversion_reasoning(self, state: GAIAAgentState, conversion_info: Optional[tuple]) -> AgentResult: |
| """Fallback to LLM-only conversion reasoning""" |
| |
| conversion_prompt = f""" |
| Please solve this unit conversion problem: |
| |
| Question: {state.question} |
| |
| {f"Conversion detected: {conversion_info}" if conversion_info else ""} |
| |
| Apply conversion reasoning and provide a clear answer. |
| """ |
| |
| model_tier = ModelTier.COMPLEX |
| llm_result = self.llm_client.generate(conversion_prompt, tier=model_tier, max_tokens=300) |
| |
| if llm_result.success: |
| return AgentResult( |
| agent_role=AgentRole.REASONING_AGENT, |
| success=True, |
| result=llm_result.response, |
| confidence=0.65, |
| reasoning="Applied conversion reasoning (LLM-only)", |
| model_used=llm_result.model_used, |
| processing_time=llm_result.response_time, |
| cost_estimate=llm_result.cost_estimate |
| ) |
| else: |
| return self._create_failure_result("Conversion reasoning failed") |
| |
| def _create_failure_result(self, error_message: str) -> AgentResult: |
| """Create a failure result""" |
| return AgentResult( |
| agent_role=AgentRole.REASONING_AGENT, |
| success=False, |
| result=error_message, |
| confidence=0.0, |
| reasoning=error_message, |
| model_used="error", |
| processing_time=0.0, |
| cost_estimate=0.0 |
| ) |
| |
| def _process_fallback_reasoning(self, state: GAIAAgentState, original_strategy: str, error_msg: str) -> AgentResult: |
| """Enhanced fallback reasoning when primary strategy fails""" |
| |
| logger.info(f"Executing fallback reasoning after {original_strategy} failure") |
| |
| |
| try: |
| fallback_prompt = f""" |
| Please answer this question using basic reasoning: |
| |
| Question: {state.question} |
| |
| Note: Original strategy '{original_strategy}' failed with: {error_msg} |
| |
| Please provide the best answer you can using simple analysis and reasoning. |
| Focus on extracting key information from the question and providing a helpful response. |
| """ |
| |
| |
| llm_result = self.llm_client.generate(fallback_prompt, tier=ModelTier.COMPLEX, max_tokens=400) |
| |
| if llm_result.success: |
| return AgentResult( |
| agent_role=AgentRole.REASONING_AGENT, |
| success=True, |
| result=llm_result.response, |
| confidence=0.3, |
| reasoning=f"Fallback reasoning after {original_strategy} failed: {error_msg}", |
| tools_used=[], |
| model_used=llm_result.model_used, |
| processing_time=llm_result.response_time, |
| cost_estimate=llm_result.cost_estimate |
| ) |
| else: |
| raise Exception(f"Fallback LLM reasoning failed: {llm_result.error}") |
| |
| except Exception as fallback_error: |
| logger.error(f"Fallback reasoning failed: {fallback_error}") |
| return self._create_graceful_failure_result(state, f"All reasoning methods failed: {fallback_error}") |
| |
| def _create_graceful_failure_result(self, state: GAIAAgentState, error_context: str) -> AgentResult: |
| """Create a graceful failure result that allows the system to continue""" |
| |
| |
| question_analysis = f"Question analysis: {state.question[:200]}" |
| |
| return AgentResult( |
| agent_role=AgentRole.REASONING_AGENT, |
| success=False, |
| result=f"Processing encountered difficulties: {error_context}", |
| confidence=0.1, |
| reasoning=f"Reasoning failed: {error_context}", |
| tools_used=[], |
| model_used="none", |
| processing_time=0.0, |
| cost_estimate=0.0 |
| ) |