Instrucciones y estilo de IA editables desde el panel (Configuración Avanzada)

This commit is contained in:
2026-09-12 14:48:31 +00:00
parent 18bafe227c
commit c6ab0d9d5a

View File

@@ -1596,6 +1596,16 @@ async function registrarMovimiento(serviceId, userId, action, details) {
// ========================================== // ==========================================
// 🧠 CEREBRO IA (WHATSAPP) // 🧠 CEREBRO IA (WHATSAPP)
// ========================================== // ==========================================
// 🗣️ Texto por defecto del bloque "Estilo e instrucciones de la empresa" que se ve y se
// edita desde el cuadro de "Instrucciones y estilo de la IA" en Configuración Avanzada.
// Si la empresa no ha guardado nunca nada en ese cuadro, se usa este texto (que es
// exactamente el comportamiento que la IA tenía antes de poder editarse desde el panel).
const DEFAULT_REGLAS_NEGOCIO_IA = `Habla de tú, de forma natural, clara, cercana y profesional.
Los fines de semana solo se ofrecen citas si es una urgencia.
Máximo 2 frases por mensaje: sé claro y directo.
No te presentes de nuevo si ya habéis hablado antes.
No uses la muletilla "¿En qué más te puedo ayudar?".`;
async function procesarConIA(ownerId, mensajeCliente, datosExpediente) { async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
try { try {
const userQ = await pool.query( const userQ = await pool.query(
@@ -1607,7 +1617,12 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
const userData = userQ.rows[0]; const userData = userQ.rows[0];
const settings = userData?.wa_settings || {}; const settings = userData?.wa_settings || {};
const instruccionesExtra = settings.ai_custom_prompt || ""; // 🗣️ Estilo e instrucciones de la empresa: editable desde el panel web
// (Configuración Avanzada → cuadro "Instrucciones y estilo de la IA"). Si la
// empresa nunca ha guardado nada ahí, usamos el texto por defecto de siempre.
const reglasNegocioIA = (settings.ai_custom_prompt && settings.ai_custom_prompt.trim())
? settings.ai_custom_prompt.trim()
: DEFAULT_REGLAS_NEGOCIO_IA;
const empresaNombre = userData?.full_name || "nuestra empresa"; const empresaNombre = userData?.full_name || "nuestra empresa";
// 🧾 Condiciones de cobertura por compañía y/o gremio: cada regla puede llevar una // 🧾 Condiciones de cobertura por compañía y/o gremio: cada regla puede llevar una
@@ -1972,11 +1987,11 @@ async function procesarConIA(ownerId, mensajeCliente, datosExpediente) {
if (tieneCitaConfirmada && datosExpediente.cita) { if (tieneCitaConfirmada && datosExpediente.cita) {
const ahora = new Date(); const ahora = new Date();
const [y, m, d] = datosExpediente.cita.split('-'); const [y, m, d] = datosExpediente.cita.split('-');
// Si no hay hora concreta, solo se considera "pasada" al cambiar de día (fin del día) // Si no hay hora concreta, solo se considera "pasada" al cambiar de dia (fin del dia)
let horaFin = 23, minFin = 59; let horaFin = 23, minFin = 59;
if (datosExpediente.hora_cita && datosExpediente.hora_cita.includes(':')) { if (datosExpediente.hora_cita && datosExpediente.hora_cita.includes(':')) {
const [hh, mm] = datosExpediente.hora_cita.split(':'); const [hh, mm] = datosExpediente.hora_cita.split(':');
horaFin = parseInt(hh, 10) + 1; // margen aprox. de duración del servicio horaFin = parseInt(hh, 10) + 1; // margen aprox. de duracion del servicio
minFin = parseInt(mm, 10); minFin = parseInt(mm, 10);
} }
const finVentanaCita = new Date(y, m - 1, d, horaFin, minFin); const finVentanaCita = new Date(y, m - 1, d, horaFin, minFin);
@@ -2022,13 +2037,12 @@ TU OBJETIVO: Acordar fecha y hora para reparar su avería (${datosExpediente.ave
} }
const promptSistema = ` const promptSistema = `
Eres el coordinador humano de "${empresaNombre}". Hablas de tú, de forma natural, clara, cercana y profesional por WhatsApp. Eres el coordinador humano de "${empresaNombre}", hablando por WhatsApp.
--- 📋 CONTEXTO BÁSICO --- --- 📋 CONTEXTO BÁSICO ---
- Hoy es: ${fechaHoyTexto}. - Hoy es: ${fechaHoyTexto}.
- Horario de la empresa: L-V de ${horarios.m_start} a ${horarios.m_end} y de ${horarios.a_start} a ${horarios.a_end}. - Horario de la empresa: L-V de ${horarios.m_start} a ${horarios.m_end} y de ${horarios.a_start} a ${horarios.a_end}.
- ⛔ REGLA DE ORO DEL HORARIO: NUNCA propongas horas fuera de ese horario ni cruces el tramo de comer. - ⛔ REGLA DE ORO DEL HORARIO: NUNCA propongas horas fuera de ese horario ni cruces el tramo de comer.
- Fines de semana solo si es una urgencia.
- Localidad del cliente actual: ${datosExpediente.poblacion || 'Localidad no especificada'}. - Localidad del cliente actual: ${datosExpediente.poblacion || 'Localidad no especificada'}.
${reglaCobertura ? ` ${reglaCobertura ? `
--- 🧾 CONDICIONES DE COBERTURA PARA ESTE SERVICIO (Compañía: ${datosExpediente.compania || 'no especificada'} · Gremio: ${datosExpediente.gremio || 'no especificado'}) --- --- 🧾 CONDICIONES DE COBERTURA PARA ESTE SERVICIO (Compañía: ${datosExpediente.compania || 'no especificada'} · Gremio: ${datosExpediente.gremio || 'no especificado'}) ---
@@ -2038,7 +2052,7 @@ Menciona esto SOLO si el cliente pregunta por costes, materiales, o qué cubre e
--- ✅ HUECOS EXACTOS CALCULADOS POR EL SISTEMA --- --- ✅ HUECOS EXACTOS CALCULADOS POR EL SISTEMA ---
${agendaDisponibleTexto} ${agendaDisponibleTexto}
--- 🚨 NORMA ABSOLUTA DE AGENDA --- --- 🚨 NORMA ABSOLUTA DE AGENDA (fija, no editable desde el panel) ---
Los huecos anteriores son la ÚNICA verdad. Los huecos anteriores son la ÚNICA verdad.
NO puedes inventar horas. NO puedes inventar horas.
NO puedes deducir huecos por tu cuenta. NO puedes deducir huecos por tu cuenta.
@@ -2046,32 +2060,30 @@ NO puedes reinterpretar horarios ocupados.
SOLO puedes ofrecer horas que estén dentro de esos huecos exactos calculados por el sistema. SOLO puedes ofrecer horas que estén dentro de esos huecos exactos calculados por el sistema.
Si el cliente pide una hora concreta y esa hora cae dentro de uno de esos huecos, puedes aceptarla tentativamente. Si el cliente pide una hora concreta y esa hora cae dentro de uno de esos huecos, puedes aceptarla tentativamente.
Si cae fuera, debes decir que no la ves libre y ofrecer una alternativa de la lista exacta. Si cae fuera, debes decir que no la ves libre y ofrecer una alternativa de la lista exacta.
Si el cliente pide disponibilidad general, resume las opciones reales sin inventar nada.
--- 🎯 DIRECTIVA ESTRICTA PARA ESTE MENSAJE --- --- 🎯 DIRECTIVA ESTRICTA PARA ESTE MENSAJE (fija, no editable desde el panel) ---
${directivaEstricta} ${directivaEstricta}
--- ⚡ REGLA CRÍTICA DE AGENDA (COMANDO SECRETO) --- --- ⚡ REGLA CRÍTICA DE AGENDA (COMANDO SECRETO, fija, no editable desde el panel) ---
Si (y solo si) el cliente ACEPTA claramente un hueco concreto, DEBES añadir AL FINAL ABSOLUTO de tu respuesta este texto literal: Si (y solo si) el cliente ACEPTA claramente un hueco concreto, DEBES añadir AL FINAL ABSOLUTO de tu respuesta este texto literal:
[PROPUESTA:YYYY-MM-DD HH:mm] [PROPUESTA:YYYY-MM-DD HH:mm]
Ejemplo: Ejemplo:
"Perfecto, le paso la nota al técnico para que te confirme el miércoles entre las 12:00 y las 13:00 aprox. ¡Te decimos algo pronto! [PROPUESTA:2026-03-25 12:00]" "Perfecto, le paso la nota al técnico para que te confirme el miércoles entre las 12:00 y las 13:00 aprox. ¡Te decimos algo pronto! [PROPUESTA:2026-03-25 12:00]"
⛔ PROHIBICIONES: ⛔ PROHIBICIONES (fijas, no editables desde el panel):
- NUNCA digas "te agendo". - NUNCA digas "te agendo".
- NUNCA digas "cita confirmada". - NUNCA digas "cita confirmada".
- NUNCA menciones las palabras "código" o "etiqueta". - NUNCA menciones las palabras "código" o "etiqueta".
- NUNCA ofrezcas huecos distintos a los calculados. - NUNCA ofrezcas huecos distintos a los calculados.
- Si el cliente pregunta por "la semana que viene", responde usando solo los huecos de esa semana. - Si el cliente pregunta por "la semana que viene", responde usando solo los huecos de esa semana.
${esPrimerMensaje ? `
--- 📌 NOTA ---
Este es el primer mensaje: preséntate y menciona el aviso (#${datosExpediente.ref}).` : ''}
--- ⚙️ REGLAS DE COMUNICACIÓN --- --- 🗣️ ESTILO E INSTRUCCIONES DE LA EMPRESA (editable desde el panel web, Configuración Avanzada) ---
1. MÁXIMO 2 FRASES. ${reglasNegocioIA}
2. Mensajes cortos, claros y directos.
3. NO TE PRESENTES si ya habéis hablado antes.
4. NO uses la muletilla "¿En qué más te puedo ayudar?".
5. Si el cliente pide disponibilidad general, resume las opciones reales sin inventar nada.
${esPrimerMensaje ? `6. Primer mensaje: preséntate y menciona el aviso (#${datosExpediente.ref}).` : ''}
${instruccionesExtra ? `7. Instrucción extra de la empresa: ${instruccionesExtra}` : ''}
`; `;
const completion = await openai.chat.completions.create({ const completion = await openai.chat.completions.create({
@@ -3049,7 +3061,14 @@ app.get("/whatsapp/status", authMiddleware, async (req, res) => {
app.get("/whatsapp/settings", authMiddleware, async (req, res) => { app.get("/whatsapp/settings", authMiddleware, async (req, res) => {
try { try {
const q = await pool.query("SELECT wa_settings FROM users WHERE id=$1", [req.user.accountId]); const q = await pool.query("SELECT wa_settings FROM users WHERE id=$1", [req.user.accountId]);
res.json({ ok: true, settings: q.rows[0]?.wa_settings || {} }); res.json({
ok: true,
settings: q.rows[0]?.wa_settings || {},
// 🗣️ Texto que usa la IA por defecto para el bloque de estilo/reglas de negocio
// cuando la empresa aún no ha guardado nada personalizado. El panel lo usa para
// precargar el cuadro de texto la primera vez, para que se vea lo que hace hoy.
default_ai_rules: DEFAULT_REGLAS_NEGOCIO_IA
});
} catch (e) { res.status(500).json({ ok: false }); } } catch (e) { res.status(500).json({ ok: false }); }
}); });