ReAct Reasoning Loop
The ReActAgent in Agent Substrate implements an autonomous Reason + Act decision cycle engineered for durable execution, strict tool validation, budget boundaries, and crash recovery.
How the Loop Works
In each step of the reasoning loop:
- Context Window Assembly: The agent compiles the recent turn history, active system instructions, and available tool schemas into a compacted prompt.
- Thought & Action Generation: The model reasons through the prompt and decides whether to output a final answer or call one or more tools.
- Tripwire & Middleware Inspection: Any proposed tool call passes through
MiddlewarePipelinestages (such as PII masking, schema validation, and rate limiting) and human approval gates if required. - Durable Execution & WAL Journaling: Tools execute with declared idempotency and risk levels (
ToolRisk.SAFE,HIGH,CRITICAL). All invocations and results are recorded in the append-only write-ahead log. - Observation Evaluation: The model receives the tool results formatted into
ToolResultBlockentries and proceeds to the next reasoning step.
Basic Configuration
Agents are pure definitions: a name, a model, instructions, and tools. They execute durably on a Runtime:
import asyncio
from substrate.agents import ReActAgent
from substrate.runtime import Runtime
from substrate.tools import ToolRisk, tool
from substrate.types import ExecutionBudget
@tool(risk=ToolRisk.SAFE, idempotent=True)
def calculate_tax(state: str, amount: float) -> float:
"""Compute state tax rate for an invoice.
Args:
state: Two-letter state code (e.g. CA, NY).
amount: Order total before tax.
"""
rate = 0.0825 if state == "CA" else 0.05
return round(amount * rate, 2)
async def main() -> None:
agent = ReActAgent(
"tax_assistant",
model=my_chat_model,
tools=[calculate_tax],
system_instructions="You are a concise financial calculation assistant.",
max_iterations=10,
execution_budget=ExecutionBudget(max_cost_usd=1.50, max_tokens=25_000),
)
# Runtime.open opens the local store in ./.substrate (or PostgreSQL in production)
async with Runtime.open("./.substrate") as runtime:
outcome = await runtime.run(agent, "Calculate CA tax for a $4,500 order.")
print(f"Status: {outcome.status.value}")
print(f"Response: {outcome.output}")
if __name__ == "__main__":
asyncio.run(main())Agent Constructor Parameters
| Parameter | Type | Description |
|---|---|---|
name | str | Unique actor identifier for this agent type. |
model | ChatModel | Any provider implementing the ChatModel protocol (OpenAI, Anthropic, Gemini, local). |
tools | list[Tool] | ToolRegistry | Callable functions decorated with @tool or classes implementing Tool. |
system_instructions | str | Static instructions injected into the system prompt. |
max_iterations | int | Maximum reasoning loop turns before terminating (default: 10). |
execution_budget | ExecutionBudget | Hard cost and token ceiling (max_cost_usd, max_tokens). |
approval_handler | ApprovalHandler | Handler invoked when a tool call meets or exceeds approval_required_risk. |
approval_required_risk | ToolRisk | Risk threshold requiring human sign-off (SAFE, LOW, MEDIUM, HIGH, CRITICAL). |
middleware | MiddlewarePipeline | Pipeline dispatched at TURN, CHAT, and TOOL stages. |
context | ContextConfig | Compaction strategy (such as SlidingWindowCompaction). |
Step Backtracking & Error Recovery
If a tool throws a connection error or returns invalid data:
- Observation Feeding: The exception is caught and formatted as an error observation rather than terminating the run.
- Self-Correction: The model reflects on the failure output and selects an alternative strategy (e.g., retrying with adjusted parameters or falling back to another tool).
- Crash Recovery: If the host process is killed midway through a tool execution, non-idempotent actions remain protected: upon restart, the runtime checks the journal and avoids duplicate external writes.
- Budget Exceeded Protection: If the agent consumes more tokens or dollars than allotted by
ExecutionBudget, the runtime halts immediately withBudgetExhaustedError.
Was this page helpful?