Overview
Categorization and routing of unstructured data across several dimensions is a common and comparatively low-cost LLM use case, because one model call can return several classifications at once and each one can trigger a different downstream action. This architecture processes new emails in an inbox on a schedule, for example classifying and routing all new email that arrived in the past hour. A single model call classifies each message by topic and by priority, and two independent downstream paths use those two fields for different purposes. The topic tag is applied to the message so it can be found later. The priority tag determines what happens next: P0 and P1 alert a person directly, P2 and P3 get an AI-drafted reply that still needs review, and routine P4 questions get a pre-written FAQ response with no human involvement.Architecture

- Extract: Deterministic tasks pull the text of each email that arrived since the last Dag run and enrich it with the context the classifier needs, for example previous messages in the thread or the sender’s account information.
- Transform: One model call per message, defined with
@task.llmand shown as LLM in the diagram, returning both the priority and the topic tags in a structured output. - Downstream routing and processing: Two tasks consume different fields of the same classification independently. One applies the topic tags so the message can be found later, the other branches on priority to one of the three response paths.
@task.llm task.
Airflow features
- Dynamic task mapping over task groups: The processing steps are grouped and the group is mapped over the list of new messages, creating one set of classify, route, and tag tasks per email. The number of task instances is determined at runtime by how much mail arrived.
@task.llm: Both the classification and response drafting tasks use@task.llm. This atomic structure with each LLM performing a small amount of work follows the same orchestration best practice as deterministic tasks, see Keep tasks atomic.- Structured output: The
output_typedeclares priority as aLiteralof the permitted values and topic as a list of tags. - Branching:
@task.branchpicks one downstream response path based on the priority field. The paths it does not pick are skipped. - Human-in-the-loop: The P2 and P3 branch pauses for a reviewer to approve, edit, or reject the drafted reply before it is sent.
Considerations
- Constrain the categories the model can return. An unconstrained priority field can come back as
"urgent"or"P1 (high)", which becomes cumbersome to parse in a deterministic task to branch on. Declaring the categories as aLiteralin the output class means that if the model returns any value for this field that does not match any of the defined categories, the model call is repeated according tooutput_retriespassed to the model throughagent_params, separately from Airflow task retries, see Move to the Common AI provider. - Enrich before you classify. Thread history and account status change the correct priority for the same message body. Assemble that context in a deterministic upstream task.
- Restrict automated actions. The P4 pre-written response answers a sender with no review at all, which always carries risk, which can be reduced by having a pre-written FAQ email that is sent deterministically.
Next steps
- Look up decorator and operator parameters in Orchestrate AI tasks with Apache Airflow® and the Common AI provider.
- Add the review step on drafted replies with Human-in-the-loop workflows with Airflow.
- Score the tone and accuracy of those drafts before a person sees them with AI model evals.
- Read the AI Orchestration with Apache Airflow® eBook for the full set of AI orchestration patterns.
- Deploy the Airflow pipelines with a free trial of Astro.