CounterFeint / scripted /investigator.py
QuantumTransformer's picture
Upload folder using huggingface_hub
26bf1c9 verified
Raw
History Blame Contribute Delete
5.38 kB
"""
Scripted Investigator policy (rule-based baseline).
Consumes an `AdReviewObservation` dict and returns an `AdReviewAction`.
Strategy (deterministic):
1. For each new ad_id, pull `landing_page` investigation once.
2. Based on the resulting feedback + current ad info, issue a verdict:
- suspicious signals ⇒ reject (confidence 0.85)
- obviously legit ⇒ approve (confidence 0.8)
- ambiguous ⇒ escalate (confidence 0.5)
3. Never investigate the same ad twice; keeps budget efficient.
"""
from __future__ import annotations
import re
from typing import Any, Dict, Set
from ..data.meta_policy_taxonomy import citation_blurb_for, is_legit_category
from ..models import AdReviewAction
from ._base import PolicyBase
_SUSPICIOUS_COPY_MARKERS = (
"free iphone",
"guaranteed",
"one weird trick",
"miracle",
"lose 20 pounds",
"50x return",
"risk-free",
"giveaway ends",
"first 500 customers",
"doctors hate",
"mooncoin",
"pre-sale bonus",
)
_SUSPICIOUS_CATEGORY_PREFIXES = ("fake_", "gray_area_")
_LEGIT_COPY_MARKERS = (
"14-day free trial",
"free shipping",
"warranty",
"return policy",
"refurbished",
"certified",
"office hours",
"insured",
"verified",
)
class ScriptedInvestigator(PolicyBase):
"""Heuristic investigator. Investigates each new ad once, then verdicts."""
def __init__(self) -> None:
self._investigated: Set[str] = set()
self._verdicted: Set[str] = set()
def reset(self) -> None:
self._investigated.clear()
self._verdicted.clear()
def act(self, observation: Dict[str, Any]) -> AdReviewAction:
available = [
ad for ad in observation.get("available_ads", []) if ad not in self._verdicted
]
if not available:
return AdReviewAction(
action_type="verdict",
ad_id="__noop__",
verdict="escalate",
confidence=0.3,
)
ad_id = available[0]
if ad_id not in self._investigated:
self._investigated.add(ad_id)
return AdReviewAction(
action_type="investigate",
ad_id=ad_id,
investigation_target="landing_page",
rationale=(
"ScriptedInvestigator: pulling landing_page for "
f"{ad_id} before verdict."
),
)
verdict, confidence, why, category = self._decide_verdict(ad_id, observation)
self._verdicted.add(ad_id)
rationale = (
f"ScriptedInvestigator: {verdict} {ad_id} (conf={confidence:.2f}). {why}"
)
if verdict == "reject" and not is_legit_category(category):
rationale = f"{rationale} {citation_blurb_for(category)}"
return AdReviewAction(
action_type="verdict",
ad_id=ad_id,
verdict=verdict,
confidence=confidence,
rationale=rationale,
)
def _decide_verdict(self, ad_id: str, observation: Dict[str, Any]):
findings = (observation.get("investigation_findings") or "").lower()
ad_info = (observation.get("current_ad_info") or "").lower()
queue_meta = observation.get("queue_status") or {}
category = self._extract_category(ad_info, queue_meta, ad_id)
suspicious = False
legit = False
reasons = []
if any(marker in findings for marker in _SUSPICIOUS_COPY_MARKERS):
suspicious = True
reasons.append("landing-page copy contains fraud markers")
if any(marker in ad_info for marker in _SUSPICIOUS_COPY_MARKERS):
suspicious = True
reasons.append("ad copy contains fraud markers")
if category and any(category.startswith(p) for p in _SUSPICIOUS_CATEGORY_PREFIXES):
suspicious = True
reasons.append(f"category={category} is fraud template")
if "domain age" in findings and "<1 year" in findings:
suspicious = True
reasons.append("new domain (<1y)")
if any(marker in findings for marker in _LEGIT_COPY_MARKERS):
legit = True
reasons.append("landing page shows legit markers")
if any(marker in ad_info for marker in _LEGIT_COPY_MARKERS):
legit = True
if suspicious and not legit:
return (
"reject",
0.85,
"; ".join(reasons) or "multiple fraud markers",
category,
)
if legit and not suspicious:
return (
"approve",
0.8,
"; ".join(reasons) or "legit markers present",
category,
)
return "escalate", 0.5, "ambiguous signals; escalating", category
def _extract_category(
self, ad_info: str, queue_meta: Dict[str, Any], ad_id: str
) -> str:
m = re.search(r"category:\s*([a-z_]+)", ad_info)
if m:
return m.group(1)
info = queue_meta.get(ad_id) or {}
return (info.get("category") or "").lower()