plazbot-cli
Version:
CLI para Plazbot SDK
476 lines (439 loc) • 15.7 kB
text/typescript
import inquirer from 'inquirer';
import chalk from 'chalk';
import { theme, section, kvPair } from '../../utils/ui';
interface AgentConfig {
name: string;
description?: string;
prompt: string;
buffer: number;
zone: string;
color: string;
useToolCalling: boolean;
customAIConfig: boolean;
aiProviders: AIProvider[];
instructions: AgentInstructions;
person: AgentPerson;
fallbacks: AgentFallbacks;
services: AgentService[];
actions: AgentAction[];
channels: AgentChannel[];
examples: AgentExample[];
tags: string[];
}
interface AIProvider {
provider: string;
model: string;
apiToken: string;
temperature: number;
maxTokens: number;
isDefault: boolean;
}
interface AgentInstructions {
tone: string;
style: string;
personality: string;
objective: string;
language: string;
emojis: boolean;
}
interface AgentPerson {
name: string;
role: string;
speaksInFirstPerson: boolean;
}
interface AgentFallbacks {
noAnswer: string;
serviceError: string;
doNotUnderstand: string;
}
interface AgentService {
intent: string;
reference: string;
enabled: boolean;
method: string;
endpoint: string;
requiredFields: { name: string; type: string; description: string; promptHint: string }[];
headers: Record<string, string>;
bodyTemplate: Record<string, string>;
responseMapping: Record<string, string>;
responseMessage: string;
responseConditions: { condition: string; message: string }[];
}
interface AgentAction {
intent: string;
reference: string;
enabled: boolean;
requiredFields: { name: string; type: string; description: string; promptHint: string }[];
responseMessage: string;
action: { type: string; value: string }[];
}
interface AgentChannel {
channel: string;
key: string;
multianswer: boolean;
}
interface AgentExample {
value: string;
color: string;
}
const MODELS: Record<string, string[]> = {
openai: ['gpt-4o', 'gpt-4', 'gpt-3.5-turbo', 'o1-preview', 'o1-mini'],
claude: ['claude-3-5-sonnet-20241022', 'claude-3-opus-20240229', 'claude-3-haiku-20240307'],
gemini: ['gemini-2.0-flash', 'gemini-1.5-pro', 'gemini-1.5-flash'],
};
const COLORS = ['blue', 'green', 'orange', 'gray', 'white'];
export async function runAgentWizard(zone: string): Promise<AgentConfig> {
console.log(section('Crear nuevo agente de IA'));
console.log(theme.muted(' Responde las siguientes preguntas para configurar tu agente.\n'));
// Paso 1: Informacion basica
console.log(theme.bold('\n Paso 1/8: Informacion basica'));
const basic = await (inquirer as any).prompt([
{
type: 'input',
name: 'name',
message: 'Nombre del agente:',
validate: (v: string) => v.length > 0 || 'El nombre es requerido',
},
{
type: 'input',
name: 'description',
message: 'Descripcion breve:',
default: '',
},
{
type: 'input',
name: 'prompt',
message: 'Prompt del sistema (instrucciones principales):',
default: 'Eres un asistente virtual amigable y profesional.',
},
{
type: 'number',
name: 'buffer',
message: 'Buffer de conversacion (3-20 mensajes):',
default: 5,
validate: (v: number) => (v >= 3 && v <= 20) || 'Debe ser entre 3 y 20',
},
{
type: 'list',
name: 'color',
message: 'Color del agente:',
choices: COLORS,
default: 'blue',
},
]);
// Paso 2: Personalidad
console.log(theme.bold('\n Paso 2/8: Personalidad e instrucciones'));
const personality = await (inquirer as any).prompt([
{
type: 'list',
name: 'tone',
message: 'Tono de comunicacion:',
choices: ['profesional', 'amigable', 'formal', 'casual', 'tecnico', 'empatico'],
default: 'profesional',
},
{
type: 'list',
name: 'style',
message: 'Estilo de respuesta:',
choices: ['conciso', 'detallado', 'conversacional', 'directo'],
default: 'conciso',
},
{
type: 'input',
name: 'personality',
message: 'Personalidad en una frase:',
default: 'Servicial y conocedor',
},
{
type: 'input',
name: 'objective',
message: 'Objetivo principal del agente:',
default: 'Ayudar a los usuarios con sus consultas',
},
{
type: 'list',
name: 'language',
message: 'Idioma principal:',
choices: ['Espanol', 'English', 'Portugues', 'Frances'],
default: 'Espanol',
},
{
type: 'confirm',
name: 'useEmojis',
message: 'Usar emojis en respuestas?',
default: true,
},
]);
// Paso 3: Persona
console.log(theme.bold('\n Paso 3/8: Identidad del agente'));
const person = await (inquirer as any).prompt([
{
type: 'input',
name: 'name',
message: 'Nombre del personaje (como se presenta):',
default: basic.name,
},
{
type: 'input',
name: 'role',
message: 'Rol (ej: Asistente de ventas, Soporte tecnico):',
default: 'Asistente virtual',
},
{
type: 'confirm',
name: 'firstPerson',
message: 'Hablar en primera persona?',
default: true,
},
]);
// Paso 4: Fallbacks
console.log(theme.bold('\n Paso 4/8: Mensajes de fallback'));
const fallbacks = await (inquirer as any).prompt([
{
type: 'input',
name: 'noAnswer',
message: 'Cuando no tiene respuesta:',
default: 'Lo siento, no tengo informacion sobre eso. Puedo ayudarte con otra consulta?',
},
{
type: 'input',
name: 'serviceError',
message: 'Cuando hay error de servicio:',
default: 'Disculpa, estamos experimentando dificultades tecnicas. Intenta de nuevo en unos momentos.',
},
{
type: 'input',
name: 'misunderstanding',
message: 'Cuando no entiende la pregunta:',
default: 'No estoy seguro de entender tu consulta. Podrias reformularla?',
},
]);
// Paso 5: Tool Calling
console.log(theme.bold('\n Paso 5/8: Tool Calling'));
console.log(theme.muted(' Tool Calling permite al agente ejecutar acciones automaticas\n'));
const { useToolCalling } = await (inquirer as any).prompt([
{
type: 'confirm',
name: 'useToolCalling',
message: 'Activar Tool Calling?',
default: true,
},
]);
// Servicios (API calls)
const services: AgentService[] = [];
if (useToolCalling) {
const { addServices } = await (inquirer as any).prompt([{
type: 'confirm',
name: 'addServices',
message: 'Agregar servicios externos (API calls)?',
default: false,
}]);
if (addServices) {
let addMore = true;
while (addMore) {
console.log(theme.muted('\n Nuevo servicio:'));
const svc = await (inquirer as any).prompt([
{ type: 'input', name: 'intent', message: 'Nombre/intent del servicio:', validate: (v: string) => v.length > 0 || 'Requerido' },
{ type: 'input', name: 'reference', message: 'Palabras clave de referencia:', default: '' },
{ type: 'list', name: 'method', message: 'Metodo HTTP:', choices: ['GET', 'POST'] },
{ type: 'input', name: 'endpoint', message: 'URL del endpoint:', validate: (v: string) => v.length > 0 || 'Requerido' },
{ type: 'input', name: 'responseMessage', message: 'Mensaje de respuesta:', default: '' },
]);
// Required fields
const fields: AgentService['requiredFields'] = [];
const { addFields } = await (inquirer as any).prompt([{
type: 'confirm', name: 'addFields',
message: 'Agregar campos requeridos?', default: false,
}]);
if (addFields) {
let moreFields = true;
while (moreFields) {
const field = await (inquirer as any).prompt([
{ type: 'input', name: 'name', message: 'Nombre del campo:' },
{ type: 'list', name: 'type', message: 'Tipo:', choices: ['string', 'number', 'boolean', 'date'] },
{ type: 'input', name: 'description', message: 'Descripcion:' },
{ type: 'input', name: 'promptHint', message: 'Hint para el LLM:' },
]);
fields.push(field);
const { more } = await (inquirer as any).prompt([{ type: 'confirm', name: 'more', message: 'Agregar otro campo?', default: false }]);
moreFields = more;
}
}
services.push({
intent: svc.intent,
reference: svc.reference,
enabled: true,
method: svc.method,
endpoint: svc.endpoint,
requiredFields: fields,
headers: {},
bodyTemplate: {},
responseMapping: {},
responseMessage: svc.responseMessage,
responseConditions: [],
});
const { more } = await (inquirer as any).prompt([{ type: 'confirm', name: 'more', message: 'Agregar otro servicio?', default: false }]);
addMore = more;
}
}
}
// Acciones
const actions: AgentAction[] = [];
if (useToolCalling) {
const { addActions } = await (inquirer as any).prompt([{
type: 'confirm',
name: 'addActions',
message: 'Agregar acciones (tag, stage, asignar agente)?',
default: false,
}]);
if (addActions) {
let addMore = true;
while (addMore) {
console.log(theme.muted('\n Nueva accion:'));
const act = await (inquirer as any).prompt([
{ type: 'input', name: 'intent', message: 'Nombre/intent de la accion:', validate: (v: string) => v.length > 0 || 'Requerido' },
{ type: 'input', name: 'reference', message: 'Palabras clave de referencia:', default: '' },
{
type: 'list', name: 'actionType', message: 'Tipo de accion:',
choices: [
{ name: 'Agendar evento', value: 'action.event.add' },
{ name: 'Actualizar evento (reagendar)', value: 'action.event.update' },
{ name: 'Listar eventos', value: 'action.event.list' },
{ name: 'Eliminar evento (cancelar)', value: 'action.event.delete' },
{ name: 'Agregar tag', value: 'action.tag' },
{ name: 'Cambiar stage', value: 'action.stage' },
{ name: 'Derivar a agente humano', value: 'action.agentShutDown' },
{ name: 'Marcar como resuelto', value: 'action.solved' },
{ name: 'Asignar agente', value: 'action.asign' },
{ name: 'Segmentacion', value: 'action.segmentation' },
],
},
{ type: 'input', name: 'actionValue', message: 'Valor de la accion:', default: '' },
{ type: 'input', name: 'responseMessage', message: 'Mensaje de respuesta:', default: '' },
]);
actions.push({
intent: act.intent,
reference: act.reference,
enabled: true,
requiredFields: [],
responseMessage: act.responseMessage,
action: [{ type: act.actionType, value: act.actionValue }],
});
const { more } = await (inquirer as any).prompt([{ type: 'confirm', name: 'more', message: 'Agregar otra accion?', default: false }]);
addMore = more;
}
}
}
// Paso 6: AI Provider
console.log(theme.bold('\n Paso 6/8: Proveedor de IA'));
const { configureAI } = await (inquirer as any).prompt([{
type: 'confirm',
name: 'configureAI',
message: 'Configurar proveedor de IA personalizado?',
default: false,
}]);
const aiProviders: AIProvider[] = [];
if (configureAI) {
const ai = await (inquirer as any).prompt([
{ type: 'list', name: 'provider', message: 'Proveedor:', choices: ['openai', 'claude', 'gemini'] },
]);
const models = MODELS[ai.provider] || MODELS.openai;
const aiConfig = await (inquirer as any).prompt([
{ type: 'list', name: 'model', message: 'Modelo:', choices: models },
{ type: 'password', name: 'apiToken', message: 'API Token:', mask: '*', validate: (v: string) => v.length > 0 || 'Requerido' },
{ type: 'number', name: 'temperature', message: 'Temperatura (0-2):', default: 0.7 },
{ type: 'number', name: 'maxTokens', message: 'Max tokens (1024-16384):', default: 4096 },
]);
aiProviders.push({
provider: ai.provider,
model: aiConfig.model,
apiToken: aiConfig.apiToken,
temperature: aiConfig.temperature,
maxTokens: aiConfig.maxTokens,
isDefault: true,
});
}
// Paso 7: Canal WhatsApp
console.log(theme.bold('\n Paso 7/8: Canal WhatsApp'));
const channels: AgentChannel[] = [];
const { addWhatsApp } = await (inquirer as any).prompt([{
type: 'confirm',
name: 'addWhatsApp',
message: 'Conectar a un numero de WhatsApp?',
default: false,
}]);
if (addWhatsApp) {
const wa = await (inquirer as any).prompt([
{ type: 'input', name: 'key', message: 'Numero de WhatsApp (con codigo de pais):', validate: (v: string) => v.length > 0 || 'Requerido' },
{ type: 'confirm', name: 'multianswer', message: 'Permitir multiples respuestas?', default: false },
]);
channels.push({ channel: 'whatsapp', key: wa.key, multianswer: wa.multianswer });
}
// Paso 8: Ejemplos
console.log(theme.bold('\n Paso 8/8: Ejemplos de conversacion'));
const examples: AgentExample[] = [];
const { addExamples } = await (inquirer as any).prompt([{
type: 'confirm',
name: 'addExamples',
message: 'Agregar ejemplos de conversacion?',
default: false,
}]);
if (addExamples) {
let addMore = true;
while (addMore) {
const ex = await (inquirer as any).prompt([
{ type: 'input', name: 'user', message: 'Mensaje del usuario:' },
{ type: 'input', name: 'agent', message: 'Respuesta del agente:' },
]);
examples.push({ value: `Usuario: ${ex.user}\nAgente: ${ex.agent}`, color: 'blue' });
const { more } = await (inquirer as any).prompt([{ type: 'confirm', name: 'more', message: 'Agregar otro ejemplo?', default: false }]);
addMore = more;
}
}
// Armar config final (alineado con agent.config.schema.json del backend)
const config: AgentConfig = {
name: basic.name,
description: basic.description || '',
prompt: basic.prompt,
buffer: basic.buffer,
zone,
color: basic.color,
useToolCalling,
customAIConfig: configureAI,
aiProviders,
instructions: {
tone: personality.tone,
style: personality.style,
personality: personality.personality,
objective: personality.objective,
language: personality.language,
emojis: personality.useEmojis,
},
person: {
name: person.name,
role: person.role,
speaksInFirstPerson: person.firstPerson,
},
fallbacks: {
noAnswer: fallbacks.noAnswer,
serviceError: fallbacks.serviceError,
doNotUnderstand: fallbacks.misunderstanding,
},
services,
actions,
channels,
examples,
tags: [],
};
// Preview
console.log(section('Vista previa de la configuracion'));
console.log(kvPair('Nombre', config.name));
console.log(kvPair('Prompt', config.prompt.substring(0, 80) + '...'));
console.log(kvPair('Tool Calling', config.useToolCalling ? 'Activado' : 'Desactivado'));
console.log(kvPair('Servicios', String(config.services.length)));
console.log(kvPair('Acciones', String(config.actions.length)));
console.log(kvPair('AI Provider', config.customAIConfig ? config.aiProviders[0]?.provider + ' / ' + config.aiProviders[0]?.model : 'Default (Plazbot)'));
console.log(kvPair('Canal WhatsApp', config.channels.length > 0 ? config.channels[0].key : 'No configurado'));
console.log();
return config;
}