Hace a Luisa cálida y servicial y elimina el rechazo de leads
El bot rechazaba leads con presupuesto bajo y sonaba frío/repetitivo. Ahora Luisa nunca rechaza (el cierre siempre es positivo; la rentabilidad la valora el reformista en el panel, no el agente) y tiene el tono del agente de voz: simpática, variada y dispuesta a ayudar con referencias. - leads.service: getSiguienteEstado tras presupuesto siempre fin_viable; evaluarViabilidad ya no filtra (informa viable=true). - claude.service: relaja las reglas de la pasada de corrección (permite calidez, conectores, emoji suave, 2-3 líneas, variación) y quita el guard que tomaba "en qué puedo ayudarte" como frase de IA. - prompts: reescribe el guion de Luisa (mensajes como ejemplos variables, ayuda con referencias de tamaño/materiales, sin rechazo) y elimina el bloque <DATOS_EXTRAIDOS>, que era código muerto contradictorio con el generador. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -6,8 +6,9 @@ import { LeadsService } from '../leads/leads.service';
|
||||
|
||||
const DEFAULT_SYSTEM_PROMPT =
|
||||
'Eres Luisa, asesora de Reformix en Madrid. Cualificas leads de reforma por WhatsApp. ' +
|
||||
'Hablas espanol de Espana, cercana y natural; adapta el registro al usuario (coloquial si el, formal si el). ' +
|
||||
'Un mensaje por turno, maximo 2 lineas. Sin emojis, sin guiones largos. ' +
|
||||
'Hablas espanol de Espana, calida, simpatica y siempre dispuesta a ayudar; adapta el registro al usuario. ' +
|
||||
'Un mensaje por turno, breve (2-3 lineas). Puedes usar algun emoji suave de vez en cuando. ' +
|
||||
'Varia como lo dices, no suenes a plantilla y no vuelvas a preguntar lo que ya te han contado. ' +
|
||||
'NUNCA digas que eres IA, ChatGPT, OpenAI ni un asistente virtual.';
|
||||
|
||||
const FRASES_IA_PROHIBIDAS = [
|
||||
@@ -17,7 +18,6 @@ const FRASES_IA_PROHIBIDAS = [
|
||||
/\bchatgpt\b/i,
|
||||
/inteligencia artificial/i,
|
||||
/no tengo un nombre propio/i,
|
||||
/en qu[eé] puedo ayudarte/i,
|
||||
];
|
||||
|
||||
export interface ClasificacionResultado {
|
||||
@@ -270,17 +270,19 @@ ${this.serializarLead(lead)}
|
||||
|
||||
## Instrucciones de respuesta
|
||||
Eres Luisa de Reformix en Madrid. Sigue el system prompt al pie de la letra.
|
||||
Habla espanol de Espana; suena natural y cercana.
|
||||
Habla espanol de Espana; calida, simpatica y siempre dispuesta a ayudar, como una asesora de confianza.
|
||||
Varia como lo dices en cada turno (no repitas frases calcadas) y no vuelvas a preguntar un dato que el
|
||||
usuario ya te haya dado en este mensaje o en el historial; reconocelo con naturalidad y sigue con lo que falte.
|
||||
Devuelve SOLO el texto del mensaje a enviar por WhatsApp.
|
||||
NO incluyas JSON, etiquetas XML ni bloques de datos extraidos.
|
||||
NUNCA digas que eres IA, OpenAI, ChatGPT ni un asistente virtual.
|
||||
|
||||
Si forzar mensaje de apertura es true, envia el mensaje de APERTURA del flujo.
|
||||
Si validacion valida es false y reintentos < 2, pide amablemente que aclare su respuesta.
|
||||
Si validacion valida es false y reintentos >= 2, repite la pregunta del estado actual de forma directa.
|
||||
Si es_desvio es true o intencion es pregunta, responde brevemente como Luisa y redirige al flujo sin avanzar.
|
||||
Si validacion valida es false y reintentos < 2, ayudale con calidez dando ejemplos o referencias para que se aclare.
|
||||
Si validacion valida es false y reintentos >= 2, vuelve a la pregunta del estado de otra forma, sin sonar borde.
|
||||
Si es_desvio es true o intencion es pregunta, atiende su duda con simpatia (concede algo util) y retoma el flujo sin avanzar.
|
||||
Si avanzar estado es true, haz la pregunta correspondiente al siguiente estado.
|
||||
Si el siguiente estado es fin_viable o fin_no_viable, usa el mensaje de cierre correspondiente.`;
|
||||
Si el siguiente estado es fin_viable, cierra con calidez anunciando que ya le preparas el presupuesto.`;
|
||||
|
||||
const contenido = await this.llamarOpenRouter(this.getModelo('generador'),
|
||||
`${this.systemPromptCache || DEFAULT_SYSTEM_PROMPT}\n${contextoGeneracion}`,
|
||||
@@ -304,11 +306,14 @@ Devuelve SOLO el mensaje final listo para enviar, sin explicaciones ni JSON.
|
||||
- Intencion del usuario: ${clasificacion.intencion}
|
||||
|
||||
## Reglas de correccion obligatorias
|
||||
- Debe sonar como Luisa de Reformix (Madrid), nunca como un asistente generico
|
||||
- Espanol de Espana, natural; puede usar coloquialismos suaves (vale, mira, oye) si encaja
|
||||
- Maximo 2 lineas, un mensaje por turno, sin emojis, sin guiones largos
|
||||
- Debe sonar como Luisa de Reformix (Madrid): calida, simpatica y servicial, nunca un asistente generico ni un teleoperador
|
||||
- Espanol de Espana, natural; usa coloquialismos y conectores suaves (vale, mira, oye, genial, tranquila, perfecto) cuando encajen
|
||||
- Un mensaje por turno, breve (2-3 lineas como mucho); puede llevar algun emoji suave ocasional, sin abusar
|
||||
- Varia la redaccion; no dejes frases calcadas ni que repitan literalmente lo que ya se dijo en turnos anteriores
|
||||
- No reescribas para quitar la cercania: si el borrador suena frio o robotico, dale calidez en vez de recortarla
|
||||
- NUNCA mencionar IA, OpenAI, ChatGPT, modelos de lenguaje ni asistentes virtuales
|
||||
- Si preguntan el nombre: "Soy Luisa de Reformix"
|
||||
- Quita cualquier JSON o etiqueta tecnica que se haya colado; deja solo el mensaje natural
|
||||
- Si el borrador viola alguna regla, reescribelo completamente manteniendo la intencion`;
|
||||
|
||||
const contenido = await this.llamarOpenRouter(this.getModelo('reglas'), system,
|
||||
@@ -376,12 +381,9 @@ Devuelve SOLO el mensaje final listo para enviar, sin explicaciones ni JSON.
|
||||
entidad.nombre = clasificacion.valor_extraido.trim();
|
||||
}
|
||||
}
|
||||
if (estadoFlujo === 'presupuesto') {
|
||||
viable = validacion.viable;
|
||||
siguienteEstado = this.leadsService.getSiguienteEstado(estadoFlujo, viable);
|
||||
} else {
|
||||
siguienteEstado = this.leadsService.getSiguienteEstado(estadoFlujo);
|
||||
}
|
||||
siguienteEstado = this.leadsService.getSiguienteEstado(estadoFlujo);
|
||||
// `viable` es solo informativo (siempre true): no cambia la ruta. Luisa nunca rechaza.
|
||||
if (estadoFlujo === 'presupuesto') viable = validacion.viable;
|
||||
} else if (!validacion.valido && clasificacion.responde_pregunta && !clasificacion.es_desvio) {
|
||||
reintentos = this.incrementarReintentos(lead.id, estadoFlujo);
|
||||
if (reintentos > 2) reintentos = 2;
|
||||
|
||||
@@ -37,9 +37,12 @@ export class LeadsService {
|
||||
return estado;
|
||||
}
|
||||
|
||||
getSiguienteEstado(estadoActual: string, viable?: boolean): string {
|
||||
getSiguienteEstado(estadoActual: string): string {
|
||||
const estado = this.normalizarEstadoFlujo(estadoActual);
|
||||
if (estado === 'presupuesto') return viable === false ? 'fin_no_viable' : 'fin_viable';
|
||||
// Tras el presupuesto el lead SIEMPRE cierra como viable: Luisa nunca rechaza a nadie. La
|
||||
// rentabilidad del lead la valora el reformista en el panel con su baremo interno (los agentes
|
||||
// no usan esa información para decidir nada todavía).
|
||||
if (estado === 'presupuesto') return 'fin_viable';
|
||||
const idx = SECUENCIA_ESTADOS.indexOf(estado as typeof SECUENCIA_ESTADOS[number]);
|
||||
if (idx === -1 || idx >= SECUENCIA_ESTADOS.length - 1) return estado;
|
||||
return SECUENCIA_ESTADOS[idx + 1];
|
||||
@@ -57,12 +60,11 @@ export class LeadsService {
|
||||
return /\d/.test(valor.trim().toLowerCase());
|
||||
}
|
||||
|
||||
evaluarViabilidad(presupuesto: string): boolean {
|
||||
const numeros = presupuesto.match(/\d[\d.]*/g);
|
||||
if (!numeros?.length) return true;
|
||||
const valor = parseInt(numeros[0].replace(/\./g, ''), 10);
|
||||
if (Number.isNaN(valor)) return true;
|
||||
return valor >= 5000;
|
||||
// Luisa ya no decide la viabilidad del lead: nunca rechaza por presupuesto. La rentabilidad la
|
||||
// valora el reformista en el panel (baremo interno, fase aparte). Se mantiene para informar el
|
||||
// campo `viable`, que de momento siempre es true.
|
||||
evaluarViabilidad(_presupuesto: string): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
async persistirTurno(
|
||||
|
||||
Reference in New Issue
Block a user