ReAct (Reasoning and Acting)
ReAct is a powerful feature in Helios Engine that enables agents to reason about tasks before taking actions. This pattern leads to more thoughtful, systematic problem-solving and makes the agent's decision-making process transparent.
What is ReAct?
ReAct (Reasoning and Acting) is a pattern where the agent follows a two-phase approach:
- 💭 Reasoning Phase: The agent analyzes the task, identifies what's needed, and creates a plan
- ⚡ Action Phase: The agent executes the plan using available tools
This separation helps agents handle complex, multi-step tasks more effectively and provides visibility into their thinking process.
Enabling ReAct Mode
Enabling ReAct is incredibly simple - just add .react() to your agent builder:
use helios_engine::{Agent, Config, CalculatorTool}; #[tokio::main] async fn main() -> helios_engine::Result<()> { let config = Config::from_file("config.toml")?; let mut agent = Agent::builder("ReActAgent") .config(config) .tool(Box::new(CalculatorTool)) .react() // ✨ Enable ReAct mode .build() .await?; let response = agent.chat("Calculate (25 * 4) + (100 / 5)").await?; println!("{}", response); Ok(()) }
How It Works
When you send a message to a ReAct-enabled agent, here's what happens:
User Query: "Calculate (25 * 4) + (100 / 5)"
💭 Reasoning Phase:
Agent thinks: "I need to:
1. Calculate 25 * 4 = 100
2. Calculate 100 / 5 = 20
3. Add the results: 100 + 20 = 120"
⚡ Action Phase:
- Uses calculator tool: 25 * 4 → 100
- Uses calculator tool: 100 / 5 → 20
- Uses calculator tool: 100 + 20 → 120
Response: "The result is 120"
The reasoning is displayed with a 💭 ReAct Reasoning: prefix, making it easy to follow the agent's thought process.
Custom Reasoning Prompts
For domain-specific tasks, you can customize the reasoning prompt:
use helios_engine::{Agent, Config, CalculatorTool}; #[tokio::main] async fn main() -> helios_engine::Result<()> { let config = Config::from_file("config.toml")?; let math_prompt = r#"As a mathematical problem solver: 1. Identify the mathematical operations needed 2. Break down complex calculations into steps 3. Determine the order of operations (PEMDAS) 4. Plan which calculator functions to use 5. Verify the logic of your approach Provide clear mathematical reasoning."#; let mut agent = Agent::builder("MathExpert") .config(config) .system_prompt("You are a mathematics expert.") .tool(Box::new(CalculatorTool)) .react_with_prompt(math_prompt) // 🎯 Custom reasoning .build() .await?; let response = agent.chat("Calculate ((15 * 8) + (20 * 3)) / 2").await?; println!("{}", response); Ok(()) }
When to Use ReAct
Use ReAct When:
- Complex Multi-Step Tasks: Tasks that require planning and coordination
- Debugging: When you want to see how the agent approaches problems
- Critical Operations: When accuracy is more important than speed
- Learning: Understanding agent behavior and decision-making
- Domain-Specific Tasks: With custom prompts for specialized reasoning
❌ Don't Use ReAct When:
- Simple Queries: Straightforward tasks where reasoning adds unnecessary overhead
- Speed Critical: Applications where latency is paramount
- No Tools Available: ReAct is designed for tool-using agents
- High-Volume Operations: When the extra LLM call impacts throughput
Examples
Example 1: Basic ReAct Agent
use helios_engine::{Agent, Config, CalculatorTool, EchoTool}; #[tokio::main] async fn main() -> helios_engine::Result<()> { let config = Config::from_file("config.toml")?; let mut agent = Agent::builder("Assistant") .config(config) .tools(vec![ Box::new(CalculatorTool), Box::new(EchoTool), ]) .react() .build() .await?; // Multi-step task let response = agent .chat("Calculate 15 * 7, then echo the result") .await?; println!("{}", response); Ok(()) }
Example 2: Domain-Specific Reasoning
use helios_engine::{Agent, Config, FileReadTool, CalculatorTool}; #[tokio::main] async fn main() -> helios_engine::Result<()> { let config = Config::from_file("config.toml")?; let data_analysis_prompt = r#"As a data analyst: 1. UNDERSTAND: What data am I working with? 2. EXTRACT: What information do I need? 3. PROCESS: What calculations are required? 4. TOOLS: Which tools should I use? 5. OUTPUT: How should I present the result? Think through the data pipeline systematically."#; let mut analyst = Agent::builder("DataAnalyst") .config(config) .system_prompt("You are a data analysis expert.") .tools(vec![ Box::new(FileReadTool), Box::new(CalculatorTool), ]) .react_with_prompt(data_analysis_prompt) .build() .await?; let response = analyst .chat("Analyze the numbers: 10, 20, 30, 40, 50. Calculate their average.") .await?; println!("{}", response); Ok(()) }
Example 3: Comparing With and Without ReAct
use helios_engine::{Agent, Config, CalculatorTool}; #[tokio::main] async fn main() -> helios_engine::Result<()> { let config1 = Config::from_file("config.toml")?; let config2 = Config::from_file("config.toml")?; // Standard agent let mut standard = Agent::builder("Standard") .config(config1) .tool(Box::new(CalculatorTool)) .build() .await?; // ReAct agent let mut react = Agent::builder("ReAct") .config(config2) .tool(Box::new(CalculatorTool)) .react() .build() .await?; let query = "Calculate (15 * 3) + (20 * 2)"; println!("Standard agent:"); let r1 = standard.chat(query).await?; println!("{}\n", r1); println!("ReAct agent:"); let r2 = react.chat(query).await?; println!("{}\n", r2); Ok(()) }
Builder Methods
.react()
Enables ReAct mode with the default reasoning prompt.
#![allow(unused)] fn main() { let agent = Agent::builder("MyAgent") .config(config) .react() .build() .await?; }
.react_with_prompt(prompt)
Enables ReAct mode with a custom reasoning prompt.
#![allow(unused)] fn main() { let custom_prompt = "Think step by step about this problem..."; let agent = Agent::builder("MyAgent") .config(config) .react_with_prompt(custom_prompt) .build() .await?; }
Both methods can be placed anywhere in the builder chain:
#![allow(unused)] fn main() { // Before tools let agent = Agent::builder("Agent") .config(config) .react() .tool(Box::new(CalculatorTool)) .build() .await?; // After tools let agent = Agent::builder("Agent") .config(config) .tool(Box::new(CalculatorTool)) .react() .build() .await?; }
Performance Considerations
Latency
ReAct adds one extra LLM call for reasoning:
- Without ReAct: 1 LLM call + tool executions
- With ReAct: 2 LLM calls + tool executions
Impact: Approximately 1-2 seconds additional latency (varies by model)
Token Usage
Additional tokens are consumed for:
- Reasoning prompt: ~50 tokens
- Reasoning response: ~100-300 tokens
- Context storage: ~100-300 tokens
Impact: ~250-650 additional tokens per query
Optimization Tips
For applications where performance matters:
#![allow(unused)] fn main() { // Use ReAct selectively if query_is_complex { react_agent.chat(query).await? } else { standard_agent.chat(query).await? } // Or create specialized agents let quick_agent = Agent::builder("Quick") .config(config) .build() .await?; let thinking_agent = Agent::builder("Thinker") .config(config) .react() .build() .await?; }
Benefits
1. Better Accuracy
Thinking before acting reduces errors and improves decision quality.
2. Transparency
See exactly how the agent approaches problems, making debugging easier.
3. Complex Task Handling
Multi-step problems are handled more systematically with clear planning.
4. Explainability
Understand agent reasoning for compliance, auditing, or learning purposes.
5. Domain Adaptation
Custom prompts tailor reasoning to specific domains or tasks.
Best Practices
1. Use Descriptive System Prompts
#![allow(unused)] fn main() { .system_prompt("You are a methodical assistant who thinks through problems carefully.") }
2. Combine with Appropriate Tools
#![allow(unused)] fn main() { .tools(vec![ Box::new(CalculatorTool), Box::new(FileReadTool), Box::new(JsonParserTool), ]) .react() }
3. Set Reasonable Iteration Limits
#![allow(unused)] fn main() { .max_iterations(15) // Allow enough steps for complex reasoning .react() }
4. Monitor Reasoning Output
Watch the 💭 ReAct Reasoning: output to understand agent behavior and optimize prompts.
5. Use Custom Prompts for Specific Domains
Tailor the reasoning prompt to match your use case (mathematics, data analysis, planning, etc.).
Troubleshooting
Reasoning Not Showing
Problem: No reasoning output visible
Solution:
- Ensure
.react()or.react_with_prompt()is called - Verify the agent has tools registered
- Check stdout for
💭 ReAct Reasoning:prefix
Too Much Overhead
Problem: ReAct adds too much latency
Solution:
- Use ReAct selectively for complex tasks only
- Consider disabling for simple queries
- Use faster models for reasoning phase
Poor Reasoning Quality
Problem: Agent reasoning is unclear or unhelpful
Solution:
- Improve the system prompt to encourage better thinking
- Use more capable models (e.g., GPT-4 vs GPT-3.5)
- Create custom reasoning prompts with examples
- Adjust the prompt structure for your specific domain
Next Steps
- Check out the examples directory for complete working examples
- See react_agent.rs for a basic demo
- See react_custom_prompt.rs for domain-specific examples
- Read the Tools documentation to learn about available tools
Summary
ReAct mode enables agents to think before acting, leading to:
- 🎯 More accurate results
- 👁️ Transparent decision-making
- 🧩 Better handling of complex tasks
- 🔧 Easier debugging and optimization
Simply add .react() to your agent builder to enable this powerful feature!