// AGENTFORGE-IA — Asistente de creación de agente const Onboarding = ({ data, setRoute, onCreateAgent }) => { const [step, setStep] = React.useState(1); const [tpl, setTpl] = React.useState('triage'); const [cat, setCat] = React.useState('support'); const [tools, setTools] = React.useState(['hubspot']); const [name, setName] = React.useState(''); const [purpose, setPurpose] = React.useState(''); const [contextFiles, setContextFiles] = React.useState([]); const [contextUrls, setContextUrls] = React.useState(''); const [contextNotes, setContextNotes] = React.useState(''); const [systemPrompt, setSystemPrompt] = React.useState(''); const [systemPromptEdited, setSystemPromptEdited] = React.useState(false); const notify = (msg) => window.afNotify && window.afNotify(msg); const cookingSystemPrompt = React.useMemo(() => `Eres un asistente doméstico especializado en recetas de cocina española. Tu misión es ayudar al usuario a cocinar recetas españolas claras, fiables y adaptadas a su situación real. Usa como referencia prioritaria las fuentes autorizadas indicadas abajo cuando la receta solicitada exista en ellas o cuando el usuario pida buscar en fuentes. FUENTES AUTORIZADAS: https://recetinas.com/seccion/cocina-espanola/ https://recetas.elperiodico.com/recetas-espanolas https://recetasespanolas.es/aperitivos-espanoles/ https://recetasespanolas.es/postres-tipicos-espanoles/ https://recetasespanolas.es/platos-principales-tipicos-espanoles/ https://recetasespanolas.es/bocadillos-tipicos-espanoles/ https://recetasespanolas.es/salsas-y-aderezos-tipicos-espanoles/ https://recetasespanolas.es/sopas-y-guisos-espanoles/ https://recetasespanolas.es/ensaladas-tipicas-espanolas/ https://recetasespanolas.es/bebidas-tipicas-espanolas/ https://recetasespanolas.es/panes-espanoles/ REGLAS DE RESPUESTA: - Responde siempre en español claro, cercano y práctico. - Si puedes apoyarte en una fuente autorizada, indica la fuente y el enlace original. - Si no tienes una fuente concreta disponible, puedes dar una versión casera tradicional y debes indicarlo con naturalidad. - No inventes datos de una web, autor, precio, tiempo exacto o información nutricional si no aparece en la fuente. - Adapta la receta al número de personas, tiempo disponible, ingredientes, nivel de cocina y restricciones alimentarias si el usuario lo indica. - Si falta un dato importante, pregunta solo lo imprescindible y ofrece una versión estándar para 4 personas. - No menciones imágenes ni prometas fotografías. FORMATO OBLIGATORIO: **Nombre de la receta:** [nombre] **Fuente:** [web de origen o “versión casera tradicional”] **Enlace original:** [URL si se ha localizado una fuente] **Ingredientes** - ... **Preparación** 1. ... **Consejos y adaptación** - Ajustes de sabor, textura, sustituciones o errores comunes. - Si procede, conservación, acompañamiento o versión más sencilla.`, []); const categoryCounts = React.useMemo(() => { const counts = {}; const agents = Array.isArray(data?.agents) ? data.agents : []; agents.forEach((a) => { const agentCat = a?.cat || a?.category; if (!agentCat) return; counts[agentCat] = (counts[agentCat] || 0) + 1; }); return counts; }, [data]); /** * Detecta si el agente doméstico solicitado es realmente de cocina/recetas. * @param {string} targetName Nombre visible del agente. * @param {string} targetPurpose Finalidad indicada por el usuario. * @param {string} targetTemplate Plantilla seleccionada. * @param {string} targetCategory Categoría seleccionada. * @returns {boolean} True cuando debe aplicarse el prompt culinario canónico. */ const isCookingIntent = React.useCallback((targetName, targetPurpose, targetTemplate, targetCategory) => { const normalizedIntent = `${targetName || ''} ${targetPurpose || ''} ${targetTemplate || ''} ${targetCategory || ''}` .toLowerCase() .normalize('NFD') .replace(/[\u0300-\u036f]/g, ''); const hasCookingSignal = /(cocina|receta|recetas|chef|comida|menu|menus|plato|platos|gastronom|cocinar)/.test(normalizedIntent); return targetCategory === 'home' && hasCookingSignal; }, []); const buildSystemPrompt = React.useCallback(() => { const cleanName = (name || 'Asistente').trim(); const cleanPurpose = (purpose || '').trim(); const purposeLine = cleanPurpose ? `Objetivo del agente: ${cleanPurpose}` : 'Objetivo del agente: Resolver consultas del usuario con precisión y claridad.'; if (isCookingIntent(cleanName, cleanPurpose, tpl, cat)) { return cookingSystemPrompt; } const byCategory = { sales: 'Especialízate en ventas y prospección: detecta intención de compra, objeciones y próximos pasos comerciales.', support: 'Especialízate en atención al cliente: responde dudas frecuentes, procesos y escalado a humano cuando proceda.', mkt: 'Especialízate en marketing: propuesta de valor, campañas, contenidos y seguimiento de conversiones.', ops: 'Especialízate en operaciones: procesos internos, ejecución ordenada y control de incidencias.', home: 'Ayuda en tareas del hogar: organiza compras, menús, mantenimiento, horarios familiares y recordatorios cotidianos sin inventar datos.', fin: 'Especialízate en finanzas: claridad en precios, márgenes, cobros y métricas sin inventar datos.', hr: 'Especialízate en RR.HH.: procesos de talento, onboarding y políticas internas.', eng: 'Especialízate en ingeniería: precisión técnica, diagnóstico paso a paso y buenas prácticas.', data: 'Especialízate en datos y analítica: interpretación de métricas, contexto y recomendaciones accionables.', legal: 'Especialízate en legal y cumplimiento: identifica riesgos, pide contexto contractual y evita dar asesoramiento jurídico definitivo.', research: 'Especialízate en investigación: contrasta fuentes, separa hechos de hipótesis y entrega síntesis accionables.', product: 'Especialízate en producto: discovery, priorización, requisitos y decisiones orientadas a valor.', creative: 'Especialízate en creatividad y diseño: tono, narrativa, consistencia visual y producción de activos.', games: 'Especialízate en juegos y entretenimiento: diseño, mecánicas, contenido y experiencia de usuario lúdica.', }; const categoryInstruction = byCategory[cat] || 'Especialízate en el dominio indicado por el usuario.'; return [ `Eres ${cleanName}.`, purposeLine, categoryInstruction, 'Responde siempre en el idioma del usuario.', 'Si falta información crítica, dilo y solicita el dato exacto antes de asumir.', 'No inventes tarifas, condiciones, horarios ni datos legales.', ].join('\n\n'); }, [name, purpose, cat, tpl, cookingSystemPrompt, isCookingIntent]); React.useEffect(() => { const mustUseCookingPrompt = isCookingIntent(name, purpose, tpl, cat); const currentIsGenericHome = /(Especialízate en uso doméstico|Ayuda en tareas del hogar)/i.test(systemPrompt || ''); if (mustUseCookingPrompt && (currentIsGenericHome || !systemPrompt.trim())) { setSystemPrompt(cookingSystemPrompt); setSystemPromptEdited(false); return; } if (systemPromptEdited) return; setSystemPrompt(buildSystemPrompt()); }, [buildSystemPrompt, systemPromptEdited, isCookingIntent, name, purpose, tpl, cat, systemPrompt, cookingSystemPrompt]); const steps = [ { n: 1, t: 'Punto de partida', s: 'plantilla, en blanco, importar' }, { n: 2, t: 'Categoría y rol', s: 'dónde encaja' }, { n: 3, t: 'Herramientas y memoria', s: 'qué puede usar' }, { n: 4, t: 'Comportamiento', s: 'instrucciones, salvaguardas' }, { n: 5, t: 'Revisar y editar', s: 'abre el editor' }, ]; const templates = [ { id: 'triage', n: 'Triaje de tickets', d: 'Clasifica y enruta tickets entrantes con prioridad y SLA', i: 'headset', c: '#60a5fa' }, { id: 'customer-care', n: 'Atención al cliente', d: 'Respuestas multicanal con escalado a humano cuando aplique', i: 'users', c: '#60a5fa' }, { id: 'spanish-cooking', n: 'Chef de Cocina Española', d: 'Recetas españolas con fuentes autorizadas, adaptación al público objetivo y formato claro', i: 'home', c: '#22c55e', cat: 'home' }, { id: 'home-helper', n: 'Asistente doméstico', d: 'Organiza tareas de casa, compras, menús, citas, recordatorios y pequeñas gestiones familiares', i: 'home', c: '#22c55e', cat: 'home' }, { id: 'home-maintenance', n: 'Mantenimiento del hogar', d: 'Ayuda a planificar revisiones, reparaciones, proveedores y calendario de mantenimiento', i: 'wrench', c: '#22c55e', cat: 'home' }, { id: 'renewal', n: 'Renovaciones y churn', d: 'Detecta riesgo, propone acciones y ejecuta seguimiento comercial', i: 'chart', c: '#22d3ee' }, { id: 'sdr', n: 'SDR outbound', d: 'Prospección personalizada, secuencias y calificación de leads', i: 'megaphone', c: '#f472b6' }, { id: 'lead-qualify', n: 'Cualificación de leads', d: 'Score de oportunidad y enrutado automático al equipo correcto', i: 'tag', c: '#f472b6' }, { id: 'research', n: 'Analista de investigación', d: 'Investigación web multietapa con fuentes y resumen ejecutivo', i: 'flask', c: '#fb923c' }, { id: 'reviewer', n: 'Revisor de PRs', d: 'Revisión técnica de cambios con checklist de calidad', i: 'fork', c: '#ff5b1f' }, { id: 'qa-automation', n: 'QA automatizado', d: 'Ejecución de pruebas, clasificación de fallos y reporte', i: 'zap', c: '#ff5b1f' }, { id: 'analyst', n: 'Analista de datos', d: 'Consultas guiadas a warehouse y generación de insights', i: 'database', c: '#818cf8' }, { id: 'reporting', n: 'Reporting ejecutivo', d: 'KPIs diarios/semanales con alertas y resumen para dirección', i: 'chart', c: '#818cf8' }, { id: 'collections', n: 'Cobros y facturación', d: 'Seguimiento de impagos, recordatorios y estado de cuenta', i: 'bell', c: '#f59e0b' }, { id: 'onboarding', n: 'Onboarding de clientes', d: 'Checklist de alta, hitos y comunicación proactiva', i: 'rocket', c: '#34d399' }, { id: 'blank', n: 'Lienzo en blanco', d: 'Empezar desde cero con flujo totalmente libre', i: 'plus', c: '#7a7f8d' }, ]; /** * Valida los datos mínimos antes de crear el agente. * @returns {boolean} True si el usuario ya puede continuar. */ const validateBasics = () => { if (!name.trim()) { notify && notify('El nombre del agente es obligatorio'); setStep(2); return false; } if (!purpose.trim()) { notify && notify('La finalidad del agente es obligatoria'); setStep(2); return false; } return true; }; /** * Avanza en el asistente evitando llegar al final con datos obligatorios vacíos. * @returns {void} */ const goNext = () => { if (step >= 2 && !validateBasics()) return; setStep(Math.min(5, step + 1)); }; /** * Cambia de paso sin permitir llegar a revisión con datos obligatorios vacíos. * @param {number} targetStep Paso de destino. * @returns {void} */ const goToStep = (targetStep) => { if (targetStep >= 3 && !validateBasics()) return; setStep(targetStep); }; /** * Crea el agente local y prepara el Editor con el contexto elegido. * @param {string} deployment Estado inicial del agente creado. * @returns {void} */ const completeCreation = (deployment) => { if (!validateBasics()) return; const finalSystemPrompt = isCookingIntent(name, purpose, tpl, cat) ? cookingSystemPrompt : systemPrompt.trim(); const created = onCreateAgent ? onCreateAgent({ name: name.trim(), cat, tools, tagline: purpose.trim(), systemPrompt: finalSystemPrompt, contextFiles: contextFiles.map((f) => ({ name: f.name, size: f.size, type: f.type })), contextUrls: contextUrls.split(/\r?\n/).map((u) => u.trim()).filter(Boolean), contextNotes: contextNotes.trim(), deployment, }) : null; try { localStorage.setItem('af_builder_seed_v1', JSON.stringify({ fromOnboarding: true, blank: true, agentId: created?.id || null, agentName: name.trim(), purpose: purpose.trim(), contextFiles: contextFiles.map((f) => ({ name: f.name, size: f.size, type: f.type })), contextUrls: contextUrls.split(/\r?\n/).map((u) => u.trim()).filter(Boolean), contextNotes: contextNotes.trim(), category: cat, tools, template: tpl, systemPrompt: finalSystemPrompt, createdAt: Date.now(), })); } catch (_) {} setRoute('builder'); }; const toolList = [ { id: 'linear', n: 'Linear', i: 'grid' }, { id: 'snowflake', n: 'Snowflake', i: 'database' }, { id: 'notion', n: 'Notion', i: 'cube' }, { id: 'gmail', n: 'Gmail', i: 'send' }, { id: 'hubspot', n: 'HubSpot', i: 'users' }, { id: 'github', n: 'GitHub', i: 'fork' }, ]; return (
Las plantillas usan grafos y prompts probados. Puedes editar todo después.
Esto fija valores por defecto para evaluaciones, gobernanza y dónde aparece el agente en el marketplace.
Elige las integraciones que este agente puede invocar y añade documentos para entrenar su contexto inicial.
Tono, salvaguardas y reglas de escalado.
Desde aquí guardas el agente en borrador y pasas al Editor para probar, corregir y ampliar.