AgentRouter

Organize large agents across multiple files with AgentRouter — Agentfield's FastAPI-style router for reasoners and skills.

AgentRouter

AgentRouter lets you organize reasoners and skills across multiple files, then mount them on an Agent with app.include_router(). It mirrors FastAPI's APIRouter pattern.

When to Use

Use AgentRouter when your agent has:

  • More than 5–6 reasoners
  • Distinct functional groups (e.g., analysis, synthesis, retrieval)
  • Multiple team members working on the same agent

Import

from agentfield import AgentRouter

Constructor

AgentRouter(
    prefix: str = "",
    tags: Optional[List[str]] = None
)
ParameterTypeDescription
prefixstrURL prefix for all routes (e.g., "/analysis")
tagsList[str]Tags inherited by all reasoners and skills in this router

Decorators

@router.reasoner()

Registers an AI-powered reasoner. Same signature as @app.reasoner():

router.reasoner(
    path: Optional[str] = None,
    tags: Optional[List[str]] = None,
    vc_enabled: Optional[bool] = None,
)

@router.skill()

Registers a deterministic skill. Same signature as @app.skill():

router.skill(
    tags: Optional[List[str]] = None,
    path: Optional[str] = None,
    vc_enabled: Optional[bool] = None,
)

Mounting on an Agent

Use app.include_router(router) in your main.py to register all collected reasoners and skills:

app.include_router(router)

All reasoners and skills from the router are registered on the agent. Direct calls between reasoners continue to work after mounting.

Complete Example

# reasoners/analysis.py
from agentfield import AgentRouter
from pydantic import BaseModel
from typing import Literal, Optional

router = AgentRouter(prefix="/analysis", tags=["analysis"])

class SentimentResult(BaseModel):
    sentiment: Literal["positive", "negative", "neutral"]
    confidence: float
    reasoning: str

@router.reasoner()
async def analyze_sentiment(text: str) -> SentimentResult:
    from main import app
    return await app.ai(
        system="Classify the sentiment of this text.",
        user=text,
        schema=SentimentResult,
        temperature=0.2,
    )

@router.reasoner()
async def classify_topic(text: str, model: Optional[str] = None) -> dict:
    from main import app
    return await app.ai(
        user=f"What is the main topic? {text}",
        model=model,
    )
# reasoners/synthesis.py
from agentfield import AgentRouter
from pydantic import BaseModel

router = AgentRouter(prefix="/synthesis", tags=["synthesis"])

class SummaryResult(BaseModel):
    summary: str
    key_points: list[str]
    action_required: bool

@router.reasoner()
async def summarize(content: str) -> SummaryResult:
    from main import app
    return await app.ai(
        user=f"Summarize this content: {content}",
        schema=SummaryResult,
    )
# main.py
import os
from agentfield import Agent, AIConfig
from reasoners.analysis import router as analysis_router
from reasoners.synthesis import router as synthesis_router
import asyncio

app = Agent(
    node_id="multi-module-agent",
    agentfield_server=os.getenv("AGENTFIELD_SERVER", "http://localhost:8080"),
    ai_config=AIConfig(model=os.getenv("SMALL_MODEL", "openai/gpt-4o-mini")),
)

app.include_router(analysis_router)
app.include_router(synthesis_router)

@app.reasoner()
async def orchestrate(query: str) -> dict:
    sentiment, topic = await asyncio.gather(
        analyze_sentiment(query),
        classify_topic(query),
    )
    summary = await summarize(query)

    return {
        "sentiment": sentiment.dict(),
        "topic": topic,
        "summary": summary.dict(),
    }

if __name__ == "__main__":
    app.run(auto_port=True)
my-agent/
├── main.py                 # Agent init + top-level orchestrators
├── models.py               # Shared Pydantic schemas
├── reasoners/
│   ├── __init__.py         # Optional: re-export routers
│   ├── analysis.py         # Analysis group (router + reasoners)
│   └── synthesis.py        # Synthesis group (router + reasoners)
└── requirements.txt

Import app from main.py inside reasoner functions (not at module level) to avoid circular imports.