FEAT: Roteamento para atendentes melhorado
All checks were successful
Deploy Dev / deploy (push) Successful in 3s
All checks were successful
Deploy Dev / deploy (push) Successful in 3s
This commit is contained in:
parent
d495698c02
commit
2fd19e9bae
@ -31,7 +31,7 @@ export class KnowledgeBaseService implements OnModuleInit {
|
|||||||
ALTER TABLE bot_triage_intents
|
ALTER TABLE bot_triage_intents
|
||||||
ADD COLUMN IF NOT EXISTS response_message TEXT,
|
ADD COLUMN IF NOT EXISTS response_message TEXT,
|
||||||
ADD COLUMN IF NOT EXISTS resolution_question TEXT,
|
ADD COLUMN IF NOT EXISTS resolution_question TEXT,
|
||||||
ADD COLUMN IF NOT EXISTS escalation_message TEXT NOT NULL DEFAULT 'Certo, vou encaminhar seu atendimento para um especialista no assunto.';
|
ADD COLUMN IF NOT EXISTS escalation_message TEXT NOT NULL DEFAULT 'Certo, vou encaminhar seu atendimento para o time responsável.';
|
||||||
`).catch(() => undefined);
|
`).catch(() => undefined);
|
||||||
|
|
||||||
await this.database.query(`
|
await this.database.query(`
|
||||||
@ -529,7 +529,7 @@ export class KnowledgeBaseService implements OnModuleInit {
|
|||||||
audience_id, label, area_id, keywords, response_message, resolution_question,
|
audience_id, label, area_id, keywords, response_message, resolution_question,
|
||||||
escalation_message, sort_order, active, updated_at
|
escalation_message, sort_order, active, updated_at
|
||||||
)
|
)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, COALESCE($7, 'Certo, vou encaminhar seu atendimento para um especialista no assunto.'), $8, TRUE, CURRENT_TIMESTAMP)
|
VALUES ($1, $2, $3, $4, $5, $6, COALESCE($7, 'Certo, vou encaminhar seu atendimento para o time responsável.'), $8, TRUE, CURRENT_TIMESTAMP)
|
||||||
RETURNING *
|
RETURNING *
|
||||||
`,
|
`,
|
||||||
[
|
[
|
||||||
|
|||||||
@ -421,7 +421,7 @@ export class WhatsappAssignmentService implements OnModuleInit {
|
|||||||
return builderFlowResult;
|
return builderFlowResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
const configuredFlowResult = await this.routeConfiguredFlow(chatId, cleanMessage, current, messageId);
|
const configuredFlowResult = await this.routeConfiguredFlow(chatId, cleanMessage, current, messageId, variables);
|
||||||
if (configuredFlowResult) {
|
if (configuredFlowResult) {
|
||||||
return configuredFlowResult;
|
return configuredFlowResult;
|
||||||
}
|
}
|
||||||
@ -429,7 +429,14 @@ export class WhatsappAssignmentService implements OnModuleInit {
|
|||||||
const detectedArea = await this.detectKnownArea(cleanMessage);
|
const detectedArea = await this.detectKnownArea(cleanMessage);
|
||||||
|
|
||||||
if (detectedArea) {
|
if (detectedArea) {
|
||||||
const assignment = await this.queueChat(chatId, detectedArea.id, `Roteado automaticamente pelo ${VIRTUAL_AGENT_NAME}`);
|
const assignment = await this.queueChat(
|
||||||
|
chatId,
|
||||||
|
detectedArea.id,
|
||||||
|
this.buildVirtualAgentTransferNote({
|
||||||
|
name: variables.nome,
|
||||||
|
intentLabel: detectedArea.name,
|
||||||
|
}),
|
||||||
|
);
|
||||||
await this.markBotRoute(chatId, messageId);
|
await this.markBotRoute(chatId, messageId);
|
||||||
const hasPreviousTriage = current?.status === 'bot_triage';
|
const hasPreviousTriage = current?.status === 'bot_triage';
|
||||||
const botMessage = hasPreviousTriage
|
const botMessage = hasPreviousTriage
|
||||||
@ -453,7 +460,14 @@ export class WhatsappAssignmentService implements OnModuleInit {
|
|||||||
|
|
||||||
if (attempts >= 2) {
|
if (attempts >= 2) {
|
||||||
const supportArea = await this.getAreaByName('Suporte');
|
const supportArea = await this.getAreaByName('Suporte');
|
||||||
const assignment = await this.queueChat(chatId, supportArea.id, 'Roteado para suporte por falta de classificacao');
|
const assignment = await this.queueChat(
|
||||||
|
chatId,
|
||||||
|
supportArea.id,
|
||||||
|
this.buildVirtualAgentTransferNote({
|
||||||
|
name: variables.nome,
|
||||||
|
fallbackReason: 'não teve a solicitação classificada pelo Agente Virtual',
|
||||||
|
}),
|
||||||
|
);
|
||||||
await this.markBotRoute(chatId, messageId);
|
await this.markBotRoute(chatId, messageId);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -590,7 +604,11 @@ export class WhatsappAssignmentService implements OnModuleInit {
|
|||||||
const assignment = await this.queueChat(
|
const assignment = await this.queueChat(
|
||||||
chatId,
|
chatId,
|
||||||
areaId,
|
areaId,
|
||||||
`Roteado pelo fluxo do ${VIRTUAL_AGENT_NAME}: ${matchedChild.title}`,
|
this.buildVirtualAgentTransferNote({
|
||||||
|
name: variables.nome,
|
||||||
|
audienceLabel: currentNode.title,
|
||||||
|
intentLabel: matchedChild.title,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
await this.markBotRoute(chatId, messageId);
|
await this.markBotRoute(chatId, messageId);
|
||||||
|
|
||||||
@ -601,7 +619,7 @@ export class WhatsappAssignmentService implements OnModuleInit {
|
|||||||
'Atendente virtual',
|
'Atendente virtual',
|
||||||
VIRTUAL_AGENT_NAME,
|
VIRTUAL_AGENT_NAME,
|
||||||
this.applyBotVariables(
|
this.applyBotVariables(
|
||||||
matchedChild.message_text || `Certo, vou encaminhar seu atendimento para ${matchedChild.area_nome || 'a especialidade correta'}.`,
|
matchedChild.message_text || 'Certo, vou encaminhar seu atendimento para o time responsável.',
|
||||||
variables,
|
variables,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -628,7 +646,11 @@ export class WhatsappAssignmentService implements OnModuleInit {
|
|||||||
const assignment = await this.queueChat(
|
const assignment = await this.queueChat(
|
||||||
chatId,
|
chatId,
|
||||||
fallbackAreaId,
|
fallbackAreaId,
|
||||||
`Fallback do fluxo do ${VIRTUAL_AGENT_NAME}: ${currentNode.title}`,
|
this.buildVirtualAgentTransferNote({
|
||||||
|
name: variables.nome,
|
||||||
|
audienceLabel: currentNode.title,
|
||||||
|
fallbackReason: 'não teve a solicitação classificada pelo Agente Virtual',
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
await this.markBotRoute(chatId, messageId);
|
await this.markBotRoute(chatId, messageId);
|
||||||
|
|
||||||
@ -659,7 +681,13 @@ export class WhatsappAssignmentService implements OnModuleInit {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async routeConfiguredFlow(chatId: string, message: string, current: any, messageId?: string) {
|
private async routeConfiguredFlow(
|
||||||
|
chatId: string,
|
||||||
|
message: string,
|
||||||
|
current: any,
|
||||||
|
messageId?: string,
|
||||||
|
variables: BotMessageVariables = {},
|
||||||
|
) {
|
||||||
const flow = await this.getActiveTriageFlow();
|
const flow = await this.getActiveTriageFlow();
|
||||||
if (!flow || !flow.audiences.length) return null;
|
if (!flow || !flow.audiences.length) return null;
|
||||||
|
|
||||||
@ -694,7 +722,7 @@ export class WhatsappAssignmentService implements OnModuleInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (current.triage_step === 'resolution' && current.triage_intent_id) {
|
if (current.triage_step === 'resolution' && current.triage_intent_id) {
|
||||||
return this.routeConfiguredResolution(chatId, message, flow, current, messageId);
|
return this.routeConfiguredResolution(chatId, message, flow, current, messageId, variables);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.routeConfiguredAudience(chatId, message, flow, current, messageId);
|
return this.routeConfiguredAudience(chatId, message, flow, current, messageId);
|
||||||
@ -778,14 +806,29 @@ export class WhatsappAssignmentService implements OnModuleInit {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private async routeConfiguredResolution(chatId: string, message: string, flow: TriageFlow, current: any, messageId?: string) {
|
private async routeConfiguredResolution(
|
||||||
const intent = flow.audiences
|
chatId: string,
|
||||||
.flatMap((audience) => audience.intents)
|
message: string,
|
||||||
.find((item) => Number(item.id) === Number(current.triage_intent_id));
|
flow: TriageFlow,
|
||||||
|
current: any,
|
||||||
|
messageId?: string,
|
||||||
|
variables: BotMessageVariables = {},
|
||||||
|
) {
|
||||||
|
const audience = flow.audiences.find((item) =>
|
||||||
|
item.intents.some((intentItem) => Number(intentItem.id) === Number(current.triage_intent_id)),
|
||||||
|
);
|
||||||
|
const intent = audience?.intents.find((item) => Number(item.id) === Number(current.triage_intent_id));
|
||||||
|
|
||||||
if (!intent) {
|
if (!intent) {
|
||||||
const fallbackAreaId = flow.fallback_area_id || (await this.getAreaByName('Suporte')).id;
|
const fallbackAreaId = flow.fallback_area_id || (await this.getAreaByName('Suporte')).id;
|
||||||
const assignment = await this.queueChat(chatId, fallbackAreaId, `Fallback configurado pelo ${VIRTUAL_AGENT_NAME}`);
|
const assignment = await this.queueChat(
|
||||||
|
chatId,
|
||||||
|
fallbackAreaId,
|
||||||
|
this.buildVirtualAgentTransferNote({
|
||||||
|
name: variables.nome,
|
||||||
|
fallbackReason: 'não teve a solicitação classificada pelo Agente Virtual',
|
||||||
|
}),
|
||||||
|
);
|
||||||
await this.markBotRoute(chatId, messageId);
|
await this.markBotRoute(chatId, messageId);
|
||||||
return {
|
return {
|
||||||
assignment,
|
assignment,
|
||||||
@ -846,7 +889,11 @@ export class WhatsappAssignmentService implements OnModuleInit {
|
|||||||
const assignment = await this.queueChat(
|
const assignment = await this.queueChat(
|
||||||
chatId,
|
chatId,
|
||||||
intent.area_id,
|
intent.area_id,
|
||||||
`Cliente solicitou especialista apos orientacao do ${VIRTUAL_AGENT_NAME}: ${intent.label}`,
|
this.buildVirtualAgentTransferNote({
|
||||||
|
name: variables.nome,
|
||||||
|
audienceLabel: audience?.label,
|
||||||
|
intentLabel: intent.label,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
await this.markBotRoute(chatId, messageId);
|
await this.markBotRoute(chatId, messageId);
|
||||||
|
|
||||||
@ -856,7 +903,7 @@ export class WhatsappAssignmentService implements OnModuleInit {
|
|||||||
botMessage: this.formatSenderMessage(
|
botMessage: this.formatSenderMessage(
|
||||||
'Atendente virtual',
|
'Atendente virtual',
|
||||||
VIRTUAL_AGENT_NAME,
|
VIRTUAL_AGENT_NAME,
|
||||||
intent.escalation_message || `Certo, vou encaminhar seu atendimento para ${intent.area_nome}.`,
|
intent.escalation_message || 'Certo, vou encaminhar seu atendimento para o time responsável.',
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1249,6 +1296,58 @@ export class WhatsappAssignmentService implements OnModuleInit {
|
|||||||
return ['ferias'].includes(normalized) ? 'as' : 'o';
|
return ['ferias'].includes(normalized) ? 'as' : 'o';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private buildVirtualAgentTransferNote(input: {
|
||||||
|
name?: string | null;
|
||||||
|
audienceLabel?: string | null;
|
||||||
|
intentLabel?: string | null;
|
||||||
|
fallbackReason?: string;
|
||||||
|
}) {
|
||||||
|
const subject = this.getFirstName(input.name) || 'Cliente';
|
||||||
|
const audienceDescription = this.getAudienceDescription(input.audienceLabel);
|
||||||
|
const topic = this.getTopicLabel(input.intentLabel);
|
||||||
|
|
||||||
|
if (audienceDescription && topic) {
|
||||||
|
return `${subject} ${audienceDescription} e quer saber sobre ${topic}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (topic) {
|
||||||
|
return `${subject} quer saber sobre ${topic}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (audienceDescription && input.fallbackReason) {
|
||||||
|
return `${subject} ${audienceDescription} e ${input.fallbackReason}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input.fallbackReason) {
|
||||||
|
return `${subject} ${input.fallbackReason}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `Roteado pelo ${VIRTUAL_AGENT_NAME}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getAudienceDescription(value?: string | null) {
|
||||||
|
const normalized = this.normalize(value || '');
|
||||||
|
|
||||||
|
if (!normalized) return '';
|
||||||
|
if (normalized.includes('ex-colaborador') || normalized.includes('ex colaborador')) return 'é um ex-colaborador';
|
||||||
|
if (normalized.includes('candidato') || normalized.includes('vaga')) return 'é um candidato';
|
||||||
|
if (normalized.includes('colaborador') || normalized.includes('funcionario')) return 'é um colaborador';
|
||||||
|
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private getTopicLabel(value?: string | null) {
|
||||||
|
const text = this.cleanVariable(value);
|
||||||
|
const normalized = this.normalize(text);
|
||||||
|
const knownTopics: Record<string, string> = {
|
||||||
|
beneficios: 'Benefícios',
|
||||||
|
ferias: 'Férias',
|
||||||
|
rescisao: 'Rescisão',
|
||||||
|
};
|
||||||
|
|
||||||
|
return knownTopics[normalized] || text;
|
||||||
|
}
|
||||||
|
|
||||||
private applyBotVariables(message: string, variables: BotMessageVariables) {
|
private applyBotVariables(message: string, variables: BotMessageVariables) {
|
||||||
const firstName = this.getFirstName(variables.nome);
|
const firstName = this.getFirstName(variables.nome);
|
||||||
const fullName = this.cleanVariable(variables.nome);
|
const fullName = this.cleanVariable(variables.nome);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user