When an agent project grows to more than 4 reasoners, you should move them into a reasoners/ package to maintain clean architecture.
Recommended Directory Structure
<slug>/
├── main.py
└── reasoners/
├── __init__.py
├── models.py # All Pydantic schemas
├── helpers.py # Plain Python utilities
└── <domain>.py # One AgentRouter per logical grouping
Component Roles
models.py: Contains every Pydantic schema used in the system. This centralizes type-checking and prevents circular imports.helpers.py: Contains plain Python utilities (math, prose rendering, etc.). Do not use decorators here. Use @app.skill() only if the function needs to be discoverable by the control plane for external calls. Internal logic should remain as plain Python to avoid decorator overhead.<domain>.py: Defines an AgentRouter for a specific logical grouping of reasoners.
Using AgentRouter
An AgentRouter allows you to group reasoners. Its methods (ai, call, memory, harness) proxy to the main agent.
Important Gotchas:
router.node_id and other data attributes do not proxy. You must read the node ID from the environment: NODE_ID = os.getenv("AGENT_NODE_ID", "<slug>").- The
prefix argument in AgentRouter(prefix="...") controls namespacing. If prefix="clauses", reasoner IDs become clauses_<func_name>. Use prefix="" to keep them raw.
Example Router Implementation:
import os
from agentfield import AgentRouter
from .models import MyResult
NODE_ID = os.getenv("AGENT_NODE_ID", "<slug>")
router = AgentRouter(prefix="", tags=["domain_name"])
@router.reasoner()
async def my_reasoner(payload: dict, model: str | None = None) -> MyResult:
return await router.ai(system="...", user=str(payload), schema=MyResult, model=model)
Registering a Router in main.py:
from reasoners import domain_router
# ... inside app setup
app.include_router(domain_router)
import os
from agentfield import AgentRouter
from .models import MyResult
NODE_ID = os.getenv("AGENT_NODE_ID", "<slug>")
router = AgentRouter(prefix="", tags=["domain_name"])
@router.reasoner()
async def my_reasoner(payload: dict, model: str | None = None) -> MyResult:
return await router.ai(system="...", user=str(payload), schema=MyResult, model=model)