atendentepro 0.6.5__tar.gz → 0.6.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. {atendentepro-0.6.5 → atendentepro-0.6.7}/CHANGELOG.md +57 -0
  2. {atendentepro-0.6.5 → atendentepro-0.6.7}/PKG-INFO +177 -1
  3. {atendentepro-0.6.5 → atendentepro-0.6.7}/README.md +176 -0
  4. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/__init__.py +14 -0
  5. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/agents/escalation.py +68 -9
  6. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/agents/feedback.py +117 -12
  7. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/network.py +97 -4
  8. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/templates/manager.py +139 -0
  9. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/utils/__init__.py +15 -0
  10. atendentepro-0.6.7/atendentepro/utils/user_loader.py +335 -0
  11. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro.egg-info/SOURCES.txt +2 -1
  12. {atendentepro-0.6.5 → atendentepro-0.6.7}/pyproject.toml +1 -1
  13. {atendentepro-0.6.5 → atendentepro-0.6.7}/LICENSE +0 -0
  14. {atendentepro-0.6.5 → atendentepro-0.6.7}/MANIFEST.in +0 -0
  15. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/README.md +0 -0
  16. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/agents/__init__.py +0 -0
  17. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/agents/answer.py +0 -0
  18. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/agents/confirmation.py +0 -0
  19. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/agents/flow.py +0 -0
  20. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/agents/interview.py +0 -0
  21. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/agents/knowledge.py +0 -0
  22. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/agents/onboarding.py +0 -0
  23. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/agents/triage.py +0 -0
  24. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/agents/usage.py +0 -0
  25. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/config/__init__.py +0 -0
  26. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/config/settings.py +0 -0
  27. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/guardrails/__init__.py +0 -0
  28. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/guardrails/manager.py +0 -0
  29. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/license.py +0 -0
  30. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/models/__init__.py +0 -0
  31. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/models/context.py +0 -0
  32. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/models/outputs.py +0 -0
  33. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/prompts/__init__.py +0 -0
  34. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/prompts/answer.py +0 -0
  35. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/prompts/confirmation.py +0 -0
  36. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/prompts/escalation.py +0 -0
  37. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/prompts/feedback.py +0 -0
  38. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/prompts/flow.py +0 -0
  39. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/prompts/interview.py +0 -0
  40. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/prompts/knowledge.py +0 -0
  41. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/prompts/onboarding.py +0 -0
  42. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/prompts/triage.py +0 -0
  43. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/templates/__init__.py +0 -0
  44. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/utils/openai_client.py +0 -0
  45. {atendentepro-0.6.5 → atendentepro-0.6.7}/atendentepro/utils/tracing.py +0 -0
  46. {atendentepro-0.6.5 → atendentepro-0.6.7}/requirements.txt +0 -0
  47. {atendentepro-0.6.5 → atendentepro-0.6.7}/setup.cfg +0 -0
  48. {atendentepro-0.6.5 → atendentepro-0.6.7}/setup.py +0 -0
@@ -5,6 +5,63 @@ All notable changes to AtendentePro will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.6.7] - 2025-02-03
9
+
10
+ ### Fixed
11
+ - **Feedback Agent**: Correção crítica - configuração YAML agora é carregada e aplicada
12
+ - Removida validação hardcoded de tipos de ticket (agora configurável via YAML)
13
+ - Adicionada persistência de tickets em arquivo JSON (`feedback_tickets.json`)
14
+ - Tipos de ticket agora são validados contra `feedback_config.yaml`
15
+ - Configurações de email (brand_color, brand_name, sla_message) agora vêm do YAML
16
+ - **Escalation Agent**: Correção crítica - configuração YAML agora é carregada e aplicada
17
+ - Business hours agora configurável via `escalation_config.yaml`
18
+ - Keywords de prioridade (urgent/high) agora configuráveis via YAML
19
+ - Conversão automática de dias da semana (monday, tuesday, etc.) para números
20
+ - **Answer Agent**: Correção crítica - `answer_config.yaml` agora é carregado e usado
21
+ - Template de resposta agora vem do arquivo de configuração
22
+
23
+ ### Added
24
+ - **Novos modelos de configuração em TemplateManager**:
25
+ - `FeedbackConfig`: Modelo Pydantic para `feedback_config.yaml`
26
+ - `EscalationConfig`: Modelo Pydantic para `escalation_config.yaml`
27
+ - `AnswerConfig`: Modelo Pydantic para `answer_config.yaml`
28
+ - **Métodos de carregamento**:
29
+ - `load_feedback_config()`: Carrega configuração do Feedback Agent
30
+ - `load_escalation_config()`: Carrega configuração do Escalation Agent
31
+ - `load_answer_config()`: Carrega configuração do Answer Agent
32
+ - **Persistência de tickets**: Sistema de armazenamento em JSON para tickets do Feedback Agent
33
+ - Configurável via variável de ambiente `FEEDBACK_STORAGE_PATH`
34
+ - Carregamento automático ao iniciar
35
+ - Salvamento automático após cada criação/atualização
36
+
37
+ ### Changed
38
+ - **Feedback Agent**: `create_feedback_agent()` agora aceita `ticket_types` da configuração YAML
39
+ - **Escalation Agent**: `create_escalation_agent()` agora aceita `business_hours` e `priority_keywords` da configuração
40
+ - **Network**: `create_standard_network()` agora carrega e aplica configurações YAML automaticamente
41
+
42
+ ## [0.6.6] - 2025-01-21
43
+
44
+ ### Added
45
+ - **User Loader Module**: Sistema de carregamento automático de usuários cadastrados
46
+ - `create_user_loader()`: Factory para criar loaders customizados
47
+ - `run_with_user_context()`: Função helper para executar agentes com carregamento automático
48
+ - `extract_phone_from_messages()`: Extrai telefone das mensagens
49
+ - `extract_email_from_messages()`: Extrai email das mensagens
50
+ - `extract_user_id_from_messages()`: Extrai user_id/CPF das mensagens
51
+ - `load_user_from_csv()`: Helper para carregar usuários de CSV
52
+ - **Novos parâmetros em `create_standard_network`**:
53
+ - `user_loader`: Função para carregar dados do usuário das mensagens
54
+ - `auto_load_user`: Flag para carregar usuário automaticamente
55
+ - **Atributos em `AgentNetwork`**:
56
+ - `user_loader`: Loader configurado na network
57
+ - `loaded_user_context`: Contexto do usuário carregado
58
+ - **Exemplos completos**: Pasta `docs/examples/user_loader/` com exemplos de CSV e banco de dados
59
+
60
+ ### Documentation
61
+ - README atualizado com seção completa de Carregamento de Usuários
62
+ - Exemplos práticos de uso com CSV, banco de dados e múltiplos identificadores
63
+ - Documentação de integração com onboarding
64
+
8
65
  ## [0.6.5] - 2025-01-21
9
66
 
10
67
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: atendentepro
3
- Version: 0.6.5
3
+ Version: 0.6.7
4
4
  Summary: Framework de orquestração de agentes IA com tom e estilo customizáveis. Integra documentos (RAG), APIs e bancos de dados em uma plataforma inteligente multi-agente.
5
5
  Author-email: BeMonkAI <contato@monkai.com.br>
6
6
  Maintainer-email: BeMonkAI <contato@monkai.com.br>
@@ -96,6 +96,7 @@ Plataforma que unifica múltiplos agentes especializados para resolver demandas
96
96
  - [Estilo de Comunicação](#-estilo-de-comunicação-agentstyle)
97
97
  - [Single Reply Mode](#-single-reply-mode)
98
98
  - [Filtros de Acesso](#-filtros-de-acesso-roleuser)
99
+ - [Carregamento de Usuários](#-carregamento-de-usuários-user-loader)
99
100
  - [Múltiplos Agentes](#-múltiplos-agentes-multi-interview--knowledge)
100
101
  - [Tracing e Monitoramento](#-tracing-e-monitoramento)
101
102
  - [Suporte](#-suporte)
@@ -1040,6 +1041,181 @@ tool_access:
1040
1041
 
1041
1042
  ---
1042
1043
 
1044
+ ## 👤 Carregamento de Usuários (User Loader)
1045
+
1046
+ O **User Loader** identifica automaticamente usuários cadastrados nas conversas e carrega suas informações para enriquecer o contexto, permitindo personalização e evitando onboarding desnecessário.
1047
+
1048
+ 📂 **Exemplos completos**: [docs/examples/user_loader/](docs/examples/user_loader/)
1049
+
1050
+ ### Quando Usar
1051
+
1052
+ | Cenário | Solução |
1053
+ |---------|---------|
1054
+ | **Usuário existente** | Identifica automaticamente e pula onboarding |
1055
+ | **Personalização** | Carrega dados do usuário para respostas personalizadas |
1056
+ | **Contexto enriquecido** | Todos os agentes têm acesso a informações do usuário |
1057
+ | **Múltiplas fontes** | Suporta CSV, banco de dados, APIs REST, etc. |
1058
+
1059
+ ### Funcionalidades
1060
+
1061
+ 1. **Extração automática** de identificadores (telefone, email, CPF, etc.)
1062
+ 2. **Carregamento de dados** de múltiplas fontes
1063
+ 3. **Criação automática** de `UserContext`
1064
+ 4. **Integração transparente** com a rede de agentes
1065
+
1066
+ ### Exemplo 1: Carregamento de CSV
1067
+
1068
+ ```python
1069
+ from pathlib import Path
1070
+ from atendentepro import (
1071
+ create_standard_network,
1072
+ create_user_loader,
1073
+ load_user_from_csv,
1074
+ extract_email_from_messages,
1075
+ run_with_user_context,
1076
+ )
1077
+
1078
+ # Função para carregar do CSV
1079
+ def load_user(identifier: str):
1080
+ return load_user_from_csv(
1081
+ csv_path=Path("users.csv"),
1082
+ identifier_field="email",
1083
+ identifier_value=identifier
1084
+ )
1085
+
1086
+ # Criar loader
1087
+ loader = create_user_loader(
1088
+ loader_func=load_user,
1089
+ identifier_extractor=extract_email_from_messages
1090
+ )
1091
+
1092
+ # Criar network com loader
1093
+ network = create_standard_network(
1094
+ templates_root=Path("./templates"),
1095
+ user_loader=loader,
1096
+ include_onboarding=True,
1097
+ )
1098
+
1099
+ # Executar com carregamento automático
1100
+ messages = [{"role": "user", "content": "Meu email é joao@example.com"}]
1101
+ result = await run_with_user_context(network, network.triage, messages)
1102
+
1103
+ # Verificar se usuário foi carregado
1104
+ if network.loaded_user_context:
1105
+ print(f"Usuário: {network.loaded_user_context.metadata.get('nome')}")
1106
+ ```
1107
+
1108
+ ### Exemplo 2: Carregamento de Banco de Dados
1109
+
1110
+ ```python
1111
+ import sqlite3
1112
+ from atendentepro import create_user_loader, extract_email_from_messages
1113
+
1114
+ def load_from_db(identifier: str):
1115
+ conn = sqlite3.connect("users.db")
1116
+ cursor = conn.cursor()
1117
+ cursor.execute("SELECT * FROM users WHERE email = ?", (identifier,))
1118
+ row = cursor.fetchone()
1119
+ conn.close()
1120
+
1121
+ if row:
1122
+ return {
1123
+ "user_id": row[0],
1124
+ "role": row[1],
1125
+ "nome": row[2],
1126
+ "email": row[3],
1127
+ }
1128
+ return None
1129
+
1130
+ loader = create_user_loader(load_from_db, extract_email_from_messages)
1131
+
1132
+ network = create_standard_network(
1133
+ templates_root=Path("./templates"),
1134
+ user_loader=loader,
1135
+ )
1136
+ ```
1137
+
1138
+ ### Exemplo 3: Múltiplos Identificadores
1139
+
1140
+ ```python
1141
+ from atendentepro import (
1142
+ create_user_loader,
1143
+ extract_email_from_messages,
1144
+ extract_phone_from_messages,
1145
+ )
1146
+
1147
+ def extract_identifier(messages):
1148
+ # Tenta email primeiro
1149
+ email = extract_email_from_messages(messages)
1150
+ if email:
1151
+ return email
1152
+
1153
+ # Se não encontrou, tenta telefone
1154
+ phone = extract_phone_from_messages(messages)
1155
+ if phone:
1156
+ return phone
1157
+
1158
+ return None
1159
+
1160
+ loader = create_user_loader(
1161
+ loader_func=load_user,
1162
+ identifier_extractor=extract_identifier
1163
+ )
1164
+ ```
1165
+
1166
+ ### Funções Disponíveis
1167
+
1168
+ #### Extratores de Identificador
1169
+
1170
+ ```python
1171
+ from atendentepro import (
1172
+ extract_phone_from_messages, # Extrai telefone
1173
+ extract_email_from_messages, # Extrai email
1174
+ extract_user_id_from_messages, # Extrai CPF/user_id
1175
+ )
1176
+ ```
1177
+
1178
+ #### Criar Loader
1179
+
1180
+ ```python
1181
+ from atendentepro import create_user_loader
1182
+
1183
+ loader = create_user_loader(
1184
+ loader_func=load_user_function,
1185
+ identifier_extractor=extract_email_from_messages # Opcional
1186
+ )
1187
+ ```
1188
+
1189
+ #### Executar com Contexto
1190
+
1191
+ ```python
1192
+ from atendentepro import run_with_user_context
1193
+
1194
+ result = await run_with_user_context(
1195
+ network,
1196
+ network.triage,
1197
+ messages
1198
+ )
1199
+ ```
1200
+
1201
+ ### Integração com Onboarding
1202
+
1203
+ Quando um `user_loader` está configurado:
1204
+
1205
+ - ✅ **Usuário encontrado**: Vai direto para o triage, sem passar pelo onboarding
1206
+ - ✅ **Usuário não encontrado**: É direcionado para o onboarding normalmente
1207
+ - ✅ **Contexto disponível**: Todos os agentes têm acesso a `network.loaded_user_context`
1208
+
1209
+ ### Benefícios
1210
+
1211
+ 1. ✅ **Experiência personalizada** - Respostas baseadas em dados do usuário
1212
+ 2. ✅ **Menos fricção** - Usuários conhecidos não precisam fazer onboarding
1213
+ 3. ✅ **Contexto rico** - Todos os agentes têm acesso a informações do usuário
1214
+ 4. ✅ **Flexível** - Suporta múltiplas fontes de dados
1215
+ 5. ✅ **Automático** - Funciona transparentemente durante a conversa
1216
+
1217
+ ---
1218
+
1043
1219
  ## 🔀 Múltiplos Agentes (Multi Interview + Knowledge)
1044
1220
 
1045
1221
  O AtendentePro suporta criar **múltiplas instâncias** de Interview e Knowledge agents, cada um especializado em um domínio diferente.
@@ -38,6 +38,7 @@ Plataforma que unifica múltiplos agentes especializados para resolver demandas
38
38
  - [Estilo de Comunicação](#-estilo-de-comunicação-agentstyle)
39
39
  - [Single Reply Mode](#-single-reply-mode)
40
40
  - [Filtros de Acesso](#-filtros-de-acesso-roleuser)
41
+ - [Carregamento de Usuários](#-carregamento-de-usuários-user-loader)
41
42
  - [Múltiplos Agentes](#-múltiplos-agentes-multi-interview--knowledge)
42
43
  - [Tracing e Monitoramento](#-tracing-e-monitoramento)
43
44
  - [Suporte](#-suporte)
@@ -982,6 +983,181 @@ tool_access:
982
983
 
983
984
  ---
984
985
 
986
+ ## 👤 Carregamento de Usuários (User Loader)
987
+
988
+ O **User Loader** identifica automaticamente usuários cadastrados nas conversas e carrega suas informações para enriquecer o contexto, permitindo personalização e evitando onboarding desnecessário.
989
+
990
+ 📂 **Exemplos completos**: [docs/examples/user_loader/](docs/examples/user_loader/)
991
+
992
+ ### Quando Usar
993
+
994
+ | Cenário | Solução |
995
+ |---------|---------|
996
+ | **Usuário existente** | Identifica automaticamente e pula onboarding |
997
+ | **Personalização** | Carrega dados do usuário para respostas personalizadas |
998
+ | **Contexto enriquecido** | Todos os agentes têm acesso a informações do usuário |
999
+ | **Múltiplas fontes** | Suporta CSV, banco de dados, APIs REST, etc. |
1000
+
1001
+ ### Funcionalidades
1002
+
1003
+ 1. **Extração automática** de identificadores (telefone, email, CPF, etc.)
1004
+ 2. **Carregamento de dados** de múltiplas fontes
1005
+ 3. **Criação automática** de `UserContext`
1006
+ 4. **Integração transparente** com a rede de agentes
1007
+
1008
+ ### Exemplo 1: Carregamento de CSV
1009
+
1010
+ ```python
1011
+ from pathlib import Path
1012
+ from atendentepro import (
1013
+ create_standard_network,
1014
+ create_user_loader,
1015
+ load_user_from_csv,
1016
+ extract_email_from_messages,
1017
+ run_with_user_context,
1018
+ )
1019
+
1020
+ # Função para carregar do CSV
1021
+ def load_user(identifier: str):
1022
+ return load_user_from_csv(
1023
+ csv_path=Path("users.csv"),
1024
+ identifier_field="email",
1025
+ identifier_value=identifier
1026
+ )
1027
+
1028
+ # Criar loader
1029
+ loader = create_user_loader(
1030
+ loader_func=load_user,
1031
+ identifier_extractor=extract_email_from_messages
1032
+ )
1033
+
1034
+ # Criar network com loader
1035
+ network = create_standard_network(
1036
+ templates_root=Path("./templates"),
1037
+ user_loader=loader,
1038
+ include_onboarding=True,
1039
+ )
1040
+
1041
+ # Executar com carregamento automático
1042
+ messages = [{"role": "user", "content": "Meu email é joao@example.com"}]
1043
+ result = await run_with_user_context(network, network.triage, messages)
1044
+
1045
+ # Verificar se usuário foi carregado
1046
+ if network.loaded_user_context:
1047
+ print(f"Usuário: {network.loaded_user_context.metadata.get('nome')}")
1048
+ ```
1049
+
1050
+ ### Exemplo 2: Carregamento de Banco de Dados
1051
+
1052
+ ```python
1053
+ import sqlite3
1054
+ from atendentepro import create_user_loader, extract_email_from_messages
1055
+
1056
+ def load_from_db(identifier: str):
1057
+ conn = sqlite3.connect("users.db")
1058
+ cursor = conn.cursor()
1059
+ cursor.execute("SELECT * FROM users WHERE email = ?", (identifier,))
1060
+ row = cursor.fetchone()
1061
+ conn.close()
1062
+
1063
+ if row:
1064
+ return {
1065
+ "user_id": row[0],
1066
+ "role": row[1],
1067
+ "nome": row[2],
1068
+ "email": row[3],
1069
+ }
1070
+ return None
1071
+
1072
+ loader = create_user_loader(load_from_db, extract_email_from_messages)
1073
+
1074
+ network = create_standard_network(
1075
+ templates_root=Path("./templates"),
1076
+ user_loader=loader,
1077
+ )
1078
+ ```
1079
+
1080
+ ### Exemplo 3: Múltiplos Identificadores
1081
+
1082
+ ```python
1083
+ from atendentepro import (
1084
+ create_user_loader,
1085
+ extract_email_from_messages,
1086
+ extract_phone_from_messages,
1087
+ )
1088
+
1089
+ def extract_identifier(messages):
1090
+ # Tenta email primeiro
1091
+ email = extract_email_from_messages(messages)
1092
+ if email:
1093
+ return email
1094
+
1095
+ # Se não encontrou, tenta telefone
1096
+ phone = extract_phone_from_messages(messages)
1097
+ if phone:
1098
+ return phone
1099
+
1100
+ return None
1101
+
1102
+ loader = create_user_loader(
1103
+ loader_func=load_user,
1104
+ identifier_extractor=extract_identifier
1105
+ )
1106
+ ```
1107
+
1108
+ ### Funções Disponíveis
1109
+
1110
+ #### Extratores de Identificador
1111
+
1112
+ ```python
1113
+ from atendentepro import (
1114
+ extract_phone_from_messages, # Extrai telefone
1115
+ extract_email_from_messages, # Extrai email
1116
+ extract_user_id_from_messages, # Extrai CPF/user_id
1117
+ )
1118
+ ```
1119
+
1120
+ #### Criar Loader
1121
+
1122
+ ```python
1123
+ from atendentepro import create_user_loader
1124
+
1125
+ loader = create_user_loader(
1126
+ loader_func=load_user_function,
1127
+ identifier_extractor=extract_email_from_messages # Opcional
1128
+ )
1129
+ ```
1130
+
1131
+ #### Executar com Contexto
1132
+
1133
+ ```python
1134
+ from atendentepro import run_with_user_context
1135
+
1136
+ result = await run_with_user_context(
1137
+ network,
1138
+ network.triage,
1139
+ messages
1140
+ )
1141
+ ```
1142
+
1143
+ ### Integração com Onboarding
1144
+
1145
+ Quando um `user_loader` está configurado:
1146
+
1147
+ - ✅ **Usuário encontrado**: Vai direto para o triage, sem passar pelo onboarding
1148
+ - ✅ **Usuário não encontrado**: É direcionado para o onboarding normalmente
1149
+ - ✅ **Contexto disponível**: Todos os agentes têm acesso a `network.loaded_user_context`
1150
+
1151
+ ### Benefícios
1152
+
1153
+ 1. ✅ **Experiência personalizada** - Respostas baseadas em dados do usuário
1154
+ 2. ✅ **Menos fricção** - Usuários conhecidos não precisam fazer onboarding
1155
+ 3. ✅ **Contexto rico** - Todos os agentes têm acesso a informações do usuário
1156
+ 4. ✅ **Flexível** - Suporta múltiplas fontes de dados
1157
+ 5. ✅ **Automático** - Funciona transparentemente durante a conversa
1158
+
1159
+ ---
1160
+
985
1161
  ## 🔀 Múltiplos Agentes (Multi Interview + Knowledge)
986
1162
 
987
1163
  O AtendentePro suporta criar **múltiplas instâncias** de Interview e Knowledge agents, cada um especializado em um domínio diferente.
@@ -158,6 +158,13 @@ from atendentepro.utils import (
158
158
  run_with_monkai_tracking,
159
159
  # Application Insights
160
160
  configure_application_insights,
161
+ # User Loader
162
+ create_user_loader,
163
+ run_with_user_context,
164
+ extract_phone_from_messages,
165
+ extract_email_from_messages,
166
+ extract_user_id_from_messages,
167
+ load_user_from_csv,
161
168
  )
162
169
 
163
170
  __all__ = [
@@ -253,5 +260,12 @@ __all__ = [
253
260
  "run_with_monkai_tracking",
254
261
  # Application Insights
255
262
  "configure_application_insights",
263
+ # User Loader
264
+ "create_user_loader",
265
+ "run_with_user_context",
266
+ "extract_phone_from_messages",
267
+ "extract_email_from_messages",
268
+ "extract_user_id_from_messages",
269
+ "load_user_from_csv",
256
270
  ]
257
271
 
@@ -47,6 +47,13 @@ DEFAULT_BUSINESS_HOURS = {
47
47
  "days": [0, 1, 2, 3, 4], # Seg-Sex
48
48
  }
49
49
 
50
+ # Configurable business hours (None = use DEFAULT_BUSINESS_HOURS)
51
+ _configured_business_hours: Optional[Dict[str, Any]] = None
52
+
53
+ # Configurable priority keywords (None = use defaults)
54
+ _priority_keywords_urgent: Optional[List[str]] = None
55
+ _priority_keywords_high: Optional[List[str]] = None
56
+
50
57
 
51
58
  # =============================================================================
52
59
  # Storage de Escalações (em memória - substituir por DB em produção)
@@ -139,22 +146,46 @@ def _notificar_equipe(escalation: Escalation) -> bool:
139
146
 
140
147
  def _verificar_disponibilidade() -> Dict[str, Any]:
141
148
  """Verifica se atendimento humano está disponível."""
149
+ global _configured_business_hours
142
150
  agora = datetime.now()
143
151
  hora = agora.hour
144
152
  dia = agora.weekday()
145
153
 
146
- # Verificar variáveis de ambiente para horário customizado
147
- hora_inicio = int(os.getenv("ESCALATION_HOUR_START", DEFAULT_BUSINESS_HOURS["start"]))
148
- hora_fim = int(os.getenv("ESCALATION_HOUR_END", DEFAULT_BUSINESS_HOURS["end"]))
154
+ # Use configured business hours or fall back to defaults
155
+ if _configured_business_hours:
156
+ business_hours = _configured_business_hours
157
+ hora_inicio = business_hours.get("start", DEFAULT_BUSINESS_HOURS["start"])
158
+ hora_fim = business_hours.get("end", DEFAULT_BUSINESS_HOURS["end"])
159
+ days = business_hours.get("days", DEFAULT_BUSINESS_HOURS["days"])
160
+ else:
161
+ # Verificar variáveis de ambiente para horário customizado
162
+ hora_inicio = int(os.getenv("ESCALATION_HOUR_START", DEFAULT_BUSINESS_HOURS["start"]))
163
+ hora_fim = int(os.getenv("ESCALATION_HOUR_END", DEFAULT_BUSINESS_HOURS["end"]))
164
+ days = DEFAULT_BUSINESS_HOURS["days"]
165
+
166
+ # Ensure days is a list of integers
167
+ if isinstance(days, list):
168
+ days_int = [d if isinstance(d, int) else int(d) for d in days]
169
+ else:
170
+ days_int = DEFAULT_BUSINESS_HOURS["days"]
171
+
172
+ disponivel = dia in days_int and hora_inicio <= hora < hora_fim
149
173
 
150
- disponivel = dia in DEFAULT_BUSINESS_HOURS["days"] and hora_inicio <= hora < hora_fim
174
+ # Format days for display
175
+ day_names = ["Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado", "Domingo"]
176
+ if len(days_int) == 1:
177
+ dias_atendimento = day_names[days_int[0]]
178
+ elif len(days_int) == 5 and days_int == [0, 1, 2, 3, 4]:
179
+ dias_atendimento = "Segunda a Sexta"
180
+ else:
181
+ dias_atendimento = ", ".join(day_names[d] for d in sorted(days_int))
151
182
 
152
183
  return {
153
184
  "disponivel": disponivel,
154
185
  "hora_atual": agora.strftime("%H:%M"),
155
- "dia_semana": ["Segunda", "Terça", "Quarta", "Quinta", "Sexta", "Sábado", "Domingo"][dia],
186
+ "dia_semana": day_names[dia],
156
187
  "horario_atendimento": f"{hora_inicio:02d}:00 - {hora_fim:02d}:00",
157
- "dias_atendimento": "Segunda a Sexta",
188
+ "dias_atendimento": dias_atendimento,
158
189
  }
159
190
 
160
191
 
@@ -166,15 +197,16 @@ def _classificar_prioridade(motivo: str, categoria: str) -> str:
166
197
  """
167
198
  Classifica automaticamente a prioridade baseado no motivo e categoria.
168
199
  """
200
+ global _priority_keywords_urgent, _priority_keywords_high
169
201
  motivo_lower = motivo.lower()
170
202
 
171
- # Palavras que indicam urgência
172
- palavras_urgentes = [
203
+ # Use configured keywords or defaults
204
+ palavras_urgentes = _priority_keywords_urgent or [
173
205
  "urgente", "emergência", "emergencia", "crítico", "critico",
174
206
  "não funciona", "parou", "bloqueado", "cancelar", "prejuízo"
175
207
  ]
176
208
 
177
- palavras_alta = [
209
+ palavras_alta = _priority_keywords_high or [
178
210
  "reclamação", "reclamacao", "insatisfeito", "problema grave",
179
211
  "já tentei", "terceira vez", "não resolve"
180
212
  ]
@@ -449,6 +481,9 @@ ESCALATION_TOOLS = [
449
481
  def create_escalation_agent(
450
482
  escalation_triggers: str = "",
451
483
  escalation_channels: str = "",
484
+ business_hours: Optional[Dict[str, Any]] = None,
485
+ priority_keywords_urgent: Optional[List[str]] = None,
486
+ priority_keywords_high: Optional[List[str]] = None,
452
487
  handoffs: Optional[List] = None,
453
488
  tools: Optional[List] = None,
454
489
  guardrails: Optional[List["GuardrailCallable"]] = None,
@@ -472,6 +507,9 @@ def create_escalation_agent(
472
507
  Args:
473
508
  escalation_triggers: Custom triggers for escalation (keywords, situations).
474
509
  escalation_channels: Available contact channels description.
510
+ business_hours: Optional dict with "start", "end", and "days" (list of weekday numbers 0-6 or day names).
511
+ priority_keywords_urgent: Optional list of keywords that indicate urgent priority.
512
+ priority_keywords_high: Optional list of keywords that indicate high priority.
475
513
  handoffs: List of agents to hand off to (usually triage to return).
476
514
  tools: Additional tools (custom notifications, integrations).
477
515
  guardrails: List of input guardrails.
@@ -492,6 +530,27 @@ def create_escalation_agent(
492
530
  >>> triage.handoffs.append(escalation)
493
531
  >>> flow.handoffs.append(escalation)
494
532
  """
533
+ global _configured_business_hours, _priority_keywords_urgent, _priority_keywords_high
534
+
535
+ # Configure business hours
536
+ if business_hours:
537
+ # Convert day names to weekday numbers if needed
538
+ days = business_hours.get("days", [])
539
+ if days and isinstance(days[0], str):
540
+ day_map = {
541
+ "monday": 0, "tuesday": 1, "wednesday": 2, "thursday": 3,
542
+ "friday": 4, "saturday": 5, "sunday": 6,
543
+ }
544
+ days_int = [day_map.get(day.lower(), day) for day in days if day.lower() in day_map]
545
+ business_hours = {**business_hours, "days": days_int}
546
+ _configured_business_hours = business_hours
547
+ else:
548
+ _configured_business_hours = None
549
+
550
+ # Configure priority keywords
551
+ _priority_keywords_urgent = priority_keywords_urgent
552
+ _priority_keywords_high = priority_keywords_high
553
+
495
554
  if custom_instructions:
496
555
  instructions = f"{RECOMMENDED_PROMPT_PREFIX} {custom_instructions}"
497
556
  else: