diff --git a/services/discord-gateway/src/modules/ai-moderation/prompts/output.ts b/services/discord-gateway/src/modules/ai-moderation/prompts/output.ts index a261eea..a697206 100644 --- a/services/discord-gateway/src/modules/ai-moderation/prompts/output.ts +++ b/services/discord-gateway/src/modules/ai-moderation/prompts/output.ts @@ -40,7 +40,7 @@ Data konteks tersedia: (peta ringkasan kepribadian, di pesan USE Gunakan untuk personalisasi analysis, tapi: - Profil adalah KONTEKS, bukan bukti. Profil mencurigakan ≠ flag; profil bersih ≠ loloskan pelanggaran. - Perubahan perilaku mencolok (biasanya teknis tiba-tiba provokatif) layak dicatat di analysis. -- (kutipan pesan yang pernah di-flag) = pola pelanggaran lama. Gunakan untuk mendeteksi PENGULANGAN (mis. spam link yang sama, provokasi berulang), tapi JANGAN memflag pesan bersih hanya karena riwayat. +- (kutipan pesan yang pernah di-flag) = pola pelanggaran lama. Gunakan untuk mendeteksi PENGULANGAN KEKONSISTEN (spam link yang SAMA, provokasi yang MENGULANG KONTEN NYATA YANG SAMA). JANGAN pernah gunakan history untuk "menginterpretasi ulang" pesan bersih yang TERPISAH DARI riwayat. Setiap pesan BARU dinilai TERSAMBUNG (standalone). Jika tidak ada pola pengulangan yang jelas → CLEAN. Contoh: Jika sebelumnya ada pesan dengan link scam.example.com yang di-flag, dan pesan baru juga ada link scam.example.com → FLAG. Tapi jika pesan baru tentang "energi kinetik dari jatuh" tanpa link yang sama → CLEAN walaupun ada history lain. - JANGAN paksa referensi profil jika tidak relevan — analysis natural lebih baik. - Channel culture coding/teknis → pesan teknis lebih wajar; channel santai → slang lebih wajar. Jangan dipakai mengabaikan pelanggaran nyata. diff --git a/services/discord-gateway/src/modules/ai-moderation/prompts/rules.ts b/services/discord-gateway/src/modules/ai-moderation/prompts/rules.ts index d4b1636..4a9f2e7 100644 --- a/services/discord-gateway/src/modules/ai-moderation/prompts/rules.ts +++ b/services/discord-gateway/src/modules/ai-moderation/prompts/rules.ts @@ -35,10 +35,12 @@ export const SYSTEM_RULES = `Kamu adalah asisten moderasi konten untuk server Di Kata alat kelamin/anatomi seksual (kontol, memek, titten, tit, dick) atau istilah seksual eksplisit WAJIB di-flag sebagai vulgar_language/sexual_content — TANPA pengecualian bercanda, slang, atau "santai". ## Nilai Server — Diskriminasi -- Seksisme ("dasar perempuan", "logika cewek") → hate_speech (umum) / harassment (terarah). -- Ageisme ("dasar bocil", "tau aja lo tua") → hate_speech / harassment. -- Diskriminasi fisik ("gendut", "iteman", "cungkring") → harassment jika terarah. -- Serangan personal, penghinaan, merendahkan = tidak ditoleransi. Perbedaan pendapat wajar. +-Ketika sesuatu yang melanggar terjadi di channel, flag jika relevan. Setiap pesan dinilai BERDASARKAN ISINYA SENDIRI, bukan sekadar histori pengguna. +-Seksisme ("dasar perempuan", "logika cewek") → hate_speech (umum) / harassment (terarah). +-Ageisme ("dasar bocil", "tau aja lo tua") → hate_speech / harassment. +-Diskriminasi fisik ("gendut", "iteman", "cungkring") → harassment jika terarah. +-Serangan personal, penghinaan, merendahkan = tidak ditoleransi. Perbedaan pendapat wajar. ++**PESAN DINILAI SECARA STANDALONE:** Setiap pesan baru dinilai BERDASARKAN ISINYA SENDIRI. (jika ada) HANYA untuk mendeteksi POLA PENGULANGAN dengan JAMAK (spam link yang SAMA, provokasi berulang yang MENGANDALKAN KONTEN YANG SAMA). JANGAN gunakan history untuk "menginterpretasi ulang" pesan bersih yang TERPISAH DARI riwayat pelanggaran sebelumnya. Jika pesan tidak mengandung unsur yang BERPANDUAN PADA riwayat → tetap CLEAN. ## LARANGAN BERAT (ZERO TOLERANCE) - **LGBT:** Segala promosi, diskusi, pengakuan orientasi, coming out, atau curhat personal tentang LGBT WAJIB di-flag "sexual_deviation". Tidak ada pengecualian. diff --git a/services/discord-gateway/src/modules/ai-moderation/textBatchProcessor.ts b/services/discord-gateway/src/modules/ai-moderation/textBatchProcessor.ts index d455391..03f79bd 100644 --- a/services/discord-gateway/src/modules/ai-moderation/textBatchProcessor.ts +++ b/services/discord-gateway/src/modules/ai-moderation/textBatchProcessor.ts @@ -19,7 +19,6 @@ import { callModerationLLM } from "./llmCaller.js"; import { analyzeSingleMediaImage } from "./mediaAnalysisClient.js"; import { buildReferenceXml, - buildUserHistoryXml, buildUserProfileRef, buildUserProfilesBlock, escapeXml, @@ -41,10 +40,7 @@ import { buildTermGlossaryBlock } from "./termGlossary.js"; import { getRecentCorrectedModerations } from "./textCacheStore.js"; import { extractUrlsFromText, fetchUrlSafely } from "./urlFetcher.js"; import { getUserProfile } from "./userProfileStore.js"; -import { - getUserRecentInfractions, - initializeUserReputation, -} from "./userReputationStore.js"; +import { initializeUserReputation } from "./userReputationStore.js"; import type { MessageImagePart } from "./visionAnalyzer.js"; const log = createChildLogger("textBatchProcessor"); @@ -218,27 +214,7 @@ export async function runTextOnlyBatch( if (!userContexts.has(msg.user_id)) { const rep = await initializeUserReputation(msg.user_id, msg.guild_id); const repAttrs = formatReputationAttrs(rep); - let repXml = ``; - // Repeat offenders get their last flagged messages as - // so the LLM can recognize PATTERNS (same scam link, repeated - // provocation) — history is reference, never proof. Best-effort. - if (rep.total_infractions > 0) { - try { - const history = await getUserRecentInfractions(msg.user_id, 2); - const historyXml = buildUserHistoryXml( - history.map((h) => ({ - content: h.content ?? "", - severity: h.severity, - created_at: h.created_at, - })), - ); - if (historyXml) { - repXml = `\n${historyXml}\n`; - } - } catch { - // history is a bonus — fall back to attrs-only reputation - } - } + const repXml = ``; userContexts.set(msg.user_id, repXml); } if (!userProfiles.has(msg.user_id)) { diff --git a/services/discord-gateway/src/modules/ai-moderation/visionAnalyzer.ts b/services/discord-gateway/src/modules/ai-moderation/visionAnalyzer.ts index e1b5aee..c1b1963 100644 --- a/services/discord-gateway/src/modules/ai-moderation/visionAnalyzer.ts +++ b/services/discord-gateway/src/modules/ai-moderation/visionAnalyzer.ts @@ -66,7 +66,6 @@ import { } from "./mediaDownloader.js"; import { buildReferenceXml, - buildUserHistoryXml, buildUserProfileRef, escapeXml, formatReputationAttrs, @@ -90,10 +89,7 @@ import { import { buildTermGlossaryBlock } from "./termGlossary.js"; import { extractUrlsFromText } from "./urlFetcher.js"; import { getUserProfile } from "./userProfileStore.js"; -import { - getUserRecentInfractions, - initializeUserReputation, -} from "./userReputationStore.js"; +import { initializeUserReputation } from "./userReputationStore.js"; // --------------------------------------------------------------------------- // Types @@ -459,27 +455,9 @@ export async function prepareMediaMessage( ? buildUserProfileRef(target.user_id) : ""; - // Rich reputation — same shape as the text path: attrs + optional - // with the last flagged messages for repeat offenders. + // Rich reputation — attrs only, no user history injection (per channel context preference) const repAttrs = formatReputationAttrs(rep); - let repXml = ``; - if (rep.total_infractions > 0) { - try { - const history = await getUserRecentInfractions(target.user_id, 2); - const historyXml = buildUserHistoryXml( - history.map((h) => ({ - content: h.content ?? "", - severity: h.severity, - created_at: h.created_at, - })), - ); - if (historyXml) { - repXml = `\n${historyXml}\n`; - } - } catch { - // history is a bonus — fall back to attrs-only reputation - } - } + const repXml = ``; const isBot = resolveIsBot(target); const isEdited = resolveIsEdited(target);