> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Handler Tasks

> Run Python code as a task step — no agent, no LLM call

A handler task runs your Python function instead of an LLM.

> [!NOTE]
>
> Available under both `PraisonAIAgents` / `AgentTeam` and the standalone `Workflow` engine as of PraisonAI [PR #4907](https://github.com/MervinPraison/PraisonAI/pull/4907). Earlier releases only honoured `handler` inside the standalone `Workflow` engine.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    A[🔍 Agent Task] --> H[⚡ Handler Task]
    H --> W[✍️ Agent Task]

    classDef agent fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef handler fill:#189AB4,stroke:#7C90A0,color:#fff

    class A,W agent
    class H handler
```

## Quick Start

**1. Handler only**

A task with a `handler` runs your function directly — no agent required.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent, Task, PraisonAIAgents

score = Task(name="score", handler=lambda ctx: {"score": 92})

team = PraisonAIAgents(tasks=[score])
team.start()
```

**2. Handler in a team with agents**

An agent researches, a handler post-processes, and another agent writes the report.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent, Task, PraisonAIAgents

researcher = Agent(name="Researcher", instructions="Research the topic and list key facts.")
writer     = Agent(name="Writer",     instructions="Write a short report from the facts.")

research = Task(
    name="research",
    description="Research renewable energy trends",
    agent=researcher,
    output_variable="research_data",
)

def summarise(ctx):
    facts = ctx.variables.get("research_data", "")
    return {"summary": facts[:500]}

process = Task(name="process", handler=summarise, context=[research])

report = Task(
    name="report",
    description="Write a report using {{summary}}",
    agent=writer,
    context=[process],
)

team = PraisonAIAgents(
    agents=[researcher, writer],
    tasks=[research, process, report],
)
team.start()
```

***

## How It Works

The team runs each task in order, calling your function for handler tasks and the model for agent tasks.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant PraisonAIAgents
    participant Handler
    participant Agent

    User->>PraisonAIAgents: start()
    PraisonAIAgents->>Agent: run agent task
    Agent-->>PraisonAIAgents: output
    PraisonAIAgents->>Handler: run handler(ctx)
    Handler-->>PraisonAIAgents: dict / string
    PraisonAIAgents-->>User: final result
```

***

## The context argument

The handler receives a `WorkflowContext` with `.input`, `.current_step`, and `.variables`.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Task

def enrich(ctx):
    upstream = ctx.variables["research_data"]
    return {"enriched": upstream.upper()}

Task(name="enrich", handler=enrich)
```

| Field | Type | Description |
| - | - | - |
| `input` | `str` | Original run input |
| `current_step` | `str` | Name of the current task |
| `variables` | `dict` | Shared variables from upstream tasks (keyed by `output_variable`) |

***

## Async handlers

An `async def` handler works too — the SDK awaits it under both `start()` and `astart()`.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Task

async def fetch_price(ctx):
    return {"price": 42.0}

Task(name="fetch_price", handler=fetch_price)
```

***

## Common Patterns

A deterministic data step between two LLM steps keeps parsing predictable.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent, Task, PraisonAIAgents

analyst = Agent(name="Analyst", instructions="Return a JSON list of numbers.")

collect = Task(name="collect", description="List sales figures", agent=analyst, output_variable="figures")

def total(ctx):
    figures = ctx.variables.get("figures", "")
    return {"total": sum(int(x) for x in figures.split() if x.isdigit())}

sum_task = Task(name="sum", handler=total, context=[collect])

PraisonAIAgents(agents=[analyst], tasks=[collect, sum_task]).start()
```

Skip the LLM when the answer already lives in variables.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Task

def use_cached(ctx):
    return ctx.variables.get("cached_answer", "no answer yet")

Task(name="answer", handler=use_cached)
```

***

## Best Practices

> [accordion] Keep handlers pure
>
> A handler should compute from its inputs and return a value — avoid hidden global state so runs stay repeatable.

> [accordion] Return strings or dicts
>
> Return a plain string or a dict. A dict merges into the shared variables so later tasks can read each key.

> [accordion] Use output\_variable to hand data to the next step
>
> Set `output_variable` on the handler task to name its result, then read it downstream with `{{name}}` or `ctx.variables["name"]`.

> [accordion] Prefer handlers for API and DB calls
>
> Reach for a handler when a step is a deterministic API or database call that does not need an LLM.

***

## Related

> [card] [Tasks](https://praison.ai/docs/concepts/tasks.md)
>
> The full Task parameter reference

> [card] [Conditions](https://praison.ai/docs/features/conditions.md)
>
> Skip tasks with should\_run gates

> [card] [Output Variables](https://praison.ai/docs/features/task-output-variables.md)
>
> Pass values between tasks

> [card] [Workflows](https://praison.ai/docs/features/workflows.md)
>
> Chain agents into pipelines


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.