phishing defense

Detecting Business Email Compromise with AI

Deploy AI and NLP-powered detection systems to identify business email compromise attacks by analyzing writing style, behavioral patterns, and contextual anomalies that evade traditional rule-based filters.

aibecbehavioral-analyticsemail-securityfraud-detectionimpersonationmachine-learningnlp
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

AI-powered BEC detection uses machine learning, NLP, and behavioral analytics to identify sophisticated impersonation attacks that contain no malicious links or attachments. Traditional rule-based filters miss these attacks because BEC relies purely on social engineering. Modern AI approaches analyze writing style, tone, vocabulary, grammatical patterns, and behavioral context to determine if an email genuinely comes from the stated sender. BERT-based models achieve 98.65% accuracy in BEC detection, and AI-enhanced platforms show a 25% increase in phishing identification over keyword-based rules.

When to Use

  • When investigating security incidents that require detecting business email compromise with ai
  • When building detection rules or threat hunting queries for this domain
  • When SOC analysts need structured procedures for this analysis type
  • When validating security monitoring coverage for related attack techniques

Prerequisites

  • AI-powered email security platform (Abnormal Security, Tessian, Microsoft Defender)
  • Historical email data for baseline training (minimum 30 days)
  • Integration with email platform (Microsoft 365 or Google Workspace)
  • SIEM for alert correlation and investigation
  • Understanding of BEC attack types (FBI IC3 classification)

Workflow

Step 1: Deploy AI Email Security Platform

  • Select API-based solution (Abnormal Security, Tessian, Ironscales) or enhance existing SEG
  • Connect to Microsoft Graph API or Google Workspace API
  • Allow 48-hour baseline learning period on historical email data
  • Configure integration to scan inbound, outbound, and internal email
  • Verify API permissions for message access and remediation

Step 2: Configure Behavioral Baselines

  • AI learns normal communication patterns: who emails whom, frequency, tone
  • Establish writing style profiles for each user (vocabulary, sentence structure)
  • Map typical request types per role (finance processes payments, HR handles PII)
  • Baseline email metadata: typical sending times, devices, locations
  • Flag deviations from established baselines as anomalous

Step 3: Train NLP Models for BEC Detection

  • Deploy transformer-based models (BERT, GPT) for email content analysis
  • Detect urgency and manipulation language patterns
  • Identify mismatches between sender identity and writing style
  • Analyze sentiment shifts indicating social engineering pressure
  • Classify email intent: information request, payment request, credential request

Step 4: Configure Detection Policies

  • VIP impersonation: AI compares new email against known executive communication patterns
  • Vendor impersonation: detect payment change requests from vendor lookalike domains
  • Account compromise: detect sudden changes in employee email behavior
  • Supply chain BEC: monitor for impersonation of trusted partners
  • Configure confidence thresholds for auto-block vs. warning banner vs. analyst review

Step 5: Integrate with Response Workflow

  • Auto-quarantine high-confidence BEC detections
  • Add warning banners for moderate-confidence detections
  • Route suspicious emails to SOC analyst queue for review
  • Integrate with SOAR for automated response playbooks
  • Feed BEC verdicts back into training data for model improvement

Tools & Resources

  • Abnormal Security: API-based AI email security with behavioral analysis
  • Microsoft Defender for O365: Built-in AI anti-BEC with Impostor Classifier
  • Tessian (Proofpoint): AI-powered email security with human layer protection
  • Ironscales: AI + human-in-the-loop BEC detection
  • Darktrace Email: Self-learning AI for email threat detection

Validation

  • AI detects test BEC email with no malicious indicators (pure social engineering)
  • Writing style analysis identifies impersonation of known executive
  • Behavioral baseline flags unusual payment request from compromised account
  • NLP correctly classifies urgency manipulation in test scenario
  • False positive rate below 0.05% after baseline training
  • Detection rate exceeds traditional rule-based filters by 25%+
Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md2.2 KB

API Reference: Detecting BEC with AI

NLP Feature Extraction

Feature Description BEC Signal
urgency_score Ratio of urgency words to total High = suspicious
pressure_score Ratio of secrecy/pressure words High = suspicious
financial_score Ratio of financial terms High = suspicious
authority_score Ratio of executive title mentions High = suspicious
caps_ratio Uppercase character ratio High = aggressive tone
unique_word_ratio Vocabulary diversity metric Low = template-like

scikit-learn Classification Pipeline

from sklearn.pipeline import Pipeline
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.ensemble import RandomForestClassifier
 
pipeline = Pipeline([
    ("tfidf", TfidfVectorizer(max_features=5000, ngram_range=(1, 2))),
    ("clf", RandomForestClassifier(n_estimators=100, random_state=42))
])
pipeline.fit(X_train, y_train)
predictions = pipeline.predict(X_test)

Writing Style Analysis (Stylometry)

# Sentence length distribution for author verification
import re, math
sentences = re.split(r'[.!?]+', text)
lengths = [len(s.split()) for s in sentences if s.strip()]
mean_len = sum(lengths) / len(lengths)
variance = sum((l - mean_len)**2 for l in lengths) / len(lengths)
std_dev = math.sqrt(variance)

Microsoft Graph API - Suspicious Mail Rules

GET https://graph.microsoft.com/v1.0/users/{id}/mailFolders/inbox/messageRules
Authorization: Bearer {token}
 
# Detect forwarding rules (T1114.003)
GET https://graph.microsoft.com/v1.0/users/{id}/mailFolders/inbox/messageRules?$filter=actions/forwardTo ne null

Impersonation Signal Patterns

# Mobile signature (creates urgency excuse)
r"sent from my (iphone|ipad|android|mobile)"
# Discourages verification
r"(please|kindly).*(do not|don't).*(reply|respond|call)"
# Unavailability excuse
r"(i am|i'm).*(in a meeting|traveling|on a flight)"
# Time pressure
r"(handle|process|complete).*(today|immediately|by end of day)"

CLI Usage

python agent.py --file email_body.txt
python agent.py --file email_body.txt --baseline-file sender_style.json
standards.md1.3 KB

Standards & References: Detecting BEC with AI

MITRE ATT&CK References

  • T1566.001/002: Phishing (Spearphishing Attachment/Link)
  • T1534: Internal Spearphishing
  • T1656: Impersonation
  • T1586.002: Compromise Accounts: Email Accounts
  • T1114.003: Email Collection: Email Forwarding Rule

AI/ML Techniques for BEC Detection

Technique Application Accuracy
BERT embeddings + SVC Email classification 98.65%
Transformer NLP Writing style analysis 96%+
Anomaly detection Behavioral baseline deviation 94%+
Graph neural networks Communication pattern analysis 93%+
Sentiment analysis Urgency/manipulation detection 91%+

FBI IC3 BEC Statistics

  • $2.9 billion losses reported in 2023
  • BEC accounts for 27% of all cybercrime financial losses
  • Average loss per BEC incident: $125,000
  • 21,832 BEC complaints filed in 2023

Detection Categories

  • Impostor Detection: AI identifies display name/domain impersonation
  • Account Takeover Detection: Behavioral anomalies from compromised accounts
  • Writing Style Analysis: NLP compares email to sender's historical style
  • Intent Classification: ML classifies email as payment/credential/data request
  • Relationship Analysis: Graph analysis of sender-recipient communication patterns
workflows.md1.4 KB

Workflows: Detecting BEC with AI

Workflow 1: AI-Powered BEC Detection Pipeline

Inbound email arrives
  |
  v
[Feature extraction]
  +-- Sender metadata (domain, IP, authentication)
  +-- Email content (subject, body, NLP features)
  +-- Behavioral context (communication history, timing)
  +-- Relationship graph (sender-recipient pattern)
  |
  v
[Multi-model analysis (parallel)]
  +-- Impostor classifier: Display name/domain impersonation
  +-- NLP model: Writing style vs. sender baseline
  +-- Behavioral model: Request anomaly detection
  +-- Intent classifier: Payment/credential/data request
  |
  v
[Confidence scoring]
  +-- Aggregate model outputs
  +-- Weight by model confidence and context
  +-- Generate overall BEC probability score
  |
  v
[Action]
  +-- Score >= 90%: Auto-quarantine + SOC alert
  +-- Score 70-89%: Warning banner + analyst queue
  +-- Score 50-69%: Warning banner only
  +-- Score < 50%: Deliver normally

Workflow 2: Model Feedback Loop

BEC verdict generated
  |
  v
[User/analyst feedback]
  +-- User reports false positive (legitimate email flagged)
  +-- Analyst confirms true positive (BEC caught)
  +-- User reports missed BEC (false negative)
  |
  v
[Feedback integration]
  +-- Update sender trust score
  +-- Retrain model with corrected labels
  +-- Adjust confidence thresholds
  +-- Update behavioral baselines

Scripts 2

agent.py5.3 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""AI-powered BEC detection agent using NLP features for email classification.

Extracts linguistic features (urgency, sentiment, writing style metrics) and
uses scikit-learn to classify emails as BEC or legitimate.
"""

import argparse
import json
import math
import re
from collections import Counter

URGENCY_WORDS = {"urgent", "immediately", "asap", "deadline", "critical",
                 "important", "expedite", "priority", "rush", "now"}
PRESSURE_WORDS = {"confidential", "secret", "private", "classified",
                  "between us", "do not share", "don't discuss", "quiet"}
FINANCIAL_WORDS = {"wire", "transfer", "payment", "invoice", "bank",
                   "account", "routing", "ach", "swift", "funds"}
AUTHORITY_WORDS = {"ceo", "cfo", "president", "director", "boss",
                   "chairman", "executive", "management", "vp"}


def extract_features(text):
    words = text.lower().split()
    word_count = len(words) if words else 1
    sentences = re.split(r'[.!?]+', text)
    sentence_count = len([s for s in sentences if s.strip()]) or 1

    word_freq = Counter(words)
    unique_ratio = len(set(words)) / word_count

    urgency_score = sum(1 for w in words if w.strip(".,!?") in URGENCY_WORDS) / word_count
    pressure_score = sum(1 for w in words if w.strip(".,!?") in PRESSURE_WORDS) / word_count
    financial_score = sum(1 for w in words if w.strip(".,!?") in FINANCIAL_WORDS) / word_count
    authority_score = sum(1 for w in words if w.strip(".,!?") in AUTHORITY_WORDS) / word_count

    exclamation_ratio = text.count("!") / sentence_count
    caps_ratio = sum(1 for c in text if c.isupper()) / max(len(text), 1)
    avg_word_len = sum(len(w) for w in words) / word_count
    avg_sentence_len = word_count / sentence_count

    return {
        "word_count": word_count,
        "sentence_count": sentence_count,
        "unique_word_ratio": round(unique_ratio, 4),
        "urgency_score": round(urgency_score, 4),
        "pressure_score": round(pressure_score, 4),
        "financial_score": round(financial_score, 4),
        "authority_score": round(authority_score, 4),
        "exclamation_ratio": round(exclamation_ratio, 4),
        "caps_ratio": round(caps_ratio, 4),
        "avg_word_length": round(avg_word_len, 2),
        "avg_sentence_length": round(avg_sentence_len, 2),
    }


def compute_bec_probability(features):
    weights = {
        "urgency_score": 3.5, "pressure_score": 3.0, "financial_score": 4.0,
        "authority_score": 2.5, "exclamation_ratio": 1.0, "caps_ratio": 1.5,
    }
    raw = sum(features.get(k, 0) * w for k, w in weights.items())
    probability = 1 / (1 + math.exp(-10 * (raw - 0.15)))
    return round(probability, 4)


def analyze_writing_style(text):
    sentences = [s.strip() for s in re.split(r'[.!?]+', text) if s.strip()]
    if not sentences:
        return {"style_consistency": 1.0}
    lengths = [len(s.split()) for s in sentences]
    mean_len = sum(lengths) / len(lengths)
    variance = sum((l - mean_len) ** 2 for l in lengths) / len(lengths)
    std_dev = math.sqrt(variance)
    return {
        "mean_sentence_length": round(mean_len, 2),
        "sentence_length_std": round(std_dev, 2),
        "style_consistency": round(1 - min(std_dev / 20, 1), 4),
    }


def detect_impersonation_signals(text, known_sender_style=None):
    signals = []
    if re.search(r"sent from my (iphone|ipad|android|mobile)", text, re.IGNORECASE):
        signals.append("mobile_signature_present")
    if re.search(r"(please|kindly).*(do not|don't).*(reply|respond|call)", text, re.IGNORECASE):
        signals.append("discourages_verification")
    if re.search(r"(i am|i'm).*(in a meeting|traveling|on a flight|busy)", text, re.IGNORECASE):
        signals.append("unavailability_excuse")
    if re.search(r"(handle|process|complete).*(today|immediately|by end of day)", text, re.IGNORECASE):
        signals.append("time_pressure")
    if known_sender_style:
        current = analyze_writing_style(text)
        diff = abs(current["mean_sentence_length"] - known_sender_style.get("mean_sentence_length", 15))
        if diff > 8:
            signals.append("writing_style_deviation")
    return signals


def analyze_email(text, known_style=None):
    features = extract_features(text)
    probability = compute_bec_probability(features)
    style = analyze_writing_style(text)
    signals = detect_impersonation_signals(text, known_style)

    risk = "CRITICAL" if probability > 0.8 else "HIGH" if probability > 0.6 else \
           "MEDIUM" if probability > 0.3 else "LOW"

    return {
        "features": features,
        "writing_style": style,
        "impersonation_signals": signals,
        "bec_probability": probability,
        "risk_level": risk,
    }


def main():
    parser = argparse.ArgumentParser(description="AI-Powered BEC Detection")
    parser.add_argument("--file", required=True, help="Email text file to analyze")
    parser.add_argument("--baseline-file", help="Known sender baseline style JSON")
    args = parser.parse_args()

    with open(args.file, "r", encoding="utf-8") as f:
        text = f.read()

    known_style = None
    if args.baseline_file:
        with open(args.baseline_file, "r") as f:
            known_style = json.load(f)

    result = analyze_email(text, known_style)
    print(json.dumps(result, indent=2))


if __name__ == "__main__":
    main()
process.py10.8 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
AI-Powered BEC Detection Engine

Combines NLP analysis, behavioral scoring, and impersonation
detection to identify Business Email Compromise attacks.

Usage:
    python process.py detect --email-json email.json --baseline-file baselines.json
    python process.py train-baseline --email-log emails.json --output baselines.json
"""

import argparse
import json
import re
import sys
import math
from dataclasses import dataclass, field, asdict
from collections import defaultdict, Counter


@dataclass
class BECAIResult:
    """AI-powered BEC detection result."""
    from_address: str = ""
    subject: str = ""
    impostor_score: float = 0.0
    nlp_score: float = 0.0
    behavioral_score: float = 0.0
    intent_class: str = ""
    overall_score: float = 0.0
    is_bec: bool = False
    confidence: float = 0.0
    action: str = ""
    indicators: list = field(default_factory=list)


# NLP feature patterns
URGENCY_PATTERNS = [
    (r'\burgent(ly)?\b', 3), (r'\bimmediately\b', 3), (r'\basap\b', 3),
    (r'\btime.?sensitive\b', 2), (r'\btoday\b', 1), (r'\bright\s+now\b', 2),
    (r'\bdeadline\b', 1), (r'\boverdue\b', 2), (r'\bcritical\b', 2),
]

SECRECY_PATTERNS = [
    (r'\bconfidential\b', 2), (r'\bdo\s+not\s+(share|tell|discuss)\b', 3),
    (r'\bkeep.*between\s+us\b', 3), (r'\bquietly\b', 2),
    (r'\bdon.t\s+mention\b', 2), (r'\bprivate\s+matter\b', 2),
]

FINANCIAL_PATTERNS = [
    (r'\bwire\s+transfer\b', 3), (r'\binvoice\b', 2), (r'\bpayment\b', 2),
    (r'\bbank\s+(account|details|transfer)\b', 3), (r'\bgift\s+card\b', 4),
    (r'\bbitcoin\b', 3), (r'\baccount\s+number\b', 2), (r'\bswift\b', 2),
]

AUTHORITY_PATTERNS = [
    (r'\bi\s+need\s+you\s+to\b', 2), (r'\bi.m\s+in\s+a\s+meeting\b', 3),
    (r'\bhandle\s+this\b', 2), (r'\bi\s+authorize\b', 2),
    (r'\bapproved\s+by\s+me\b', 3), (r'\bdon.t\s+call\b', 2),
]


def compute_nlp_score(text: str) -> tuple:
    """Compute NLP-based BEC score from email text."""
    text_lower = text.lower()
    scores = {"urgency": 0, "secrecy": 0, "financial": 0, "authority": 0}
    indicators = []

    for pattern, weight in URGENCY_PATTERNS:
        if re.search(pattern, text_lower):
            scores["urgency"] += weight
            indicators.append(f"Urgency: {pattern}")

    for pattern, weight in SECRECY_PATTERNS:
        if re.search(pattern, text_lower):
            scores["secrecy"] += weight
            indicators.append(f"Secrecy: {pattern}")

    for pattern, weight in FINANCIAL_PATTERNS:
        if re.search(pattern, text_lower):
            scores["financial"] += weight
            indicators.append(f"Financial: {pattern}")

    for pattern, weight in AUTHORITY_PATTERNS:
        if re.search(pattern, text_lower):
            scores["authority"] += weight
            indicators.append(f"Authority: {pattern}")

    total = sum(scores.values())
    normalized = min(total / 25.0, 1.0)  # Normalize to 0-1

    # Determine intent
    intent = "unknown"
    if scores["financial"] > 3:
        intent = "payment_request"
    elif scores["authority"] > 3 and scores["urgency"] > 2:
        intent = "directive"
    elif scores["secrecy"] > 2:
        intent = "sensitive_request"

    return normalized, intent, indicators


def compute_impostor_score(email_data: dict, vip_list: list = None) -> tuple:
    """Check for sender impersonation."""
    score = 0.0
    indicators = []

    from_name = email_data.get("from_display_name", "").lower()
    from_email = email_data.get("from", "")
    reply_to = email_data.get("reply_to", "")

    from_domain = ""
    match = re.search(r'@([\w.-]+)', from_email)
    if match:
        from_domain = match.group(1).lower()

    # VIP name match from external domain
    if vip_list:
        for vip in vip_list:
            vip_name = vip.get("name", "").lower()
            vip_domain = vip.get("domain", "").lower()
            if vip_name and vip_name in from_name:
                if from_domain and vip_domain and from_domain != vip_domain:
                    score += 0.5
                    indicators.append(
                        f"Display name '{from_name}' matches VIP from external domain"
                    )

    # Reply-to mismatch
    if reply_to:
        reply_domain = ""
        match = re.search(r'@([\w.-]+)', reply_to)
        if match:
            reply_domain = match.group(1).lower()
        if reply_domain and from_domain and reply_domain != from_domain:
            score += 0.3
            indicators.append(f"Reply-to domain mismatch: {reply_domain} vs {from_domain}")

    return min(score, 1.0), indicators


def compute_behavioral_score(email_data: dict, baseline: dict) -> tuple:
    """Score behavioral anomalies against baseline."""
    score = 0.0
    indicators = []

    sender = email_data.get("from", "")
    recipient = email_data.get("to", "")
    hour = email_data.get("send_hour", -1)

    sender_baseline = baseline.get(sender, {})

    # Check if first-time sender to this recipient
    known_recipients = sender_baseline.get("recipients", [])
    if recipient and known_recipients and recipient not in known_recipients:
        score += 0.3
        indicators.append("First-time communication with this recipient")

    # Check unusual sending time
    typical_hours = sender_baseline.get("typical_hours", [])
    if hour >= 0 and typical_hours and hour not in typical_hours:
        score += 0.2
        indicators.append(f"Unusual sending hour: {hour}:00")

    # Check if request type is unusual for sender
    typical_topics = sender_baseline.get("typical_topics", [])
    subject = email_data.get("subject", "").lower()
    if typical_topics:
        financial = any(w in subject for w in ["payment", "wire", "invoice", "transfer"])
        if financial and "financial" not in typical_topics:
            score += 0.3
            indicators.append("Financial request from sender who doesn't typically discuss finances")

    return min(score, 1.0), indicators


def detect_bec_ai(email_data: dict, baseline: dict = None,
                  vip_list: list = None) -> BECAIResult:
    """Run full AI BEC detection pipeline."""
    result = BECAIResult()
    result.from_address = email_data.get("from", "")
    result.subject = email_data.get("subject", "")

    body = email_data.get("body", "")
    full_text = f"{result.subject} {body}"

    # NLP analysis
    result.nlp_score, result.intent_class, nlp_indicators = compute_nlp_score(full_text)
    result.indicators.extend(nlp_indicators)

    # Impostor detection
    result.impostor_score, imp_indicators = compute_impostor_score(email_data, vip_list or [])
    result.indicators.extend(imp_indicators)

    # Behavioral analysis
    if baseline:
        result.behavioral_score, beh_indicators = compute_behavioral_score(
            email_data, baseline
        )
        result.indicators.extend(beh_indicators)

    # Weighted aggregate score
    weights = {"nlp": 0.35, "impostor": 0.40, "behavioral": 0.25}
    result.overall_score = (
        result.nlp_score * weights["nlp"] +
        result.impostor_score * weights["impostor"] +
        result.behavioral_score * weights["behavioral"]
    )
    result.confidence = min(result.overall_score * 1.2, 1.0)

    # Classification
    if result.overall_score >= 0.7:
        result.is_bec = True
        result.action = "AUTO-QUARANTINE + SOC alert"
    elif result.overall_score >= 0.5:
        result.is_bec = True
        result.action = "WARNING BANNER + analyst queue"
    elif result.overall_score >= 0.3:
        result.action = "WARNING BANNER only"
    else:
        result.action = "DELIVER normally"

    return result


def train_baseline(emails: list) -> dict:
    """Build behavioral baselines from historical email data."""
    baselines = defaultdict(lambda: {
        "recipients": set(),
        "typical_hours": [],
        "typical_topics": set(),
        "email_count": 0,
    })

    for email in emails:
        sender = email.get("from", "")
        if not sender:
            continue

        b = baselines[sender]
        b["email_count"] += 1

        recipient = email.get("to", "")
        if recipient:
            b["recipients"].add(recipient)

        hour = email.get("send_hour", -1)
        if hour >= 0:
            b["typical_hours"].append(hour)

        subject = email.get("subject", "").lower()
        if any(w in subject for w in ["payment", "wire", "invoice"]):
            b["typical_topics"].add("financial")
        if any(w in subject for w in ["meeting", "schedule", "calendar"]):
            b["typical_topics"].add("scheduling")

    # Convert sets to lists for JSON serialization
    result = {}
    for sender, data in baselines.items():
        hours = data["typical_hours"]
        unique_hours = list(set(hours)) if hours else []
        result[sender] = {
            "recipients": list(data["recipients"]),
            "typical_hours": unique_hours,
            "typical_topics": list(data["typical_topics"]),
            "email_count": data["email_count"],
        }

    return result


def main():
    parser = argparse.ArgumentParser(description="AI-Powered BEC Detection")
    subparsers = parser.add_subparsers(dest="command")

    detect_parser = subparsers.add_parser("detect", help="Detect BEC in email")
    detect_parser.add_argument("--email-json", required=True)
    detect_parser.add_argument("--baseline-file")
    detect_parser.add_argument("--vip-file")

    train_parser = subparsers.add_parser("train-baseline", help="Train behavioral baseline")
    train_parser.add_argument("--email-log", required=True)
    train_parser.add_argument("--output", required=True)

    parser.add_argument("--json", action="store_true")
    args = parser.parse_args()

    if args.command == "detect":
        with open(args.email_json) as f:
            email_data = json.load(f)
        baseline = {}
        if args.baseline_file:
            with open(args.baseline_file) as f:
                baseline = json.load(f)
        vip_list = []
        if args.vip_file:
            with open(args.vip_file) as f:
                vip_list = json.load(f)

        result = detect_bec_ai(email_data, baseline, vip_list)
        if args.json:
            print(json.dumps(asdict(result), indent=2))
        else:
            print(f"BEC Score: {result.overall_score:.0%}")
            print(f"Is BEC: {'YES' if result.is_bec else 'No'}")
            print(f"Intent: {result.intent_class}")
            print(f"Action: {result.action}")
            if result.indicators:
                print("Indicators:")
                for ind in result.indicators:
                    print(f"  - {ind}")

    elif args.command == "train-baseline":
        with open(args.email_log) as f:
            emails = json.load(f)
        baseline = train_baseline(emails)
        with open(args.output, 'w') as f:
            json.dump(baseline, f, indent=2)
        print(f"Baseline trained for {len(baseline)} senders")

    else:
        parser.print_help()


if __name__ == "__main__":
    main()

Assets 1

template.mdtext/markdown · 0.9 KB
Keep exploring