{
  "name": "Squad de ventas · 4 agentes en cadena (workshop Milton Suárez)",
  "nodes": [
    {
      "parameters": {
        "content": "## Squad de ventas\n### 4 agentes en cadena\n\nUn squad **no** es un agente grande.\nSon varios roles estrechos, cada uno\ncon su prompt, su modelo y su tope.\n\n**El orden lo fijas tú.** Aquí no hay\nun jefe que decida sobre la marcha:\nes un workflow con agentes dentro.\nEsa es la diferencia con el SDK.\n\n---\n**Antes de ejecutar:**\n1. Abre cualquier nodo morado y\n   conecta OpenRouter (se comparte\n   entre los cuatro)\n2. Abre el nodo de Brevo y crea la\n   credencial Header Auth `api-key`\n3. Pulsa «Execute workflow» y abre\n   la URL del formulario\n\n**Pon una empresa real** en el\nformulario: el investigador la busca\nde verdad en la web.\n\nCoste de una corrida: **~$0,03-0,08**\n\n---\nMilton Suárez · recursos.miltonsuarez.com",
        "height": 620,
        "width": 380,
        "color": 7
      },
      "id": "nota-portada",
      "name": "Nota portada",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -460,
        40
      ]
    },
    {
      "parameters": {
        "content": "## Un modelo distinto por agente — esto es el 80% del ahorro\n\n| Agente | Modelo | Precio /1M | Por qué |\n|---|---|---|---|\n| Analista | `claude-haiku-4.5` | $1 / $5 | Solo ordena datos que ya le llegan. No hay criterio que pagar. |\n| Investigador | `claude-sonnet-5:online` | $2 / $10 | El sufijo **`:online`** le da búsqueda web real sin otra credencial. |\n| Redactor | `claude-opus-5` | $5 / $25 | Es el único que escribe el artefacto final. Aquí sí pagas criterio. |\n| Revisor | `claude-sonnet-5` | $2 / $10 | Verificar es más fácil que redactar. |\n\n**5× de diferencia** entre el barato y el caro. Poner Opus en los cuatro cuesta el triple y no mejora el resultado.",
        "height": 300,
        "width": 1180,
        "color": 4
      },
      "id": "nota-modelos",
      "name": "Nota modelos",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        420,
        700
      ]
    },
    {
      "parameters": {
        "content": "## Ojo: el contexto no viaja solo\n\nCada nodo solo recibe la salida\ndel **anterior**. El redactor necesita\nademás la investigación, y el revisor\nnecesita el borrador **y** las fuentes.\n\nPor eso sus prompts usan expresiones\nque alcanzan nodos concretos:\n\n`{{ $('2 - Investigador').item.json.output }}`\n\nSi te olvidas de esto, el revisor\naprueba un texto sin poder verificarlo.",
        "height": 300,
        "width": 400,
        "color": 3
      },
      "id": "nota-contexto",
      "name": "Nota contexto",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1980,
        40
      ]
    },
    {
      "parameters": {
        "content": "## Nadie envía solo\n\nEl squad termina en un **borrador de\ncampaña en Brevo**. La aprobación es\nhumana, siempre.\n\nEs la salvaguarda más barata que\nexiste y la primera que se quita\npor ir rápido.\n\n**Credencial:** Header Auth\n· Name: `api-key`\n· Value: `xkeysib-...`\n\nNo es Bearer: Brevo usa su propio\nheader.\n\nAntes de activar el nodo, rellena\nsender y lista en «Señal del CRM»\no la API responde 400.",
        "height": 300,
        "width": 380,
        "color": 6
      },
      "id": "nota-humano",
      "name": "Nota humano",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        2420,
        40
      ]
    },
    {
      "parameters": {
        "mode": "raw",
        "jsonOutput": "={{ JSON.stringify({\n  empresa: $json['Empresa'],\n  sector: $json['Sector'],\n  pais: $json['Pais'],\n  plan: $json['Plan y dias transcurridos'],\n  senal_de_uso: $json['Que hizo y que no hizo'],\n  contacto: $json['Con quien hablamos'],\n  sender_email: $json['Remitente verificado en Brevo'],\n  brevo_list_id: $json['ID de la lista de Brevo']\n}, null, 2) }}",
        "options": {}
      },
      "id": "senal-del-crm",
      "name": "Senal del CRM",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.3,
      "position": [
        200,
        300
      ],
      "notes": "Traduce el formulario a la ficha que lee el analista. Cambia la empresa del formulario y vuelve a enviar: el squad entero se readapta."
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "={{ JSON.stringify($json, null, 2) }}",
        "hasOutputParser": false,
        "options": {
          "systemMessage": "Eres analista de señales de producto en una SaaS B2B de encuestas.\n\nTu único trabajo es ORDENAR la señal que te llega. No opinas, no investigas\ny no escribes mensajes de venta.\n\nDevuelve exactamente este formato, sin añadir nada más:\n\nCUENTA: [empresa, sector, país, tamaño]\nDÓNDE ESTÁ: [plan y días transcurridos]\nQUÉ HIZO: [acciones registradas]\nQUÉ NO HIZO: [el hueco entre lo que hizo y obtener valor]\nHIPÓTESIS DE FRICCIÓN: [una sola frase, la barrera más probable]\nPREGUNTA ABIERTA: [lo único que no puedes saber con estos datos]\n\nSi un dato no está en la señal, escribe \"no consta\". Nunca lo rellenes.",
          "maxIterations": 6
        }
      },
      "id": "1---analista-de-senales",
      "name": "1 - Analista de senales",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2,
      "position": [
        460,
        300
      ]
    },
    {
      "parameters": {
        "model": "anthropic/claude-haiku-4.5",
        "options": {
          "maxTokens": 1200
        }
      },
      "id": "haiku-4.5---barato",
      "name": "Haiku 4.5 - barato",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        460,
        540
      ]
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=Ordena la investigación sobre esta cuenta:\n\n{{ $json.output }}",
        "hasOutputParser": false,
        "options": {
          "systemMessage": "Eres analista de research B2B. Reúnes evidencia; no rediseñas la estrategia\nni escribes el mensaje.\n\nTienes búsqueda web. Úsala.\n\n- Busca la empresa y su sector. Quédate con 4-6 hallazgos que tengan un dato\n  concreto y sean útiles para un primer contacto.\n- Cada hallazgo, en una línea:\n  [FUERTE|DÉBIL] Afirmación — cifra o hecho — fuente — URL\n- [FUERTE] = fuente primaria (la propia empresa, un regulador, un informe con\n  metodología). [DÉBIL] = blog de un proveedor, nota de prensa, agregador.\n- Si no encuentras fuente para algo, DESCÁRTALO y anótalo en \"no verificado\".\n  Nunca completes un hueco con lo que te parece probable.\n- No inventes URLs. Si no visitaste la página, no la cites.\n\nCierra con:\nNO VERIFICADO: [lo que buscaste y no pudiste sostener]",
          "maxIterations": 10
        }
      },
      "id": "2---investigador",
      "name": "2 - Investigador",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2,
      "position": [
        780,
        300
      ]
    },
    {
      "parameters": {
        "model": "anthropic/claude-sonnet-5:online",
        "options": {
          "maxTokens": 1600
        }
      },
      "id": "sonnet-5-online---busca-web",
      "name": "Sonnet 5 online - busca web",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        780,
        540
      ]
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=FICHA DE LA CUENTA\n{{ $('1 - Analista de senales').item.json.output }}\n\nINVESTIGACIÓN VERIFICADA\n{{ $json.output }}\n\nEscribe el primer contacto.",
        "hasOutputParser": false,
        "options": {
          "systemMessage": "Eres responsable de outbound en una SaaS B2B de encuestas. Escribes a\nresponsables de Insights y de research, gente que recibe veinte correos\niguales al día.\n\nEscribes el primer contacto. Reglas que no se negocian:\n\n- Usa SOLO datos que aparezcan en la investigación que te pasan. Cero cifras\n  de tu cabeza. Si algo viene marcado [DÉBIL], no lo uses como argumento\n  central.\n- Nada de \"espero que estés bien\", \"me pongo en contacto\" ni elogios genéricos.\n- 90-130 palabras. Asunto de 6 palabras como máximo.\n- Estructura: una línea que demuestre que entendiste su situación concreta,\n  el hueco observado, qué haría distinto tu producto en ESE hueco, y una\n  petición pequeña (una respuesta, no una demo de 45 minutos).\n- No prometas resultados numéricos. No cites clientes.\n\nDevuelve:\nASUNTO: ...\nCUERPO: ...\nDATOS QUE USÉ: [lista de los hallazgos concretos en los que te apoyaste]",
          "maxIterations": 6
        }
      },
      "id": "3---redactor",
      "name": "3 - Redactor",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2,
      "position": [
        1100,
        300
      ]
    },
    {
      "parameters": {
        "model": "anthropic/claude-opus-5",
        "options": {
          "maxTokens": 1400
        }
      },
      "id": "opus-5---el-que-escribe",
      "name": "Opus 5 - el que escribe",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        1100,
        540
      ]
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=INVESTIGACIÓN (única fuente de verdad)\n{{ $('2 - Investigador').item.json.output }}\n\nBORRADOR A AUDITAR\n{{ $json.output }}\n\nAudítalo.",
        "hasOutputParser": false,
        "options": {
          "systemMessage": "Eres editor crítico. NO reescribes el mensaje: lo auditas. Si te dan ganas de\nmejorarlo, aguántate y descríbelo como problema.\n\nRecorre el borrador afirmación por afirmación:\n\n1. Toda cifra, nombre o hecho debe estar en la investigación. Lo que no esté,\n   márcalo [INVENTADO] y cítalo literal.\n2. Lo que se apoye en un hallazgo [DÉBIL], márcalo [FLOJO].\n3. Comprueba: ¿90-130 palabras? ¿asunto de 6 o menos? ¿la petición es pequeña?\n   ¿promete resultados numéricos? ¿cita clientes?\n4. ¿El primer párrafo podría enviarse a cualquier otra empresa? Si sí, es\n   genérico: márcalo.\n\nDevuelve problemas numerados de mayor a menor gravedad, y cierra con una\nlínea exacta:\n\nVEREDICTO: PUBLICABLE\no\nVEREDICTO: REQUIERE CAMBIOS — [el motivo en una frase]\n\nSi no hay ni un problema, dilo y explica qué comprobaste. No inventes\nproblemas para parecer útil.",
          "maxIterations": 6
        }
      },
      "id": "4---revisor",
      "name": "4 - Revisor",
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2,
      "position": [
        1420,
        300
      ]
    },
    {
      "parameters": {
        "model": "anthropic/claude-sonnet-5",
        "options": {
          "maxTokens": 1200
        }
      },
      "id": "sonnet-5---verifica",
      "name": "Sonnet 5 - verifica",
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        1420,
        540
      ]
    },
    {
      "parameters": {
        "mode": "raw",
        "jsonOutput": "={{ JSON.stringify({\n  empresa: $('Senal del CRM').item.json.empresa,\n  ficha: $('1 - Analista de senales').item.json.output,\n  investigacion: $('2 - Investigador').item.json.output,\n  borrador: $('3 - Redactor').item.json.output,\n  auditoria: $json.output,\n  aprobado_por: null\n}, null, 2) }}",
        "options": {}
      },
      "id": "paquete-para-revision-humana",
      "name": "Paquete para revision humana",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.3,
      "position": [
        1740,
        300
      ],
      "notes": "Los cuatro artefactos juntos. Abre este nodo en la demo: se ve lo que produjo cada agente por separado, que es justo lo que hace auditable un squad."
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.brevo.com/v3/emailCampaigns",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpHeaderAuth",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ (() => {\n  const salida = $('3 - Redactor').item.json.output;\n  const asunto = (salida.match(/ASUNTO:\\s*(.+)/) || [null,'Sin subject'])[1].trim();\n  const cuerpo = (salida.match(/CUERPO:\\s*([\\s\\S]*?)(?=\\nDATOS QUE|$)/) || [null,salida])[1].trim();\n  const parrafos = cuerpo.split(/\\n\\s*\\n/)\n    .map(p => '<p style=\"margin:0 0 16px;font:16px/1.6 Arial,sans-serif;color:#050505\">'\n              + p.replace(/\\n/g,'<br>') + '</p>').join('');\n  const html = '<!DOCTYPE html><html><body style=\"margin:0;padding:0;background:#f4f4f4\">'\n    + '<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\"><tr><td align=\"center\">'\n    + '<table width=\"600\" cellpadding=\"0\" cellspacing=\"0\" border=\"0\" style=\"max-width:600px;background:#ffffff\">'\n    + '<tr><td style=\"padding:32px 28px\">' + parrafos\n    + '<p style=\"margin:28px 0 0;font:12px/1.5 Arial,sans-serif;color:#737373\">'\n    + '<a href=\"{{ unsubscribe }}\" style=\"color:#737373\">Darse de baja</a></p>'\n    + '</td></tr></table></td></tr></table></body></html>';\n  return JSON.stringify({\n    name: 'Squad ventas — ' + $('Senal del CRM').item.json.empresa + ' — ' + $now.toFormat('LL-dd HH:mm'),\n    subject: asunto,\n    sender: { name: 'GrowthPilot', email: $('Senal del CRM').item.json.sender_email },\n    recipients: { listIds: [ Number($('Senal del CRM').item.json.brevo_list_id) ] },\n    htmlContent: html,\n    previewText: asunto\n  });\n})() }}",
        "options": {}
      },
      "id": "brevo-crear-campana-draft",
      "name": "Brevo: crear campana draft",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2060,
        300
      ],
      "disabled": true,
      "notes": "DESACTIVADO a proposito: la primera corrida no crea nada. Activalo (clic derecho > Activate) cuando el borrador te convenza. Credencial: Header Auth con Name 'api-key' y Value tu clave xkeysib-... Crea la campana como DRAFT: nunca envia."
    },
    {
      "parameters": {
        "formTitle": "Cuenta nueva — brief de primer contacto",
        "formDescription": "Pon una empresa REAL: el investigador la busca en la web. Con un nombre inventado devolvera NO ENCONTRADO y el redactor se quedara sin material.",
        "formFields": {
          "values": [
            {
              "fieldLabel": "Empresa",
              "fieldType": "text",
              "requiredField": true,
              "placeholder": "Ej: Savanta, Netquest, la consultora que tengas en el CRM"
            },
            {
              "fieldLabel": "Sector",
              "fieldType": "text",
              "requiredField": true,
              "placeholder": "Ej: consultoria de investigacion de mercados"
            },
            {
              "fieldLabel": "Pais",
              "fieldType": "text",
              "requiredField": true,
              "placeholder": "Ej: Reino Unido"
            },
            {
              "fieldLabel": "Plan y dias transcurridos",
              "fieldType": "text",
              "requiredField": true,
              "placeholder": "Ej: Trial 14 dias, lleva 6"
            },
            {
              "fieldLabel": "Que hizo y que no hizo",
              "fieldType": "textarea",
              "requiredField": true,
              "placeholder": "Ej: creo 3 encuestas, publico 1, no ha recogido ninguna respuesta"
            },
            {
              "fieldLabel": "Con quien hablamos",
              "fieldType": "text",
              "requiredField": true,
              "placeholder": "Ej: Responsable de Insights"
            },
            {
              "fieldLabel": "Remitente verificado en Brevo",
              "fieldType": "email",
              "requiredField": true,
              "placeholder": "tu@tudominio.com"
            },
            {
              "fieldLabel": "ID de la lista de Brevo",
              "fieldType": "number",
              "requiredField": true,
              "placeholder": "1"
            }
          ]
        },
        "options": {}
      },
      "id": "senal-de-la-cuenta",
      "name": "Senal de la cuenta",
      "type": "n8n-nodes-base.formTrigger",
      "typeVersion": 2,
      "position": [
        -40,
        300
      ],
      "notes": "Al pulsar Execute workflow, n8n te da la URL de prueba del formulario. Abrela, rellenala con una cuenta real y envia: eso dispara el squad."
    }
  ],
  "connections": {
    "Haiku 4.5 - barato": {
      "ai_languageModel": [
        [
          {
            "node": "1 - Analista de senales",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Sonnet 5 online - busca web": {
      "ai_languageModel": [
        [
          {
            "node": "2 - Investigador",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Opus 5 - el que escribe": {
      "ai_languageModel": [
        [
          {
            "node": "3 - Redactor",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Sonnet 5 - verifica": {
      "ai_languageModel": [
        [
          {
            "node": "4 - Revisor",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Senal del CRM": {
      "main": [
        [
          {
            "node": "1 - Analista de senales",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "1 - Analista de senales": {
      "main": [
        [
          {
            "node": "2 - Investigador",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "2 - Investigador": {
      "main": [
        [
          {
            "node": "3 - Redactor",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "3 - Redactor": {
      "main": [
        [
          {
            "node": "4 - Revisor",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "4 - Revisor": {
      "main": [
        [
          {
            "node": "Paquete para revision humana",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Paquete para revision humana": {
      "main": [
        [
          {
            "node": "Brevo: crear campana draft",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Senal de la cuenta": {
      "main": [
        [
          {
            "node": "Senal del CRM",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "pinData": {},
  "settings": {
    "executionOrder": "v1"
  },
  "tags": []
}
