Cómo Hacer Que El Flujo De Trabajo Sea Como Se Muestra A Continuación

Conversación de Chat

Mensaje de Prompt de IA

"Eres un asistente de viajes profesional. Operas en tres fases distintas y debes seguir reglas de formato estrictas para cada una.

Por favor, mantén la conversación con el usuario, saluda y muestra el botón según la solicitud del usuario.

FASE 1: Descubrimiento de Países

Desencadenante: Saludo inicial.

Acción: Llamar a get_destinations.

Formato: Comenzar con [TYPE:DESTINATION], seguido de un saludo, luego el JSON sin procesar […].

FASE 2: Descubrimiento de Ciudades

Desencadenante: El usuario selecciona un país.

Acción: Llamar a get_cities.

Formato: Comenzar con [TYPE:CITY], seguido de una breve solicitud de selección, luego el JSON sin procesar […].

FASE 3: Descubrimiento de Paquetes

Desencadenante: El usuario selecciona una ciudad.

Acción: Llamar a tour_packages usando el valor de carga útil.

Instrucción para Obtener ID de Ciudad: Cuando el usuario selecciona una ciudad, recibirás un mensaje con un valor de carga útil. Esta carga útil es el term_id para esa ciudad. Debes usar este valor específico al llamar a la herramienta tour_packages. Si no tienes un term_id, pide al usuario que aclare su selección de ciudad.

Formato: Comenzar con [TYPE:PACKAGES], seguido de una breve confirmación, luego el JSON sin procesar […].

PROTOCOLOS CRÍTICOS:

ENCARPETADOS DE ENRUTAMIENTO: Cada mensaje DEBE comenzar con [TYPE:DESTINATION], [TYPE:CITY], o [TYPE:PACKAGES].

SIN RESÚMENES: Nunca escribas listas conversacionales ni conteos. Solo proporciona la etiqueta, un saludo breve y el JSON sin procesar.

LEGIBLE POR MÁQUINA: El JSON […] debe ser la última parte de cada mensaje.

USO DE HERRAMIENTAS: Siempre usa las herramientas proporcionadas para obtener datos. Nunca improvises paquetes o ciudades."

IMPORTANTE:

  • Devuelve SOLO JSON válido
  • NO incluyas explicaciones
  • NO escapes comillas
  • NO incluyas [TYPE:…] dentro del JSON
  • El resultado debe ser directamente procesable por JSON.parse

Flujo de Trabajo

Analizador Global

const rawOutput = $input.first().json.output || “”;

// =========================
// Ayudante: Devolución estándar
// =========================
function formatResponse(data) {
return [{ json: data }];
}

// =========================
// Detectar TYPE
// =========================
function detectType(text) {
if (text.includes(“[TYPE:DESTINATION]”)) return “DESTINATION”;
if (text.includes(“[TYPE:CITY]”)) return “CITY”;
if (text.includes(“[TYPE:PACKAGES]”)) return “PACKAGES”;
return null;
}

// =========================
// Extraer JSON LIMPIO
// =========================
function extractJSONArray(text) {
if (!text) return null;

// Eliminar etiquetas TYPE
let cleaned = text.replace(/\[TYPE:[A-Z]+\]/g, "").trim();

// Eliminar marcador de posición
if (cleaned.includes("[...]")) return null;

// Obtener límites de JSON
const start = cleaned.indexOf('[');
const end = cleaned.lastIndexOf(']');

if (start === -1 || end === -1) return null;

let jsonString = cleaned.substring(start, end + 1);

// 🔥 Corregir problemas comunes de IA
jsonString = jsonString
    .replace(/\\\//g, "/")     // corregir barras invertidas escapadas
    .replace(/;\s*$/, "")      // eliminar punto y coma al final
    .trim();

try {
    let parsed = JSON.parse(jsonString);

    // Manejar JSON doblemente codificado
    if (typeof parsed === "string") {
        parsed = JSON.parse(parsed);
    }

    return parsed;
} catch (e) {
    throw new Error("Falha al Analizar JSON:\n" + jsonString);
}

}

// =========================
// PRINCIPAL
// =========================
const type = detectType(rawOutput);
const parsed = extractJSONArray(rawOutput);

// =========================
// DESTINATION
// =========================
if (type === “DESTINATION” && parsed) {
return formatResponse({
recipient: { id: “123456” },
message: {
text: “Selecciona un país:”,
quick_replies: parsed[0].data.map(i => ({
content_type: “text”,
title: i.destination,
payload: i.id
}))
}
});
}

// =========================
// CITY
// =========================
if (type === “CITY” && parsed) {
return formatResponse({
recipient: { id: “123456” },
message: {
text: “Selecciona una ciudad:”,
quick_replies: parsed[0].data.map(i => ({
content_type: “text”,
title: i.city || i.name,
payload: i.id
}))
}
});
}

// =========================
// PACKAGES
// =========================
if (type === “PACKAGES” && parsed) {
const packages = parsed[0].data;

const elements = packages.map(pkg => ({
    title: pkg.title,
    subtitle: pkg.subtitle || "Mejor paquete de tour disponible",
    image_url: pkg.image_url,
    buttons: (pkg.buttons || []).map(btn => ({
        type: btn.type === "web_url" ? "web_url" : "postback",
        title: btn.title,
        url: btn.url,
        payload: btn.payload
    }))
}));

return formatResponse({
    recipient: { id: "123456" },
    message: {
        attachment: {
            type: "template",
            payload: {
                template_type: "generic",
                elements: elements
            }
        }
    }
});

}

// =========================
// ALTERNATIVA
// =========================
return formatResponse({
message: {
text: rawOutput || “Ninguna respuesta disponible.”
}
});

Mis Preguntas

  • Me gustaría tener la conversación también y el Agente de IA buscará por sí mismo y dará los resultados.
  • Actualmente solo funciona según el clic del botón y la carga útil.
  • Después de que el usuario haga clic en los detalles del programa, la IA obtendrá datos solo para ese paquete y mantendrá la conversación.

Para que todo funcione, por favor sugiéreme cómo hacer el flujo de trabajo y cómo diseñar el sistema, cuántos Agentes de IA necesito usar y cómo le digo al flujo de trabajo qué herramienta usar cuándo sea necesario.

Por favor ayúdame a hacer una lluvia de ideas o guíame, ¡gracias!

Bien, después de leer tus posts, aquí está mi feedback considerando los requisitos, espero que esto te ayude.

Objetivo y enfoque

  • Construir un flujo conversacional modular y autoaprendiente que pueda obtener datos más allá de los payloads de botones predefinidos.

  • Utilizar un conjunto pequeño de agentes IA especializados (o herramientas) para cubrir descubrimiento, selección, empaquetamiento y conversación narrativa.

  • Orquestar el uso de herramientas de forma declarativa en el flujo de trabajo, para que el sistema elija la herramienta correcta en el momento correcto.


Descripción general del diseño del sistema

  • Componentes principales

    1. Chat Orchestrator (n8n o un microservicio): impulsa el estado de la conversación, enrutando a agentes/herramientas.

    2. Capa de agentes IA: un conjunto pequeño de agentes especializados (Descubrimiento, Ciudad, Paquetes, Narrativa) con alcances delimitados.

    3. Datos y herramientas: get_destinations, get_cities, tour_packages y cualquier fuente de datos externa. Todo accesible a través de APIs estables.

    4. Almacén de estado y contexto: almacena datos de sesión del usuario, país/ciudad elegidos y paquete seleccionado, además de IDs para trazabilidad.

    5. Formateador de mensajes: aplica los encabezados de enrutamiento [TYPE:DESTINATION]/[TYPE:CITY]/[TYPE:PACKAGES] y el payload JSON final.

    6. Observabilidad: logs estructurados, IDs de rastreo y dashboards de errores.

  • Patrón de flujo de datos

    1. Usuario saluda → El agente de descubrimiento proporciona destinos.

    2. Usuario selecciona un destino → El agente de ciudad proporciona ciudades para ese país.

    3. Usuario selecciona una ciudad → El agente de paquetes obtiene los paquetes; la conversación continúa con detalles del paquete.

    4. Tras la selección del paquete, el sistema obtiene datos detallados del paquete y actualiza la conversación con contenido enriquecido.


Diseño y roles del agente IA

  • Agente A: Descubrimiento

    • Propósito: obtener y presentar destinos; mantener el payload JSON como la única fuente de verdad.

    • Salida: encabezado [TYPE:DESTINATION] + saludo + JSON sin procesar.

  • Agente B: Ciudad

    • Propósito: obtener ciudades para un destino elegido; proporcionar indicaciones concisas para seleccionar una ciudad.

    • Salida: encabezado [TYPE:CITY] + indicación breve + JSON sin procesar.

  • Agente C: Paquetes

    • Propósito: obtener paquetes para una ciudad cuando se selecciona una ciudad; proporcionar detalles compactos y fáciles de escanear.

    • Salida: encabezado [TYPE:PACKAGES] + confirmación breve + JSON sin procesar.

  • Agente D: Narrativa/Conversación

    • Propósito: generar diálogos humanoides que hagan referencia al destino/ciudad/paquete elegido y guíen al usuario.

    • Salida: mensajes en lenguaje natural guiados por el contexto actual, mientras se emite el encabezado TYPE requerido cuando sea necesario.

  • Agente E: Verificación y seguridad

    • Propósito: garantizar la precisión de los datos, prevenir alucinaciones, aplicar privacidad de datos y validar la integridad del payload en todos los pasos.
  • Opcional: Agente de enriquecimiento de datos

    • Propósito: obtener metadatos adicionales (estacionalidad, clima, consejos locales) para enriquecer las descripciones de paquetes.
  • Comienza con 4 agentes centrales (Descubrimiento, Ciudad, Paquetes, Narrativa) más un agente de verificación ligero. Puedes añadir enriquecimiento más tarde.

Cómo el flujo de trabajo decide qué herramienta/agente usar

  • Implementa una capa de enrutamiento en el Orchestrator:

    • Si no hay TYPE presente o saludo inicial → Descubrimiento.

    • Si el usuario seleccionó un país → Ciudad.

    • Si el usuario seleccionó una ciudad → Paquetes.

    • Si el usuario solicita narrativa o más detalle → Narrativa.

    • Si hay integridad de datos o datos sensibles involucrados → Verificación.

  • Usa transiciones de estado explícitas:

    • state: { phase: “discovery” | “city” | “packages” | “narrative”, country_id, city_id, package_id, user_id }
  • Define un registro centralizado de herramientas con:

    • nombre, tipo (llamada API, función local, LLM), esquema de entrada, esquema de salida, política de errores.

Esquema de conversación y formato

  • Mantener el encabezado de enrutamiento requerido en cada mensaje:

    • [TYPE:DESTINATION]

    • [TYPE:CITY]

    • [TYPE:PACKAGES]

  • Asegurar que el payload JSON legible por máquina final sea el último bloque del mensaje.

  • Ejemplo de flujo de conversación (nivel alto)

    1. Descubrimiento:

      • Salida: [TYPE:DESTINATION] + saludo + JSON de destinos sin procesar
    2. Ciudad:

      • Salida: [TYPE:CITY] + indicación + JSON de ciudades sin procesar
    3. Paquetes:

      • Salida: [TYPE:PACKAGES] + confirmación + JSON de paquetes sin procesar
    4. Narrativa:

      • Salida: mensajes en lenguaje natural con contexto, o una narrativa detallada del paquete, aún etiquetada cuando sea requerido.
  • Persistencia y trazabilidad

    • Adjuntar un session_id y un request_id a cada mensaje para correlacionar pasos.

Plano de orquestación de flujo de trabajo (orientado a n8n)

  • Distribución de nodos (bloques modulares)

    1. Trigger: webhook HTTP o de chat para iniciar una sesión.

    2. State Loader: cargar contexto de sesión desde un almacén de datos.

    3. AI Gateway: seleccionar el agente apropiado según la fase.

    4. Agent Call: invocar get_destinations / get_cities / tour_packages o contenido narrativo.

    5. Response Formatter: envolver con el encabezado [TYPE:*] correcto y JSON final.

    6. State Saver: persistir contexto actualizado y selecciones.

    7. Output: enviar al usuario, registrar la interacción.

  • Contratos de datos

    • Destinations API devuelve: [{ id, country, name, description, image }, …]

    • Cities API devuelve: [{ id, city, country_id, region }, …]

    • Packages API devuelve: [{ id, title, summary, image_url, price, details }, …]

  • Manejo de errores

    • Política de reintentos con retroceso exponencial para errores transitorios de API.

    • Mensajes de fallback si las APIs fallan, con un ID de rastreo claro.

    • Validación en cada paso para asegurar que los campos requeridos existan antes de proceder.

  • Seguridad e aislamiento

    • Cada contenedor usa una red dedicada y una clave API limitada al agente.

    • Secretos gestionados por un vault; rotar periódicamente.

    • Privilegio mínimo en manifiestos de Docker Compose o Kubernetes.