Usage
This section covers the common usage patterns for ollama-classifier.
Every backend is used the same way: instantiate a backend, wrap it in an
LLMClassifier, and call
generate() or classify().
from ollama_classifier import LLMClassifier
from ollama_classifier.backends import OllamaBackend
backend = OllamaBackend(model="llama3.2")
classifier = LLMClassifier(backend)
Basic Classification
The simplest way to classify text. classify() makes one completion-scoring
call per label and returns calibrated confidence via geometric-mean
normalization.
result = classifier.classify(
text="The goalkeeper made an incredible save!",
choices=["sports", "politics", "technology", "entertainment"],
)
print(f"Prediction: {result.prediction}")
print(f"Confidence: {result.confidence:.2%}")
print(f"Method: {result.method}") # "multi_call"
Classification with Label Descriptions
Providing descriptions helps the model understand each category better:
choices = {
"positive": "Text expresses happiness, satisfaction, or approval",
"negative": "Text expresses anger, disappointment, or disapproval",
"mixed": "Text contains both positive and negative sentiments",
"neutral": "Text is factual without strong emotional content",
}
result = classifier.classify(
text="The food was amazing but the service was terrible.",
choices=choices,
)
Custom System Prompt
Override the default system prompt for specialized tasks:
result = classifier.classify(
text="The quarterly earnings exceeded analyst expectations.",
choices=["bullish", "bearish", "neutral"],
system_prompt="You are a financial sentiment analyzer. "
"Classify financial news based on market sentiment."
)
Choosing a Scoring Method
LLMClassifier provides two scoring methods. Each returns a
ClassificationResult with a full
probability distribution.
|
|
|
|---|---|---|
How it works |
Adaptive constrained generation over a prefix trie of label tokens; per-label logprobs reconstructed from the winning path and unresolved clusters. |
Multi-call completion scoring: each label scored as a completion of the prompt without generation. |
API calls |
1 to |
N calls for N labels |
Confidence |
Divergence-aware (may be partial for labels that diverge early from the winning path) |
Exact (geometric-mean normalization) |
Approximate? |
|
Always |
|
|
|
Best for |
Speed, large label sets, when a single call suffices |
Gold-standard accuracy, small-to-medium label sets |
Adaptive Generation (generate)
generate() makes 1 to max_calls constrained API calls. Each call walks
a prefix trie of label tokens. After each call, labels are scored up to their
divergence point from the winning path. Unresolved clusters trigger
supplementary calls (recursive cluster resolution) until the budget is
exhausted.
The max_calls parameter controls the accuracy/cost tradeoff:
|
Behavior |
Calls |
|---|---|---|
|
Single constrained call. Labels are scored up to their divergence point. Fast but approximate. |
1 |
|
Adaptive resolution. Unresolved label clusters trigger supplementary calls until the budget is exhausted. |
≤ K |
|
Fully recursive resolution. Every cluster is resolved to completion. Equivalent to exact scoring. |
≤ N |
# Fast: single call, approximate confidence
result = classifier.generate(
text="The team won the championship!",
choices=["sports", "finance", "politics"],
max_calls=1,
)
print(result.prediction) # "sports"
print(result.approximate) # True (if any label had partial coverage)
print(result.coverage) # {"sports": 1.0, "finance": 1.0, "politics": 1.0}
# Adaptive: allow up to 3 calls for better resolution
result = classifier.generate(text="...", choices=[...], max_calls=3)
# Exact: resolve everything (unlimited calls)
result = classifier.generate(text="...", choices=[...], max_calls=None)
Multi-Call Scoring (classify)
Makes one completion-scoring call per label. Each label’s per-token logprobs are extracted without generation, then normalized via geometric mean to eliminate token-count bias. This is the gold-standard confidence method.
result = classifier.classify(
text="The movie was fantastic!",
choices=["positive", "negative", "neutral"],
)
print(result.method) # "multi_call"
print(result.approximate) # False
print(result.n_calls) # 3
Batch Processing
Classify multiple texts efficiently. Batch methods are parallelized via a
thread pool (sync) or asyncio.gather (async):
texts = [
"The goalkeeper made an incredible save!",
"The central bank raised interest rates.",
"The new smartphone features a revolutionary camera.",
]
results = classifier.batch_classify(
texts=texts,
choices=["sports", "finance", "technology"],
)
for text, result in zip(texts, results):
print(f"{text} -> {result.prediction} ({result.confidence:.2%})")
Async Usage
For concurrent processing, use the async methods:
import asyncio
from ollama_classifier import LLMClassifier
from ollama_classifier.backends import OllamaBackend
async def main():
backend = OllamaBackend(model="llama3.2")
classifier = LLMClassifier(backend)
# Single classification
result = await classifier.aclassify(
text="The concert was amazing!",
choices=["positive", "negative", "neutral"],
)
# Batch classification (concurrent)
results = await classifier.abatch_classify(
texts=["Text 1", "Text 2", "Text 3"],
choices=["positive", "negative", "neutral"],
)
asyncio.run(main())
Working with Results
The ClassificationResult object contains:
result = classifier.classify(
text="I love this product!",
choices=["positive", "negative", "neutral"],
)
# The predicted label
print(result.prediction) # "positive"
# Confidence score (0.0 to 1.0)
print(result.confidence) # 0.92
# Full probability distribution
print(result.probabilities)
# {"positive": 0.92, "negative": 0.05, "neutral": 0.03}
# Scoring method used
print(result.method) # "multi_call" or "adaptive_generate"
# Whether the result is approximate (only for generate())
print(result.approximate) # False for classify()
# Per-label fraction of tokens scored (only for generate())
print(result.coverage) # {"positive": 1.0, ...}
# Number of API calls made
print(result.n_calls) # 3
# Raw data for debugging
print(result.raw_response)
Using Sample Data
The package ships with ready-to-use sample datasets in
examples/sample_data.py so you can test the classifier immediately.
Two datasets are provided, both containing 20 short customer-support
ticket texts across four labels (billing, technical_support,
account, general):
DATASET_WITHOUT_DESCRIPTIONS— labels as a plainlist.DATASET_WITH_DESCRIPTIONS— labels as adictmapping each label to a human-readable description, which typically improves classification accuracy.
Each dataset is a dataclass with four fields:
texts, choices, expected_labels, and description.
Import the datasets:
from examples.sample_data import (
DATASET_WITHOUT_DESCRIPTIONS,
DATASET_WITH_DESCRIPTIONS,
)
Quick test with the basic dataset:
from ollama_classifier import LLMClassifier
from ollama_classifier.backends import OllamaBackend
from examples.sample_data import DATASET_WITHOUT_DESCRIPTIONS
backend = OllamaBackend(model="llama3.2")
classifier = LLMClassifier(backend)
results = classifier.batch_generate(
texts=DATASET_WITHOUT_DESCRIPTIONS.texts,
choices=DATASET_WITHOUT_DESCRIPTIONS.choices,
)
correct = sum(r.prediction == e for r, e in
zip(results, DATASET_WITHOUT_DESCRIPTIONS.expected_labels))
print(f"Accuracy: {correct}/{len(results)}")
Run the bundled example script directly:
python -m examples.run_sample_data