Skip to main content
A single agent with many tools quickly becomes a monolith. Motus gives you two primitives for breaking it apart:
  • agent.as_tool() turns a specialist agent into a tool that a supervisor can call.
  • agent.fork() makes an independent copy of an agent at its current conversation state so you can explore branches without touching the original.
They compose freely. A supervisor can hold several specialists as tools, and you can fork any of them whenever you want an isolated branch.

Agent as tool

as_tool() is the core building block for supervisor and specialist patterns. Wrap any agent and hand the result to another agent’s tools=[...] list.
The supervisor sees a normal tool with one parameter, request: str. When the LLM decides to call it, Motus forwards the string to the specialist, runs the specialist’s full agent loop, and returns the result to the supervisor as if it were any other tool output.
Always pass a real description. The default is "Delegate to sub-agent: <name>", which tells the supervisor’s LLM almost nothing about when to use the tool. A sentence or two about what the specialist is for, what input it expects, and what it returns makes a big difference in tool selection quality.

Parameter reference

All parameters are keyword-only.

Stateful vs. stateless

The stateful flag controls whether the specialist’s conversation history accumulates across tool calls within a single supervisor run.
Each call forks the specialist, so every invocation starts from the specialist’s original state (system prompt plus any pre-loaded messages). The fork is a lightweight memory copy, not a model call. Concurrent invocations never share memory, which makes this safe for fan-out.

Output extractors

By default the specialist’s result is passed back as-is; non-string results are JSON-encoded before reaching the supervisor. output_extractor runs on the specialist’s return value first. Two common uses:
  • Pull a field out of a structured result. If the specialist returns a Pydantic model, return just the field the supervisor actually needs.
  • Shrink a long answer. The specialist may produce pages of analysis; the supervisor may only want the headline.
The extractor runs after the specialist finishes and before the supervisor sees the result. Raising inside it aborts the tool call like any other exception.

Errors

When the specialist raises, the exception is caught at the tool boundary and surfaced to the supervisor as a regular tool error ({"error": "..."}). The supervisor’s model sees the failure as feedback and can retry with a different input or give up, the same way it handles any other tool exception.

Forking

agent.fork() makes an independent copy of the agent with the same configuration and a forked copy of the conversation. Changes to the fork’s memory never affect the original.
The fork carries over every __init__ argument (client, model, system prompt, tools, guardrails, response_format, reasoning, timeout, cache_policy, step_callback, and so on) and gets its own independent copy of the conversation memory. Forking is useful for A/B comparisons, self-consistency sampling, and any case where you want to branch from a known conversation state without mutating the original. If you just want a fresh empty agent, instantiate a new ReActAgent rather than forking; fork’s job is preserving the accumulated conversation.

Where to go next

Agents

Everything a ReActAgent can do on its own: tools, memory, guardrails, reasoning.

Workflow

Deep dive on @agent_task, the task graph, and parallel execution.

Tools

How the @tool decorator and guardrails work under the hood.

Tracing

Debugging multi-agent flows is much easier with tracing on.