Classification and sentiment
Classification is one of the cheapest, highest-ROI uses of an LLM. Few-shot examples + enum-constrained output = robust classifier in 10 lines.
Sentiment (3-class)
resp = client.chat.completions.create(
model="epithre-lyt",
messages=[
{"role": "system", "content": [
{"type": "text", "text": """Klasifikasi sentimen review produk Tokopedia.
Output: hanya satu kata: positif, netral, atau negatif.
Contoh:
Review: "Barangnya bagus, sesuai foto. Pengiriman cepet, packing rapi."
Output: positif
Review: "Barang nyampe tapi kemasan agak penyok. Isinya ok."
Output: netral
Review: "Salah kirim. Komplain 2 minggu gak direspon. Refund lama banget."
Output: negatif""",
"cache_control": {"type": "ephemeral"}}
]},
{"role": "user", "content": "Barang sampe rusak, refund cepet sih."},
],
response_format={
"type": "json_schema",
"json_schema": {
"name": "sentiment",
"strict": True,
"schema": {
"type": "object",
"properties": {
"sentiment": {"type": "string", "enum": ["positif", "netral", "negatif"]},
"confidence": {"type": "number", "minimum": 0, "maximum": 1},
},
"required": ["sentiment", "confidence"],
"additionalProperties": False,
},
},
},
)
import json
result = json.loads(resp.choices[0].message.content)
# {"sentiment": "netral", "confidence": 0.78}
Use epithre-lyt for high-volume classification. ~5x cheaper than omni and accuracy is sufficient for sentiment / intent.
Intent classification (multi-class)
intents = ["greeting", "ask_price", "ask_stock", "complaint", "compliment", "small_talk", "other"]
resp = client.chat.completions.create(
model="epithre-lyt",
messages=[
{"role": "system", "content": f"""Klasifikasi maksud pesan customer.
Pilih dari: {', '.join(intents)}.
ask_price: pertanyaan tentang harga produk
ask_stock: pertanyaan tentang ketersediaan barang
complaint: keluhan tentang produk atau layanan
compliment: pujian atau testimoni positif
greeting: sapaan / pembuka
small_talk: obrolan ringan
other: tidak masuk kategori di atas
Contoh:
"Halo, masih buka?" -> greeting
"Pupuk urea berapa harganya per kg?" -> ask_price
"Beli kemarin tapi salah kirim warna" -> complaint"""},
{"role": "user", "content": "Selamat siang, kalau kirim ke Bandung lewat ekspedisi mana?"},
],
response_format={
"type": "json_schema",
"json_schema": {
"name": "intent",
"strict": True,
"schema": {
"type": "object",
"properties": {
"intent": {"type": "string", "enum": intents},
},
"required": ["intent"],
"additionalProperties": False,
},
},
},
)
Topic / category (open-ended)
When you don't have a fixed enum:
resp = client.chat.completions.create(
model="epithre-omni",
messages=[
{"role": "system", "content":
"Klasifikasi topik artikel berita Indonesia. Output 1-3 topik utama, "
"pakai vocab konsisten (e.g. 'politik', 'ekonomi', 'olahraga')."},
{"role": "user", "content": article_text},
],
response_format={
"type": "json_object",
},
)
If you want consistent vocab across many articles, supply a controlled vocabulary as part of the system prompt + use enum schema.
Multi-label classification
resp = client.chat.completions.create(
model="epithre-lyt",
messages=[
{"role": "system", "content":
"Tag artikel dengan SEMUA topik yang relevan dari daftar."},
{"role": "user", "content": article_text},
],
response_format={
"type": "json_schema",
"json_schema": {
"name": "tags",
"strict": True,
"schema": {
"type": "object",
"properties": {
"tags": {
"type": "array",
"items": {"type": "string",
"enum": ["politik", "ekonomi", "olahraga", "hiburan",
"teknologi", "pendidikan", "kesehatan"]}
}
},
"required": ["tags"],
"additionalProperties": False,
},
},
},
)
Bulk classification via Batch
For backfilling historical data:
import json
with open("classify.jsonl", "w") as f:
for i, review in enumerate(my_reviews):
f.write(json.dumps({
"custom_id": f"review-{review['id']}",
"method": "POST",
"url": "/v1/chat/completions",
"body": {
"model": "epithre-lyt",
"messages": [
{"role": "system", "content": SENTIMENT_PROMPT},
{"role": "user", "content": review["text"]},
],
"response_format": {"type": "json_schema", "json_schema": SENTIMENT_SCHEMA},
}
}) + "\n")
# Upload + batch
file = client.files.create(file=open("classify.jsonl","rb"), purpose="batch_input")
batch = client.batches.create(input_file_id=file.id, endpoint="/v1/chat/completions")
50% off realtime. For a million-review backfill, this is the difference between Rp1,000,000 and Rp500,000.
Calibration
If you need calibrated probabilities (not just labels), ask the model to output confidence (0-1) per label. Run on a labeled holdout to compute correlation between model confidence and accuracy. The default raw output isn't well-calibrated; treat confidence as relative ordering.
For real calibration, use the evaluation cookbook.
See also
- Structured output guide
- Prompt engineering for Bahasa Indonesia - few-shot example patterns.
- Batches reference