Groups¶
Declarative multi-agent topologies. AgentGroup holds a frozen DAG of
agents connected by Edge objects; the runner is invoked through
AgentRuntime.run_group().
AgentGroup¶
AgentGroup
dataclass
¶
AgentGroup(name: str, topology: Mapping[Agent, EdgeOrEdges] = dict())
A named DAG of agents.
crew = AgentGroup( ... name="research", ... topology={ ... head: Edge(to=(minion,), mapper=head_to_minions), ... minion: Edge(to=(synthesizer,), mapper=minions_to_synth), ... synthesizer: Edge.terminal(), ... }, ... )
Multiple outgoing edges with mutually-exclusive conditions enable branch routing:
crew = AgentGroup( ... name="ticket_router", ... topology={ ... triage: ( ... Edge(to=(quick_replier,), condition=lambda o: o.severity == "low"), ... Edge(to=(escalator,), condition=lambda o: o.severity == "high"), ... ), ... quick_replier: Edge.terminal(), ... escalator: Edge.terminal(), ... }, ... )
name
instance-attribute
¶
Stable identifier — used as the registry key, the broker topic suffix
for group-level events, and the agent_name field on
:data:EventType.GROUP_* events.
topology
class-attribute
instance-attribute
¶
topology: Mapping[Agent, EdgeOrEdges] = field(default_factory=dict)
The DAG. Keys are :class:Agent instances; each value is one
:class:Edge (most common) or a tuple of edges for branch routing
with :attr:Edge.condition predicates. Validated at construction —
cycles, dangling refs, and missing entry / terminal nodes raise
:class:TopologyError.
outgoing_edges
¶
entry_nodes
¶
entry_nodes() -> tuple[Agent, ...]
Nodes with no incoming edges.
Source code in src/murmur/groups/spec.py
terminal_nodes
¶
terminal_nodes() -> tuple[Agent, ...]
Nodes with no outgoing edges (every outgoing edge has empty to).
Source code in src/murmur/groups/spec.py
topological_order
¶
topological_order() -> tuple[Agent, ...]
Kahn's algorithm — order in which to walk the DAG.
Source code in src/murmur/groups/spec.py
topological_tiers
¶
topological_tiers() -> tuple[tuple[Agent, ...], ...]
Topological order grouped into dependency tiers.
Each tier contains nodes whose dependencies are all in earlier
tiers. Within one tier the nodes are pairwise independent and
therefore safe to dispatch in parallel. Tier order across the
result preserves the DAG's dependency direction — tier i+1
is only reachable once every node in tier i has produced
a result. Topology declaration order is preserved within each
tier so the contract is "results stored, terminal returned"
rather than "node X ran before node Y".
Source code in src/murmur/groups/spec.py
Edge¶
Edge
dataclass
¶
Edge(
to: tuple[Agent, ...] = (),
mapper: EdgeMapper | None = None,
max_concurrency: int = 100,
condition: EdgeCondition | None = None,
)
A frozen value object describing one outgoing connection.
to
class-attribute
instance-attribute
¶
to: tuple[Agent, ...] = ()
Downstream agents. Empty tuple == terminal node.
mapper
class-attribute
instance-attribute
¶
mapper: EdgeMapper | None = None
Optional (upstream_output) -> TaskSpec | list[TaskSpec] transform.
max_concurrency
class-attribute
instance-attribute
¶
Width cap when this edge fans out.
condition
class-attribute
instance-attribute
¶
Optional predicate over the upstream's typed output.
None means "always fire". When set, the runner evaluates the
callable with the upstream output (same value the mapper would
receive) and only traverses to to agents when it returns truthy.
Async callables are awaited.
EdgeMapper¶
EdgeMapper
module-attribute
¶
Return TaskSpec for single dispatch, list[TaskSpec] for fan-out.
FanOut¶
FanOut
module-attribute
¶
Type annotation marking a Pydantic field as the fan-out target.
Use as FanOut[list[T]] on the field that holds the items the group
runner should split over. The runner will spawn one downstream agent per
item when no explicit mapper is set on the edge.
class DecompositionResult(BaseModel): ... sub_questions: FanOut[list[SubQuestion]] ... reasoning: str = ""
Constraints (enforced by :func:murmur.groups.get_fan_out_field):
- The annotated type must be
list[T]. Not tuple, not set. - Only one field per model may carry the marker.
get_fan_out_field¶
get_fan_out_field
¶
Return (field_name, item_types) for the model's fan-out field.
Returns None if the model has no :data:FanOut-annotated field.
item_types is always a tuple — length 1 for FanOut[list[T]]
and length N for FanOut[list[T1 | T2 | ... | TN]].
Raises :class:SpecValidationError if more than one field carries
the marker, or if the annotated type is not list[T].