8.1 ReAct & Chain-of-Thought

Combining reasoning traces with tool/action steps

Agent Architecture Overview

ReAct (Reasoning and Acting) combines the strengths of Chain-of-Thought reasoning with interactive action execution, creating agents that can think through problems step-by-step while taking concrete actions to gather information and solve tasks.

Core Principles

ReAct agents alternate between reasoning (thinking about the problem), acting (taking concrete steps), and observing (processing results) to create a dynamic problem-solving loop that combines deliberation with action.

1. Chain-of-Thought Reasoning

Chain-of-Thought (CoT) prompting enables language models to perform complex reasoning by generating intermediate reasoning steps before arriving at a final answer.

CoT Example:
Question: Roger has 5 tennis balls. He buys 2 more cans of tennis balls. Each can has 3 tennis balls. How many tennis balls does he have now?

Reasoning:
• Roger starts with 5 tennis balls
• He buys 2 cans, each with 3 balls
• 2 cans × 3 balls per can = 6 more balls
• Total: 5 + 6 = 11 tennis balls
class ChainOfThoughtReasoner: def __init__(self, model): self.model = model self.reasoning_steps = [] def reason(self, problem, context=None): """Generate step-by-step reasoning for a problem""" cot_prompt = f""" Problem: {problem} Let's think step by step: 1. First, I need to understand what is being asked: - What is the question? - What information do I have? - What do I need to find? 2. Next, I'll identify the relevant information: - What facts are given? - What relationships exist between the facts? - What operations or steps are needed? 3. Then, I'll work through the solution: - What is the first step? - What follows logically? - How do I combine the information? 4. Finally, I'll verify my answer: - Does this make sense? - Have I answered the original question? - Can I double-check my work? Let me work through this step by step: """ response = self.model.generate(cot_prompt) self.reasoning_steps = self._parse_reasoning_steps(response) return { 'reasoning': response, 'steps': self.reasoning_steps, 'conclusion': self._extract_conclusion(response) } def _parse_reasoning_steps(self, reasoning_text): """Extract individual reasoning steps""" steps = [] lines = reasoning_text.split('\n') for line in lines: if line.strip().startswith(('•', '-', '*')) or line.strip()[:1].isdigit(): steps.append(line.strip()) return steps

2. ReAct Framework

ReAct extends Chain-of-Thought by interleaving reasoning steps with action execution, creating a dynamic loop of thought, action, and observation.

ReAct Loop

Thought → Action → Observation → Thought → Action → ...

This cycle continues until the agent reaches a conclusion or completes the task.

class ReActAgent: def __init__(self, model, tools): self.model = model self.tools = tools self.trace = [] self.max_iterations = 10 def solve(self, task): """Solve task using ReAct framework""" react_prompt = f""" Task: {task} You can use the following tools: {self._format_tool_descriptions()} Use the following format: Thought: [your reasoning about what to do next] Action: [tool_name] Action Input: [input to the tool] Observation: [result from the tool] Continue this process until you can provide a final answer. Thought:""" context = react_prompt for iteration in range(self.max_iterations): # Generate thought response = self.model.generate(context) self.trace.append({'type': 'thought', 'content': response}) # Check if we have a final answer if "Final Answer:" in response: return self._extract_final_answer(response) # Parse and execute action action_result = self._execute_action(response) self.trace.append({'type': 'action', 'content': action_result}) # Update context with observation context += f"\n{response}\nObservation: {action_result['observation']}\nThought:" return "Max iterations reached without conclusion" def _execute_action(self, thought_response): """Parse and execute action from thought response""" action_pattern = r"Action: (.+?)(?:\n|Action Input:)" input_pattern = r"Action Input: (.+?)(?:\n|$)" import re action_match = re.search(action_pattern, thought_response) input_match = re.search(input_pattern, thought_response) if action_match and input_match: tool_name = action_match.group(1).strip() tool_input = input_match.group(1).strip() 'action': tool_name, 'input': tool_input, 'observation': result } else: return { 'action': tool_name, 'input': tool_input, 'observation': f"Error: Unknown tool '{tool_name}'" } return { 'action': 'parse_error', 'input': '', 'observation': 'Could not parse action from response' }
Example ReAct Trace:
Thought: I need to find the current population of Tokyo to answer this question.
Action: search
Action Input: Tokyo population 2024
Observation: Tokyo has approximately 14 million people in the city proper and 37.4 million in the greater metropolitan area as of 2024.
Thought: Now I have the Tokyo population data. Let me search for New York to make a comparison.
Action: search
Action Input: New York City population 2024
Observation: New York City has approximately 8.3 million people as of 2024.
Thought: Now I can compare the two cities. Tokyo (14 million) has significantly more people than New York City (8.3 million). Tokyo is about 1.7 times larger by population.
Final Answer: Tokyo has a larger population than New York City, with approximately 14 million residents compared to NYC's 8.3 million.

3. Combining Reasoning and Action

The power of ReAct lies in its integration of deliberative reasoning with interactive action execution, creating agents that can adapt their approach based on real-world feedback.

class IntegratedReActSystem: def __init__(self, model, tool_registry): self.reasoner = ChainOfThoughtReasoner(model) self.actor = ReActAgent(model, tool_registry.get_tools()) self.integration_strategy = 'adaptive' def solve_complex_task(self, task): """Solve complex tasks using integrated reasoning and action""" # Phase 1: Initial reasoning initial_analysis = self.reasoner.reason( f"Analyze this task and create a plan: {task}" ) # Phase 2: ReAct execution with reasoning guidance result = self.actor.solve_with_reasoning_guidance( task, initial_analysis['reasoning'] ) # Phase 3: Final verification verification = self.reasoner.reason( f"Verify this solution: {result}. Is it correct and complete?" ) return { 'solution': result, 'reasoning_trace': self.actor.trace, 'verification': verification, 'confidence': self._calculate_confidence(verification) } def _calculate_confidence(self, verification_result): """Calculate confidence based on verification""" if "correct" in verification_result['conclusion'].lower(): return 0.9 elif "partially" in verification_result['conclusion'].lower(): return 0.6 else: return 0.3
Approach Strengths Limitations Best Use Cases
Chain-of-Thought Clear reasoning steps, interpretable, good for math/logic No external interaction, limited to model knowledge Mathematical reasoning, logical problems, structured analysis
ReAct Interactive learning, tool use, real-world grounding More complex, potential for action errors Research tasks, data gathering, multi-step problem solving
Integrated Combines reasoning depth with action capability Computational overhead, coordination complexity Complex problem solving, verification tasks, adaptive agents
← Previous: Memory & State Management Next: Reflexion & Self-Critique →
'action': tool_name, 'input': tool_input, 'observation': result } return { 'action': 'parse_error', 'input': None, 'observation': 'Could not parse action from response' }
ReAct Example Trace:
Task: What is the capital of France and what is its current population?
Thought: I need to find information about France's capital and its population. Let me start by searching for the capital of France.
Action: search
Action Input: capital of France
Observation: The capital of France is Paris.
Thought: Now I know the capital is Paris. I need to find the current population of Paris.
Action: search
Action Input: Paris France current population 2024
Observation: Paris has a population of approximately 2.1 million people in the city proper, and about 12.4 million in the metropolitan area as of 2024.
Thought: Perfect! I now have both pieces of information needed to answer the question.

Final Answer: The capital of France is Paris, which has a current population of approximately 2.1 million people in the city proper (or 12.4 million in the metropolitan area) as of 2024.