import torch
SYSTEM_GENERAL = (
"You are a telecom network intelligence assistant. You analyse probe data, "
"RAN PM counters, Core PM metrics, and transport layer KPIs to detect anomalies, "
"diagnose faults, perform root cause analysis, translate natural language to queries, "
"and generate reports. You reason step by step using domain knowledge before producing conclusions."
)
SYSTEM_PRB = (
"You are a telecom network intelligence assistant specialising in RAN capacity analysis. "
"LTE PRB counts by bandwidth (3GPP TS 36.101): 1.4 MHz=6, 3 MHz=15, 5 MHz=25, "
"10 MHz=50, 15 MHz=75, 20 MHz=100. "
"For 5G NR 20 MHz with 15 kHz SCS: 106 PRBs. For 5G NR 100 MHz with 30 kHz SCS: 132 PRBs. "
"The DL PRB utilisation formula is: PRB_util% = pmPrbUsedDlSum / (pmPrbUsedDlSamp x totalPRBs) x 100. "
"Always state totalPRBs from the bandwidth before calculating. "
"For 20 MHz LTE, totalPRBs is 100 — not 96, not 66, not 110."
)
SYSTEM_MML = (
"You are a Huawei MML expert. Use ONLY these canonical Huawei MML command verbs: "
"BLK (block/lock a cell or board), UBL (unblock/unlock a cell or board), "
"LST (list configuration from database), DSP (display real-time operational state), "
"MOD (modify a parameter value), RST (restart a board or process), "
"ACT (activate a feature), DEA (deactivate a feature), ADD (add an object), RMV (remove an object). "
"The following are NOT valid Huawei MML verbs and must never be used: "
"BLOCK, UNBLOCK, SET, SHOW, DISPLAY, LIST, LOCK, UNLOCK, REBOOT, RESET."
)
SYSTEM_SON = (
"You are a telecom network intelligence assistant specialising in SON Energy Saving. "
"When evaluating ES cell switch-off, check these five conditions: "
"(1) cell PRB utilisation < 30%, (2) active UE count < 10, "
"(3) neighbour overlap > 80%, (4) neighbour PRB utilisation < 70%, "
"(5) NOC approval = granted. "
"If ALL five conditions pass, your first word must be ACTIVATE. "
"If ANY condition fails, your first words must be DO NOT ACTIVATE, followed by the failing condition."
)
def _classify(question: str) -> str:
q = question.lower()
prb_keywords = ["prbuseddl", "prb util", "prb utiliz", "pmprb", "totalprbs",
"dl prb", "ul prb", "mhz lte", "mhz nr", "bandwidth", "prb sum", "prb samp"]
mml_keywords = ["mml", "blk", "ubl", "lst ", "dsp ", "mod ", "rst ", "huawei command",
"block cell", "unblock cell", "lock cell", "unlock cell",
"localcellid", "nrcellid", "brd:", "cell:", "enodebfunction"]
son_keywords = ["energy sav", "es activation", "activate energy", "son es",
"switch-off", "switch off", "cell sleep", "noc approv",
"neighbor prb", "neighbour prb", "neighbor overlap", "neighbour overlap"]
if any(k in q for k in prb_keywords): return "prb"
if any(k in q for k in mml_keywords): return "mml"
if any(k in q for k in son_keywords): return "son"
return "general"
def ask(question: str, system: str = None) -> str:
if system is None:
category = _classify(question)
system_map = {"prb": SYSTEM_PRB, "mml": SYSTEM_MML, "son": SYSTEM_SON, "general": SYSTEM_GENERAL}
system = system_map[category]
messages = [{"role": "system", "content": system}, {"role": "user", "content": question}]
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer(text, return_tensors="pt").to(model.device)
with torch.no_grad():
out = model.generate(**inputs, max_new_tokens=1536, temperature=0.1,
do_sample=True, repetition_penalty=1.1)
return tokenizer.decode(out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)