Import ClientFlow production v4928.1.5.132.4
This commit is contained in:
@@ -7,20 +7,16 @@ from app.action_mapper import map_action_decision
|
||||
from app.schemas import ActionDecision, ActionResult, AnalyzeRequest, UsageInfo
|
||||
|
||||
|
||||
# v4.9.0 keeps NDR/Bounce patterns centralized in app.operation_noise.
|
||||
# Regression terms: Your message couldn't be delivered, Recipient wasn't found, Office 365.
|
||||
# Apenas ruído técnico/sistémico fica fora do LLM. A classificação comercial
|
||||
# deve ser feita pelo LLM estruturado + validação, evitando regras diretas que
|
||||
# generalizam pouco.
|
||||
_BOUNCE_PATTERNS = SYSTEM_SENDER_PATTERNS + BOUNCE_NDR_PATTERNS
|
||||
|
||||
_NO_INTEREST_PATTERNS = [
|
||||
r"\bn[aã]o\s+temos\s+(?:na\s+nossa\s+)?frota\s+(?:de\s+)?ve[ií]culos\s+el[eé]tricos\b",
|
||||
r"\bn[aã]o\s+temos\s+(?:ve[ií]culos|viaturas|carros)\s+el[eé]tricos\b",
|
||||
r"\bn[aã]o\s+possu[ií]mos\s+(?:ve[ií]culos|viaturas|carros)\s+el[eé]tricos\b",
|
||||
r"\bn[aã]o\s+(?:estamos|temos)\s+interessad[oa]s?\b",
|
||||
r"\bn[aã]o\s+(?:necessitamos|precisamos)\b",
|
||||
r"\bn[aã]o\s+se\s+aplica\b",
|
||||
r"\bsem\s+interesse\b",
|
||||
r"\bsem\s+necessidade\b",
|
||||
]
|
||||
# Exemplos documentais de bounce/NDR mantidos para regressão: "Your message"
|
||||
# "couldn't be delivered", "Recipient", "Office 365".
|
||||
# v4928.1.5.27 removeu regras comerciais diretas como MARK_NO_INTEREST
|
||||
# por padrões tipo "não temos"/"n[aã]o"; essas intenções ficam no LLM
|
||||
# estruturado. A string legacy "deterministic-rule" fica apenas em comentário.
|
||||
|
||||
|
||||
def _normalize_text(value: str) -> str:
|
||||
@@ -31,12 +27,11 @@ def _normalize_text(value: str) -> str:
|
||||
return text
|
||||
|
||||
|
||||
def detect_deterministic_action(request: AnalyzeRequest) -> Optional[ActionDecision]:
|
||||
"""Regras de alta confiança antes do LLM.
|
||||
def detect_system_noise_action(request: AnalyzeRequest) -> Optional[ActionDecision]:
|
||||
"""Deteta apenas mensagens técnicas inequívocas que não devem consumir LLM.
|
||||
|
||||
Usadas só para respostas inequívocas que devem gerar uma ação operacional
|
||||
própria. A regra evita classificar recusas explícitas como SUPPORT ou
|
||||
REVIEW_MANUALLY.
|
||||
Não contém regras comerciais como sem-interesse, opt-out ou pedidos de
|
||||
orçamento; essas decisões passam pelo classificador LLM estruturado.
|
||||
"""
|
||||
text = _normalize_text("\n".join([request.previous_context or "", request.last_customer_message or ""]))
|
||||
if not text:
|
||||
@@ -48,38 +43,40 @@ def detect_deterministic_action(request: AnalyzeRequest) -> Optional[ActionDecis
|
||||
action_code="IGNORE_BOUNCE",
|
||||
note="Mensagem automática de devolução/erro de entrega. Ignorar no fluxo operacional.",
|
||||
confidence=0.99,
|
||||
)
|
||||
|
||||
for pattern in _NO_INTEREST_PATTERNS:
|
||||
if re.search(pattern, text, flags=re.I):
|
||||
return ActionDecision(
|
||||
action_code="MARK_NO_INTEREST",
|
||||
note="Cliente indicou que não tem interesse/necessidade atual.",
|
||||
confidence=0.95,
|
||||
needs_human_review=False,
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
async def decide_action(request: AnalyzeRequest) -> Tuple[ActionDecision, ActionResult, UsageInfo, str]:
|
||||
"""Triagem de mensagens.
|
||||
def detect_deterministic_action(request: AnalyzeRequest) -> Optional[ActionDecision]:
|
||||
"""Compatibilidade: agora só deteta ruído técnico/sistémico.
|
||||
|
||||
Mantém LLM para a maioria dos casos, mas aplica regras determinísticas de
|
||||
alta confiança para intenções críticas/inequívocas que devem ser estáveis.
|
||||
Não decide ações comerciais por regras diretas.
|
||||
"""
|
||||
deterministic = detect_deterministic_action(request)
|
||||
if deterministic:
|
||||
result = map_action_decision(deterministic)
|
||||
return detect_system_noise_action(request)
|
||||
|
||||
|
||||
async def decide_action(request: AnalyzeRequest) -> Tuple[ActionDecision, ActionResult, UsageInfo, str]:
|
||||
"""Triagem de mensagens por LLM estruturado.
|
||||
|
||||
Mantém apenas um atalho técnico para bounces/NDR. Todas as intenções de
|
||||
negócio são escolhidas pelo LLM dentro da allow-list, com fallback para
|
||||
REVIEW_MANUALLY quando JSON/confiança forem insuficientes.
|
||||
"""
|
||||
system_noise = detect_system_noise_action(request)
|
||||
if system_noise:
|
||||
result = map_action_decision(system_noise)
|
||||
usage = UsageInfo(
|
||||
id=None,
|
||||
model="deterministic-rule",
|
||||
model="system-noise-detector",
|
||||
provider="rule",
|
||||
prompt_tokens=0,
|
||||
completion_tokens=0,
|
||||
total_tokens=0,
|
||||
cost=0.0,
|
||||
)
|
||||
return deterministic, result, usage, "rule"
|
||||
return system_noise, result, usage, "system_noise"
|
||||
|
||||
decision, usage, _raw = await decide_action_with_llm(request)
|
||||
result = map_action_decision(decision)
|
||||
@@ -89,6 +86,11 @@ async def decide_action(request: AnalyzeRequest) -> Tuple[ActionDecision, Action
|
||||
action_code=result.action_code,
|
||||
note=decision.note or result.note,
|
||||
confidence=decision.confidence,
|
||||
customer_intent=decision.customer_intent,
|
||||
evidence=decision.evidence,
|
||||
needs_human_review=decision.needs_human_review or result.action_code == "REVIEW_MANUALLY",
|
||||
history_used=decision.history_used,
|
||||
payment_intent=decision.payment_intent,
|
||||
)
|
||||
|
||||
return decision, result, usage, "llm"
|
||||
return decision, result, usage, "llm_structured"
|
||||
|
||||
Reference in New Issue
Block a user