fix(ux): Refinamento final do onboarding e correção de loop
Porque foi feita essa alteração? Resolução de Bug e Melhoria de UX. - Foi removido o tom excessivamente animado e os exemplos robóticos do onboarding. - Corrigido o loop de registro onde o usuário não era encontrado no Qdrant logo após o upsert (adicionado cache local em memória). - Implementado regex flexível para capturar nome e empresa com separadores variados (vírgula, ponto, hífen, espaço). - Removidas saudações duplicadas no fluxo de análise. - Melhorado o tratamento de erros na resposta final do agente. Quais testes foram feitos? - Execução de pytest tests/test_onboarding.py (12 testes passando). - Teste manual via Telegram validando o fluxo de registro sem vírgula e a transição direta para o agente. A alteração gerou um novo teste que precisa ser implementado no pipeline de testes? Não. Os testes unitários existentes foram atualizados para refletir a mudança nos templates de mensagem, garantindo que o fluxo continue validado.
This commit is contained in:
parent
14865c049f
commit
a0476886ab
|
|
@ -20,47 +20,38 @@ logger = logging.getLogger("ArthurOnboarding")
|
|||
# Brazil timezone
|
||||
BRAZIL_TZ = ZoneInfo("America/Sao_Paulo")
|
||||
|
||||
# Welcome message templates - will be randomized
|
||||
# Welcome message templates - will be randomized (NO examples - too robotic)
|
||||
WELCOME_TEMPLATES = [
|
||||
(
|
||||
"Olá! {greeting}. 👋\n\n"
|
||||
"Essa parece ser a primeira vez que nos falamos. "
|
||||
"Pode me confirmar seu <b>Nome Completo</b> e <b>Empresa</b>, por favor?\n\n"
|
||||
"Exemplo: <i>João Silva, iT Guys</i>"
|
||||
"Pode me confirmar seu <b>nome</b> e <b>empresa</b>?"
|
||||
),
|
||||
(
|
||||
"{greeting}! 👋\n\n"
|
||||
"Prazer em conhecê-lo! Para que eu possa te atender melhor, "
|
||||
"me conta: qual é o seu <b>nome</b> e de qual <b>empresa</b> você fala?\n\n"
|
||||
"Pode responder assim: <i>Maria Santos, Empresa XYZ</i>"
|
||||
"Prazer! Para te atender melhor, "
|
||||
"me conta seu <b>nome</b> e de qual <b>empresa</b> você fala?"
|
||||
),
|
||||
(
|
||||
"E aí! {greeting}. 😊\n\n"
|
||||
"Ainda não temos seu cadastro por aqui. "
|
||||
"Me passa seu <b>nome completo</b> e a <b>empresa</b> que você representa?\n\n"
|
||||
"Formato: <i>Nome Sobrenome, Sua Empresa</i>"
|
||||
"{greeting}. 👋\n\n"
|
||||
"Ainda não temos seu cadastro. "
|
||||
"Qual seu <b>nome</b> e <b>empresa</b>?"
|
||||
),
|
||||
]
|
||||
|
||||
# Registration confirmation templates
|
||||
# Registration confirmation templates (natural, conversational)
|
||||
REGISTRATION_SUCCESS_TEMPLATES = [
|
||||
(
|
||||
"Perfeito, <b>{first_name}</b>! Obrigado pelas informações. ✅\n\n"
|
||||
"Você está vinculado à empresa <b>{company}</b>.\n"
|
||||
"Agora pode me descrever problemas técnicos que eu analisarei.\n\n"
|
||||
"Como posso te ajudar hoje?"
|
||||
"Certo, <b>{first_name}</b>. ✅\n"
|
||||
"Você está na <b>{company}</b>. O que houve?"
|
||||
),
|
||||
(
|
||||
"Show, <b>{first_name}</b>! Cadastro feito com sucesso. ✅\n\n"
|
||||
"Te encontrei aqui como parte da <b>{company}</b>.\n"
|
||||
"Estou pronto pra ajudar com qualquer questão técnica!\n\n"
|
||||
"O que você precisa hoje?"
|
||||
"Ok <b>{first_name}</b>, te encontrei aqui na <b>{company}</b>. ✅\n"
|
||||
"Como posso ajudar?"
|
||||
),
|
||||
(
|
||||
"Muito obrigado, <b>{first_name}</b>! Tudo certo. ✅\n\n"
|
||||
"Seu perfil está associado à <b>{company}</b>.\n"
|
||||
"Pode contar comigo para análises técnicas e suporte.\n\n"
|
||||
"Em que posso ajudar?"
|
||||
"Pronto, <b>{first_name}</b>. ✅\n"
|
||||
"Associei você à <b>{company}</b>. Qual o problema?"
|
||||
),
|
||||
]
|
||||
|
||||
|
|
@ -122,10 +113,14 @@ class OnboardingManager:
|
|||
"""
|
||||
|
||||
COLLECTION_NAME = "arthur_users"
|
||||
VECTOR_SIZE = 384
|
||||
|
||||
def __init__(self):
|
||||
self._qdrant = get_qdrant_client()
|
||||
self._pending_registrations: Dict[str, Dict[str, Any]] = {}
|
||||
# Local cache to avoid Qdrant lookup issues
|
||||
self._registered_users: Dict[str, UserProfile] = {}
|
||||
self._collection_ready = False
|
||||
logger.info("OnboardingManager initialized")
|
||||
|
||||
def get_time_greeting(self) -> str:
|
||||
|
|
@ -156,6 +151,10 @@ class OnboardingManager:
|
|||
Returns:
|
||||
True if user is registered, False otherwise
|
||||
"""
|
||||
# Check local cache first (faster and more reliable)
|
||||
if telegram_id in self._registered_users:
|
||||
return self._registered_users[telegram_id].status == UserStatus.REGISTERED
|
||||
|
||||
profile = await self.get_user_profile(telegram_id)
|
||||
return profile is not None and profile.status == UserStatus.REGISTERED
|
||||
|
||||
|
|
@ -199,6 +198,10 @@ class OnboardingManager:
|
|||
Returns:
|
||||
Dict with name, company, last_ticket_summary (if any)
|
||||
"""
|
||||
# Check local cache first
|
||||
if telegram_id in self._registered_users:
|
||||
profile = self._registered_users[telegram_id]
|
||||
else:
|
||||
profile = await self.get_user_profile(telegram_id)
|
||||
|
||||
if profile is None:
|
||||
|
|
@ -287,6 +290,9 @@ class OnboardingManager:
|
|||
except Exception as e:
|
||||
logger.error(f"Failed to store user profile: {e}")
|
||||
|
||||
# Add to local cache (IMPORTANT: fixes the loop issue)
|
||||
self._registered_users[telegram_id] = profile
|
||||
|
||||
# Clean up pending registration
|
||||
self._pending_registrations.pop(telegram_id, None)
|
||||
|
||||
|
|
|
|||
|
|
@ -140,18 +140,22 @@ class TelegramListener:
|
|||
telegram_id = str(user.id)
|
||||
message = update.message.text.strip()
|
||||
|
||||
# Parse "Name, Company" format
|
||||
match = re.match(r'^(.+?),\s*(.+)$', message)
|
||||
# Parse "Name, Company" or "Name. Company" or "Name - Company" format
|
||||
# More flexible: accepts comma, period, or hyphen as separator
|
||||
match = re.match(r'^(.+?)[,\.\-]\s*(.+)$', message)
|
||||
|
||||
if not match:
|
||||
# Try splitting by last space if no separator found
|
||||
parts = message.rsplit(' ', 1)
|
||||
if len(parts) == 2 and len(parts[0]) > 2 and len(parts[1]) > 2:
|
||||
name, company = parts[0].strip(), parts[1].strip()
|
||||
else:
|
||||
await update.message.reply_text(
|
||||
"📝 Por favor, informe no formato:\n"
|
||||
"<i>Nome Completo, Empresa</i>\n\n"
|
||||
"Exemplo: João Silva, iT Guys",
|
||||
"Não entendi. Qual seu <b>nome</b> e <b>empresa</b>?",
|
||||
parse_mode="HTML"
|
||||
)
|
||||
return
|
||||
|
||||
else:
|
||||
name = match.group(1).strip()
|
||||
company = match.group(2).strip()
|
||||
|
||||
|
|
@ -220,10 +224,9 @@ class TelegramListener:
|
|||
greeting = self._onboarding.get_time_greeting()
|
||||
first_name = user_ctx.get("name", "você").split()[0]
|
||||
|
||||
# Send acknowledgment
|
||||
# Send acknowledgment (NO greeting here - already greeted in registration)
|
||||
await update.message.reply_text(
|
||||
f"👋 {greeting}, {first_name}!\n"
|
||||
"🔍 Analisando sua solicitação...",
|
||||
"Um momento...",
|
||||
parse_mode="HTML"
|
||||
)
|
||||
|
||||
|
|
@ -248,12 +251,14 @@ class TelegramListener:
|
|||
await self._onboarding.update_last_contact(telegram_id, ticket_summary)
|
||||
|
||||
# Format response
|
||||
if result.success:
|
||||
response = f"✅ Análise Concluída\n\n{result.context.final_response}"
|
||||
if result.success and result.context.final_response:
|
||||
response = result.context.final_response
|
||||
elif result.context.error:
|
||||
response = f"⚠️ Houve um problema: {result.context.error}"
|
||||
else:
|
||||
response = f"❌ Erro na Análise\n\n{result.context.error}"
|
||||
response = "Desculpe, não consegui processar sua solicitação. Pode reformular?"
|
||||
|
||||
await update.message.reply_text(response)
|
||||
await update.message.reply_text(response, parse_mode="HTML")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing telegram message: {e}")
|
||||
|
|
|
|||
|
|
@ -124,8 +124,8 @@ class TestStartRegistration:
|
|||
result = onboarding.start_registration("123456")
|
||||
|
||||
assert "Boa tarde" in result
|
||||
assert "Nome Completo" in result
|
||||
assert "Empresa" in result
|
||||
assert "nome" in result.lower()
|
||||
assert "empresa" in result.lower()
|
||||
|
||||
def test_marks_user_as_pending(self, onboarding):
|
||||
"""Should mark user as pending registration."""
|
||||
|
|
|
|||
Loading…
Reference in New Issue