A hierarchical multi-agent Deep Agent harness built on Pydantic AI
Project description
PydanTask: Deep Agentic Harness for Pydantic AI
PydanTask is a Harness for building deep, autonomous agents that don't just respond—they reason, decompose, and execute.
It builds on top of Pydantic AI and adds:
- Long‑horizon planning and hierarchical task management
- A reusable orchestration loop (
DeepAgent) with planner → supervisor → worker/researcher/producer → critic - Shared runtime state across agents (
RuntimeState) - Extensible capabilities via
CapabilityDescriptionand custom tools/agents
The goal is to give you a solid “agentic backbone” you can adapt, without having to reinvent multi‑step planning and control logic yourself.
For the most up‑to‑date implementation details and API docs, see the hosted documentation: PydanTask Documentation.
High-Level Architecture
The core orchestrator is DeepAgent:
from pydantask.agents.agent import DeepAgent
DeepAgent coordinates several built‑in agents:
- Planner – turns a single objective into a
Plan(list ofTaskItems). - Supervisor – chooses which tasks to run next, based on statuses and dependencies.
- Researcher – performs web/external research for tasks that need new information.
- Producer – synthesizes intermediate results into a final answer or artifact.
- Critic – evaluates task outputs and drives deterministic retry/fail transitions.
They all operate over a shared RuntimeState:
from pydantask.models import RuntimeState, TaskItem, TaskResult, Plan
Key concepts:
- Plan (
Plan):reasoning_steps: planner’s internal notestasks: list ofTaskIteminstances
- TaskItem: one sub‑task in the plan, with:
task_id,overall_objective,sub_task_objectivecapability(which sub‑agent to use, e.g."research_agent")sub_task_dependencies(other task IDs that must complete first)status(TaskStatus:PENDING,READY,RUNNING,NEEDS_REVIEW,COMPLETED,FAILED,ERRORED,RERUN)result(TaskResult) andtask_feedback(TaskQAResult)
- RuntimeState:
plan: Dict[int, TaskItem]objective: stragent_registry: Dict[str, CapabilityDescription]document_store,knowledge_store,runtime_steps, etc.
The control loop in DeepAgent.run():
- Planner creates a
Planfor the objective. RuntimeStateis initialized with the plan and capabilities.- In each cycle:
- Supervisor decides which tasks to execute next.
- Ready tasks (dependencies satisfied) are executed by the appropriate capability (sub‑agent).
- Critic reviews each result and calls
handle_critic_resultto update status and retry/failed state.
- Loop stops when the Supervisor sets
all_tasks_completed = Trueormax_stepsis reached.
For more detail, see docs/agents.md.
Installation & Setup
PydanTask assumes you already have Pydantic AI and an OpenAI‑compatible model configured. You’ll also need a Tavily API key for the built‑in research agent.
1. Install dependencies
From your project root:
pip install -e .
(or however you manage your environment; if you use Poetry, adjust accordingly.)
2. Environment variables
Set the following environment variables (e.g. in your shell or a .env file):
OPENAI_API_KEY– for the underlying OpenAIChatModel (or whatever your Pydantic AI provider expects).TAVILY_API_KEY– required by the defaultresearch_agent(viatavily_search_tool).
If TAVILY_API_KEY is missing, DeepAgent.__init__ will raise a ValueError.
Quickstart: Running a DeepAgent
Minimal example that creates a DeepAgent and runs it on a single objective:
import asyncio
from pydantask.agents.agent import DeepAgent
async def main() -> None:
agent = DeepAgent(
prompt="Write an overview of ghost lights folklore and summarize scientific explanations.",
model="gpt-4.1-mini", # or any compatible OpenAIChatModel name
max_steps=10,
)
runtime_state = await agent.run()
# Inspect the final plan and results
for task_id, task in sorted(runtime_state.plan.items()):
print(f"Task {task_id} [{task.status}]: {task.sub_task_objective}")
if task.result is not None:
print(" Summary:", task.result.summary)
print(" Outputs:", task.result.output_paths)
print()
if __name__ == "__main__":
asyncio.run(main())
What this does:
- Constructs a
DeepAgentwith default Planner, Supervisor, Researcher, Producer, and Critic. - Planner breaks down the objective into
TaskItems (using built‑in capabilitiesresearch_agentandproducer_agent). - Supervisor picks tasks to run in each loop iteration.
- Researcher and Producer execute those tasks, writing notes/reports to
pydantask/tools/tmp_files/when appropriate. - Critic evaluates each task result and marks tasks as
COMPLETED, retryable (READY/RERUN), orFAILEDbased on configured retry limits. - When done, you get a
RuntimeStatewith the full plan and results.
Customizing Capabilities
You can add custom sub‑agents or tools via CapabilityDescription and the sub_agents argument.
Example: custom agent capability
from pydantic_ai import Agent
from pydantask.agents.agent import DeepAgent
from pydantask.models import CapabilityDescription, RuntimeState, TaskResult
my_special_agent = Agent(
model=..., # e.g. the same OpenAIChatModel
name="_my_special_agent",
system_prompt="You are a specialized agent for security analysis.",
deps_type=RuntimeState,
output_type=TaskResult,
tools=[...], # any tools it needs
)
custom_capability = CapabilityDescription(
name="security_agent", # used in TaskItem.capability
description="Performs security-focused analysis and risk assessment.",
tool_func=my_special_agent,
)
agent = DeepAgent(
prompt="Assess the security posture of this web application.",
sub_agents=[custom_capability],
)
# Now the Planner can choose `security_agent` as a capability in the plan.
Example: simple function capability
from pydantic_ai import RunContext
from pydantask.agents.agent import DeepAgent
from pydantask.models import CapabilityDescription, RuntimeState
async def my_utility_tool(ctx: RunContext[RuntimeState], payload: str) -> str:
# do something simple with ctx.deps and payload
return f"processed: {payload}"
utility_capability = CapabilityDescription(
name="my_utility_tool",
description="Utility capability that performs a simple transformation.",
tool_func=my_utility_tool,
)
agent = DeepAgent(prompt="Some goal...", sub_agents=[utility_capability])
For more customization details, see:
docs/customization.mddocs/tools.mddocs/agents.md
Running Unit Tests
Tests live under the test/ directory and are written to be compatible with both pytest and the standard library unittest.
Recommended: pytest
From the repository root:
pip install pytest
pytest
Using unittest directly
If you prefer unittest, you can still run the suite with:
python -m unittest discover -s test -p "test_*.py"
Make sure required environment variables (e.g. TAVILY_API_KEY, OPENAI_API_KEY) are set, or that tests patch them appropriately (as in test/test_agent.py).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pydantask-0.1.0a1.tar.gz.
File metadata
- Download URL: pydantask-0.1.0a1.tar.gz
- Upload date:
- Size: 52.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cef9c3d2f33daef695474b1e9ee540b6070a2387ddce430436b00611748282f5
|
|
| MD5 |
acf83c58257d553ab2aa4955e39eaded
|
|
| BLAKE2b-256 |
c9650eb0957ceac81cb327e02bb2883c15a600475a26ac4a5674a5080b0141cf
|
Provenance
The following attestation bundles were made for pydantask-0.1.0a1.tar.gz:
Publisher:
pydantask.yml on GeorgeDittmar/pydantask
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pydantask-0.1.0a1.tar.gz -
Subject digest:
cef9c3d2f33daef695474b1e9ee540b6070a2387ddce430436b00611748282f5 - Sigstore transparency entry: 1361183243
- Sigstore integration time:
-
Permalink:
GeorgeDittmar/pydantask@ead7b2b4efce2a0a42f4e9c1db4cd7fc9cc2af51 -
Branch / Tag:
refs/tags/v0.1.0a1 - Owner: https://github.com/GeorgeDittmar
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pydantask.yml@ead7b2b4efce2a0a42f4e9c1db4cd7fc9cc2af51 -
Trigger Event:
release
-
Statement type:
File details
Details for the file pydantask-0.1.0a1-py3-none-any.whl.
File metadata
- Download URL: pydantask-0.1.0a1-py3-none-any.whl
- Upload date:
- Size: 51.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1ed1fcfdcca805ae58f9c50aa5a83dd73b0ba75c635eb29cc1d1e08e603d0542
|
|
| MD5 |
14c27de005b16d9bf619099f64525219
|
|
| BLAKE2b-256 |
565e3f6dd0bb970ba79a24a05cc544150231fb13422a935e176014f100469be1
|
Provenance
The following attestation bundles were made for pydantask-0.1.0a1-py3-none-any.whl:
Publisher:
pydantask.yml on GeorgeDittmar/pydantask
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pydantask-0.1.0a1-py3-none-any.whl -
Subject digest:
1ed1fcfdcca805ae58f9c50aa5a83dd73b0ba75c635eb29cc1d1e08e603d0542 - Sigstore transparency entry: 1361183312
- Sigstore integration time:
-
Permalink:
GeorgeDittmar/pydantask@ead7b2b4efce2a0a42f4e9c1db4cd7fc9cc2af51 -
Branch / Tag:
refs/tags/v0.1.0a1 - Owner: https://github.com/GeorgeDittmar
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pydantask.yml@ead7b2b4efce2a0a42f4e9c1db4cd7fc9cc2af51 -
Trigger Event:
release
-
Statement type: