# Classification and span labeling (/use-cases/data-labeling/classification)



Use a `Literal` for one label and a `List[Literal]` for multiple labels.

<Note>
  `output_schema` requests structured output and Agno attempts to parse it. A failed or unparseable run can leave `content` as text. Check `RunStatus.completed` and the expected Pydantic type before reading fields, indexing, or writing downstream. These examples stop on failure; an application can instead send the original input to a review queue. Schema validation checks the shape and constraints, so verify extracted facts against the source separately.
</Note>

```python
from agno.run.base import RunStatus

from typing import Literal

from agno.agent import Agent
from agno.models.google import Gemini
from pydantic import BaseModel, Field


class Classification(BaseModel):
    label: Literal["positive", "negative", "neutral"] = Field(
        ..., description="The assigned sentiment label"
    )


agent = Agent(
    model=Gemini(id="gemini-3.5-flash"),
    instructions="You classify product reviews by sentiment.",
    output_schema=Classification,
)

result_run = agent.run("It works as described, nothing special.")
if result_run.status != RunStatus.completed or not isinstance(result_run.content, Classification):
    raise RuntimeError("No validated Classification; send the input to review before continuing")
result = result_run.content
# Classification(label='neutral')
```

For a successfully parsed result, `Literal` restricts the label to the values defined in the schema.

## Multi-label [#multi-label]

A review can touch several aspects. Return any subset.

```python
from typing import List, Literal

from pydantic import BaseModel, Field

Aspect = Literal["food", "service", "value", "atmosphere", "cleanliness"]


class Tagging(BaseModel):
    tags: List[Aspect] = Field(
        ..., description="Every aspect the reviewer commented on; empty if none"
    )
```

## Hierarchical [#hierarchical]

For a taxonomy, return the parent and child path.

```python
from typing import List, Literal

from pydantic import BaseModel, Field


Parent = Literal["sports", "politics", "tech", "business", "health"]


class HierarchicalTag(BaseModel):
    parent: Parent
    child: str = Field(..., description="Sub-category under the parent")


class Tagging(BaseModel):
    tags: List[HierarchicalTag]
```

## Span labeling [#span-labeling]

Asking the model to count characters is unreliable. Have it return the exact substring and locate offsets in Python.

```python
from agno.run.base import RunStatus

from typing import List, Literal

from agno.agent import Agent
from agno.models.google import Gemini
from pydantic import BaseModel, Field


class Entity(BaseModel):
    text: str = Field(..., description="Exact substring from the input")
    label: Literal["PERSON", "ORG", "LOCATION", "DATE"]


class Entities(BaseModel):
    entities: List[Entity]


agent = Agent(
    model=Gemini(id="gemini-3.5-flash"),
    instructions=(
        "Extract all named entities. Return the exact substring as it "
        "appears, with its label. Do not paraphrase or normalize."
    ),
    output_schema=Entities,
)

text = "On March 3rd, Sarah Johnson left Acme Corp to join Lumen Labs."
result_run = agent.run(text)
if result_run.status != RunStatus.completed or not isinstance(result_run.content, Entities):
    raise RuntimeError("No validated Entities; send the input to review before continuing")
result = result_run.content

for e in result.entities:
    start = text.find(e.text)
    end = start + len(e.text) if start >= 0 else None
    print(e.label, e.text, start, end)
```

`str.find()` returns `-1` when the returned text is not a verbatim substring, which is the signal that the model paraphrased despite the instructions. Guard the offset before you use it. `find()` also returns the first match only. When the same substring appears more than once, track the search offset or match occurrences in order before assigning spans.

The same shape drives PII redaction: detect the spans, then replace each with its tag in post-processing.

## Choosing the shape [#choosing-the-shape]

| You need             | Schema                                           |
| -------------------- | ------------------------------------------------ |
| Exactly one label    | `Literal[...]`                                   |
| Any subset of labels | `List[Literal[...]]`                             |
| A taxonomy path      | A model with `parent` / `child`                  |
| Marked substrings    | A model with `text` + `label`, offsets in Python |

## Other modalities [#other-modalities]

Image, audio, video, and document classification follow the same schema pattern with a different input argument. See [Multimodal inputs](/use-cases/data-labeling/multimodal-inputs).

| Modality | Cookbook                                                                                                                  |
| -------- | ------------------------------------------------------------------------------------------------------------------------- |
| Image    | [image\_classification](https://github.com/agno-agi/agno/tree/main/cookbook/data_labeling/_06_image_classification)       |
| Audio    | [audio\_classification](https://github.com/agno-agi/agno/tree/main/cookbook/data_labeling/_10_audio_classification)       |
| Video    | [video\_classification](https://github.com/agno-agi/agno/tree/main/cookbook/data_labeling/_13_video_classification)       |
| Document | [document\_classification](https://github.com/agno-agi/agno/tree/main/cookbook/data_labeling/_15_document_classification) |

## Next steps [#next-steps]

| Task                           | Guide                                                             |
| ------------------------------ | ----------------------------------------------------------------- |
| Extract typed fields           | [Data extraction](/use-cases/data-labeling/structured-extraction) |
| Score outputs against a rubric | [LLM as judge](/use-cases/data-labeling/llm-as-judge)             |
| Add reviewer agreement         | [Quality pipeline](/use-cases/data-labeling/quality-pipeline)     |

## Developer Resources [#developer-resources]

* [Text classification cookbook](https://github.com/agno-agi/agno/tree/main/cookbook/data_labeling/_01_text_classification)
* [Span labeling cookbook](https://github.com/agno-agi/agno/tree/main/cookbook/data_labeling/_04_text_span_labeling)
