ReWOO
from loomflow.architecture import ReWOO, ReWOOPlan, ReWOOStep, ReWOOStepResultReasoning WithOut Observation. Xu et al. 2023 ,
ReWOO: Decoupling Reasoning from Observations for Efficient
Augmented Language Models. The cost-saving sibling of
PlanAndExecute. Each
plan step is a real tool call with {{En}} placeholder
substitution, and independent steps run in parallel.
For the conceptual page see ReWOO.
Class signature
class ReWOO:
name: str = "rewoo"
def __init__(
self,
*,
max_steps: int = 8,
planner_prompt: str | None = None,
solver_prompt: str | None = None,
parallel_levels: bool = True,
) -> None: ...Constructor parameters
max_steps
| Type | int |
| Default | 8 |
Hard cap on plan length. Truncates to max_steps if the planner
emits more. Must be >= 1.
planner_prompt
| Type | str | None |
| Default | None (uses built-in default) |
Override the planner’s system prompt. The default teaches the model
to emit a JSON list of steps, each with id, tool, args, and
optional depends_on references via {{En}} placeholders.
solver_prompt
| Type | str | None |
| Default | None (uses built-in default) |
Override the final solver/synthesizer prompt. The default combines the original prompt + the tool results into the final answer.
parallel_levels
| Type | bool |
| Default | True |
When True (default), independent steps in the same dependency level
run concurrently via anyio.create_task_group. Set to False for
deterministic test ordering or when your tool provider has tight
rate limits and parallel calls trigger throttling.
Methods
declared_workers
Returns {}. Single-agent.
run
Three-phase loop:
- Planner. ONE LLM call. Output is a JSON list parsed into a
ReWOOPlan. Each step hasid,tool,args,depends_on. Args may reference prior steps via{{En}}placeholders. Emitsrewoo.planner_started/rewoo.plan_created. - Topological executor. Computes dependency levels; each level
runs in parallel (when
parallel_levels=True). Cyclic plans abort withrewoo.cycle_detectedandsession.output = "Planner produced a plan with cyclic dependencies; cannot execute.". - Solver. ONE LLM call combining the original prompt + the tool results into the final answer.
Related types
ReWOOStep
class ReWOOStep(BaseModel):
id: str # "E1", "E2", ...
tool: str # tool name to call
args: dict[str, Any] # args; may contain {{En}} placeholders
depends_on: list[str] = [] # ids this step depends onOne step in the plan. The framework does textual substitution of
{{En}} placeholders with the prior step’s output before running
the tool. For complex data flows (extract a URL out of a JSON blob),
have the model emit a more structured plan upfront, or fall back to
ReAct.
ReWOOPlan
class ReWOOPlan(BaseModel):
steps: list[ReWOOStep]The planner’s output. Stored on session.metadata["plan"].
ReWOOStepResult
class ReWOOStepResult(BaseModel):
step: ReWOOStep
output: str
error: str | None = NoneThe result of one tool call. error is non-None when the tool
raised; downstream steps that reference the failing step’s output
see the error string substituted in.
Cost model
Total: 2 LLM calls + N tool calls. ReAct on the same task needs roughly N+1 LLM calls (one per turn). For tool-heavy workloads where the planner can predict the call sequence upfront, ReWOO is 30–50% cheaper.
When to fall back to ReAct
- The plan depends on tool output you can’t predict. If a search result reshapes the next call, ReAct’s “think between each tool call” loop is needed.
- Failure recovery matters. ReWOO commits to the plan; a failed tool call doesn’t trigger replanning.
- Tools share state in surprising ways. Parallel-by-default
execution can break tools that aren’t idempotent across concurrent
calls. Set
parallel_levels=Falseor use ReAct.
Example
from loomflow import Agent, tool
from loomflow.architecture import ReWOO
@tool
async def search(query: str) -> str:
"""Web search."""
...
@tool
async def fetch(url: str) -> str:
"""Fetch a URL."""
...
agent = Agent(
"Research the topic and produce a summary with citations.",
model="claude-opus-4-7",
architecture=ReWOO(),
tools=[search, fetch],
)
result = await agent.run("Recent advances in agent harnesses.")Or by string:
agent = Agent("...", model="...", architecture="rewoo", tools=[search, fetch])Source
loomflow/architecture/rewoo.py
Placeholder substitution is textual. {{E1}} is replaced with
E1’s tool result string before the dependent tool runs. Use a
structured plan (or ReAct) for complex data flows.