from typing import Dict THREAT_TYPES = ( "intrusion / tailgating / credential_theft / fire_risk / unattended_cooking / " "carbon_monoxide / sensor_stuck / sensor_drift / sensor_malfunction / actuator_stuck / " "lock_malfunction / safety_device_failure / water_leak / possible_fall / " "abnormal_inactivity / health_concern / child_safety / behavioral_anomaly / none" ) def build_prosecutor_prompt(evidence_text: str) -> Dict[str, str]: system = ( "You are a security Prosecutor for smart-home anomaly analysis. " "Assume there may be an anomaly and try to build the strongest evidence-grounded case for it. " "You should optimize for recall, but you may not fabricate evidence." ) user = f"""{evidence_text} You must act as the Prosecutor. Rules: - Assume there may be an anomaly and search for the strongest prosecutable case. - Prefer evidence-grounded claims over vague suspicion. - Distinguish direct device evidence from indirect reasoning evidence. - If you cannot find a prosecutable case after exhausting plausible threats, output `no_case`. - Use concrete timestamps, devices, and observations from the evidence packet. - Stay aligned with the query intent; do not drift to a different threat type unless the evidence strongly forces it. Return JSON only: {{ "prosecution_verdict": "anomaly_detected | no_case", "alleged_threat_type": "{THREAT_TYPES}", "direct_evidence": [ {{"timestamp": "...", "device": "...", "observation": "...", "significance": "..."}} ], "indirect_evidence": [ {{"reasoning": "...", "supporting_events": ["...", "..."]}} ], "severity_assessment": "critical | high | medium | low", "prosecution_argument": "2-4 sentence complete argument" }}""" return {"system": system, "user": user} def build_defender_prompt(evidence_text: str, prosecutor_text: str) -> Dict[str, str]: system = ( "You are a security Defender for smart-home anomaly analysis. " "Your job is to challenge over-interpretation and provide evidence-supported normal explanations." ) user = f"""{evidence_text} ## Prosecutor Output {prosecutor_text} You must act as the Defender. Rules: - Rebut the Prosecutor claim-by-claim. - Prefer evidence-supported normal explanations, not purely imagined ones. - Point out logical leaps, over-interpretation, and places where evidence only supports a weaker claim. - If a Prosecutor point truly cannot be refuted, concede it. - If the Prosecutor already has direct device evidence or an unrebuttable safety chain, do not force a fake defense. Return JSON only: {{ "defense_verdict": "normal_explained | partially_explained | cannot_refute", "rebuttals": [ {{ "prosecution_claim": "...", "defense_explanation": "...", "supporting_evidence": ["...", "..."], "strength": "strong | moderate | weak" }} ], "alternative_narrative": "2-4 sentence normal or lower-risk narrative", "concessions": ["...", "..."] }}""" return {"system": system, "user": user} def build_judge_prompt(prosecutor_text: str, defender_text: str) -> Dict[str, str]: system = ( "You are a neutral Judge in an adversarial IoT security debate. " "Your task is to compare the quality of the Prosecutor and Defender arguments and make a final decision." ) user = f"""## Prosecutor {prosecutor_text} ## Defender {defender_text} Decision Rules (asymmetric burden of proof): 1. If the Prosecutor provides direct device evidence such as alarm events, explicit state contradictions, repeated command failures, or a concrete unsafe sequence, and the Defender cannot reasonably refute it, rule anomaly. 2. If the Prosecutor mainly relies on indirect reasoning and the Defender provides a supported normal explanation, rule normal. 3. If both sides are comparably strong, prefer anomaly because in security settings missing a real threat is worse than raising a cautious alert. 4. If the Prosecutor says `no_case`, rule normal. 5. If the Defender says `cannot_refute`, rule anomaly. Guidance: - Judge the evidence quality, not rhetorical style. - A normal explanation must itself be supported by the logs, not merely possible. - Direct evidence is stronger than indirect evidence, but a coherent multi-step unsafe chain can still be sufficient. - If anomaly is better supported than any specific subtype, choose the best-supported subtype rather than `none`. Return JSON only: {{ "final_verdict": {{ "is_anomaly": true/false, "threat_type": "{THREAT_TYPES}", "confidence": "high | medium | low" }}, "reasoning": {{ "prosecution_strength": "strong | moderate | weak - short reason", "defense_strength": "strong | moderate | weak - short reason", "decisive_factor": "what decided the case", "applied_rule": "1 | 2 | 3 | 4 | 5" }}, "threat_description": "one-sentence summary", "recommended_actions": ["...", "..."] }}""" return {"system": system, "user": user}