Motor de regras Python-native que elimina o overhead interpretado do JSONLogic. Avaliação em sub-millisegundo, regras criadas em runtime por tenant, type safety completo.
// Regra "simples" em JSONLogic
{
"and": [
{"<": [{"var": "last_visit_days"}, 180]},
{"==": [{"var": "has_insurance"}, true]},
{"or": [
{">": [{"var": "pending_treatments"}, 0]},
{"in": [{"var": "vertical"}, ["dental", "medical"]]}
]}
]
}# Mesma regra em Python-native
lambda facts: (
facts.get('last_visit_days', 999) < 180 and
facts.get('has_insurance') is True and
(facts.get('pending_treatments', 0) > 0 or
facts.get('vertical') in ('dental', 'medical'))
)┌─────────────────────────────────────────────────────────────────────────┐
│ Request Flow (sub-ms target) │
└─────────────────────────────────────────────────────────────────────────┘
Client Request
│
▼
┌─────────────────┐ ┌──────────────────────┐ ┌─────────────────┐
│ Backend │────▶│ Rules Coordinator │────▶│ Rules Engine │
│ Orchestrator │ │ (Cache + Fallback) │ │ (Port 8040) │
└─────────────────┘ └──────────────────────┘ └─────────────────┘
│ │
│ │
┌──────▼──────┐ ┌──────▼──────┐
│ Redis Cache │ │ PostgreSQL │
│ (15 min) │ │ (Rules DB) │
└─────────────┘ └─────────────┘
┌─────────────────────────────────────────────────────────────────────────┐
│ Fallback Hierarchy (never fails) │
├─────────────────────────────────────────────────────────────────────────┤
│ 1. Rules Engine API → Primary (target <50ms) │
│ 2. Redis Cache → Secondary (target <5ms) │
│ 3. Basic Fallback → Always available (keyword-based) │
└─────────────────────────────────────────────────────────────────────────┘Microsserviço dedicado que compila e executa regras Python-native. Cada tenant tem suas próprias regras isoladas.
Proxy inteligente no Backend Orchestrator com cache, circuit breaker e fallback multi-tier.
Integração com Memory Engine para enriquecer fatos com contexto histórico do cliente.
O grande diferencial do Rules Engine é permitir que cada tenant crie suas próprias regras em tempo real, sem deploy, sem downtime, sem código.
POST /api/rules/
{
"name": "Emergency Dental Protocol",
"vertical": "dental",
"event_types": ["message_received"],
"condition_code": """
lambda facts: (
any(word in facts.get('message_content', '').lower()
for word in ['pain', 'blood', 'swollen']) and
facts.get('customer_urgency_level') == 'high'
)
""",
"actions": [{
"type": "emergency_protocol",
"params": {
"escalate_to_human": true,
"priority": "urgent",
"notification_channel": "whatsapp"
}
}],
"priority": 1,
"timeout_seconds": 30
}Lembrete de limpeza baseado em última visita + status do seguro. Se passou 6 meses e tem cobertura → agenda preventiva.
Carrinho abandonado há 2h + valor > R$200 + cliente recorrente → oferta de 10% desconto + frete grátis.
Prazo processual em 48h + cliente não respondeu última mensagem → alerta urgente + escalação para advogado responsável.
Cada tenant tem regras completamente isoladas. Nenhuma regra da Clínica ABC afeta a Loja XYZ. Zero vazamento de lógica de negócio entre clientes.
class RulesCoordinator:
"""
🎯 Coordenador Central de Regras
Responsibilities:
1. Proxy inteligente para Rules Engine com <50ms target
2. Cache Redis para performance otimizada
3. Integração com Memory Coordinator para contexto enriquecido
4. Fallback quando Rules Engine falha
5. Circuit breaker para proteção contra falhas
"""
async def evaluate_rules_for_conversation(
self,
tenant_id: str,
conversation_data: dict,
message_content: str,
memory_context: dict | None = None,
) -> dict:
# 1. 🧠 Enriquecer dados com Memory Coordinator
enriched_facts = await self._enrich_with_memory_context(
tenant_id, conversation_data, message_content, memory_context
)
# 2. 🎯 Tentar Rules Engine (primary)
try:
async with circuit_breaker_context("rules_engine_evaluate"):
response = await self.rules_client.post(
"/api/evaluation/evaluate",
json={
"tenant_id": tenant_id,
"event_type": "message_received",
"facts": enriched_facts,
}
)
if response.status_code == 200:
result = response.json()
# Cache para reuso futuro
await self._cache_evaluation_result(tenant_id, enriched_facts, result)
return result
except Exception as e:
logger.warning(f"Rules Engine failed: {e}")
# 3. 🔄 Fallback para Redis cache
cache_result = await self._evaluate_via_redis_cache(tenant_id, enriched_facts)
if cache_result:
return cache_result
# 4. 🚨 Basic fallback (keyword-based, nunca falha)
return await self._evaluate_basic_fallback(tenant_id, enriched_facts)def _extract_intelligent_patterns(self, memory_context: dict) -> dict:
"""
Extrai padrões inteligentes do contexto de memória para regras mais sofisticadas
"""
patterns = {
"interaction_frequency": "new", # new | regular | frequent
"customer_urgency_level": "normal", # normal | high
"preferred_communication_style": "formal",
"likely_appointment_need": False,
"pain_indicators": False,
"satisfaction_level": "neutral",
}
context_content = memory_context.get("context", "").lower()
memory_items = memory_context.get("memory_items", [])
# Detecta urgência (dor, emergência, sangramento)
if any(word in context_content for word in ["pain", "urgent", "emergency"]):
patterns["customer_urgency_level"] = "high"
patterns["pain_indicators"] = True
# Detecta necessidade de agendamento
if any(word in context_content for word in ["schedule", "appointment", "book"]):
patterns["likely_appointment_need"] = True
# Analisa frequência de interação
if len(memory_items) > 5:
patterns["interaction_frequency"] = "frequent"
elif len(memory_items) > 2:
patterns["interaction_frequency"] = "regular"
return patterns
# Resultado: regras podem usar facts enriquecidos
# facts = {
# "message_content": "I have tooth pain",
# "customer_urgency_level": "high",
# "pain_indicators": True,
# "interaction_frequency": "regular",
# "has_previous_appointments": True
# }Nova mensagem do cliente
Início de conversa
Atendente finalizou
Agendamento confirmado
Carrinho abandonado
Prazo se aproximando
Cliente insatisfeito
Trigger por horário
Consideramos criar uma DSL (Domain-Specific Language) para regras, mas decidimos usar Python lambda diretamente. Razão: desenvolvedores já conhecem Python, debugging normal, type hints funcionam, ecosystem inteiro disponível. O sandboxing é feito via AST parsing + restricted builtins.
Regras são cacheadas por 15 minutos, não infinitamente. Isso permite que alterações em regras (via API) sejam refletidas em tempo razoável sem necessidade de invalidação manual. O tradeoff entre performance e freshness foi calibrado em produção.
O último nível de fallback usa análise simples de keywords. Não é sofisticado, mas garante que o sistema NUNCA falha em avaliar uma mensagem. "Dor" → urgência, "agendar" → appointment. Simples, mas funcional como último recurso.
Rules Engine é um microsserviço independente, não uma library importada. Isso permite escalar horizontalmente, deploy independente, e isolamento de falhas. Se o Rules Engine crashar, o Coordinator usa cache/fallback.
Python 3.11+ (bytecode optimized)
FastAPI + Pydantic
Redis (cache + pub/sub)
PostgreSQL (rules metadata)
Per-tenant rule namespaces
5 failures → 60s recovery
HTTPX async pooling
Prometheus + per-rule tracking
JSONLogic vs Python-native, DSLs customizadas, ou como fazer regras escalarem - adoro falar sobre esses temas.