mirror of
https://github.com/x1xhlol/system-prompts-and-models-of-ai-tools.git
synced 2026-06-18 23:39:34 +00:00
174 lines
5.3 KiB
Python
174 lines
5.3 KiB
Python
"""
|
|
Next-Best-Action engine.
|
|
|
|
Takes a fully-scored account and returns:
|
|
action: one of call | gmail_draft | linkedin_manual | partner_intro |
|
|
enrich_more | block | wait_followup
|
|
rationale: one-line explanation
|
|
priority_bucket: P0 | P1 | P2 | P3 | BLOCKED
|
|
|
|
Formula for priority_score (0..100):
|
|
0.30 * fit_score (max 40 → 12 contribution)
|
|
+ 0.25 * intent_score (max 30 → 7.5 contribution)
|
|
+ 0.20 * urgency_score (max 30 → 6 contribution)
|
|
+ 0.15 * revenue_score (max 15 → 2.25 contribution)
|
|
- 0.10 * risk_score (subtract up to 10)
|
|
|
|
Then mapped to a bucket:
|
|
>= 60 → P0
|
|
>= 45 → P1
|
|
>= 30 → P2
|
|
< 30 → P3
|
|
risk > 50 OR opt_out → BLOCKED
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import asdict, dataclass
|
|
from typing import Any
|
|
|
|
|
|
@dataclass
|
|
class NextAction:
|
|
action: str
|
|
rationale: str
|
|
priority_bucket: str
|
|
priority_score: float
|
|
fit_contribution: float
|
|
intent_contribution: float
|
|
urgency_contribution: float
|
|
revenue_contribution: float
|
|
risk_penalty: float
|
|
|
|
def to_dict(self) -> dict[str, Any]:
|
|
return asdict(self)
|
|
|
|
|
|
def compute_priority(
|
|
*,
|
|
fit_score: float,
|
|
intent_score: float,
|
|
urgency_score: float,
|
|
revenue_score: float,
|
|
risk_score: float,
|
|
) -> float:
|
|
"""Apply the weighted formula. Clamp to [0, 100]."""
|
|
score = (
|
|
0.30 * fit_score
|
|
+ 0.25 * intent_score
|
|
+ 0.20 * urgency_score
|
|
+ 0.15 * revenue_score
|
|
- 0.10 * risk_score
|
|
)
|
|
return max(0.0, min(100.0, round(score, 1)))
|
|
|
|
|
|
def decide(
|
|
*,
|
|
fit_score: float = 0,
|
|
intent_score: float = 0,
|
|
urgency_score: float = 0,
|
|
revenue_score: float = 0,
|
|
risk_score: float = 0,
|
|
opt_out: bool = False,
|
|
has_business_email: bool = False,
|
|
has_phone: bool = False,
|
|
has_linkedin_handle: bool = False,
|
|
is_potential_partner: bool = False,
|
|
sector: str | None = None,
|
|
allowed_use: str | None = None,
|
|
) -> NextAction:
|
|
"""Return the recommended next-best-action for this account."""
|
|
priority_score = compute_priority(
|
|
fit_score=fit_score, intent_score=intent_score,
|
|
urgency_score=urgency_score, revenue_score=revenue_score,
|
|
risk_score=risk_score,
|
|
)
|
|
|
|
# Block conditions (override score)
|
|
if opt_out:
|
|
return NextAction(
|
|
action="block",
|
|
rationale="opt_out_true",
|
|
priority_bucket="BLOCKED",
|
|
priority_score=priority_score,
|
|
fit_contribution=fit_score * 0.30,
|
|
intent_contribution=intent_score * 0.25,
|
|
urgency_contribution=urgency_score * 0.20,
|
|
revenue_contribution=revenue_score * 0.15,
|
|
risk_penalty=risk_score * 0.10,
|
|
)
|
|
if risk_score > 50:
|
|
return NextAction(
|
|
action="block",
|
|
rationale=f"risk_score_too_high:{risk_score:.0f}",
|
|
priority_bucket="BLOCKED",
|
|
priority_score=priority_score,
|
|
fit_contribution=fit_score * 0.30,
|
|
intent_contribution=intent_score * 0.25,
|
|
urgency_contribution=urgency_score * 0.20,
|
|
revenue_contribution=revenue_score * 0.15,
|
|
risk_penalty=risk_score * 0.10,
|
|
)
|
|
if not allowed_use or allowed_use in {"unknown", ""}:
|
|
return NextAction(
|
|
action="block",
|
|
rationale="allowed_use_missing",
|
|
priority_bucket="BLOCKED",
|
|
priority_score=priority_score,
|
|
fit_contribution=fit_score * 0.30,
|
|
intent_contribution=intent_score * 0.25,
|
|
urgency_contribution=urgency_score * 0.20,
|
|
revenue_contribution=revenue_score * 0.15,
|
|
risk_penalty=risk_score * 0.10,
|
|
)
|
|
|
|
# Bucket from score
|
|
if priority_score >= 60:
|
|
bucket = "P0"
|
|
elif priority_score >= 45:
|
|
bucket = "P1"
|
|
elif priority_score >= 30:
|
|
bucket = "P2"
|
|
else:
|
|
bucket = "P3"
|
|
|
|
# Action selection
|
|
if is_potential_partner:
|
|
action = "partner_intro"
|
|
rationale = "agency_or_consulting_partner_path"
|
|
elif bucket in ("P0", "P1") and has_business_email:
|
|
action = "gmail_draft"
|
|
rationale = f"{bucket}_high_intent_business_email_present"
|
|
elif bucket in ("P0", "P1") and has_phone:
|
|
action = "call"
|
|
rationale = f"{bucket}_high_intent_phone_only"
|
|
elif bucket == "P2" and has_linkedin_handle:
|
|
action = "linkedin_manual"
|
|
rationale = "P2_with_linkedin_present"
|
|
elif bucket == "P2" and has_business_email:
|
|
action = "gmail_draft"
|
|
rationale = "P2_business_email_lower_priority_drip"
|
|
elif bucket == "P2":
|
|
action = "call"
|
|
rationale = "P2_phone_only"
|
|
elif bucket == "P3":
|
|
action = "wait_followup"
|
|
rationale = "P3_low_priority_revisit_in_30d"
|
|
else:
|
|
action = "enrich_more"
|
|
rationale = "needs_more_data_before_outreach"
|
|
|
|
contributions = NextAction(
|
|
action=action,
|
|
rationale=rationale,
|
|
priority_bucket=bucket,
|
|
priority_score=priority_score,
|
|
fit_contribution=round(fit_score * 0.30, 2),
|
|
intent_contribution=round(intent_score * 0.25, 2),
|
|
urgency_contribution=round(urgency_score * 0.20, 2),
|
|
revenue_contribution=round(revenue_score * 0.15, 2),
|
|
risk_penalty=round(risk_score * 0.10, 2),
|
|
)
|
|
return contributions
|