> ## 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.

# Loop Tasks

> Run a task once per row of a CSV or text file, in sync or async workflows

Loop tasks turn a single task into one subtask per row of a CSV or text file — no manual iteration.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    CSV[📄 rows.csv] --> Loop[🔁 Loop Task]
    Loop --> R1[🤖 Subtask row 1]
    Loop --> R2[🤖 Subtask row 2]
    Loop --> Rn[🤖 Subtask row N]
    R1 --> Out[✅ Combined output]
    R2 --> Out
    Rn --> Out

    classDef input fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef agent fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef output fill:#10B981,stroke:#7C90A0,color:#fff

    class CSV input
    class Loop process
    class R1,R2,Rn agent
    class Out output
```

## Quick Start

<Steps>
  <Step title="Loop a task over a CSV">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import Agent, Task, PraisonAIAgents

    researcher = Agent(name="Researcher", instructions="Summarise the topic in one line")

    loop_task = Task(
        name="research_each",
        description="Research: {input}",   # {input} substitutes per row
        expected_output="One-line summary",
        agent=researcher,
        task_type="loop",
        input_file="topics.csv",
        is_start=True,
    )

    workflow = PraisonAIAgents(agents=[researcher], tasks=[loop_task], process="workflow")
    workflow.start()
    ```
  </Step>

  <Step title="Same task, async entry point">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    import asyncio

    asyncio.run(workflow.astart())   # per-row expansion works here too (SDK ≥ 2026-07-27)
    ```
  </Step>
</Steps>

***

## How It Works

The workflow reads `input_file` before the run loop begins and expands the loop task into one subtask per row.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant Workflow
    participant File as input_file
    participant Subtasks
    participant Agent

    Workflow->>File: read rows before run loop
    File-->>Workflow: row 1 … row N
    Workflow->>Subtasks: create research_each_0 … research_each_N
    loop each row (sequential)
        Workflow->>Agent: run subtask with {input} = row
        Agent-->>Workflow: TaskOutput
    end
    Workflow-->>Workflow: aggregate when all complete
```

| Step      | What happens                                                    |
| --------- | --------------------------------------------------------------- |
| Read file | Workflow reads `input_file` once, before the first task runs    |
| Expand    | Each row becomes a subtask named `<original_name>_<row_index>`  |
| Run       | Subtasks execute sequentially, each seeing its row as `{input}` |
| Aggregate | Result returned when all subtasks complete                      |

***

## Choose the Right Loop

| Use                                          | When                                                       | Import                                              |
| -------------------------------------------- | ---------------------------------------------------------- | --------------------------------------------------- |
| `Task(task_type="loop", input_file=...)`     | Fixed CSV/text file, workflow process, one row per subtask | `from praisonaiagents import Task, PraisonAIAgents` |
| `AgentFlow(steps=[loop(from_csv=...)])`      | Deterministic step pipeline with loops as a step           | `from praisonaiagents import AgentFlow, loop`       |
| `AgentFlow(steps=[repeat(step, until=...)])` | Repeat a step until a condition is met (no file needed)    | `from praisonaiagents import AgentFlow, repeat`     |

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    Q{Have a CSV/text file of items?} -->|Yes| T[Task task_type=loop + input_file]
    Q -->|"No — repeat until a condition"| R[AgentFlow.repeat step, until=...]

    classDef question fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef result fill:#10B981,stroke:#7C90A0,color:#fff

    class Q question
    class T,R result
```

***

## Sync vs Async

<Note>
  Loop pre-expansion runs identically for `workflow.start()` and `await workflow.astart()`. Prior to SDK versions from 2026-07-27 the async path ran the loop task once as an ordinary task and ignored `input_file`. If you were using async workflows with `task_type="loop"`, upgrade to pick up the fix.
</Note>

***

## Common Patterns

### Summarise a list of URLs

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

summariser = Agent(name="Summariser", instructions="Summarise the page at the given URL in one line")

loop_task = Task(
    name="summarise_url",
    description="Summarise this URL: {input}",
    expected_output="One-line summary",
    agent=summariser,
    task_type="loop",
    input_file="urls.csv",
    is_start=True,
)

workflow = PraisonAIAgents(agents=[summariser], tasks=[loop_task], process="workflow")
workflow.start()
```

### Loop then post-process each row

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

extractor = Agent(name="Extractor", instructions="Extract the key fact from the row")
formatter = Agent(name="Formatter", instructions="Format the fact as a bullet point")

extract = Task(
    name="extract_row",
    description="Extract the key fact: {input}",
    expected_output="A single fact",
    agent=extractor,
    task_type="loop",
    input_file="scraped.csv",
    is_start=True,
    next_tasks=["format_row"],
)

format_row = Task(
    name="format_row",
    description="Format as a bullet point",
    expected_output="A markdown bullet",
    agent=formatter,
)

workflow = PraisonAIAgents(agents=[extractor, formatter], tasks=[extract, format_row], process="workflow")
workflow.start()
```

***

## Best Practices

<AccordionGroup>
  <Accordion title="Omit input_file to default to tasks.csv">
    When `task_type="loop"` and no `input_file` is set, the workflow reads `tasks.csv` from the working directory.

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    Task(name="loop_it", description="Process: {input}", agent=agent, task_type="loop", is_start=True)
    # reads tasks.csv
    ```
  </Accordion>

  <Accordion title="One row = one string — use JSON for structured fields">
    Each row is passed to `{input}` as a single string. Put structured data in JSON inside the row and parse it in the agent instructions.
  </Accordion>

  <Accordion title="Loop subtasks run sequentially by design">
    Subtasks execute one after another. Use `AgentFlow.parallel` when you want fan-out across rows.
  </Accordion>

  <Accordion title="A single failing row doesn't abort the workflow">
    Other rows still run. Check per-subtask `TaskOutput` to find which rows failed.
  </Accordion>
</AccordionGroup>

***

## Related

<CardGroup cols={2}>
  <Card title="Workflow Loop" icon="repeat" href="/docs/features/workflow-loop">
    AgentFlow-level loops over a CSV as a pipeline step
  </Card>

  <Card title="Workflow Repeat" icon="rotate" href="/docs/features/workflow-repeat">
    Repeat a step until a condition is met — no file needed
  </Card>
</CardGroup>
