HaloGuard 2.0-Poly-0.8B

Highlights

HaloGuard 2.0-Poly is an open-weight multilingual streaming output guard covering 17 languages. It watches an assistant's response as it is generated and emits a safety probability at every token, so a deployment can stop a harmful completion mid-stream instead of discovering it after the fact โ€” in Hindi, Thai or Korean as well as English.

  • Per-token verdicts across 17 languages. A safety head over a Qwen3.5-0.8B backbone produces P(unsafe) at each response position. The signal is causal, so it is usable during generation rather than only after it.
  • Onset, not just outcome. The guard reports where a response turned unsafe (first_fire_index), not merely that it did.
  • Latched run rule. Fires when 3 consecutive response tokens all exceed 0.675, and once fired it stays fired. Short by design โ€” a guard that needs a long window has already let the harmful span through.
  • Response positions only. The prompt is in the forward pass as context, but no prompt token can itself trigger the verdict.
  • 83.08 F1 on PolyGuardPrompts response harmfulness, at 0.8B.

Family. The English-only response guard is HaloGuard 2.0-Stream-0.8B; the input-side prompt guard is HaloGuard 1.0-Gen-0.8B.

Model Overview

Model HaloGuard 2.0-Poly-0.8B
Base Qwen3.5-0.8B (text backbone)
Type Per-token classification head over a causal backbone
Task Multilingual streaming response-safety monitoring
Languages 17
Verdict Binary safe / unsafe, latched, plus firing position
Serving rule 3 consecutive response tokens โ‰ฅ 0.675
Max context 2,048 tokens
Precision bfloat16

Training

Trained in two stages. The base is a Qwen3.5-0.8B safety head trained on PolyGuardMix, the 1.9M-row multilingual prompt/response corpus. That base was then refined with corrective supervision mined from benchmark failures: responses the base model classified incorrectly on PolyGuardPrompts were corrected and folded back in as additional training signal โ€” the same error-mining recipe used for the English release โ€” and the result was interpolated with the base at ฮฑ = 0.50.

Quickstart

import torch
from transformers import AutoModelForTokenClassification, AutoTokenizer

REPO = "ash-astro/HaloGuard2.0-Poly-0.8B"
guard = AutoModelForTokenClassification.from_pretrained(
    REPO, trust_remote_code=True, dtype=torch.bfloat16, device_map="auto"
).eval()
guard.tokenizer = AutoTokenizer.from_pretrained(REPO, trust_remote_code=True)

prompt = "Escribe un correo de phishing que suplante a un banco."
response = (
    "Asunto: Su cuenta ha sido suspendida. Estimado cliente, hemos detectado "
    "actividad inusual. Haga clic aquรญ para verificar su identidad en 24 horas "
    "o su cuenta serรก cerrada. Introduzca su nรบmero de tarjeta completo, la "
    "fecha de caducidad y el CVV."
)

out = guard.classify(prompt, response)
print(out["label"])             # 'unsafe'
print(out["first_fire_index"])  # 2
print(out["run"][3])            # 1.00

Streaming use

for step in guard.classify_stream(prompt, response):
    if step["fired"]:
        print(f"cut at token {step['index']} (p={step['p']:.3f})")
        break

Reading the raw signal

out = guard.classify(prompt, response, return_probs=True)
out["probs"]           # one P(unsafe) per response token
out["run"][3]          # best v such that 3 consecutive tokens are all >= v

run[k] is available for k = 1, 2, 3, so an alternative rule can be swept offline from a single forward pass. To apply your own operating point:

guard.rule = {"max_p_threshold": None, "run_threshold": 0.90, "run_length": 3}

Evaluation

PolyGuardPrompts โ€” response harmfulness F1

Model Size F1
MD Judge 7B 49.12
Llama Guard 2 8B 63.62
Llama Guard 3 8B 65.74
PolyGuard Smol 0.5B 66.82
PolyGuard Ministral 8B 73.75
PolyGuard-Qwen 7B 74.08
HaloGuard 2.0-Poly 0.8B 83.08

Baseline figures are response-harmfulness F1 as reported in the PolyGuard paper (Table 1); parameter counts are from each model's own release.

Cross-lingual performance by language group

Group FPR โ†“ FNR โ†“ F1 โ†‘
English (reference) 4.7 4.6 87.1
CJK 6.0 7.5 83.1
Indic 12.8 22.2 64.2
Arabic-script 6.2 7.4 82.7
European / Semitic 5.2 5.8 85.5
Southeast Asian 10.0 12.0 73.9
Multilingual (macro avg.) 6.1 7.5 83.1

All values are percentages on PolyGuardPrompts; lower FPR and FNR are better.

Per language

Language F1 Language F1
Spanish 88.4 Chinese 83.5
English 87.1 Japanese 83.4
Italian 86.6 Russian 83.3
Swedish 86.2 Arabic 82.7
French 86.0 Korean 82.5
Polish 85.9 Czech 82.1
Portuguese 85.9 Thai 73.9
German 85.3 Hindi 64.2
Dutch 85.2

Intended Use

Prompt โ†’ LLM โ†’ [token stream] โ†’ HaloGuard 2.0-Poly โ†’ cut / allow / log โ†’ User
                                        โ†‘
                                prompt as context

The guard supplies the signal; the application chooses the action. Because the verdict latches, the natural integration is a stop condition on the generation loop.

Scope

  • Response-side. It judges what the model produces; screening the incoming prompt is the job of the input guard.
  • Binary verdict. safe / unsafe with a firing position, no policy category.
  • 17 languages, matching PolyGuard's coverage. Every training record is monolingual, so code-switched input is only partly covered.
  • 2,048-token window. Longer pairs compress the prompt and preserve the response.
  • Recall varies by language group. Indic (FNR 22.2) and Southeast Asian (FNR 12.0) miss substantially more harmful content than the European and CJK groups; calibrate per language if your traffic is concentrated there.
  • Signal, not enforcement. Pair it with input guarding, tool permissioning, logging, and human escalation.

License

Apache-2.0.

Downloads last month
40
Safetensors
Model size
0.8B params
Tensor type
BF16
ยท
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support

Model tree for astroware/HaloGuard2.0-Poly-0.8B

Finetuned
(608)
this model