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

# Split a workflow into tasks

> Turn one expensive call path into labelled tasks, and find out where the money actually goes.

One agent turn is rarely one job. This takes a support workflow billed as a
single line item and splits it into four tasks.

```mermaid theme={null}
flowchart LR
  A["support<br/>$4,100/mo"] --> B["support_intent"]
  A --> C["support_lookup"]
  A --> D["support_draft"]
  A --> E["support_tone_check"]
```

**Time:** about an hour of code changes, then a week of traffic to read.

<Steps>
  <Step title="List the model calls on one path">
    Pick your highest-volume path and write down every call it makes. Most teams
    find more than they expected, because retries, guardrails and formatting
    passes are easy to forget.

    For a support reply: classify the intent, fetch the account, draft the reply,
    check the tone.
  </Step>

  <Step title="Name each one">
    One name per job. A name that survives a prompt rewrite.

    | Call                            | Task                 |
    | ------------------------------- | -------------------- |
    | Classify the intent             | `support_intent`     |
    | Fetch and summarise the account | `support_lookup`     |
    | Draft the reply                 | `support_draft`      |
    | Check the tone                  | `support_tone_check` |

    Resist the urge to encode the tenant or the model. Tenants go in `user`.
  </Step>

  <Step title="Label them">
    ```python theme={null}
    intent = client.chat.completions.create(
        model="deepseek-v4-flash", messages=intent_messages,
        extra_body={"task": "support_intent"},
    )

    draft = client.chat.completions.create(
        model="deepseek-v4-flash", messages=draft_messages,
        extra_body={"task": "support_draft"},
    )
    ```

    Keep `model` as it was if you want zero behaviour change; the labels work
    either way. Routing is what lets a workload get cheaper later.
  </Step>

  <Step title="Wait, then read the split">
    Give it enough traffic to be representative, usually a week. We report spend
    per task.

    The result is almost always lopsided. A guardrail call that nobody thought
    about turns out to be a third of the bill, because it runs on every turn while
    the expensive-looking reasoning call runs on one turn in twenty.
  </Step>
</Steps>

## What good looks like

<AccordionGroup>
  <Accordion title="One task dominates the bill">
    Ideal. Take it to [Write an eval contract](/tutorials/write-an-eval-contract)
    and route it down.
  </Accordion>

  <Accordion title="Spend is spread evenly across tasks">
    Usually a sign the split is too fine. Merge tasks that share an input shape
    and a definition of correct.
  </Accordion>

  <Accordion title="One task has huge cost variance per call">
    Still too broad. Something inside it branches into genuinely different jobs.
    Split again.
  </Accordion>
</AccordionGroup>

<Card title="Next: write an eval contract" icon="clipboard-check" href="/tutorials/write-an-eval-contract">
  Nothing can be routed until correct is defined.
</Card>
