17 KiB
Panduan Classification Runner (Laya / laya.cpp)
Dokumen ini menjelaskan cara pakai classify_runner.py — runner klasifikasi teks pakai
Laya, model keputusan (decision model) multilingual 322M parameter, yang dijalankan lewat
runtime native laya.cpp di CPU.
Untuk ringkasan proyek dan setup runner lain, lihat README.md.
Daftar isi
- Cara kerja singkat
- Menjalankan
- Cara baca output
- Tiga tipe pertanyaan
- Pertanyaan sendiri
- Mode server (lebih cepat)
- Output JSON
- Semua opsi CLI
- Konfigurasi
- Setup dari nol
- Catatan penting
1. Cara kerja singkat
Laya bukan classifier satu-label biasa. Kamu memberi dia sebuah state (teks, email, ticket, atau JSON) plus beberapa pertanyaan bertipe. Tiap pertanyaan mendeklarasikan ruang jawabannya sendiri, jadi tidak perlu training/finetune untuk memakainya di kasus baru.
Semua pertanyaan dijawab dalam satu forward pass, dengan probabilitas — jadi tidak ada teks yang perlu diparse dan tidak ada risiko hallucination seperti pada LLM generatif.
state + pertanyaan → satu forward pass → jawaban bertipe + probabilitas
Runner mendukung tiga mode pemanggilan:
| Mode | Kapan dipakai | Kondisi | Kecepatan |
|---|---|---|---|
| CLI | satu kali jalan | tanpa setup | ~5–7 detik |
| Server | dipakai berulang | harus nyalakan server | ~0,6 detik |
2. Menjalankan
Cara paling dasar
./usage-classify.sh "Saya dikenakan biaya dua kali untuk invoice 4411, tolong kembalikan uang saya."
Kalau teksnya panjang, pakai file
./usage-classify.sh --state-file ticket.txt
Kalau state-nya punya beberapa kolom
Kalau teks diawali { atau [, runner otomatis mengirimnya sebagai struktur JSON, bukan teks
biasa. Jadi email dengan kolom subject/body bisa langsung:
./usage-classify.sh '{"subject":"Refund not received","body":"I cancelled two weeks ago and still have no refund."}'
Atau dari file JSON:
./usage-classify.sh --state-file ticket.json
3. Cara baca output
Contoh output nyata:
State : Saya dikenakan biaya dua kali untuk invoice 4411, tolong kembalikan uang saya.
Questions : 3 (cli)
Backend : CPU (12th Gen Intel(R) Core(TM) i7-12700H)
Elapsed : 6.953 s
Inference : 0.732 s
Tokens : 176 in / 0 out
department : billing
billing=1.0000 technical=0.0000 sales=0.0000 other=0.0000
urgency : 1.7297 (immediate)
0=0.0306 1=0.2091 2=0.7603
refund : 0.7751
act_probability=1.0000
Bagian atas
| Baris | Arti |
|---|---|
State |
State yang dikirim (dipotong 120 karakter kalau panjang) |
Questions |
Berapa pertanyaan dijawab, dan mode yang dipakai (cli atau server) |
Backend |
Backend dan perangkat yang dipakai. Hanya muncul di mode CLI |
Elapsed |
Total waktu wall-clock, termasuk load bobot model |
Inference |
Waktu forward pass saja. Hanya muncul di mode CLI |
Tokens |
Jumlah token input yang diproses (output selalu 0, karena model ini tidak generatif) |
Kenapa
Elapsedjauh lebih besar dariInference? Model ini tidak generatif, jadi forward pass-nya cuma ~0,6 detik. Sisanya (~5 detik) adalah waktu baca bobot 644MB dari disk. Ini normal. Kalau kamu pemakai berulang, pakai mode server.
Bagian jawaban
Nama di kiri adalah ID pertanyaan yang kamu tentukan sendiri. Formatnya berbeda per tipe:
choice — jawaban berupa salah satu label. Baris kedua menampilkan probabilitas tiap opsi,
diurutkan dari yang tertinggi:
department : billing
billing=1.0000 technical=0.0000 sales=0.0000 other=0.0000
score — jawaban berupa angka desimal (rata-rata tertimbang dari distribusi), diikuti label
level yang paling dekat dalam kurung. Baris kedua menampilkan probabilitas tiap level dalam
urutan legend (tidak diurutkan):
urgency : 1.7297 (immediate)
0=0.0306 1=0.2091 2=0.7603
noul — jawaban berupa peluang pernyataan itu benar (0.0 = pasti salah, 1.0 = pasti benar).
Tipe ini tidak punya objek probabilities, jadi baris kedua menampilkan
act_probability, yaitu peluang bahwa kasus ini perlu di-escalate ke manusia:
refund : 0.7751
act_probability=1.0000
4. Tiga tipe pertanyaan
| Tipe | Isi criteria |
Output | Kapan pakai |
|---|---|---|---|
choice |
objek label → deskripsi | label terpilih + peluang tiap label | klasifikasi ke kategori |
score |
array berurutan | angka desimal + label level | intensitas, prioritas, sentimen |
noul |
tidak ada | peluang pernyataan true |
deteksi ya/tidak |
Contoh lengkap ketiga tipe (ini isi preset triage di config/classify.py):
{
"department": {
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"billing": "invoices, payments, refunds",
"technical": "bugs, outages, system errors",
"sales": "pricing, plans, new contracts",
"other": "everything else"
}
},
"urgency": {
"type": "score",
"instructions": "How urgent is the request?",
"criteria": ["not urgent", "soon", "immediate"]
},
"refund": {
"type": "noul",
"instructions": "Does the customer ask for money back?"
}
}
Aturan penting
choicejangan lebih dari ~20 opsi. Semua opsi berbagi satu budget token yang tetap, jadi kalau label terlalu banyak, tiap label cuma kebagian sedikit token dan akurasinya jatuh tajam.scorepakai array, bukan objek. Urutan array itu penting — itulah yang menentukan level 0, 1, 2, dan seterusnya.instructionsditulis dalam bahasa Inggris walau isinya bisa bahasa apa pun. Instruksi bagian kepala (256 token) dan state diletakkan terpisah oleh[SEP]dalam model, jadi instruksi tidak ikut mengotori bahasa input.- Nama kolom output bebas kamu pilih. Kolom di kiri output itu key yang kamu pakai di
objek
questions, bukan dariinstructions.
Multibahasa
Checkpoint-nya memang varian multilingual (100+ bahasa), jadi input non-Inggris diharapkan berfungsi:
$ ./usage-classify.sh "二重に請求されました。返金をお願いします。"
refund : 0.9976
$ ./usage-classify.sh "双重收费了,请退款。"
refund : 0.9892
5. Pertanyaan sendiri
Preset triage cuma comprise 3 pertanyaan default. Untuk kasus kamu, bikin file JSON:
cat > my_questions.json <<'EOF'
{
"bahasa": {
"type": "choice",
"instructions": "What language is this text written in?",
"criteria": {
"id": "Indonesian",
"en": "English",
"fr": "French",
"zh": "Chinese"
}
},
"komplain": {
"type": "noul",
"instructions": "Does the customer complain or express dissatisfaction?"
},
"sentimen": {
"type": "score",
"instructions": "How positive is the tone?",
"criteria": ["very negative", "neutral", "positive", "very positive"]
}
}
EOF
./usage-classify.sh --questions my_questions.json "produk ini error terus sejak kemarin"
File-nya boleh berisi objek questions langsung (seperti di atas), atau objek request
lengkap yang punya key "questions". Runner mendeteksi keduanya.
Kamu juga bisa simpan pertanyaan sebagai preset permanen di config/classify.py supaya bisa
dipakai cuma dengan --preset nama:
PRESETS = {
"triage": { ... }, # bawaan
"bahasa": { ... }, # preset kamu
}
DEFAULT_PRESET = "triage"
./usage-classify.sh --preset bahasa "produk ini error terus sejak kemarin"
6. Mode server (lebih cepat)
Tiap kali runner dipanggil dalam mode CLI, ia me-spawn laya-cli dan load ulang bobot
644MB dari disk. Itu yang bikin total ~5–7 detik, padahal inferensinya cuma 0,6 detik.
Kalau kamu classifies berulang, biarkan model tetap resident di memory lewat HTTP server bawaan laya.cpp:
third_party/laya.cpp/build-cpu/bin/laya-cli --server --port 8080 \
--model models/convaiinnovations/laya --variant multilingual --cpu &
Lalu arahkan runner ke sana:
LAYA_URL=http://127.0.0.1:8080 ./usage-classify.sh "teks kamu"
Hasilnya ~0,6 detik bukan ~6 detik — sekitar 8x lebih cepat, dengan jawaban yang identik (bisa diverifikasi langsung: bandingkan output CLI dan server untuk input yang sama).
Urutan prioritas URL
--urldi CLI- environment variable
LAYA_URL LAYA_URLdiconfig/classify.py
Kalau server mati
Runner otomatis fallback ke mode CLI, dengan informasi di stderr:
laya.cpp server at http://127.0.0.1:8080 unavailable (Connection refused). Falling back to the CLI.
Jadi tidak ada risiko kalau lupa nyalakan server atau server-nya crash.
Endpoint lain yang tersedia
Server laya.cpp juga menyediakan POST /predict (bisa batch beberapa request sekaligus, dan
mengembalikan envelope CLI lengkap termasuk elapsed_ms), GET /health, dan GET /v1/models.
Runner memakai /v1/systemone yang bentuk balasannya lebih ringkas.
7. Output JSON
./usage-classify.sh "teks" --json
Menghasilkan envelope mentah laya.cpp dengan probabilitas presisi penuh (bukan 4 desimal) — berguna kalau mau pipe ke program lain:
{
"results": [
{
"model": "laya-rl-agent",
"answers": {
"refund": {
"type": "noul",
"confidence": 0.7751,
"action": { "act_probability": 1.0 },
"noul": 0.7751
}
},
"usage": { "input_tokens": 176, "output_tokens": 0 }
}
],
"elapsed_ms": 732.14,
"backend": "CPU",
"device": "12th Gen Intel(R) Core(TM) i7-12700H"
}
Bentuk tiap tipe jawaban:
| Tipe | Field yang ada |
|---|---|
choice |
type, choice, probabilities, confidence, action.act_probability |
score |
type, score, legend, probabilities, confidence, action.act_probability |
noul |
type, noul, confidence, action.act_probability |
8. Semua opsi CLI
usage: classify_runner.py [-h] [--state-file STATE_FILE] [--preset PRESET]
[--questions QUESTIONS] [--url URL] [--json]
[state]
positional arguments:
state State yang akan diklasifikasi (teks biasa atau JSON).
Hilangkan kalau mau baca dari --state-file.
options:
--state-file STATE_FILE File yang berisi state (teks biasa atau JSON)
--preset PRESET Nama preset pertanyaan dari config/classify.py
--questions QUESTIONS File JSON berisi objek questions, mengoverride --preset
--url URL Base URL server laya.cpp, mengoverride LAYA_URL dan config
--json Cetak envelope respons mentah sebagai JSON
-h, --help Tampilkan bantuan
Catatan:
--statebukan sebuah flag — state diberikan sebagai argumen posisi. Writer--stateakan menghasilkan error, bukan diam-diam diperlakukan sebagai nama file.
Exit code:
| Kode | Arti |
|---|---|
0 |
Berhasil |
1 |
Gagal — preset tidak dikenal, file tidak ada, state kosong, atau inference error |
Contoh pemakaian error yang umum:
$ ./usage-classify.sh --preset salah "teks"
Unknown preset: salah. Available: triage
$ ./usage-classify.sh
Usage: ./usage-classify.sh <state> [--preset <name>] [--questions <file.json>] [--json]
9. Konfigurasi
Semua default ada di config/classify.py:
LAYACPP = <repo>/third_party/laya.cpp
CLI = LAYACPP / "build-cpu" / "bin" / "laya-cli"
MODEL_ROOT = MODEL_DIR / "convaiinnovations" / "laya"
VARIANT = "multilingual"
CHECKPOINT = MODEL_ROOT / VARIANT / "model.safetensors"
BACKEND = "cpu" # cpu | cuda | vulkan | coreml
ALLOW_TRUNCATION = False
LAYA_URL = "" # "" = spawn CLI per jalan
TIMEOUT = 120
PRESETS = { "triage": {...} }
DEFAULT_PRESET = "triage"
| Variabel | Fungsi |
|---|---|
CLI |
Lokasi binary laya-cli hasil build |
MODEL_ROOT |
Direktori induk checkpoint |
VARIANT |
Varian checkpoint: multilingual, english, atau typed-decisions |
BACKEND |
Backend komputasi. cpu = pure CPU, tanpa GPU sama sekali |
ALLOW_TRUNCATION |
Kalau True, input kepanjangan dipotong diam-diam. Default: request ditolak |
LAYA_URL |
Fallback URL server kalau env dan flag tidak di-set |
PRESETS |
Kumpulan pertanyaan siap pakai |
DEFAULT_PRESET |
Preset yang dipakai kalau --preset tidak diberikan |
Ganti ke GPU nanti
Ubah BACKEND jadi "cuda" atau "vulkan". Tapi kamu harus pakai binary laya.cpp yang dikompilasi
dengan backend tersebut — build CPU-only (yang sekarang) tidak bisa. Detail di
README.md.
10. Setup dari nol
Di mesin ini sudah selesai. Kalau perlu diulang (misal di mesin lain):
./build-laya.sh
Script-nya tidak butuh sudo sama sekali:
cmakedanninjadiambil dari pip wheelnlohmann/jsondiekstrak ke prefix lokalthird_party/nlohmann-install- satu-satunya kebutuhan sistem adalah
libicu-dev
Script aman dijalankan berulang — setiap langkah dilewati kalau output-nya sudah ada.
Yang dilakukan script:
- Clone
laya.cppbeserta submodulnya (ggml,cpp-httplib) kethird_party/laya.cpp - Pasang
cmake(versi 3.x) danninjalewat pip kalau belum ada - Ekstrak
nlohmann/jsonke prefix lokal - Build CPU-only ke
third_party/laya.cpp/build-cpu
Tiga hal yang wajib
| Flag | Kenapa wajib |
|---|---|
-DLAYA_CUDA=OFF |
Default-nya ON, dan konfigurasi akan gagal mencari nvcc |
cmake versi 3.x |
ggml yang di-pin laya.cpp deklarasikan cmake_minimum_required(3.14...3.28) dan tidak bisa dikonfigurasi di CMake 4 |
--cpu saat runtime |
Default laya-cli adalah backend CUDA, jadi build CPU-only akan gagal start tanpa flag ini |
Kenapa harus build dari sumber?
Upstream laya.cpp tidak menyediakan binary prebuilt khusus CPU — rilis Linux hanya CUDA 12, CUDA 13, dan Vulkan. Kalau di mesin dengan GPU NVIDIA, kamu bisa unduh binary prebuilt dan menghemat langkah build, tapi build CPU-only ini nol dependensi GPU.
Struktur checkpoint
Checkpoint ada di models/convaiinnovations/laya/multilingual/.
Penting:
laya-climenambahkan nama varian ke path--modeluntuk semua varian non-English. Jadi checkpoint multilingual harus berada di<MODEL_ROOT>/multilingual/.Ada salinan bobot yang identik di
models/convaiinnovations/laya-multilingual/, tapi layout itu tidak bisa dipakai laya.cpp — direktori itu untuk implementasi referensi Python.
11. Catatan penting
Dua keterbatasan dari model card upstream yang belum diatasi runner ini. Keduanya penting sebelum kamu mempercayai angkanya:
1. Probabilitasnya belum dikalibrasi
Model ini di-ship uncalibrated dengan temperature = [1.0, 1.0, 1.0] tanpa bucket per
jumlah opsi. Akibatnya dia sistematis over-confident — rata-rata confidence 0,75–0,83
padahal akurasinya jauh lebih rendah.
Jadi nilai confidence=1.0 di output belum tentu akurat.
Untuk membaikkannya, refit satu temperatur per kombinasi (tipe pertanyaan, jumlah opsi) di data held-out milikmu sendiri. Dari model card, ini memindahkan mean ECE dari 0,314 → 0,106. Lakukan ini sebelum memperlakukan probabilitas sebagai ambang keputusan.
2. Tipe noul bisa under-report true
Pada input yang jelas positif, satu pengukuran menempatkan P(true) di kisaran 0,5 sementara kasus
negatifnya benar-benar dekat 0. Kalau jawaban noul terasa lemah, cek ulang dengan versi dua
opsi:
{
"cek": {
"type": "choice",
"instructions": "Does the text ask for a refund?",
"criteria": {
"A": "no, it does not ask for a refund",
"B": "yes, it asks for a refund"
}
}
}
3. Bahasa dengan sumber daya rendah masih lemah
SWE, Tamil, Amharic masih berada di rentang 0,110–0,250. Check akurasi di bahasa-bahasa yang memang kamu pakai, jangan diasumsikan seragam.
4. Model multilingual lebih lemah untuk teks Inggris
Makro accuracy 0,619 (multilingual) vs 0,684 (checkpoint Inggris). Kalau mostly English, routing
ke checkpoint laya (Inggris) lebih akurat daripada memaksakan pakai multilingual.
Ringkasan perintah
# Klasifikasi teks
./usage-classify.sh "teks kamu"
# Dari file
./usage-classify.sh --state-file ticket.txt
# Pertanyaan sendiri
./usage-classify.sh --questions my_questions.json "teks kamu"
# Output JSON untuk di-pipe
./usage-classify.sh "teks kamu" --json
# Mode cepat: nyalakan server sekali
third_party/laya.cpp/build-cpu/bin/laya-cli --server --port 8080 \
--model models/convaiinnovations/laya --variant multilingual --cpu &
LAYA_URL=http://127.0.0.1:8080 ./usage-classify.sh "teks kamu"
# Setup (sekali saja, tanpa sudo)
./build-laya.sh