yaab.multiagent¶
Multi-agent composition: Sequential, Parallel, Map, Loop, Swarm.
yaab.multiagent ¶
Multi-agent orchestration patterns over the one runtime.
These workflow agents compose other agents and expose the same run /
run_sync / as_tool surface as a plain :class:~yaab.agent.Agent, so they
nest arbitrarily and drop into tools, graphs, and servers:
- :class:
SequentialAgent— run sub-agents in order, sharing one state; - :class:
ParallelAgent— run sub-agents concurrently on the same input; - :class:
MapAgent— fan one sub-agent across many inputs; - :class:
LoopAgent— re-run a sub-agent until a condition or a cap; - :class:
Swarm— autonomous hand-off between peer agents; - :class:
RouterAgent— run exactly one of N branches (exclusive choice); - :class:
~yaab.flow.Flow— explicit branching, cycles, fan-out/merge, and durable human pauses, when the control flow itself must be inspectable.
Every pattern shares one :class:~yaab.state.State object across all its
children for a run, so a value written by one step is read by the next by key.
A step can declare writes="key" to capture its (typed) output into that
shared state; the next step reads it via {key} instruction injection or a
tool. Usage is rolled up across all sub-agents so cost/token accounting stays
whole.
Any unit in a pattern may be wrapped in a :class:~yaab.conditions.Step to add
when= (an input guard — do I run?), stop= (an output guard — does the
pattern stop?), or else_= (a fallback unit on skip or failure). The same
three keywords behave identically on every pattern, because every pattern runs
its children through one guarded-execution seam (:func:_run_guarded).
Branch
dataclass
¶
One guarded branch of a :class:~yaab.multiagent.RouterAgent.
when is the input-guard form of a :class:Condition (a Condition, a
(input, ctx) -> bool callable, or an expression string over
input/state/deps). Branches are evaluated in declared order; the
first whose guard is true is the only agent run.
Step
dataclass
¶
The uniform carrier of conditional metadata for any unit.
Wraps an :class:~yaab.agent.Agent, a workflow agent, a tool, or a
sub-agent with optional when= (input guard), stop= (output guard),
else_= (fallback unit on skip or failure), writes= (capture the
unit's output into shared state under a key), and a per-step timeout.
A bare unit in a list is implicitly Step(unit) with no guards, which is
what keeps every existing plain [agent_a, agent_b] list working.
SequentialAgent ¶
Bases: _WorkflowBase
Run sub-agents in sequence over one shared state.
Each child runs against the same :class:~yaab.state.State, so a child that
declares writes="key" lands its output where the next child can read it
(by {key} injection or a tool). pipe_output (default True) keeps
the classic convenience of feeding the prior step's text as the next prompt.
Any child may be a :class:~yaab.conditions.Step with when= (skip the
step), else_= (run a fallback instead), or writes=. The agent-level
stop= is an output guard checked after each step; stop_when= /
early_stop= are deprecated aliases that emit a DeprecationWarning.
ParallelAgent ¶
Bases: _WorkflowBase
Run sub-agents concurrently on the same prompt over one shared state.
All branches see the same state object. Each branch that declares
writes="key" lands its result under a key downstream steps read by name —
two branches writing the same session-scoped key without a reducer is a
declared :class:~yaab.state.StateConflictError, not a silent clobber. The
returned output is a name -> result map.
A branch may be a :class:~yaab.conditions.Step with when=: its guard is
evaluated against the shared input before scheduling, so an excluded branch
never runs and is, by default, absent from the result map (the map stays
a clean name -> output). Set include_skipped=True to add
name -> RunResult(status="skipped") entries for a total map.
MapAgent ¶
Bases: _WorkflowBase
Fan one sub-agent out across many inputs concurrently over one shared state.
Given a list of prompts (or a function that derives them from the incoming
prompt), run the same agent on each in parallel and return the list of
outputs. max_concurrency bounds simultaneous runs. The children share the
run's state object and its session_id so they replay the same session.
Wrap the mapped agent in a :class:~yaab.conditions.Step with when= to
filter inputs: each derived input whose guard is false is dropped (absent
from the results), so when="len(input) > 0" is the per-input filter.
LoopAgent ¶
Bases: _WorkflowBase
Re-run a sub-agent over one accumulating shared state until a stop condition.
Each iteration runs against the same :class:~yaab.state.State, so a tool
that increments state["count"] accumulates across iterations. stop= is
the output guard (state-aware: stop="state.score >= 0.9" reads what each
iteration wrote); the loop also stops at max_iterations. until= is a
deprecated alias of stop=. else_= runs when the cap is hit without
stop= firing. pipe_output (default True) feeds the prior output
as the next prompt for the common refine-in-place case.
SwarmState ¶
Bases: BaseModel
Shared, mutable state threaded through a swarm via DI.
shared is kept for backward compatibility; under the unified model the
swarm's structured state lives on the run's one :class:~yaab.state.State,
and the handoff target is an ordinary run-local state write — not a fourth
backing store.
Swarm ¶
Bases: _WorkflowBase
Autonomous hand-off between peer agents (swarm) over one shared state.
Each member is augmented with handoff_to_<peer> tools. When an agent
decides another is better suited, it calls the handoff tool; the swarm then
continues the task with that agent. Every member runs against the same
:class:~yaab.state.State, and the handoff target is recorded as a run-local
state write (temp:__handoff__) rather than a magic attribute on a separate
object. stop= is an output guard checked after each member runs (e.g.
stop="len(state['temp:__handoff_log__']) > 4" caps a handoff cycle).
Runs until no further hand-off (or a cap).
RouterAgent ¶
Bases: _WorkflowBase
Exclusive-choice routing — run exactly one of N branches.
A peer of Sequential/Parallel/Map/Loop/Swarm that evaluates input guards in declared order and runs exactly one branch (or the default). The routing decision spends zero model calls — it is a plain-Python/expression picker, so it is deterministic and fully auditable. Because exactly one branch runs and its output is returned unmodified, there is no skip-cascade and no merge node.
on_no_match is "default" (run the default branch) or "error"
(raise). A router with no matching branch and no default returns a result
with status "skipped". writes= captures the chosen branch's output
into shared state. Nests like every workflow agent and works as a tool.
from_picker
classmethod
¶
from_picker(name: str, picker: Callable[[Any, Any], str], to: dict[str, Any], *, default: Any = None, on_no_match: str = 'default', writes: str | None = None) -> RouterAgent
Build a router from a label-returning picker.
picker(input, ctx) -> label classifies the input; to maps each
label to an agent. A picker may only return a key present in to — an
unknown label raises immediately (a typo is a loud error, never a silent
fall-through to the default). The str-returning picker is adapted to
bool guards here, so each :class:Branch is always a boolean guard.