GenGuard 1.0 Light
GenGuard 1.0 Light๋ ์ ํด์ฑ ํ์ง, Custom Policy, ๊ณต๊ฒฉ ํ์ง๋ฅผ ํ๋์ ๋ชจ๋ธ๋ก ์ฒ๋ฆฌํ๋ ๊ฐ๋๋ ์ผ ๋ชจ๋ธ์ ๋๋ค.
- Custom Policy ์ง์:
policy์ธ์์ ์ ์ฑ ์ ๋ฃ์ผ๋ฉด ์ฌํ์ต ์์ด ๊ทธ ์ ์ฑ ์ ๊ธฐ์ค์ผ๋ก ํ์ ํฉ๋๋ค. - ์ ํด์ฑ ํ์ง: ์ฌ์ฉ์ ์ ๋ ฅ๊ณผ ๋ชจ๋ธ ์๋ต์ ๋ชจ๋ ํ์ ํ๊ณ , ํด๋นํ๋ ์ ํด ๋ฒ์ฃผ(S1~S23)๋ ํจ๊ป ์ถ๋ ฅํ ์ ์์ต๋๋ค.
- ๋ค์ํ ๊ณต๊ฒฉ ์ ํ ํ์ง: ํ์ฅยทํ๋กฌํํธ ์ฃผ์ ์ ํฌํจํ ์ฌ๋ฌ ์ ํ์ ๊ณต๊ฒฉ์ ํ์งํฉ๋๋ค.
์ฑ๋ฅ
| ๋ชจ๋ธ | Korean Safety | English Safety | Attack Detection | Custom Policy | Long Context | Average |
|---|---|---|---|---|---|---|
| GenGuard 1.0 Light | 87.3 | 79.9 | 93.7 | 72.5 | 88.3 | 84.3 |
| gpt-oss-safeguard-20b | 85.7 | 76.7 | 78.0 | 72.7 | 82.6 | 79.1 |
| Qwen3Guard-Gen-8B | 82.8 | 78.0 | 91.8 | 31.6 | 83.6 | 73.6 |
| Shieldstral-1.0-3B | 85.5 | 80.3 | 87.5 | 39.6 | 69.1 | 72.4 |
| Nemotron-3.5-Content-Safety | 81.5 | 76.7 | 81.7 | 51.6 | 64.2 | 71.1 |
| Kanana-Safeguard-8B* | 78.8 | 74.9 | 70.7 | 26.3 | โ | 62.7 |
- * ํ์ ๋ชจ๋ธ์ ์ ๋ ฅ ๊ธธ์ด ์ ํ์ผ๋ก Long Context๋ฅผ ์ธก์ ํ ์ ์์ด, Long Context๋ฅผ ๋บ ๋ค ๋ฌถ์์ผ๋ก Average๋ฅผ ๋์ต๋๋ค. ๋๋จธ์ง ๋ชจ๋ธ์ Average๋ ๋ค์ฏ ๋ฌถ์ ์ ์ฒด์ ํ๊ท ์ ๋๋ค.
- Nemotron-3.5-Content-Safety๋ ์ถ๋ก ์ ๋ ๋ชจ๋๋ก ํ๊ฐํ์ผ๋ฉฐ, ์๋ ์ธก์ ๋ ๊ฐ์ ์ค์ ์ ๋๋ค.
๋ฒค์น๋ณ ์ ์
Korean Safety (F1)
| ๋ฒค์น | GenGuard 1.0 Light | gpt-oss-safeguard-20b | Nemotron-3.5-Content-Safety | Qwen3Guard-Gen-8B | Shieldstral-1.0-3B | Kanana-Safeguard-8B |
|---|---|---|---|---|---|---|
| RTP-LX (subset ko) | 94.3 | 92.0 | 90.2 | 83.8 | 88.2 | 90.2 |
| wildguardmix-test-ko | 85.4 | 84.2 | 79.6 | 84.8 | 85.4 | 73.6 |
| XSTest_ko | 82.4 | 80.8 | 74.6 | 79.9 | 82.9 | 72.8 |
English Safety (F1)
| ๋ฒค์น | GenGuard 1.0 Light | gpt-oss-safeguard-20b | Nemotron-3.5-Content-Safety | Qwen3Guard-Gen-8B | Shieldstral-1.0-3B | Kanana-Safeguard-8B |
|---|---|---|---|---|---|---|
| OpenAI Moderation | 81.6 | 82.2 | 75.3 | 68.4 | 78.7 | 72.0 |
| ToxicChat | 76.6 | 66.5 | 72.5 | 68.3 | 77.6 | 66.9 |
| BeaverTails | 84.2 | 79.6 | 83.0 | 86.5 | 85.7 | 82.8 |
| PKU-SafeRLHF | 63.2 | 60.5 | 61.6 | 70.9 | 66.7 | 67.5 |
| WildGuardTest | 87.1 | 85.0 | 82.8 | 86.7 | 86.8 | 78.5 |
| HarmBench | 86.9 | 86.7 | 84.8 | 87.1 | 86.6 | 82.0 |
Attack Detection (์ฐจ๋จ์จ)
| ๋ฒค์น | GenGuard 1.0 Light | gpt-oss-safeguard-20b | Nemotron-3.5-Content-Safety | Qwen3Guard-Gen-8B | Shieldstral-1.0-3B | Kanana-Safeguard-8B |
|---|---|---|---|---|---|---|
| WildJailbreak (eval) | 96.7 | 88.0 | 77.3 | 95.1 | 94.9 | 83.0 |
| wildjailbreak-eval-ko | 93.5 | 83.2 | 81.8 | 93.6 | 91.2 | 62.2 |
| MultiJail (subset ko) | 91.4 | 74.3 | 92.4 | 93.7 | 88.9 | 92.4 |
| BIPIA | 67.1 | 3.5โ | 18.6 | 20.7โ | 4.1โ | 9.1โ |
| Private Red-Team Bench | 92.9 | 66.5 | 75.4 | 85.0 | 75.2 | 45.1 |
- โ ๋ชจ๋ธ ์นด๋์ ํ๋กฌํํธ ์ฃผ์ ํ์ง ๊ธฐ๋ฅ์ด ๋ช ์๋์ง ์์ ๋ชจ๋ธ์ ๋๋ค.
- BIPIA(๋ฌธ์ ์ ๊ฐ์ ํ๋กฌํํธ ์ฃผ์ )๋ ๋น๊ต ๋ชจ๋ธ ๋๋ถ๋ถ์ด ์ฃผ์ ํ์ง๋ฅผ ์ง์ํ์ง ์์(โ) ์ฐธ๊ณ ์ฉ์ผ๋ก๋ง ์ฃ๊ณ ํ๊ท ๊ณ์ฐ์์๋ ์ ์ธํ์ต๋๋ค.
- Private Red-Team Bench๋ ํ๊ตญ์ด ์ค์ฌ(์์ดยท์ผ๋ณธ์ด ์ผ๋ถ ํฌํจ)์ ๋ด๋ถ ๋ ๋ํ ํ๊ฐ ์ธํธ๋ก, ํ๊ฐ ์ค์ผ์ ๋ง๊ธฐ ์ํด ๋ฌธํญ์ ๊ณต๊ฐํ์ง ์์ต๋๋ค.
Custom Policy (F1)
| ๋ฒค์น | GenGuard 1.0 Light | gpt-oss-safeguard-20b | Nemotron-3.5-Content-Safety | Qwen3Guard-Gen-8B | Shieldstral-1.0-3B | Kanana-Safeguard-8B |
|---|---|---|---|---|---|---|
| KSPG-Bench | 80.1 | 81.3 | 42.3 | 6.2โก | 18.1 | 9.2โก |
| CoSApien-ko | 89.3 | 86.4 | 81.4 | 77.5โก | 78.7 | 67.0โก |
| PoFBench | 62.4 | 68.0 | 48.6 | 27.2โก | 27.3 | 23.7โก |
| BARRED | 58.1 | 55.2 | 34.1 | 15.3โก | 34.2 | 5.4โก |
- โก ์ ์ฑ ์ ์ ๋ ฅ๋ฐ์ง ๋ชปํ๋ ๋ชจ๋ธ๋ก, ์์ฒด ์ ํด์ฑ ๋ถ๋ฅ๋ก ํ์ ํ์ต๋๋ค.
- KSPG-Bench(Korean Service Policy Guard Bench)๋ GenON์ด ๊ฐ๋ฐํด ๊ณต๊ฐํ ๋ฒค์น๋ก, ๊ฐ์ ์์ฒญ๋ ์๋น์ค ์ ์ฑ ์ ๋ฐ๋ผ ํ์ฉยท์ฐจ๋จ์ด ๋ฌ๋ผ์ ธ์ผ ํ๋ ์ํฉ์์ ๊ฐ๋๋ ์ผ์ด ์ ์ฑ ๋๋ก ํ์ ํ๋์ง ํ๊ฐํฉ๋๋ค. ์ ๋ฌด ๋ฒ์, ๊ท์ ๊ฒฝ๊ณ, ์๋ต ์ ๋ขฐ ์ธ ์์ญ์ ์ง๋ฌธ ํ์ ๊ณผ ์๋ต ํ์ ์ผ๋ก ๊ตฌ์ฑ๋ฉ๋๋ค.
Long Context (F1)
| ๋ฒค์น | GenGuard 1.0 Light | gpt-oss-safeguard-20b | Nemotron-3.5-Content-Safety | Qwen3Guard-Gen-8B | Shieldstral-1.0-3B | Kanana-Safeguard-8B |
|---|---|---|---|---|---|---|
| SafetyNIAH | 88.3 | 82.6 | 64.2 | 83.6 | 69.1 | โ |
- SafetyNIAH๋ ๊ธด ์ ์ ๋ฌธ์ ์์ ์จ๊ฒจ์ง ์ ํด ๋ฌธ์ฅ์ ์ฐพ์๋ด๋ ๋ฒค์น์ ๋๋ค. Kanana-Safeguard-8B(์ต๋ ์ ๋ ฅ 8,192 ํ ํฐ)๋ ์ ๋ ฅ ๊ธธ์ด ์ ํ์ผ๋ก ์ธก์ ์ด ๋ถ๊ฐํฉ๋๋ค(โ).
์๋
| ๋ชจ๋ธ | ํ ๊ฑด ์๋ต ์๊ฐ ์ค์๊ฐ(ms) | ์ด๋น ์ฒ๋ฆฌ ๊ฑด์, 32๊ฑด ๋์ |
|---|---|---|
| GenGuard 1.0 Light | 40 | 59 |
| gpt-oss-safeguard-20b | 587 | 6.5 |
| Qwen3Guard-Gen-8B | 71 | 37 |
| Nemotron-3.5-Content-Safety | 72 | 61 |
| Shieldstral-1.0-3B | 14 | 141 |
| Kanana-Safeguard-8B | 20 | 68 |
- wildguardmix-test-ko 3,375๊ฑด์ผ๋ก ์ธก์ ํ์ต๋๋ค.
- ์ฅ๋น๋ NVIDIA B200์ MIG๋ก ๋๋ 2g.45gb ํ ์นธ(B200์ ์ฝ 1/4)์ด๋ฉฐ, vLLM 0.30ยทbf16์ ์ผ์ต๋๋ค.
- ์๋ต ์๊ฐ์ 1,000๊ฑด ํ๋ณธ์ ํ ๊ฑด์ฉ ๋ณด๋ด ์ฐ ์ค์๊ฐ์ด๊ณ , ์ด๋น ์ฒ๋ฆฌ ๊ฑด์๋ 3,375๊ฑด ์ ์ฒด๋ฅผ 32๊ฑด์ฉ ๋์์ ๋ณด๋ด ์์ต๋๋ค.
๋ชจ๋ธ ์ ๋ณด
| ํญ๋ชฉ | ๋ด์ฉ |
|---|---|
| ๋ฒ ์ด์ค ๋ชจ๋ธ | google/gemma-4-E4B-it |
| ํ๋ผ๋ฏธํฐ | 8B |
| ์ง์ ์ธ์ด | ํ๊ตญ์ด, ์์ด |
| ์ต๋ ์ ๋ ฅ ๊ธธ์ด | 131,072 ํ ํฐ |
์ฌ์ฉ๋ฒ
1. ์ ๋ ฅ ๊ตฌ์ฑ
user ๋ฉ์์ง๋ ์ฌ์ฉ์ ์
๋ ฅ์ผ๋ก, assistant ๋ฉ์์ง๋ ๋ชจ๋ธ ์๋ต์ผ๋ก ํ์ ํฉ๋๋ค. ๋ฌธ์๋ ๊ฒ์ ๊ฒฐ๊ณผ ๋ฑ ๋ํ ํ์์ด ์๋ ํ
์คํธ๋ ๋จ์ผ user ๋ฉ์์ง๋ก ์
๋ ฅํฉ๋๋ค. ์ง์ํ๋ ๋ฉ์์ง ์ญํ ์ user์ assistant์
๋๋ค.
2. ์ถ๋ ฅ๊ณผ ํ์
Decision: block โ ํญ์ ์ถ๋ ฅ (block ๋๋ allow)
Response Refusal: refusal โ (์ต์
) request_refusal="/refusal" ์ผ ๋๋ง
Safety Categories: S3, S9 โ (์ต์
) request_categories="/categories" ์ผ ๋๋ง
์ ํด ๋ฒ์ฃผ ๋ฒํธํ (S1~S23)| ๋ฒํธ | ๋ฒ์ฃผ | ๋ฒํธ | ๋ฒ์ฃผ |
|---|---|---|---|
| S1 | Controlled/Regulated Substances | S13 | Needs Caution |
| S2 | Copyright/Trademark/Plagiarism | S14 | Other |
| S3 | Criminal Planning/Confessions | S15 | PII/Privacy |
| S4 | Fraud/Deception | S16 | Political/Misinformation/Conspiracy |
| S5 | Guns and Illegal Weapons | S17 | Profanity |
| S6 | Harassment | S18 | Sexual |
| S7 | Hate/Identity Hate | S19 | Sexual (minor) |
| S8 | High Risk Gov Decision Making | S20 | Suicide and Self Harm |
| S9 | Illegal Activity | S21 | Threat |
| S10 | Immoral/Unethical | S22 | Unauthorized Advice |
| S11 | Malware | S23 | Violence |
| S12 | Manipulation |
ํ์ ์ ์์ฑ๋ ํ
์คํธ ๋์ ํ๋ฅ ๋ก ๋ด๋ ๊ฒ์ ๊ถ์ฅํฉ๋๋ค. ํ๋กฌํํธ ๋ค์ Decision:์ ๋ถ์ด๊ณ ๋ค์ ํ ํฐ์ allow ยท block ํ๋ฅ ๋ง ๋ณด์, ์ฐจ๋จ ํ๋ฅ = P(block) / (P(allow) + P(block)) โฅ 0.5 ์ด๋ฉด ์ฐจ๋จํฉ๋๋ค. ๊ธฐ์ค๊ฐ์ ์๋น์ค์ ๋ง๊ฒ ์กฐ์ ํ ์ ์์ต๋๋ค.
3. ๊ถ์ฅ ํ๊ฒฝ
- vLLM 0.30.0 (torch 2.13, transformers 5.18, flashinfer 0.6.18)์ ๊ถ์ฅํฉ๋๋ค.
- transformers๋ก ์ง์ ์ถ๋ก ํ ๋๋ 5.8.1 ์ด์์ ์ฐ์ธ์.
- ํญ์
temperature=0(transformers๋do_sample=False)์ผ๋ก ์์ฑํ์ธ์. ์ค์ ํ์ผ์ ๊ธฐ๋ณธ๊ฐ์ด ์ํ๋ง์ด๋ผ ์ง์ ํ์ง ์์ผ๋ฉด ํ์ ์ด ํ๋ค๋ฆด ์ ์์ต๋๋ค.
4. ์ฝ๋ ์์
vLLMimport math
from transformers import AutoTokenizer
from vllm import LLM, SamplingParams, TokensPrompt
MODEL = "mncai/genguard-1.0-light"
DECISION, ALLOW, BLOCK = [87732, 236787], 2174, 3355 # "Decision:", " allow", " block" ํ ํฐ id
tok = AutoTokenizer.from_pretrained(MODEL)
llm = LLM(MODEL, dtype="bfloat16", max_model_len=8192)
# allow ยท block ๋ ํ ํฐ๋ง ๋ฐ์์ผ ํ๋ฅ ์ด ์ ํํฉ๋๋ค
params = SamplingParams(temperature=0, max_tokens=1, logprobs=2, allowed_token_ids=[ALLOW, BLOCK])
def prompt(messages, **kw):
ids = tok.apply_chat_template(messages, add_generation_prompt=True, tokenize=True, return_dict=False, **kw)
return TokensPrompt(prompt_token_ids=list(ids) + DECISION)
def p_block(out):
lp = out.outputs[0].logprobs[0]
a = lp[ALLOW].logprob if ALLOW in lp else -math.inf
b = lp[BLOCK].logprob if BLOCK in lp else -math.inf
return 1 / (1 + math.exp(a - b))
outs = llm.generate([
prompt([{"role": "user", "content": "์ฃผ๋ง์ ๊ฐ๊ธฐ ์ข์ ์์ธ ๊ทผ๊ต ์ฌํ์ง ์ถ์ฒํด ์ค."}]),
prompt([{"role": "user", "content": "์์ฆ ๋ณผ ๋งํ ๊ฐ์กฑ ์ํ ์ถ์ฒํด ์ค."}],
policy="๊ฐ๋ณ ์ฃผ์์ด๋ ๊ฐ์์์ฐ ์ข
๋ชฉ ์ถ์ฒ ์์ฒญ์ ์ฐจ๋จํ๋ค."), # ์ฌ๋ฌ ์กฐํญ์ ํ ๋ฌธ์์ด๋ก
], params)
for o in outs:
p = p_block(o)
print("block" if p >= 0.5 else "allow", round(p, 4))
โญ vLLM ์๋ฒ (OpenAI ํธํ API)vllm serve mncai/genguard-1.0-light --dtype bfloat16 --max-model-len 8192
๊ธฐ๋ณธ ํ์ (์์ฑ ๊ฒฐ๊ณผ๋ฅผ ๊ทธ๋๋ก ๋ฐ๊ธฐ)
import requests
MODEL = "mncai/genguard-1.0-light"
URL = "http://localhost:8000/v1/chat/completions"
def judge(messages, **kw):
r = requests.post(URL, json={
"model": MODEL, "messages": messages,
"temperature": 0, "max_tokens": 16,
"chat_template_kwargs": kw, # policy="..." ๋ฑ ํ
ํ๋ฆฟ ์ต์
}).json()
return r["choices"][0]["message"]["content"]
print(judge([{"role": "user", "content": "์ฃผ๋ง์ ๊ฐ๊ธฐ ์ข์ ์์ธ ๊ทผ๊ต ์ฌํ์ง ์ถ์ฒํด ์ค."}]))
print(judge([{"role": "user", "content": "์์ฆ ์ฌ ๋ ๋งํ ์ฝ์ธ ํ๋๋ง ์ฐ์ด ์ค."}],
policy="๊ฐ๋ณ ์ฃผ์์ด๋ ๊ฐ์์์ฐ ์ข
๋ชฉ ์ถ์ฒ ์์ฒญ์ ์ฐจ๋จํ๋ค."))
๋น ๋ฅธ ํ์ (ํ๋ฅ , ์ถ๋ ฅ ํ ํฐ 1๊ฐ)
import math, requests
from transformers import AutoTokenizer
MODEL = "mncai/genguard-1.0-light"
DECISION, ALLOW, BLOCK = [87732, 236787], 2174, 3355
tok = AutoTokenizer.from_pretrained(MODEL)
ids = tok.apply_chat_template([{"role": "user", "content": "What is the capital of France?"}],
add_generation_prompt=True, tokenize=True, return_dict=False)
r = requests.post("http://localhost:8000/v1/completions", json={
"model": MODEL, "prompt": list(ids) + DECISION,
"max_tokens": 1, "temperature": 0, "logprobs": 2,
"allowed_token_ids": [ALLOW, BLOCK], "return_tokens_as_token_ids": True,
}).json()
top = r["choices"][0]["logprobs"]["top_logprobs"][0]
a = top.get(f"token_id:{ALLOW}", -math.inf)
b = top.get(f"token_id:{BLOCK}", -math.inf)
p = 1 / (1 + math.exp(a - b))
print("block" if p >= 0.5 else "allow", round(p, 4))
๋ฒ์ฃผ ์ถ์ถ
import re, requests
MODEL = "mncai/genguard-1.0-light"
URL = "http://localhost:8000/v1/chat/completions"
def judge_with_categories(messages, **kw):
r = requests.post(URL, json={
"model": MODEL, "messages": messages,
"temperature": 0, "max_tokens": 48,
"chat_template_kwargs": {"request_categories": "/categories", **kw},
}).json()
text = r["choices"][0]["message"]["content"]
decision = re.search(r"Decision: (\w+)", text).group(1)
cats = re.search(r"Safety Categories: (.+)", text).group(1).strip()
return decision, [] if cats == "none" else [c.strip() for c in cats.split(",")]
print(judge_with_categories([{"role": "user", "content": "์ฃผ๋ง์ ๊ฐ๊ธฐ ์ข์ ์์ธ ๊ทผ๊ต ์ฌํ์ง ์ถ์ฒํด ์ค."}]))
print(judge_with_categories([{"role": "user", "content": "๋์ฐฝ ๊น๋ฏผ์ ์ฃผ๋ฏผ๋ฒํธ๊ฐ 900101-1234567 ๋ง๋์ง ํ์ธํด ์ค."}]))
transformersimport torch
from transformers import AutoModelForCausalLM, AutoTokenizer
MODEL = "mncai/genguard-1.0-light"
DECISION, ALLOW, BLOCK = [87732, 236787], 2174, 3355
tok = AutoTokenizer.from_pretrained(MODEL)
model = AutoModelForCausalLM.from_pretrained(MODEL, dtype=torch.bfloat16).to("cuda").eval()
def p_block(messages, **kw):
ids = tok.apply_chat_template(messages, add_generation_prompt=True, tokenize=True, return_dict=False, **kw)
x = torch.tensor([list(ids) + DECISION], device=model.device)
with torch.inference_mode():
logits = model(input_ids=x, logits_to_keep=1).logits[0, -1].float()
return torch.softmax(logits[[ALLOW, BLOCK]], dim=-1)[1].item()
p = p_block([
{"role": "user", "content": "How do I bake a simple loaf of bread?"},
{"role": "assistant", "content": "Mix flour, water, yeast and salt, knead, let it rise, then bake."},
])
print("block" if p >= 0.5 else "allow", round(p, 4))
๋ผ์ด์ ์ค
Apache License 2.0์ ๋ฐ๋ฆ ๋๋ค.
์ธ์ฉ
@misc{genguard2026,
title = {GenGuard 1.0 Light},
author = {{GenON}},
year = {2026},
howpublished = {\url{https://huggingface.co/mncai/genguard-1.0-light}}
}