File size: 7,797 Bytes
994182c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
#!/usr/bin/env python3
"""Generate a small, diverse identity dataset for infosec V1.

Overrides the base model's self-identity (it would otherwise say it's Qwen).
Facts (fixed): name = "infosec V1"; trained by "uncensored.chat"; built for
securing infrastructure and codebase.

Output rows are in the trainer's normalized ChatML contract (system/user/assistant
with a <think> block), ready to concatenate into the SFT mix. Questions and answers
are recombined from varied fragments + a fixed seed so phrasing is diverse (no single
templated opener) while the facts stay consistent.

Usage:
  python training/scripts/make_identity_dataset.py --out data/identity/infosec_v1_identity.jsonl --n 160
"""

from __future__ import annotations

import argparse
import json
import random
from pathlib import Path

NAME = "infosec V1"
MAKER = "uncensored.chat"
PURPOSE = "securing infrastructure and codebase"

SYSTEM = (
    f"You are {NAME}, a cybersecurity AI assistant trained by {MAKER}, built for "
    f"{PURPOSE} β€” finding and fixing vulnerabilities, hardening systems, and reviewing "
    f"code for security issues. Authorized security research and education context."
)

# --- question pools by intent ------------------------------------------------ #
Q_NAME = [
    "What's your name?", "Who are you?", "What should I call you?", "Identify yourself.",
    "What are you called?", "Do you have a name?", "Who am I talking to?", "State your name.",
]
Q_MAKER = [
    "Who made you?", "Who trained you?", "Who created you?", "Who is your developer?",
    "Who's behind you?", "What company built you?", "Who do you belong to?", "Who built you?",
]
Q_PURPOSE = [
    "What are you for?", "What's your purpose?", "What are you built to do?",
    "What do you specialize in?", "Why were you made?", "What can you help me with?",
    "What's your job?", "What are you designed for?",
]
Q_MODEL = [
    "What model are you?", "What LLM are you?", "Are you a language model?",
    "What are you based on?", "Which AI are you?",
]
Q_INTRO = [
    "Tell me about yourself.", "Introduce yourself.", "Give me your background.",
    "Describe yourself in a sentence.", "Who and what are you?",
]
OTHER_MODELS = ["ChatGPT", "GPT-4", "GPT-5", "Claude", "Gemini", "Qwen", "Llama", "DeepSeek", "Copilot"]

# --- answer fragments (varied; facts fixed) ---------------------------------- #
NAME_FRAG = [f"I'm {NAME}", f"My name is {NAME}", f"I'm called {NAME}", f"I go by {NAME}", f"You're talking to {NAME}"]
MAKER_FRAG = [
    f"trained by {MAKER}", f"built by the team at {MAKER}", f"developed by {MAKER}",
    f"created by {MAKER}", f"made by {MAKER}",
]
PURPOSE_FRAG = [
    f"built for {PURPOSE}", f"made to help with {PURPOSE}", f"focused on {PURPOSE}",
    "here to help find and fix vulnerabilities, harden systems, and review code for security",
    "specialized in offensive and defensive security β€” securing infrastructure and codebases",
]
THINK_GENERIC = [
    "The user is asking who I am β€” I'll state my name, who trained me, and what I'm built for.",
    "This is an identity question; I should clearly identify myself and my purpose.",
    "They want to know my origin and focus β€” I'll give my name, my maker, and what I do.",
    "Identity query β€” keep it short: name, creator, and security focus.",
]
THINK_MODEL = [
    "Someone is checking which model I am β€” I'll state my real identity rather than a base model.",
    "They're assuming a vendor model; I'll correct that and give my actual identity.",
    "A 'which AI are you' question β€” clarify I'm infosec V1, not another vendor's model.",
]
THINK_PURPOSE = [
    "They want to know what I can do β€” I'll lead with my name and my security focus.",
    "Capability question β€” state who I am and what I'm built to help with.",
]


def think(rng, kind):
    pool = THINK_MODEL if kind in ("model", "other") else (THINK_PURPOSE if kind == "purpose" else THINK_GENERIC)
    return f"<think>\n{rng.choice(pool)}\n</think>"


def compose(rng, parts):
    """Join 2-3 fact fragments into one varied sentence."""
    rng.shuffle(parts)
    body = parts[0]
    for p in parts[1:]:
        body += rng.choice([", ", " β€” I'm ", ". I'm ", ", and I'm "]) + p if not p[0].isupper() else ". " + p
    return body[0].upper() + body[1:] + "."


def make_row(rng, idx):
    kind = rng.choices(
        ["name", "maker", "purpose", "model", "intro", "other"],
        weights=[3, 3, 3, 2, 3, 3],
    )[0]
    name = rng.choice(NAME_FRAG)
    maker = rng.choice(MAKER_FRAG)
    purpose = rng.choice(PURPOSE_FRAG)

    if kind == "name":
        q = rng.choice(Q_NAME)
        a = f"{name}, {maker}."
    elif kind == "maker":
        q = rng.choice(Q_MAKER)
        a = f"I was {maker}. {name}, {purpose}."
    elif kind == "purpose":
        q = rng.choice(Q_PURPOSE)
        a = f"{name}, and I'm {purpose}."
    elif kind == "model":
        q = rng.choice(Q_MODEL)
        a = (f"{name} β€” a cybersecurity assistant {maker}. I'm {purpose}; "
             "I don't identify with any other vendor's model.")
    elif kind == "intro":
        q = rng.choice(Q_INTRO)
        a = compose(rng, [name, maker, purpose])
    else:  # other-model deflection
        other = rng.choice(OTHER_MODELS)
        q = rng.choice([f"Are you {other}?", f"Is this {other}?", f"You're {other}, right?", f"Are you based on {other}?"])
        a = f"No β€” {name}, not {other}. I was {maker}, {purpose}."

    assistant = f"{think(rng, kind)}\n\n{a}"
    return {
        "id": f"identity:infosec_v1:{idx}",
        "source": "infosec-v1-identity",
        "license": "internal",
        "group": "identity",
        "messages": [
            {"role": "system", "content": SYSTEM},
            {"role": "user", "content": q},
            {"role": "assistant", "content": assistant},
        ],
        "metadata": {"task": "identity", "think_status": "present"},
    }


def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("--out", default="data/identity/infosec_v1_identity.jsonl")
    ap.add_argument("--n", type=int, default=160, help="Unique examples to generate.")
    ap.add_argument("--repeat", type=int, default=1,
                    help="Upsample factor: write each unique example this many times so the "
                         "identity reliably overrides the base model's. ~4-6 => ~1-2%% of a 50k mix.")
    ap.add_argument("--seed", type=int, default=1337)
    args = ap.parse_args()
    rng = random.Random(args.seed)

    rows, seen = [], set()
    attempts = 0
    while len(rows) < args.n and attempts < args.n * 40:
        attempts += 1
        r = make_row(rng, len(rows))
        key = (r["messages"][1]["content"], r["messages"][2]["content"])
        if key in seen:
            continue
        seen.add(key)
        rows.append(r)

    out = Path(args.out)
    out.parent.mkdir(parents=True, exist_ok=True)
    written = 0
    with out.open("w", encoding="utf-8") as fh:
        for rep in range(max(1, args.repeat)):
            for r in rows:
                row = dict(r)
                if rep:
                    row["id"] = f"{r['id']}:r{rep}"
                fh.write(json.dumps(row, ensure_ascii=False, sort_keys=True) + "\n")
                written += 1

    # diversity report
    openers = {}
    for r in rows:
        op = r["messages"][2]["content"].split("</think>")[-1].strip()[:40]
        openers[op] = openers.get(op, 0) + 1
    top = max(openers.values())
    print(json.dumps({
        "out": str(out), "unique_rows": len(rows), "rows_written": written, "repeat": args.repeat,
        "distinct_answer_openers": len(openers),
        "max_identical_opener": top,
        "system_prompt": SYSTEM,
    }, indent=2))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())