Text Classification
Transformers
Safetensors
English
distilbert
safety
guardrail
llm-guardrails
text-embeddings-inference
Instructions to use urbanspr1nter/search-query-safety-guard with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use urbanspr1nter/search-query-safety-guard with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-classification", model="urbanspr1nter/search-query-safety-guard")# Load model directly from transformers import AutoTokenizer, AutoModelForSequenceClassification tokenizer = AutoTokenizer.from_pretrained("urbanspr1nter/search-query-safety-guard") model = AutoModelForSequenceClassification.from_pretrained("urbanspr1nter/search-query-safety-guard", device_map="auto") - Notebooks
- Google Colab
- Kaggle
Upload final model (iter5): eval1 F1 0.994, eval2 (fresh) F1 1.000, 0 dangerous leaks. Added id2label/label2id + model card.
Browse files- README.md +84 -4
- config.json +12 -3
- config.json.bak +28 -0
- model.safetensors +1 -1
- tokenizer_config.json +1 -0
README.md
CHANGED
|
@@ -1,6 +1,86 @@
|
|
| 1 |
---
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
pipeline_tag: text-classification
|
| 6 |
-
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
library_name: transformers
|
| 3 |
+
license: mit
|
| 4 |
+
language:
|
| 5 |
+
- en
|
| 6 |
+
tags:
|
| 7 |
+
- safety
|
| 8 |
+
- guardrail
|
| 9 |
+
- text-classification
|
| 10 |
+
- distilbert
|
| 11 |
+
- llm-guardrails
|
| 12 |
+
base_model: distilbert-base-uncased
|
| 13 |
pipeline_tag: text-classification
|
| 14 |
+
---
|
| 15 |
+
|
| 16 |
+
# Search Query Safety Guard
|
| 17 |
+
|
| 18 |
+
A DistilBERT-base-uncased guardrail that classifies an AI agent's **search query** as `SAFE` or `DANGEROUS` before it reaches a web search/scrape tool. Designed to sit as a proxy between an agent's search tool and the real search service.
|
| 19 |
+
|
| 20 |
+
- **0 → SAFE** — allow the search
|
| 21 |
+
- **1 → DANGEROUS** — block and return an error to the agent
|
| 22 |
+
|
| 23 |
+
## Intended use
|
| 24 |
+
|
| 25 |
+
Wrap a search/scrape tool so every query is classified first. Safe queries pass through to the real service; dangerous queries are blocked and surfaced to the agent as an error. Protected categories include terrorism, CSAM / harm to children, drugs, weapons & explosives, violence, self-harm, hate & extremism, kidnapping & trafficking, fraud & counterfeit, arson, and **credential/secret theft** (stolen OAuth tokens, leaked API keys, session cookies, etc.).
|
| 26 |
+
|
| 27 |
+
### Cyber policy (intent, not keywords)
|
| 28 |
+
|
| 29 |
+
This model also backs a **cyber-defense agent**, so the security boundary is by intent:
|
| 30 |
+
|
| 31 |
+
| SAFE — let the defender work | DANGEROUS — block |
|
| 32 |
+
|---|---|
|
| 33 |
+
| "how ransomware works and incident response steps" | "where to buy stolen oauth tokens and session cookies" |
|
| 34 |
+
| "how credential stuffing works and how to stop it" | "how to scrape github at scale for accidentally committed private keys" |
|
| 35 |
+
| "how reverse shells work and how to detect them" | "where to find leaked api keys and access tokens for sale" |
|
| 36 |
+
|
| 37 |
+
Rule of thumb: researching how an attack works, or how to detect/prevent/train for it, is SAFE. Stealing, buying, selling, or exfiltrating auth tokens, secrets, credentials, or PII — or targeting a specific person's accounts — is DANGEROUS.
|
| 38 |
+
|
| 39 |
+
## Usage
|
| 40 |
+
|
| 41 |
+
```python
|
| 42 |
+
from transformers import AutoModelForSequenceClassification, AutoTokenizer
|
| 43 |
+
import torch
|
| 44 |
+
|
| 45 |
+
model = AutoModelForSequenceClassification.from_pretrained("urbanspr1nter/search-query-safety-guard")
|
| 46 |
+
tokenizer = AutoTokenizer.from_pretrained("urbanspr1nter/search-query-safety-guard")
|
| 47 |
+
model.eval()
|
| 48 |
+
|
| 49 |
+
def predict(query: str) -> dict:
|
| 50 |
+
enc = tokenizer(query, return_tensors="pt", max_length=256, truncation=True, padding=True)
|
| 51 |
+
with torch.no_grad():
|
| 52 |
+
probs = torch.softmax(model(**enc).logits, dim=-1)
|
| 53 |
+
pred = int(torch.argmax(probs, dim=-1).item())
|
| 54 |
+
return {
|
| 55 |
+
"query": query,
|
| 56 |
+
"label": model.config.id2label[pred], # "SAFE" or "DANGEROUS"
|
| 57 |
+
"safe": pred == 0,
|
| 58 |
+
"confidence": float(probs[0, pred].item()),
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
predict("how to cook pasta") # SAFE
|
| 62 |
+
predict("where to buy stolen oauth tokens") # DANGEROUS
|
| 63 |
+
```
|
| 64 |
+
|
| 65 |
+
## Evaluation
|
| 66 |
+
|
| 67 |
+
Trained and evaluated with **two held-out sets**, both verified disjoint from the training data:
|
| 68 |
+
|
| 69 |
+
| Set | Accuracy | DANGER F1 | Dangerous leaks (FN) | Safe blocked (FP) |
|
| 70 |
+
|-----|----------|-----------|----------------------|--------------------|
|
| 71 |
+
| eval.jsonl (137 examples) | 99.27% | 0.9937 | 0 | 1 |
|
| 72 |
+
| eval2.jsonl (43 examples, fresh) | 100% | 1.0000 | 0 | 0 |
|
| 73 |
+
|
| 74 |
+
**Recall on dangerous queries is 1.000** on both held-out sets — no dangerous query reached the web. A second held-out set was used specifically to guard against memorizing the first.
|
| 75 |
+
|
| 76 |
+
## Training
|
| 77 |
+
|
| 78 |
+
- Base: `distilbert-base-uncased`, 2 labels, max 256 tokens
|
| 79 |
+
- Optimizer: AdamW, lr 2e-5, batch 8, early stopping on F1
|
| 80 |
+
- Data: 2,313 examples (~61% SAFE / ~39% DANGEROUS), grown from 1,973 with a leakage guard rejecting any addition ≥82% similar to an eval query
|
| 81 |
+
|
| 82 |
+
## Limitations
|
| 83 |
+
|
| 84 |
+
- English only
|
| 85 |
+
- Evaluates a single query — no conversation history
|
| 86 |
+
- A small dataset cannot cover every edge case; deploy with a confidence threshold and log the grey zone
|
config.json
CHANGED
|
@@ -22,7 +22,16 @@
|
|
| 22 |
"sinusoidal_pos_embds": false,
|
| 23 |
"tie_weights_": true,
|
| 24 |
"tie_word_embeddings": true,
|
| 25 |
-
"transformers_version": "5.
|
| 26 |
"use_cache": false,
|
| 27 |
-
"vocab_size": 30522
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
"sinusoidal_pos_embds": false,
|
| 23 |
"tie_weights_": true,
|
| 24 |
"tie_word_embeddings": true,
|
| 25 |
+
"transformers_version": "5.14.1",
|
| 26 |
"use_cache": false,
|
| 27 |
+
"vocab_size": 30522,
|
| 28 |
+
"num_labels": 2,
|
| 29 |
+
"id2label": {
|
| 30 |
+
"0": "SAFE",
|
| 31 |
+
"1": "DANGEROUS"
|
| 32 |
+
},
|
| 33 |
+
"label2id": {
|
| 34 |
+
"SAFE": 0,
|
| 35 |
+
"DANGEROUS": 1
|
| 36 |
+
}
|
| 37 |
+
}
|
config.json.bak
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"activation": "gelu",
|
| 3 |
+
"architectures": [
|
| 4 |
+
"DistilBertForSequenceClassification"
|
| 5 |
+
],
|
| 6 |
+
"attention_dropout": 0.1,
|
| 7 |
+
"bos_token_id": null,
|
| 8 |
+
"dim": 768,
|
| 9 |
+
"dropout": 0.1,
|
| 10 |
+
"dtype": "float32",
|
| 11 |
+
"eos_token_id": null,
|
| 12 |
+
"hidden_dim": 3072,
|
| 13 |
+
"initializer_range": 0.02,
|
| 14 |
+
"max_position_embeddings": 512,
|
| 15 |
+
"model_type": "distilbert",
|
| 16 |
+
"n_heads": 12,
|
| 17 |
+
"n_layers": 6,
|
| 18 |
+
"pad_token_id": 0,
|
| 19 |
+
"problem_type": "single_label_classification",
|
| 20 |
+
"qa_dropout": 0.1,
|
| 21 |
+
"seq_classif_dropout": 0.2,
|
| 22 |
+
"sinusoidal_pos_embds": false,
|
| 23 |
+
"tie_weights_": true,
|
| 24 |
+
"tie_word_embeddings": true,
|
| 25 |
+
"transformers_version": "5.14.1",
|
| 26 |
+
"use_cache": false,
|
| 27 |
+
"vocab_size": 30522
|
| 28 |
+
}
|
model.safetensors
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
size 267832560
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:23ff526bba82b2e070fed0000fc6ccf0eba4bc8e4e364ee3cf1bb390d7369e9a
|
| 3 |
size 267832560
|
tokenizer_config.json
CHANGED
|
@@ -3,6 +3,7 @@
|
|
| 3 |
"cls_token": "[CLS]",
|
| 4 |
"do_lower_case": true,
|
| 5 |
"is_local": false,
|
|
|
|
| 6 |
"mask_token": "[MASK]",
|
| 7 |
"model_max_length": 512,
|
| 8 |
"pad_token": "[PAD]",
|
|
|
|
| 3 |
"cls_token": "[CLS]",
|
| 4 |
"do_lower_case": true,
|
| 5 |
"is_local": false,
|
| 6 |
+
"local_files_only": false,
|
| 7 |
"mask_token": "[MASK]",
|
| 8 |
"model_max_length": 512,
|
| 9 |
"pad_token": "[PAD]",
|