Skip to content

SubAgentSpec

SubAgentSpec — specification for a named subagent.

SubAgentSpec

Specification for a named subagent in a multi-agent system.

Each SubAgentSpec entry registers a build recipe for a subagent under a human-readable name. The name serves as the registry key on the parent coordinator and as the enum value the coordinator's UseSubAgentTool presents to the LLM for dispatch.

The spec is pure data: it never constructs or holds a live agent. UseSubAgentTool builds a fresh LLMAgent from builder on every dispatch. Whatever the builder was given directly (an LLM, Memory stores, MCPToolProvider instances) is reused as-is across builds — only the LLMAgent shell itself is rebuilt each time, mirroring the existing fresh-TaskHandler-per-run() pattern.

A plain class, not a pydantic BaseModel, for the same reason Skill/Memory/LLMAgentBuilder are plain classes: those are reserved for behavior-bearing objects that wrap a live collaborator, not passive serializable data. BaseModel would only have bought required-field validation (a plain __init__ already raises TypeError on missing required args) at the cost of needing arbitrary_types_allowed=True for builder, a non-pydantic type.

Attributes:

Name Type Description
name

Unique registry key for this subagent. Appears as an enum value in UseSubAgentTool's dispatch schema — must be human-readable and stable.

description

Short routing signal shown to the coordinator LLM in the <available_subagents> catalog. Should describe capability, not implementation.

builder

The recipe used to build this subagent fresh on every dispatch.

max_steps

Optional cap on the number of steps the subagent may take per dispatch. Passed to agent.run() to bound runaway executions. None uses the agent's own default.

skills_scopes

Optional scopes to scan for skills on this subagent's dispatches. Passed to agent.run(). None uses the agent's own default ([USER, PROJECT]).

explicit_only_skills

Optional skill names to exclude from this subagent's model catalog on dispatch. Passed to agent.run(). None uses the agent's own default (no exclusions).

Source code in src/llm_agents_from_scratch/subagents/spec.py
class SubAgentSpec:
    """Specification for a named subagent in a multi-agent system.

    Each ``SubAgentSpec`` entry registers a build recipe for a subagent
    under a human-readable name. The name serves as the registry key on
    the parent coordinator and as the enum value the coordinator's
    ``UseSubAgentTool`` presents to the LLM for dispatch.

    The spec is pure data: it never constructs or holds a live agent.
    ``UseSubAgentTool`` builds a fresh ``LLMAgent`` from ``builder`` on
    every dispatch. Whatever the builder was given directly (an
    ``LLM``, ``Memory`` stores, ``MCPToolProvider`` instances) is reused
    as-is across builds — only the ``LLMAgent`` shell itself is rebuilt
    each time, mirroring the existing fresh-``TaskHandler``-per-``run()``
    pattern.

    A plain class, not a pydantic ``BaseModel``, for the same reason
    ``Skill``/``Memory``/``LLMAgentBuilder`` are plain classes: those
    are reserved for behavior-bearing objects that wrap a live
    collaborator, not passive serializable data. ``BaseModel`` would
    only have bought required-field validation (a plain ``__init__``
    already raises ``TypeError`` on missing required args) at the cost
    of needing ``arbitrary_types_allowed=True`` for ``builder``, a
    non-pydantic type.

    Attributes:
        name: Unique registry key for this subagent. Appears as an enum
            value in ``UseSubAgentTool``'s dispatch schema — must be
            human-readable and stable.
        description: Short routing signal shown to the coordinator LLM in
            the ``<available_subagents>`` catalog. Should describe capability,
            not implementation.
        builder: The recipe used to build this subagent fresh on every
            dispatch.
        max_steps: Optional cap on the number of steps the subagent may
            take per dispatch. Passed to ``agent.run()`` to bound runaway
            executions. ``None`` uses the agent's own default.
        skills_scopes: Optional scopes to scan for skills on this
            subagent's dispatches. Passed to ``agent.run()``. ``None``
            uses the agent's own default (``[USER, PROJECT]``).
        explicit_only_skills: Optional skill names to exclude from this
            subagent's model catalog on dispatch. Passed to
            ``agent.run()``. ``None`` uses the agent's own default (no
            exclusions).
    """

    def __init__(  # noqa: PLR0913, PLR0917
        self,
        name: str,
        description: str,
        builder: LLMAgentBuilder,
        max_steps: int | None = None,
        skills_scopes: list[SkillScope] | None = None,
        explicit_only_skills: set[str] | None = None,
    ) -> None:
        """Initialize a SubAgentSpec.

        Args:
            name: Unique registry key for this subagent.
            description: Routing signal shown to the coordinator LLM in
                the ``<available_subagents>`` catalog.
            builder: The recipe used to build this subagent per
                dispatch.
            max_steps: Optional cap on subagent steps per dispatch.
                Defaults to ``None`` (agent's own default).
            skills_scopes: Optional scopes to scan for skills on this
                subagent's dispatches. Defaults to ``None`` (agent's
                own default).
            explicit_only_skills: Optional skill names to exclude from
                this subagent's model catalog on dispatch. Defaults to
                ``None`` (agent's own default).
        """
        self.name = name
        self.description = description
        self.builder = builder
        self.max_steps = max_steps
        self.skills_scopes = skills_scopes
        self.explicit_only_skills = explicit_only_skills

    def __repr__(self) -> str:
        """Readable repr listing every field."""
        return (
            f"{type(self).__name__}("
            f"name={self.name!r}, "
            f"description={self.description!r}, "
            f"builder={self.builder!r}, "
            f"max_steps={self.max_steps!r}, "
            f"skills_scopes={self.skills_scopes!r}, "
            f"explicit_only_skills={self.explicit_only_skills!r})"
        )

    def catalog(self) -> str:
        """Return XML entry for this spec in the subagents catalog."""
        return CATALOG_SPEC_TEMPLATE.format(
            name=self.name,
            description=self.description,
        )

__init__

__init__(
    name,
    description,
    builder,
    max_steps=None,
    skills_scopes=None,
    explicit_only_skills=None,
)

Initialize a SubAgentSpec.

Parameters:

Name Type Description Default
name str

Unique registry key for this subagent.

required
description str

Routing signal shown to the coordinator LLM in the <available_subagents> catalog.

required
builder LLMAgentBuilder

The recipe used to build this subagent per dispatch.

required
max_steps int | None

Optional cap on subagent steps per dispatch. Defaults to None (agent's own default).

None
skills_scopes list[SkillScope] | None

Optional scopes to scan for skills on this subagent's dispatches. Defaults to None (agent's own default).

None
explicit_only_skills set[str] | None

Optional skill names to exclude from this subagent's model catalog on dispatch. Defaults to None (agent's own default).

None
Source code in src/llm_agents_from_scratch/subagents/spec.py
def __init__(  # noqa: PLR0913, PLR0917
    self,
    name: str,
    description: str,
    builder: LLMAgentBuilder,
    max_steps: int | None = None,
    skills_scopes: list[SkillScope] | None = None,
    explicit_only_skills: set[str] | None = None,
) -> None:
    """Initialize a SubAgentSpec.

    Args:
        name: Unique registry key for this subagent.
        description: Routing signal shown to the coordinator LLM in
            the ``<available_subagents>`` catalog.
        builder: The recipe used to build this subagent per
            dispatch.
        max_steps: Optional cap on subagent steps per dispatch.
            Defaults to ``None`` (agent's own default).
        skills_scopes: Optional scopes to scan for skills on this
            subagent's dispatches. Defaults to ``None`` (agent's
            own default).
        explicit_only_skills: Optional skill names to exclude from
            this subagent's model catalog on dispatch. Defaults to
            ``None`` (agent's own default).
    """
    self.name = name
    self.description = description
    self.builder = builder
    self.max_steps = max_steps
    self.skills_scopes = skills_scopes
    self.explicit_only_skills = explicit_only_skills

__repr__

__repr__()

Readable repr listing every field.

Source code in src/llm_agents_from_scratch/subagents/spec.py
def __repr__(self) -> str:
    """Readable repr listing every field."""
    return (
        f"{type(self).__name__}("
        f"name={self.name!r}, "
        f"description={self.description!r}, "
        f"builder={self.builder!r}, "
        f"max_steps={self.max_steps!r}, "
        f"skills_scopes={self.skills_scopes!r}, "
        f"explicit_only_skills={self.explicit_only_skills!r})"
    )

catalog

catalog()

Return XML entry for this spec in the subagents catalog.

Source code in src/llm_agents_from_scratch/subagents/spec.py
def catalog(self) -> str:
    """Return XML entry for this spec in the subagents catalog."""
    return CATALOG_SPEC_TEMPLATE.format(
        name=self.name,
        description=self.description,
    )