Skip to main content

Overview

The ReAct Agent implements the Reasoning + Acting pattern, enabling agents to think step-by-step and use tools to solve complex tasks. Built on the ractor Actor model, it supports concurrent execution and message passing.

Core Types

ReActAgent

The main ReAct agent implementation that orchestrates the thought-action-observation loop.
pub struct ReActAgent {
    llm: Arc<LLMAgent>,
    tools: Arc<RwLock<HashMap<String, Arc<dyn ReActTool>>>>,
    config: ReActConfig,
}
Key Methods:
run
async fn
Executes a task using the ReAct loop
pub async fn run(&self, task: impl Into<String>) -> LLMResult<ReActResult>
Parameters:
  • task: The task description (string)
Returns: LLMResult<ReActResult> with execution details
register_tool
async fn
Registers a tool for the agent to use
pub async fn register_tool(&self, tool: Arc<dyn ReActTool>)
Parameters:
  • tool: Tool implementation wrapped in Arc

ReActAgentBuilder

Builder pattern for constructing ReAct agents with configuration.
let agent = ReActAgent::builder()
    .with_llm(llm_agent)
    .with_tool(Arc::new(SearchTool))
    .with_max_iterations(5)
    .with_temperature(0.7)
    .build_async()
    .await?;
Builder Methods:
with_llm
Arc<LLMAgent>
required
Sets the LLM agent for reasoning
with_tool
Arc<dyn ReActTool>
Adds a single tool to the agent
with_tools
Vec<Arc<dyn ReActTool>>
Adds multiple tools at once
with_max_iterations
usize
default:"10"
Sets maximum reasoning iterations
with_temperature
f32
default:"0.7"
Sets LLM temperature for reasoning
with_system_prompt
String
Provides custom system prompt
with_verbose
bool
default:"true"
Enables detailed logging
build
fn
Builds the agent synchronously (spawns async task for tool registration)
pub fn build(self) -> LLMResult<ReActAgent>
build_async
async fn
Builds the agent asynchronously (waits for tool registration)
pub async fn build_async(self) -> LLMResult<ReActAgent>

Configuration

ReActConfig

Configuration options for ReAct agent behavior.
max_iterations
usize
default:"10"
Maximum number of thought-action-observation cycles
stream_output
bool
default:"false"
Enable streaming of reasoning steps
temperature
f32
default:"0.7"
LLM temperature for reasoning
system_prompt
Option<String>
Custom system prompt (overrides default)
verbose
bool
default:"true"
Show detailed reasoning information
max_tokens_per_step
Option<u32>
default:"Some(2048)"
Maximum tokens per reasoning step

Results

ReActResult

Complete execution result from a ReAct agent run.
task_id
String
Unique identifier for this execution
task
String
Original task description
answer
String
Final answer to the task
steps
Vec<ReActStep>
All execution steps (thoughts, actions, observations)
success
bool
Whether execution completed successfully
error
Option<String>
Error message if execution failed
iterations
usize
Number of reasoning iterations performed
duration_ms
u64
Total execution time in milliseconds

ReActStep

A single step in the reasoning process.
step_type
ReActStepType
Type of step: Thought, Action, Observation, or FinalAnswer
content
String
Content of the step
tool_name
Option<String>
Name of tool used (for Action steps)
tool_input
Option<String>
Input passed to tool (for Action steps)
step_number
usize
Sequential step number
timestamp
u64
Unix timestamp in milliseconds

Tools

ReActTool Trait

Trait for implementing custom tools.
#[async_trait::async_trait]
pub trait ReActTool: Send + Sync {
    fn name(&self) -> &str;
    fn description(&self) -> &str;
    fn parameters_schema(&self) -> Option<serde_json::Value>;
    async fn execute(&self, input: &str) -> Result<String, String>;
    fn to_llm_tool(&self) -> Tool;
}
name
fn() -> &str
required
Unique tool identifier
description
fn() -> &str
required
Description for LLM to understand tool purpose
parameters_schema
fn() -> Option<Value>
JSON Schema for tool parameters (optional)
execute
async fn(&str) -> Result<String, String>
required
Executes the tool with given input

Built-in Tools

MoFA provides several built-in tools:

calculator

Evaluates mathematical expressions

string_tool

String manipulation operations (length, upper, lower, reverse, count)

json_tool

JSON parsing and querying (parse, get, keys, stringify)

datetime_tool

Date and time operations (now, timestamp, millis)

echo_tool

Simple echo for testing
Usage:
use mofa_foundation::react::prelude::*;

let agent = ReActAgent::builder()
    .with_llm(llm_agent)
    .with_tools(all_builtin_tools())
    .build_async()
    .await?;

Actor Model

ReActActor

Actor-based implementation for concurrent ReAct execution. Message Types:
RunTask
Execute a task and return result
RunTask {
    task: String,
    reply: oneshot::Sender<LLMResult<ReActResult>>,
}
RunTaskStreaming
Execute task with streaming step updates
RunTaskStreaming {
    task: String,
    step_tx: mpsc::Sender<ReActStep>,
    reply: oneshot::Sender<LLMResult<ReActResult>>,
}
RegisterTool
Register a new tool dynamically
RegisterTool { tool: Arc<dyn ReActTool> }
GetStatus
Query current actor status
CancelTask
Cancel currently running task
Stop
Stop the actor

ReActActorRef

Convenient wrapper for interacting with ReAct actors.
let (actor_ref, handle) = spawn_react_actor(
    "my-agent",
    llm_agent,
    ReActConfig::default(),
    tools,
).await?;

// Execute task
let result = actor_ref.run_task("What is Rust?").await?;

// Stream steps
let (step_rx, result_rx) = actor_ref.run_task_streaming(task).await?;
while let Some(step) = step_rx.recv().await {
    println!("Step: {:?}", step);
}

// Stop actor
actor_ref.stop()?;

Execution Patterns

ChainAgent

Sequential execution where each agent’s output becomes the next agent’s input.
let chain = ChainAgent::new()
    .add("researcher", researcher_agent)
    .add("writer", writer_agent)
    .add("editor", editor_agent)
    .with_transform(|prev_output, next_name| {
        format!("Previous: {}\n\nTask for {}: continue", prev_output, next_name)
    })
    .with_continue_on_error(false)
    .with_verbose(true);

let result = chain.run("Write an article about Rust").await?;
add
fn(name, Arc<ReActAgent>)
Adds a ReAct agent to the chain
add_llm
fn(name, Arc<LLMAgent>)
Adds a simple LLM agent to the chain
with_transform
Fn(&str, &str) -> String
Transform function to adapt output for next agent
with_continue_on_error
bool
default:"false"
Whether to continue chain if a step fails

ParallelAgent

Concurrent execution with result aggregation.
let parallel = ParallelAgent::new()
    .add("expert1", expert1_agent)
    .add("expert2", expert2_agent)
    .add("expert3", expert3_agent)
    .with_aggregation(AggregationStrategy::LLMSummarize(summarizer))
    .with_timeout_ms(30000);

let result = parallel.run("Analyze market trends").await?;
with_aggregation
AggregationStrategy
Strategy for combining results:
  • Concatenate: Simple concatenation
  • ConcatenateWithSeparator(String): Join with separator
  • FirstSuccess: Return first successful result
  • CollectAll: Return all as JSON array
  • Vote: Majority voting
  • LLMSummarize(Arc<LLMAgent>): LLM-based synthesis
  • Custom(Fn): Custom aggregation function
with_aggregate_on_partial_failure
bool
default:"true"
Aggregate even if some agents fail
with_timeout_ms
u64
Timeout for parallel execution
with_task_template
fn(agent_name, template)
Customize task for specific agent using {task} placeholder

MapReduceAgent

Map-Reduce pattern for decomposing and aggregating tasks.
let map_reduce = MapReduceAgent::new()
    .with_mapper(|input| {
        input.lines()
            .map(|line| format!("Process: {}", line))
            .collect()
    })
    .with_worker(worker_agent)
    .with_reducer(reducer_agent)
    .with_concurrency_limit(4);

let result = map_reduce.run("item1\nitem2\nitem3").await?;
with_mapper
Fn(&str) -> Vec<String>
required
Splits input into sub-tasks
with_worker
Arc<ReActAgent>
required
Agent to process each sub-task
with_reducer
Arc<ReActAgent>
required
Agent to aggregate results
with_concurrency_limit
usize
Maximum concurrent workers

AutoAgent

Intelligent agent that automatically selects execution strategy.
let auto_agent = AutoAgent::new(llm_agent.clone(), react_agent)
    .with_auto_mode(true);

// Automatically chooses Direct or ReAct mode based on task complexity
let result = auto_agent.run("What is 2+2?").await?; // Uses Direct
let result = auto_agent.run("Search and analyze data").await?; // Uses ReAct

Examples

Basic ReAct Agent

use mofa_foundation::react::{ReActAgent, ReActTool};
use async_trait::async_trait;
use std::sync::Arc;

// Define custom tool
struct SearchTool;

#[async_trait]
impl ReActTool for SearchTool {
    fn name(&self) -> &str { "search" }
    fn description(&self) -> &str { "Search for information" }
    
    async fn execute(&self, input: &str) -> Result<String, String> {
        Ok(format!("Results for: {}", input))
    }
}

// Create agent
let agent = ReActAgent::builder()
    .with_llm(llm_agent)
    .with_tool(Arc::new(SearchTool))
    .with_max_iterations(5)
    .build_async()
    .await?;

// Execute task
let result = agent.run("Find information about Rust").await?;
println!("Answer: {}", result.answer);

Chain Execution

use mofa_foundation::react::patterns::*;

let chain = chain_agents(vec![
    ("researcher", researcher),
    ("writer", writer),
    ("editor", editor),
]);

let result = chain.run("Create article").await?;
println!("Final: {}", result.final_output);

Parallel with Summarization

let parallel = parallel_agents_with_summarizer(
    vec![
        ("expert1", expert1),
        ("expert2", expert2),
    ],
    summarizer_llm,
);

let result = parallel.run("Analyze topic").await?;

Error Handling

All ReAct operations return LLMResult<T> which is an alias for Result<T, LLMError>.
match agent.run(task).await {
    Ok(result) if result.success => {
        println!("Success: {}", result.answer);
    }
    Ok(result) => {
        eprintln!("Failed: {:?}", result.error);
    }
    Err(e) => {
        eprintln!("Error: {}", e);
    }
}

Source Reference

  • Core implementation: ~/workspace/source/crates/mofa-foundation/src/react/core.rs
  • Actor model: ~/workspace/source/crates/mofa-foundation/src/react/actor.rs
  • Patterns: ~/workspace/source/crates/mofa-foundation/src/react/patterns.rs
  • Tools: ~/workspace/source/crates/mofa-foundation/src/react/tools.rs
  • Examples: ~/workspace/source/examples/react_agent/src/main.rs

Build docs developers (and LLMs) love