ReAct Loop

ReAct stands for Reasoning + Acting. The agent thinks about its current state, chooses an action, observes the result, and repeats until it completes the objective or hits a limit.


Iteration Lifecycle

Each iteration follows a strict cycle:

sequenceDiagram participant Engine participant LLM participant Tool Engine->>LLM: System prompt + history + "What next?" LLM->>Engine: { thought, action, action_input } Engine->>Engine: Validate action is in tool allowlist Engine->>Tool: Execute action with action_input Tool->>Engine: Observation (result) Engine->>Engine: Append to history, check limits Note over Engine: Repeat until __complete__<br/>or max_iterations reached

Step 1 — Prompt. The engine constructs a prompt containing the objective, available tools, and the history of all previous iterations.

Step 2 — Think + Decide. The LLM responds with a structured JSON object: what it's thinking, what action to take, and with what parameters.

Step 3 — Validate. The engine checks that the chosen action is in the declared tool list. Undeclared actions are rejected.

Step 4 — Execute. The engine runs the tool and captures the result as an observation.

Step 5 — Accumulate. The thought, action, and observation are appended to the iteration history. The engine checks whether to continue, stop, or trigger stuck detection.


LLM Response Format

The agent must respond with this JSON structure on every iteration:

json
{
  "thought": "I need to look up the customer's order history before I can assess the refund request.",
  "action": "get_order_history",
  "action_input": {
    "customer_id": "cust-12345",
    "limit": 10
  }
}
Field Type Description
thought string The agent's reasoning — what it's considering, what it knows, what it needs
action string The tool to call — must be in the declared tools list
action_input object Parameters for the tool call

The thought field is critical for auditability. It captures why the agent chose this action, not just what it did. Every thought is stored in the reasoning trace.


Prompt Construction

The engine constructs the LLM prompt from three parts:

System Prompt

Sets the agent's role, objective, and available tools:

text
You are an AI agent. Your objective is:
{objective}

You have access to these tools:
- lookup_ticket: Look up a support ticket by ID
- analyze_sentiment: Analyze text sentiment and urgency
- slack_post: Post a message to a Slack channel
- __complete__: Signal that you have finished the task
- __pause_for_human__: Request human input

On each turn, respond with a JSON object containing:
- "thought": your reasoning about what to do next
- "action": the tool to use
- "action_input": the parameters for the tool

When you have completed the task, use __complete__ with your final answer.

Each tool's description and parameter schema are included so the agent knows what each tool does and what parameters it expects.

Iteration History

Every previous iteration is appended as context:

text
Iteration 1:
  Thought: "I need to look up ticket #12345 to understand the issue."
  Action: lookup_ticket
  Input: { "ticket_id": "12345" }
  Observation: { "subject": "Billing Error", "status": "open", "priority": "high" }

Iteration 2:
  Thought: "The ticket is about a billing error. Let me analyze the sentiment."
  Action: analyze_sentiment
  Input: { "text": "I've been charged twice for my subscription..." }
  Observation: { "sentiment": "negative", "urgency": "high" }

Current Turn Prompt

After the history, the engine asks: "Given the above, what is your next step?"


Context Accumulation

As iterations progress, the agent builds up knowledge:

Iteration What the Agent Knows
1 Objective only
2 Objective + first tool result
3 Objective + two tool results + its own reasoning about them
N Full history of thoughts, actions, and observations

Each observation becomes available for the agent to reference in subsequent reasoning. The agent effectively has a growing "working memory" of everything it has done and learned.


Token Management

The iteration history grows with each cycle. For long-running agents, this can approach the LLM's context window limit. Hyphen manages this by:

  • Including the full history up to the context window limit
  • Summarizing older iterations when the history exceeds available tokens
  • Prioritizing recent iterations and the original objective

Set max_iterations conservatively to prevent excessive token consumption. Most tasks complete in 5–10 iterations. Complex orchestration tasks may need 15–20.


Termination Conditions

The loop ends when any of these occur:

Condition Result
Agent calls __complete__ Success — the agent's answer is stored in context
max_iterations reached Failure — timeout, no answer produced
timeout_ms exceeded Failure — execution time limit
Agent calls __pause_for_human__ Paused — waiting for human input
Stuck detection triggers with action: "fail" Failure — agent was looping
Stuck detection triggers with action: "escalate" Paused — routed to human

Configuration Reference

json
{
  "type": "loop",
  "properties": {
    "mode": "react",
    "objective": "Your task description here",
    "tools": [
      { "type": "action", "name": "tool_a" },
      { "type": "action", "name": "tool_b" }
    ],
    "model": "gpt-4",
    "max_iterations": 10,
    "timeout_ms": 300000,
    "temperature": 0.7,
    "on_stuck": {
      "iterations": 3,
      "action": "escalate"
    },
    "include_reasoning_trace": true,
    "result_key": "agentResult"
  }
}

Implicit tools are auto-injected. Built-in tools like __complete__, __pause_for_human__, __store_memory__, __retrieve_memory__, and __log_progress__ are always available — you don't need to declare them. Only declare your custom action and workflow tools. See Tool Declarations for the full format reference.

→ Next: [Built-in Tools](/agents/built-in-tools)