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

# Classification and span labeling

> Assign one label, a set of labels, a taxonomy path, or marked spans.

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

```python theme={null}
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 = agent.run("It works as described, nothing special.").content
# Classification(label='neutral')
```

The `Literal` constrains the result to the labels defined in the schema.

## Multi-label

A review can touch several aspects. Return any subset.

```python theme={null}
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

For a taxonomy, return the parent and child path.

```python theme={null}
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

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

```python theme={null}
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 = agent.run(text).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

| 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

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

| 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

* [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)
