refactor: add rule engine, risk service, quote state machine, and replay tests
This commit is contained in:
78
services/risk_service.py
Normal file
78
services/risk_service.py
Normal file
@@ -0,0 +1,78 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Callable, Dict, Awaitable
|
||||
|
||||
from db.customer_risk_db import risk_db
|
||||
from utils.content_filter import should_block_customer_smart
|
||||
|
||||
|
||||
@dataclass
|
||||
class RiskDecision:
|
||||
blocked: bool
|
||||
category: str = "none"
|
||||
reason: str = ""
|
||||
source: str = "none" # manual/ai_filter/fallback
|
||||
profile: Dict[str, Any] | None = None
|
||||
|
||||
|
||||
class RiskService:
|
||||
def evaluate_customer(self, customer_id: str) -> Dict[str, Any]:
|
||||
return risk_db.evaluate_customer(customer_id)
|
||||
|
||||
def check_manual_block(self, customer_id: str) -> RiskDecision:
|
||||
profile = self.evaluate_customer(customer_id)
|
||||
if bool(profile.get("do_not_serve")):
|
||||
return RiskDecision(
|
||||
blocked=True,
|
||||
category="manual_block",
|
||||
reason="do_not_serve",
|
||||
source="manual",
|
||||
profile=profile,
|
||||
)
|
||||
return RiskDecision(blocked=False, source="manual", profile=profile)
|
||||
|
||||
async def check_text_block(
|
||||
self,
|
||||
text: str,
|
||||
*,
|
||||
political_detector: Callable[[str], bool],
|
||||
map_detector: Callable[[str], bool],
|
||||
) -> RiskDecision:
|
||||
try:
|
||||
hit, category, reason = await should_block_customer_smart(text)
|
||||
map_hit = map_detector(text) or category == "map"
|
||||
political_hit = political_detector(text) or category == "political"
|
||||
if hit or map_hit or political_hit:
|
||||
c = category
|
||||
if map_hit:
|
||||
c = "map"
|
||||
elif political_hit:
|
||||
c = "political"
|
||||
return RiskDecision(
|
||||
blocked=True,
|
||||
category=c or "other",
|
||||
reason=reason or "sensitive_text",
|
||||
source="ai_filter",
|
||||
)
|
||||
return RiskDecision(blocked=False, category="none", source="ai_filter")
|
||||
except Exception:
|
||||
map_hit = map_detector(text)
|
||||
political_hit = political_detector(text)
|
||||
if map_hit:
|
||||
return RiskDecision(blocked=True, category="map", reason="fallback_match", source="fallback")
|
||||
if political_hit:
|
||||
return RiskDecision(blocked=True, category="political", reason="fallback_match", source="fallback")
|
||||
return RiskDecision(blocked=False, category="none", source="fallback")
|
||||
|
||||
@staticmethod
|
||||
def build_reject_text(category: str) -> str:
|
||||
if category == "map":
|
||||
return "地图这类不做哈,这边不接地图相关需求。"
|
||||
if category == "sexual":
|
||||
return "这类不做哈,涉黄擦边内容都不接。"
|
||||
if category == "violent":
|
||||
return "这类不做哈,暴力血腥相关都不接。"
|
||||
if category == "political":
|
||||
return "这类不做哈,政治相关图片和人物都不接。"
|
||||
return "这类不做哈,这边不接这类需求。"
|
||||
Reference in New Issue
Block a user