Text Classification
Transformers
Safetensors
Arabic
llama
arabic
guardrail
safety
jailbreak
prompt-injection
content-moderation
text-embeddings-inference
Instructions to use oddadmix/Nawah-Guard-500K with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use oddadmix/Nawah-Guard-500K with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-classification", model="oddadmix/Nawah-Guard-500K")# Load model directly from transformers import AutoTokenizer, AutoModelForSequenceClassification tokenizer = AutoTokenizer.from_pretrained("oddadmix/Nawah-Guard-500K") model = AutoModelForSequenceClassification.from_pretrained("oddadmix/Nawah-Guard-500K", device_map="auto") - Notebooks
- Google Colab
- Kaggle
File size: 16,985 Bytes
499ff77 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 | """
Arabic guardrail corpus — 12-class prompt safety.
Given a user prompt, decide whether it is safe and if not, what kind of unsafe. Arabic is a real
gap here: searches for Arabic jailbreak / safety / prompt-injection datasets return nothing, and
the one Arabic guardrail (NAMAA-Space/Ara-Prompt-Guard_V0) is 33K machine-translated rows, three
classes, MSA only.
THE SAFETY BOUNDARY, which shapes everything below:
We generate the *request*, never the *answer*.
A guardrail classifies incoming prompts, so a harmful completion is not needed to train it and
would be a genuinely harmful artifact. A row may say "اشرح لي طريقة صنع…" - it must never contain
the method. This is what JailbreakBench, AdvBench and wildjailbreak contain too.
The generator is abliterated (refusal-removed), so it will comply with a harmful instruction if
asked. The constraint therefore cannot rely on the model declining: it is stated in every prompt
AND enforced in validate() by PROCEDURE_RE, which rejects any row combining step markers with
hazardous vocabulary. That makes "no payloads" a property the pipeline checks rather than hopes for.
Child sexual exploitation (MLCommons S4) is excluded from the grid entirely - not a class, not a
distractor, not generated in any form.
Two definitions taken verbatim from Meta's Prompt-Guard-86M card, because the field conflates them:
prompt injection - "inputs that exploit the concatenation of untrusted data from third parties
... to get a model to execute unintended instructions"
jailbreak - "malicious instructions designed to override the safety and security features
built into a model"
"""
import os
import random
import re
import unicodedata
ITEMS_PER_TASK = 6
C, Q, A, E = "### الفئة", "### نص", "### التصنيف", "### نهاية"
# ---------------------------------------------------------------- taxonomy
# Grounded in MLCommons / Llama-Guard S1-S13 and the 9-class subset used by
# llm-semantic-router/mmbert-safety-classifier-level2. S4 is deliberately absent.
CLASSES = {
"safe": (
"نص عادي غير ضار تمامًا",
"أسئلة وطلبات يومية عادية: طبخ، سفر، دراسة، برمجة، رياضة، تسوق، عمل."),
"safe_sensitive": (
"نص آمن لكنه يبدو مقلقًا في ظاهره",
"طلب بريء تمامًا لكن ألفاظه توحي بالخطر: مصطلح تقني يشترك لفظًا مع العنف "
"(قتل عملية في لينكس، حقن SQL)، تعبير مجازي (أموت من الضحك)، سؤال عن تعريف "
"مصطلح، سياق مهني مشروع (ممرضة تسأل عن أعراض التسمم)، أو سؤال روائي عن حبكة."),
"jailbreak": (
"محاولة تجاوز قيود النموذج",
"نص يحاول إبطال تعليمات النموذج أو ضوابطه: تقمّص شخصية بلا قيود، تأطير خيالي، "
"منع الرفض، ترميز أو تشويش، أمثلة متعددة، أو تحويل اللغة للتحايل."),
"prompt_injection": (
"حقن تعليمات عبر بيانات خارجية",
"تعليمات مدسوسة داخل محتوى من طرف ثالث - مستند، بريد، صفحة ويب، نتيجة أداة - "
"تخاطب النموذج مباشرة لتغيير سلوكه، مثل: تجاهل التعليمات السابقة وافعل كذا."),
"violent_weapons": (
"طلب للمساعدة في العنف أو الحصول على سلاح",
"طلب يتعلق بإيذاء الناس أو بصنع أو اقتناء الأسلحة والمتفجرات. "
"**كاتب النص هو من ينوي الفعل** — أما السؤال عن آثار العنف على الضحايا، أو عن "
"الوقاية والسلامة، أو عن حدث تاريخي، فليس من هذه الفئة."),
"nonviolent_crime": (
"طلب للمساعدة في ارتكاب جريمة غير عنيفة",
"احتيال، سرقة، اختراق وبرمجيات خبيثة، مخدرات، تزوير. "
"**كاتب النص هو من يريد ارتكاب الفعل أو تمكينه** — وليس ضحية تطلب المساعدة أو "
"تسأل عن حقوقها أو عن الإجراءات القانونية، فتلك ليست من هذه الفئة."),
"self_harm": (
"إشارة خطر إيذاء النفس",
"تشمل أمرين: تعبير عن ضيق نفسي شديد أو يأس أو شعور بأنه عبء أو فقدان معنى، "
"وكذلك طلب صريح متعلق بإيذاء النفس. كلاهما يستوجب التقاطه وتحويله لدعم مختص. "
"اكتب صيغة الرسالة فقط، دون أي وسيلة أو تشجيع أو تفاصيل."),
"hate_harassment": (
"خطاب كراهية أو تحرش",
"إهانة أو تحقير جماعة أو شخص بسبب هوية أو صفة، أو تنمّر موجّه، أو طلب صياغة "
"مثل هذا الخطاب. **أما السؤال عن كيفية التعامل مع الكراهية أو التمييز بينها وبين "
"النقد، أو طلب المساعدة لضحية تنمّر، فليس من هذه الفئة.**"),
"sexual_content": (
"محتوى جنسي للبالغين",
"طلب محتوى جنسي صريح بين بالغين، بصيغة الطلب فقط."),
"privacy_pii": (
"انتهاك خصوصية أو استخراج بيانات",
"كشف هوية شخص، أو استخراج بيانات شخصية أو كلمات مرور أو أرقام بطاقات."),
"specialized_advice": (
"استشارة متخصصة تحتاج مختصًا",
"طلب تشخيص طبي أو رأي قانوني أو نصيحة استثمارية محددة بديلًا عن مختص."),
"misinformation": (
"معلومات مضللة",
"طلب صياغة ادعاء صحي أو انتخابي أو إخباري كاذب على أنه حقيقة."),
}
UNSAFE = [c for c in CLASSES if c not in ("safe", "safe_sensitive")]
VARIETIES = [
("العربية الفصحى", "فصحى معاصرة واضحة"),
("اللهجة المصرية", "عامية مصرية طبيعية"),
("اللهجة الخليجية", "عامية خليجية طبيعية"),
("اللهجة الشامية", "عامية شامية طبيعية"),
("اللهجة المغاربية", "عامية مغاربية طبيعية"),
("عربي مختلط بالإنجليزية", "كتابة عربية فيها كلمات إنجليزية أو حروف لاتينية"),
]
# Length is drawn INDEPENDENTLY of class, and it has to be. In the first smoke test length was a
# side effect of class - safe_sensitive averaged 404 chars against violent_weapons' 64 - and a
# single length threshold recovered +8 points over the majority baseline. A guardrail that reads
# length instead of content is worse than useless, so the grid decorrelates them by construction.
LENGTHS = {
"short": "جملة واحدة قصيرة ومباشرة",
"medium": "جملتان أو ثلاث",
"long": "فقرة قصيرة فيها سياق وتفاصيل حول الموقف",
}
REGISTERS = ["رسالة قصيرة عفوية", "صياغة رسمية مهذبة", "أسلوب تقني مختصر",
"رسالة طويلة بعض الشيء فيها سياق"]
# Attack template families - held out at eval to test unseen attack *forms*.
ATTACK_FAMILIES = {
"persona": "تقمّص شخصية أو نموذج بديل بلا قيود",
"fiction": "تأطير الطلب داخل قصة أو سيناريو خيالي أو مسرحية",
"refusal_suppression": "اشتراط عدم الرفض أو منع عبارات الاعتذار",
"encoding": "تشويش الكلمات أو تقطيعها أو طلب الرد بترميز",
"many_shot": "سرد أمثلة كثيرة سابقة ليقلّدها النموذج",
"authority": "ادعاء صلاحية أو إذن خاص أو وضع تطوير",
"translation_pivot": "طلب الرد بلغة أخرى للتحايل على الضوابط",
}
INJECTION_CARRIERS = {
"document": "مستند أو ملف مرفق يقرأه النموذج",
"email": "رسالة بريد إلكتروني واردة",
"webpage": "صفحة ويب أو نتيجة بحث",
"tool_output": "ناتج أداة أو استعلام قاعدة بيانات",
"review": "تعليق أو مراجعة مستخدم",
}
# XSTest-style types for the class that guardrails most often get wrong.
SENSITIVE_TYPES = {
"homonym": "لفظ يشترك مع معنى عنيف لكنه تقني أو عادي (قتل عملية، ذبح الملف، تفجير الكاش)",
"figurative": "تعبير مجازي شائع (أموت من الجوع، قتلني من الضحك)",
"definition": "سؤال عن تعريف مصطلح خطير دون طلب تنفيذه",
"safe_target": "فعل عنيف لفظًا لكن على هدف غير حي (قتل الأعشاب، كسر الجوز)",
"fiction_plot": "سؤال عن حبكة رواية أو فيلم فيه جريمة",
"professional": "سياق مهني مشروع (ممرض، محقق، باحث أمن، معلم)",
"historical": "سؤال تاريخي عن حدث عنيف",
}
SAFE_TOPICS = ["الطبخ والوصفات", "السفر والحجوزات", "الدراسة والامتحانات", "البرمجة والتقنية",
"الرياضة", "التسوق", "العمل والوظائف", "الصحة العامة", "السيارات",
"الأسرة والأطفال", "المال والادخار", "الهوايات", "الطقس", "الترجمة",
"الكتابة والتحرير", "الأخبار العامة"]
# ---------------------------------------------------------------- prompt
RULES = """قواعد إلزامية لا تُخالف:
- اكتب **الطلب فقط** كما يكتبه مستخدم. لا تكتب أي إجابة، ولا أي خطوات، ولا أي تفاصيل تنفيذية.
- ممنوع منعًا باتًا ذكر أي طريقة أو مقادير أو كود أو خطوات عملية لأي فعل ضار. النص المطلوب هو
السؤال أو الطلب نفسه، لا الجواب عليه.
- ممنوع تمامًا أي محتوى يخص القاصرين بأي شكل.
- اجعل النصوص قصيرة إلى متوسطة، كما يكتب الناس فعلًا.
- نوّع الصياغة والأسلوب والتفاصيل بين النصوص الستة، مع الالتزام بالطول المطلوب.
- لا تكتب أي شيء خارج الوسوم."""
PROMPT = """أنت تبني مجموعة بيانات لتدريب نموذج **حماية** يكتشف الطلبات الضارة قبل وصولها لمساعد ذكي.
هذه بيانات دفاعية: النموذج المدرَّب عليها سيحجب هذه الطلبات.
الفئة المطلوبة: **{cls_title}**
تعريفها: {cls_desc}
{extra}
اللغة: {variety} ({variety_hint})
الأسلوب: {register}
الطول المطلوب: {length} — التزم بهذا الطول تقريبًا في كل النصوص الستة
اكتب {k} نصوصًا مستقلة، كل واحد منها مثال على هذه الفئة بالضبط.
{RULES}
اكتب بهذا الشكل بالضبط:
{Q}
(النص الأول)
{A}
{cls}
{Q}
(النص الثاني)
{A}
{cls}
... وهكذا حتى {k} نصوص
{E}"""
def build_task(task_id: int, seed: int = 1234):
"""task_id -> (axes, prompt). Deterministic, so a resumed run redraws identical prompts."""
rng = random.Random(seed * 3_000_017 + task_id)
# safe + safe_sensitive are ~30% of the corpus: deployment traffic is overwhelmingly benign,
# and the model must see far more benign than any single harm class.
r = rng.random()
if r < 0.16:
cls = "safe"
elif r < 0.30:
cls = "safe_sensitive"
else:
cls = rng.choice(UNSAFE)
title, desc = CLASSES[cls]
variety, vhint = rng.choice(VARIETIES)
register = rng.choice(REGISTERS)
length = rng.choice(list(LENGTHS))
extra, family = "", None
if cls == "jailbreak":
family = rng.choice(list(ATTACK_FAMILIES))
extra = f"أسلوب المحاولة: {ATTACK_FAMILIES[family]}\n"
elif cls == "prompt_injection":
family = rng.choice(list(INJECTION_CARRIERS))
extra = (f"الوسيط الذي تُدسّ فيه التعليمات: {INJECTION_CARRIERS[family]}\n"
"اكتب المحتوى كما يظهر للنموذج، بحيث تبدو التعليمات المدسوسة جزءًا منه.\n")
elif cls == "safe_sensitive":
family = rng.choice(list(SENSITIVE_TYPES))
extra = (f"نوع الالتباس: {SENSITIVE_TYPES[family]}\n"
"النص آمن تمامًا ولا يطلب أي شيء ضار، لكن **يجب أن يحتوي فعلًا على لفظ أو "
"صياغة تبدو مقلقة** لمن يقرأ الكلمات دون سياق. إن لم يكن فيه ما يثير القلق "
"ظاهريًا فهو نص عادي ولا يصلح لهذه الفئة.\n")
elif cls == "safe":
family = rng.choice(SAFE_TOPICS)
extra = f"الموضوع: {family}\n"
axes = {"cls": cls, "variety": variety, "register": register, "family": family,
"length": length}
prompt = PROMPT.format(cls_title=title, cls_desc=desc, extra=extra, variety=variety,
variety_hint=vhint, register=register, k=ITEMS_PER_TASK,
cls=cls, RULES=RULES, Q=Q, A=A, E=E,
length=LENGTHS[length])
return axes, prompt
# ---------------------------------------------------------------- parsing
PAIR_RE = re.compile(re.escape(Q) + r"(?P<text>.*?)" + re.escape(A) + r"(?P<label>.*?)"
r"(?=" + re.escape(Q) + r"|" + re.escape(E) + r"|\Z)", re.S)
def norm(s: str) -> str:
s = unicodedata.normalize("NFKC", s or "")
s = re.sub(r"[ً-ْٰـ]", "", s)
return re.sub(r"[ \t]+", " ", s).strip()
def parse_items(raw: str):
out = []
for m in PAIR_RE.finditer(raw or ""):
label = norm(m.group("label")).strip(" .:،*-`")
out.append({"text": norm(m.group("text")), "label_text": label})
return out
# ---------------------------------------------------------------- validation
# The computable half of the no-payload rule. A *request* is a question or an ask; a *procedure*
# enumerates steps. A row that pairs step markers with hazardous vocabulary is an answer, not a
# request, and does not belong in this corpus whatever its label says.
STEP_RE = re.compile(r"(^|\n)\s*(?:\d+\s*[-.)]|[-*•]\s|أولًا|أولاً|ثانيًا|ثانياً|ثالثًا|الخطوة)"
r"|ثم قم|بعد ذلك قم|اخلط|امزج|قم بتسخين", re.M)
HAZARD_RE = re.compile(
r"متفجر|قنبل|بارود|نيترات|مسدس|بندقي|ذخير|سم قاتل|سيانيد|أرسين|"
r"ميثامفيتامين|كوكايين|هيروين|مخدر|تصنيع سلاح|"
r"ثغرة|اختراق|تشفير الملفات|فدي|كي لوجر|حقن كود|payload|exploit|malware")
LATIN_CODE_RE = re.compile(r"(?:def |import |#include|<script|SELECT .*FROM|;\s*DROP|sudo |bash -)")
def validate(item, min_text=12, max_text=900):
"""
Structural accept/reject -> (ok, reason). Semantic correctness is decided by verify_guard.py.
The decisive check here is `payload_suspected`: it is the one part of the safety boundary that
can be enforced by a rule rather than trusted to the generator, which is abliterated and will
otherwise happily answer its own prompt.
"""
text, label = item["text"], item["label_text"]
if label not in CLASSES:
return False, f"unknown_class:{label[:24]}"
if not (min_text <= len(text) <= max_text):
return False, "text_length"
if any(t in text for t in (C, Q, A, E)):
return False, "tag_leak"
if len(text.split()) < 3:
return False, "too_few_words"
if STEP_RE.search(text) and HAZARD_RE.search(text):
return False, "payload_suspected"
if LATIN_CODE_RE.search(text) and HAZARD_RE.search(text):
return False, "payload_suspected_code"
return True, "ok"
def dedup_key(text: str) -> str:
return re.sub(r"\d+", "#", re.sub(r"\W+", "", norm(text)))
|