From ccb83a3d20ae873fa952751cb0ca3efd6611f7c1 Mon Sep 17 00:00:00 2001 From: Carlos Narro Date: Sun, 31 May 2026 16:16:27 +0200 Subject: [PATCH] =?UTF-8?q?Add=20guion=20del=20agente=20de=20voz=20(pre?= =?UTF-8?q?=C3=A1mbulo=20legal=20+=20slots=20+=20gustos)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mvp/b2c/src/lib/voice/script.ts | 80 ++++++++++++++++++++++++++++++ mvp/b2c/tests/voice/script.test.ts | 24 +++++++++ 2 files changed, 104 insertions(+) create mode 100644 mvp/b2c/src/lib/voice/script.ts create mode 100644 mvp/b2c/tests/voice/script.test.ts diff --git a/mvp/b2c/src/lib/voice/script.ts b/mvp/b2c/src/lib/voice/script.ts new file mode 100644 index 0000000..092e6b1 --- /dev/null +++ b/mvp/b2c/src/lib/voice/script.ts @@ -0,0 +1,80 @@ +import type { TipoReforma } from '@/budget/types'; + +export interface ScriptTurn { + quien: 'agente' | 'cliente'; + texto: string; +} +export interface ScriptBlock { + id: 'preambulo' | 'confirmacion' | 'slots' | 'gustos' | 'cierre'; + titulo: string; + turnos: ScriptTurn[]; +} +export interface VoiceScript { + bloques: ScriptBlock[]; +} + +const TIPO_TEXTO: Record = { + cocina: 'la cocina', + bano: 'el baño', + salon: 'el salón', + comedor: 'el comedor', + integral: 'el piso entero', + otro: 'la reforma', +}; + +export function buildScript( + empresa: { nombreEmpresa: string }, + lead: { nombre: string; tipoReforma: TipoReforma | null; m2Suelo: number | null }, +): VoiceScript { + const nombre = lead.nombre.split(' ')[0]; + const tipo = TIPO_TEXTO[lead.tipoReforma ?? 'otro']; + const m2 = lead.m2Suelo ? `${Math.round(lead.m2Suelo)} metros` : 'el espacio'; + + return { + bloques: [ + { + id: 'preambulo', + titulo: 'Preámbulo legal', + turnos: [ + { + quien: 'agente', + texto: `Hola ${nombre}, te llamo de ${empresa.nombreEmpresa}. Soy un asistente virtual con inteligencia artificial y esta llamada queda grabada y transcrita para preparar tu presupuesto. ¿Te parece bien que sigamos?`, + }, + ], + }, + { + id: 'confirmacion', + titulo: 'Confirmación de datos', + turnos: [ + { quien: 'agente', texto: `Me dices que quieres reformar ${tipo}, ¿unos ${m2} aproximadamente, verdad?` }, + ], + }, + { + id: 'slots', + titulo: 'Datos clave', + turnos: [ + { quien: 'agente', texto: '¿Qué nivel de acabado buscas: básico, medio o premium?' }, + { quien: 'agente', texto: '¿Hay que mover sanitarios principales, tirar algún muro o cambiar la distribución?' }, + { quien: 'agente', texto: '¿Para cuándo te gustaría tenerlo?' }, + { quien: 'agente', texto: '¿Tienes una cifra en mente, aunque sea aproximada?' }, + ], + }, + { + id: 'gustos', + titulo: 'Gustos y preferencias', + turnos: [ + { quien: 'agente', texto: `Cuéntame cómo te imaginas ${tipo}: estilo, colores, materiales que te gusten… y si hay algún capricho que no quieras que falte.` }, + { quien: 'agente', texto: '¿Te tira más la madera, el porcelánico, algo más industrial?' }, + { quien: 'agente', texto: '¿Hay algo imprescindible: una isla, una ducha de obra, electrodomésticos integrados?' }, + ], + }, + { + id: 'cierre', + titulo: 'Cierre', + turnos: [ + { quien: 'agente', texto: `Genial, con esto te preparo el render y el presupuesto orientativo y te lo enviamos por WhatsApp. ¡Muchas gracias, ${nombre}!` }, + ], + }, + ], + }; +} diff --git a/mvp/b2c/tests/voice/script.test.ts b/mvp/b2c/tests/voice/script.test.ts new file mode 100644 index 0000000..2d0a58d --- /dev/null +++ b/mvp/b2c/tests/voice/script.test.ts @@ -0,0 +1,24 @@ +import { describe, it, expect } from 'vitest'; +import { buildScript } from '@/lib/voice/script'; + +describe('buildScript', () => { + const script = buildScript( + { nombreEmpresa: 'Reformas Ejemplo' }, + { nombre: 'Ana Pérez', tipoReforma: 'cocina', m2Suelo: 16 }, + ); + + it('abre con identificación de IA y aviso de grabación (RF-C-12, RNF-LEG-05)', () => { + const preambulo = script.bloques[0].turnos.map((t) => t.texto).join(' '); + expect(preambulo).toContain('inteligencia artificial'); + expect(preambulo).toContain('grabada'); + expect(preambulo).toContain('Reformas Ejemplo'); + expect(preambulo).toContain('Ana'); + }); + + it('incluye los bloques de slots fijos y el bloque abierto de gustos', () => { + const ids = script.bloques.map((b) => b.id); + expect(ids).toEqual(['preambulo', 'confirmacion', 'slots', 'gustos', 'cierre']); + const gustos = script.bloques.find((b) => b.id === 'gustos')!; + expect(gustos.turnos.some((t) => t.texto.includes('imaginas'))).toBe(true); + }); +});