feat(ai-moderation): parse moderation category from LLM fallback response instead of using static defaults

This commit is contained in:
MythEclipse
2026-06-04 13:49:35 +07:00
parent 3fc26592fc
commit 259b0babb5
@@ -1740,23 +1740,40 @@ Jawab HANYA dengan satu kata: clean, warn, atau flagged`;
status = "clean"; status = "clean";
} }
// ── Step 2: Real analysis text (only if not clean) ── // ── Step 2: Real analysis text + category (only if not clean) ──
// We ask the LLM for a real reason — no static/dummy text. // We ask the LLM for a real reason and a category word — no complex JSON.
let analysis: string; let analysis: string;
let category = "";
if (status === "clean") { if (status === "clean") {
analysis = `${message.username ?? "user"}: ${content.length > 200 ? content.slice(0, 200) + "..." : content}. Percakapan normal, tidak ada pelanggaran.`; analysis = `${message.username ?? "user"}: ${content.length > 200 ? content.slice(0, 200) + "..." : content}. Percakapan normal, tidak ada pelanggaran.`;
} else { } else {
category = status === "flagged" ? "harassment" : "spam"; // default fallback
const categoryOptions =
status === "flagged" ? "harassment, gambling, atau sara" : "spam";
const reasonPrompt = `Pesan berikut telah diklasifikasikan sebagai "${status}". const reasonPrompt = `Pesan berikut telah diklasifikasikan sebagai "${status}".
Pesan: "${truncatedContent}" Pesan: "${truncatedContent}"
Jelaskan dalam 1-2 kalimat Bahasa Indonesia: APA yang melanggar dan KENAPA. Jangan gunakan kata "mungkin" atau "sepertinya". Jangan tulis ulang pesan. Langsung ke alasan. Jelaskan dalam 1-2 kalimat Bahasa Indonesia: APA yang melanggar dan KENAPA. Jangan gunakan kata "mungkin" atau "sepertinya". Jangan tulis ulang pesan. Langsung ke alasan.
Contoh jawaban untuk "flagged": "Mengandung kata kasar terarah ke individu tertentu sebagai hinaan." Setelah alasan, sebutkan Kategori: ${categoryOptions}
Contoh jawaban untuk "flagged": "Promosi situs judi online dengan link dan ajakan."
Contoh jawaban untuk "warn": "Promosi channel Discord tanpa konteks, berpotensi spam." Contoh untuk "flagged":
Contoh jawaban untuk "warn": "Bahasa kasar ringan yang tidak terarah, tidak melanggar berat tapi perlu diingatkan."`; Mengandung kata kasar terarah ke individu tertentu sebagai hinaan.
Kategori: harassment
Contoh untuk "flagged":
Promosi situs judi online dengan link dan ajakan.
Kategori: gambling
Contoh untuk "warn":
Promosi channel Discord tanpa konteks, berpotensi spam.
Kategori: spam
Contoh untuk "warn":
Bahasa kasar ringan yang tidak terarah.
Kategori: spam`;
try { try {
const completion = await llmChat({ const completion = await llmChat({
@@ -1772,9 +1789,25 @@ Contoh jawaban untuk "warn": "Bahasa kasar ringan yang tidak terarah, tidak mela
analysis = `Pesan diklasifikasikan sebagai ${status} oleh sistem moderasi otomatis.`; analysis = `Pesan diklasifikasikan sebagai ${status} oleh sistem moderasi otomatis.`;
} }
// ── Parse category from "Kategori: xxx" line ──
const categoryMatch = analysis.match(
/[Kk]ategori:\s*(\w+)/i,
);
if (categoryMatch) {
const parsedCat = categoryMatch[1].toLowerCase();
// Only accept known categories
if (
["harassment", "spam", "gambling", "sara"].includes(parsedCat)
) {
category = parsedCat;
}
// Strip the "Kategori:" line from the analysis text so it's cleaner
analysis = analysis.replace(/[Kk]ategori:\s*\w+\s*/i, "").trim();
}
log.info( log.info(
{ messageId: message.id, status, analysis: analysis.slice(0, 100) }, { messageId: message.id, status, category, analysis: analysis.slice(0, 100) },
"Simple fallback step 2 — reason", "Simple fallback step 2 — reason + category",
); );
} catch (error) { } catch (error) {
analysis = `Pesan diklasifikasikan sebagai ${status} oleh sistem moderasi otomatis berdasarkan analisis konten.`; analysis = `Pesan diklasifikasikan sebagai ${status} oleh sistem moderasi otomatis berdasarkan analisis konten.`;
@@ -1788,7 +1821,11 @@ Contoh jawaban untuk "warn": "Bahasa kasar ringan yang tidak terarah, tidak mela
} }
} }
// Build the result fields // Build the result fields using parsed category
const flags: string[] =
status === "clean" ? [] : [category];
const categories: string[] =
status === "clean" ? [] : [category];
const score = status === "flagged" ? 0.7 : status === "warn" ? 0.4 : 0; const score = status === "flagged" ? 0.7 : status === "warn" ? 0.4 : 0;
const severity: "none" | "low" | "medium" | "high" | "critical" = const severity: "none" | "low" | "medium" | "high" | "critical" =
status === "flagged" ? "medium" : status === "warn" ? "low" : "none"; status === "flagged" ? "medium" : status === "warn" ? "low" : "none";
@@ -1797,12 +1834,10 @@ Contoh jawaban untuk "warn": "Bahasa kasar ringan yang tidak terarah, tidak mela
return { return {
messageId: message.id, messageId: message.id,
status, status,
flags: flags,
status === "flagged" ? ["harassment"] : status === "warn" ? ["spam"] : [],
score, score,
analysis, analysis,
categories: categories,
status === "flagged" ? ["harassment"] : status === "warn" ? ["spam"] : [],
severity, severity,
confidence, confidence,
recommendedAction: recommendedAction: