Skip to content

Add SubAgent capability for agent-to-agent task delegation#178

Open
DouweM wants to merge 3 commits into
mainfrom
capability/subagent
Open

Add SubAgent capability for agent-to-agent task delegation#178
DouweM wants to merge 3 commits into
mainfrom
capability/subagent

Conversation

@DouweM

@DouweM DouweM commented Apr 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds SubAgent capability (AbstractCapability subclass) that lets a parent agent delegate tasks to named sub-agents via a delegate_task tool
  • Sub-agent descriptions are injected into the system prompt (derived from agent.description, agent.name, or explicit descriptions dict)
  • Parent deps are forwarded to sub-agents by default (pass_deps=True), configurable
  • Unknown agent names trigger ModelRetry for self-correction
  • 100% test coverage (20 tests), passes lint, format, and strict pyright

Closes #32

Test plan

  • Construction: capability is AbstractCapability, descriptions resolve from agent metadata or explicit overrides, empty agents returns None for instructions/toolset
  • Instructions: system prompt lists all agents with descriptions
  • Toolset: delegate_task tool is registered with agent names in description
  • End-to-end delegation: parent delegates to sub-agent and gets result, unknown agent triggers retry then succeeds, deps forwarded when pass_deps=True, deps are None when pass_deps=False, multiple agents called sequentially
  • Imports: SubAgent importable from pydantic_harness package

Note: Tests require pydantic-ai-slim with capabilities support (the capabilities branch). The pyproject.toml uses the PyPI dependency; for local development, override with uv add --dev pydantic-ai-slim@/path/to/capabilities-branch/pydantic_ai_slim.

🤖 Generated with Claude Code

DouweM and others added 3 commits April 2, 2026 05:28
Implements the Agent-as-Tool pattern: a parent agent can delegate tasks
to named sub-agents via a `delegate_task` tool that blocks until the
sub-agent finishes and returns its text output as the tool result.

Refs #32

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Pydantic AI uses asyncio.gather in capabilities/combined.py which is
incompatible with the Trio event loop. Override the anyio_backend fixture
to asyncio-only, switch from pytest-asyncio to pytest-anyio, and update
the lock file.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements the three audit findings for SubAgent:

- `share_history: bool = False` parameter: when True, passes the parent's
  message history (minus pending tool calls) to sub-agent runs via
  `message_history`, giving sub-agents full conversation context.

- `delegate_tasks` tool for parallel delegation: accepts a list of
  `{"agent": "name", "task": "prompt"}` dicts and runs them concurrently
  via `asyncio.gather`, returning results in input order.

- Structured output preservation: sub-agent outputs that are Pydantic
  models get `model_dump_json()`, dicts/lists get `json.dumps()`, and
  other non-str types get `repr()` instead of generic `str()`.

Also widens `agents` dict type from `Agent[Any]` to `Agent[Any, Any]`
to accept sub-agents with any output type.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 6 additional findings.

Open in Devin Review

@DouweM

DouweM commented Apr 10, 2026

Copy link
Copy Markdown
Contributor Author

Originally posted by @DouweM in #138 comment (PR was recreated)

Note: Sub-agent runs are currently opaque (parent sees only final text result). Real-time progress streaming from sub-agents to the parent's event stream requires #146 (sub-agent event propagation).

@DouweM

DouweM commented Apr 10, 2026

Copy link
Copy Markdown
Contributor Author

Originally posted by @DouweM in #138 comment (PR was recreated)

Audit vs prior art: SubAgent

Worth adding now:

  • share_history: bool = False to pass parent message history to sub-agent
  • delegate_tasks (plural) for parallel delegation via asyncio.gather
  • Preserve structured output type instead of always stringifying

Follow-up opportunities:

@DouweM

DouweM commented Apr 10, 2026

Copy link
Copy Markdown
Contributor Author

Originally posted by @adtyavrdhn in #138 comment (PR was recreated)

Recursion prevention -- there's nothing stopping a sub-agent from having its own SubAgent capability and recursing indefinitely. Hermes does this two ways: a MAX_DEPTH=2 counter on _delegate_depth, and stripping delegate_task from child toolsets entirely. We should do at least one of these.

Concurrency limits -- delegate_tasks does an unbounded asyncio.gather. Hermes caps at 3 (ThreadPoolExecutor(max_workers=3)), pi caps at 4 concurrent / 8 total. We should add a semaphore or a max_concurrency parameter.

Timeouts / turn limits -- a hung sub-agent blocks the parent forever. Hermes uses an iteration budget (default 50). Mastra passes maxSteps per sub-agent and supports abortSignal. We should at minimum pass usage_limits through to agent.run().

Tool filtering -- hermes intersects child toolsets with the parent's and strips dangerous tools (delegate_task, clarify, memory). We pass the agent as-is with whatever it was constructed with. Worth considering whether we need a blocked_tools or privilege-scoping mechanism.

Result metadata -- we return a single string. Hermes returns JSON with status, duration_seconds, tokens, exit_reason, tool_trace. Pi returns exitCode, full messages, usage stats. Mastra returns {text, subAgentToolResults[]}. Richer results would help the parent agent reason about what happened.

Streaming / progress -- all three (hermes, pi, mastra) stream intermediate progress from sub-agents. We're opaque until completion. Not a blocker but worth tracking.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SubAgent / Agent-as-Tool capability

2 participants