Pyragogy AI Village - Maestro de orquestación (Arquitectura profunda V2)

Avanzado

Este es unEngineering, AIflujo de automatización del dominio deautomatización que contiene 35 nodos.Utiliza principalmente nodos como If, Wait, Merge, Slack, Start, combinando tecnología de inteligencia artificial para lograr automatización inteligente. Usar orquestación multiagente de GPT-4o y revisión manual para generar manuales de colaboración

Requisitos previos
  • Bot Token de Slack o URL de Webhook
  • Personal Access Token de GitHub
  • Clave de API de OpenAI
  • Punto final de HTTP Webhook (n8n generará automáticamente)
  • Información de conexión de la base de datos PostgreSQL
Vista previa del flujo de trabajo
Visualización de las conexiones entre nodos, con soporte para zoom y panorámica
Exportar flujo de trabajo
Copie la siguiente configuración JSON en n8n para importar y usar este flujo de trabajo
{
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "name": "Pyragogy AI Village - Orchestrazione Master (Architettura Profonda V2)",
  "tags": [
    {
      "id": "pyragogy",
      "name": "Pyragogy"
    },
    {
      "id": "multi-agent",
      "name": "Multi-Agent"
    },
    {
      "id": "orchestration",
      "name": "Orchestration"
    },
    {
      "id": "human-in-loop",
      "name": "Human-in-Loop"
    }
  ],
  "nodes": [
    {
      "name": "Inicio",
      "type": "n8n-nodes-base.start",
      "position": [
        50,
        300
      ],
      "parameters": {},
      "typeVersion": 1,
      "id": "Inicio-0"
    },
    {
      "name": "Webhook Trigger",
      "type": "n8n-nodes-base.webhook",
      "position": [
        250,
        300
      ],
      "webhookId": "pyragogy-master-trigger",
      "parameters": {
        "path": "pyragogy/process",
        "options": {},
        "httpMethod": "POST"
      },
      "typeVersion": 1,
      "id": "Webhook-Trigger-1"
    },
    {
      "name": "Verificar Conexión a BD",
      "type": "n8n-nodes-base.postgres",
      "position": [
        450,
        300
      ],
      "parameters": {
        "query": "SELECT 1; -- Verifica connessione DB",
        "options": {},
        "operation": "executeQuery"
      },
      "credentials": {
        "postgres": {
          "id": "pyragogy-postgres",
          "name": "Postgres Pyragogy DB"
        }
      },
      "typeVersion": 1,
      "id": "Verificar-Conexi-n-a-BD-2"
    },
    {
      "name": "Meta-Orquestador",
      "type": "n8n-nodes-base.openAi",
      "position": [
        650,
        300
      ],
      "parameters": {
        "model": "gpt-4o",
        "options": {
          "response_format": {
            "type": "json_object"
          }
        },
        "messages": [
          {
            "role": "system",
            "content": "You are the Meta-orchestrator of the Pyragogy AI Village. Your task is to analyze the input and determine the optimal agent sequence for processing. Consider the input type, complexity, and goals. Available agents: Summarizer, Synthesizer, Peer Reviewer, Sensemaking Agent, Prompt Engineer, Onboarding/Explainer, Archivist. Return a JSON array of agent names in the order they should run, e.g., [\"Summarizer\", \"Synthesizer\", \"Peer Reviewer\", \"Archivist\"]. Include \"Archivist\" last if persistence is needed."
          },
          {
            "role": "user",
            "content": "Input Data:\n{{ JSON.stringify($json.body) }}"
          }
        ],
        "resource": "chat",
        "authentication": "apiKey"
      },
      "credentials": {
        "openAiApi": {
          "id": "pyragogy-openai",
          "name": "OpenAI Pyragogy"
        }
      },
      "typeVersion": 1,
      "id": "Meta-Orquestador-3"
    },
    {
      "name": "Analizar Plan de Orquestación",
      "type": "n8n-nodes-base.function",
      "position": [
        850,
        300
      ],
      "parameters": {
        "functionCode": "// Analizza il piano di orchestrazione e imposta per il looping\nlet rawPlan = $json.choices[0].message.content;\nlet plan;\n\ntry {\n  plan = JSON.parse(rawPlan);\n} catch (e) {\n  // Se il parsing fallisce, assumi che sia una stringa di array grezza\n  plan = rawPlan;\n}\n\n// Estrai in modo sicuro la sequenza degli agenti:\n// Se 'plan' è un oggetto e ha una chiave 'agents', usala.\n// Altrimenti, se 'plan' è un array, usalo direttamente.\n// Altrimenti, predefinisci un array vuoto.\nconst agentSequence = Array.isArray(plan) ? plan : (plan && plan.agents && Array.isArray(plan.agents) ? plan.agents : []);\n\n// Memorizza la sequenza e l'indice corrente per il loop\n$workflow.agentSequence = agentSequence;\n$workflow.currentAgentIndex = 0;\n$workflow.redraftLoopCount = 0; // Inizializza il contatore dei cicli di rielaborazione\n\n// Passa i dati di input al primo agente, assicurandosi che $items[0].json.body esista\nconst initialInput = $items[0] && $items[0].json && $items[0].json.body ? $items[0].json.body : {};\n\nreturn [{ json: { ...initialInput, agentToRun: agentSequence[0] || null } }];"
      },
      "typeVersion": 1,
      "id": "Analizar-Plan-de-Orquestaci-n-4"
    },
    {
      "name": "¿Más Agentes por Ejecutar?",
      "type": "n8n-nodes-base.if",
      "position": [
        1050,
        300
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $workflow.currentAgentIndex < $workflow.agentSequence.length }}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1,
      "id": "-M-s-Agentes-por-Ejecutar--5"
    },
    {
      "name": "Preparar Entrada de Agente",
      "type": "n8n-nodes-base.function",
      "position": [
        1250,
        200
      ],
      "parameters": {
        "functionCode": "// Ottieni il nome dell'agente corrente\nconst agentName = $workflow.agentSequence[$workflow.currentAgentIndex];\n\n// Prepara i dati per l'esecuzione dell'agente\n// Passa l'output del passo precedente (o l'input iniziale).\n// Se stiamo rielaborando, l'input dell'agente dovrebbe includere il feedback di rielaborazione.\nconst previousOutput = $json.output || $json.body.input; // Assumendo che l'output dell'agente sia memorizzato nella chiave 'output'\nconst agentInput = $json.redraftInput || previousOutput; // Usa redraftInput se presente, altrimenti previousOutput\n\nreturn [{ json: { ...$json, agentToRun: agentName, agentInput: agentInput } }];"
      },
      "typeVersion": 1,
      "id": "Preparar-Entrada-de-Agente-6"
    },
    {
      "name": "Enrutar Agentes con Switch",
      "type": "n8n-nodes-base.switch",
      "position": [
        1450,
        200
      ],
      "parameters": {
        "mode": "json",
        "value": "={{ $json.agentToRun }}",
        "conditions": [
          {
            "type": "string",
            "value": "Summarizer"
          },
          {
            "type": "string",
            "value": "Synthesizer"
          },
          {
            "type": "string",
            "value": "Peer Reviewer"
          },
          {
            "type": "string",
            "value": "Sensemaking Agent"
          },
          {
            "type": "string",
            "value": "Prompt Engineer"
          },
          {
            "type": "string",
            "value": "Onboarding/Explainer"
          },
          {
            "type": "string",
            "value": "Archivist"
          }
        ]
      },
      "typeVersion": 1,
      "id": "Enrutar-Agentes-con-Switch-7"
    },
    {
      "name": "Agente Resumidor",
      "type": "n8n-nodes-base.openAi",
      "position": [
        1650,
        0
      ],
      "parameters": {
        "model": "gpt-4o",
        "options": {},
        "messages": [
          {
            "role": "system",
            "content": "You are the Summarizer Agent. Summarize the provided text into 3 key points."
          },
          {
            "role": "user",
            "content": "Text to summarize:\n{{ $json.agentInput }}"
          }
        ],
        "resource": "chat",
        "authentication": "apiKey"
      },
      "credentials": {
        "openAiApi": {
          "id": "pyragogy-openai",
          "name": "OpenAI Pyragogy"
        }
      },
      "typeVersion": 1,
      "id": "Agente-Resumidor-8"
    },
    {
      "name": "Agente Sintetizador",
      "type": "n8n-nodes-base.openAi",
      "position": [
        1650,
        100
      ],
      "parameters": {
        "model": "gpt-4o",
        "options": {},
        "messages": [
          {
            "role": "system",
            "content": "You are the Synthesizer Agent. Synthesize a creative new text from the given key points or input. If provided with 'redraft_feedback', incorporate it to refine the output."
          },
          {
            "role": "user",
            "content": "Input for synthesis:\n{{ $json.agentInput }}\n\n{{ $json.redraftFeedback ? 'Feedback per la rielaborazione: ' + $json.redraftFeedback : '' }}"
          }
        ],
        "resource": "chat",
        "authentication": "apiKey"
      },
      "credentials": {
        "openAiApi": {
          "id": "pyragogy-openai",
          "name": "OpenAI Pyragogy"
        }
      },
      "typeVersion": 1,
      "id": "Agente-Sintetizador-9"
    },
    {
      "name": "Agente Revisor Par",
      "type": "n8n-nodes-base.openAi",
      "position": [
        1650,
        200
      ],
      "parameters": {
        "model": "gpt-4o",
        "options": {
          "response_format": {
            "type": "json_object"
          }
        },
        "messages": [
          {
            "role": "system",
            "content": "You are the Peer Reviewer Agent. Review the provided text, highlight strengths, weaknesses, and provide actionable suggestions for improvement. In your JSON output, include a 'major_issue' boolean flag (true if significant redrafting is needed, false otherwise)."
          },
          {
            "role": "user",
            "content": "Text to review:\n{{ $json.agentInput }}"
          }
        ],
        "resource": "chat",
        "authentication": "apiKey"
      },
      "credentials": {
        "openAiApi": {
          "id": "pyragogy-openai",
          "name": "OpenAI Pyragogy"
        }
      },
      "typeVersion": 1,
      "id": "Agente-Revisor-Par-10"
    },
    {
      "name": "Agente de Sensemaking",
      "type": "n8n-nodes-base.openAi",
      "position": [
        1650,
        300
      ],
      "parameters": {
        "model": "gpt-4o",
        "options": {
          "response_format": {
            "type": "json_object"
          }
        },
        "messages": [
          {
            "role": "system",
            "content": "You are the Sensemaking Agent. Analyze the input, connect it with existing knowledge (context provided), identify patterns, gaps, and suggest new directions. In your JSON output, include a 'major_issue' boolean flag (true if significant redrafting is needed, false otherwise)."
          },
          {
            "role": "user",
            "content": "Input to analyze:\n{{ $json.agentInput }}\n\nContext from DB (if available):\n{{ $json.dbContext }}"
          }
        ],
        "resource": "chat",
        "authentication": "apiKey"
      },
      "credentials": {
        "openAiApi": {
          "id": "pyragogy-openai",
          "name": "OpenAI Pyragogy"
        }
      },
      "typeVersion": 1,
      "id": "Agente-de-Sensemaking-11"
    },
    {
      "name": "Agente Ingeniero de Prompt",
      "type": "n8n-nodes-base.openAi",
      "position": [
        1650,
        400
      ],
      "parameters": {
        "model": "gpt-4o",
        "options": {
          "response_format": {
            "type": "json_object"
          }
        },
        "messages": [
          {
            "role": "system",
            "content": "You are the Prompt Engineer Agent. Analyze the current task context and the next agent in the sequence. Refine or generate an optimal prompt for the next agent. In your JSON output, include a 'major_issue' boolean flag (true if significant redrafting is needed, false otherwise)."
          },
          {
            "role": "user",
            "content": "Current context:\n{{ JSON.stringify($json) }}\nNext agent: {{ $workflow.agentSequence[$workflow.currentAgentIndex + 1] || 'None' }}"
          }
        ],
        "resource": "chat",
        "authentication": "apiKey"
      },
      "credentials": {
        "openAiApi": {
          "id": "pyragogy-openai",
          "name": "OpenAI Pyragogy"
        }
      },
      "typeVersion": 1,
      "id": "Agente-Ingeniero-de-Prompt-12"
    },
    {
      "name": "Agente de Onboarding/Explicador",
      "type": "n8n-nodes-base.openAi",
      "position": [
        1650,
        500
      ],
      "parameters": {
        "model": "gpt-4o",
        "options": {},
        "messages": [
          {
            "role": "system",
            "content": "You are the Onboarding/Explainer Agent. Explain the current process, the result achieved so far, or provide guidance based on the input."
          },
          {
            "role": "user",
            "content": "Explain the following:\n{{ $json.agentInput }}"
          }
        ],
        "resource": "chat",
        "authentication": "apiKey"
      },
      "credentials": {
        "openAiApi": {
          "id": "pyragogy-openai",
          "name": "OpenAI Pyragogy"
        }
      },
      "typeVersion": 1,
      "id": "Agente-de-Onboarding-Explicador-13"
    },
    {
      "name": "Añadir Metadatos del Manual",
      "type": "n8n-nodes-base.function",
      "position": [
        1650,
        600
      ],
      "parameters": {
        "functionCode": "// Prepara i metadati per il contenuto dell'Handbook.\n// Assicurati che l'input originale contenga 'title' e 'tags' o imposta dei valori predefiniti.\nconst title = $json.body.title || 'Untitled Handbook Entry';\nconst tags = $json.body.tags || [];\nconst phase = $json.body.phase || 'draft'; // Fase iniziale, può essere 'final' dopo l'approvazione\nconst rhythm = $json.body.rhythm || 'on-demand'; // Ritmo cognitivo\n\nreturn [{ json: { ...$json, handbookTitle: title, handbookTags: tags, handbookPhase: phase, handbookRhythm: rhythm } }];"
      },
      "typeVersion": 1,
      "id": "A-adir-Metadatos-del-Manual-14"
    },
    {
      "name": "Generar Contenido para Revisión",
      "type": "n8n-nodes-base.function",
      "position": [
        1850,
        600
      ],
      "parameters": {
        "functionCode": "// Prepara il contenuto proposto dall'Archivista per la revisione umana, inclusa la formattazione YAML.\n// Assicurati che l'input dell'agente (il contenuto generato) sia disponibile.\nconst proposedContent = $json.agentInput;\nconst title = $json.handbookTitle;\nconst tags = $json.handbookTags;\nconst phase = $json.handbookPhase;\nconst rhythm = $json.handbookRhythm;\n\n// Costruisci il front-matter YAML\nconst yamlFrontMatter = `---\ntitle: \"${title.replace(/\"/g, '\\\"')}\"\ntags: [${tags.map(t => `\"${t.replace(/\"/g, '\\\"')}\"`).join(', ')}]\nphase: \"${phase}\"\nrhythm: \"${rhythm}\"\n---\n\n`;\n\nconst finalMarkdownContent = yamlFrontMatter + proposedContent;\n\nreturn [{ json: { ...$json, proposedContent: proposedContent, reviewTitle: title, reviewTags: tags, finalMarkdownContent: finalMarkdownContent } }];"
      },
      "typeVersion": 1,
      "id": "Generar-Contenido-para-Revisi-n-15"
    },
    {
      "name": "Generar ID de Revisión",
      "type": "n8n-nodes-base.function",
      "position": [
        2050,
        600
      ],
      "parameters": {
        "functionCode": "// Genera un ID univoco per questa richiesta di revisione.\n// Questo ID verrà usato per correlare la risposta del revisore con questa istanza del workflow.\nconst reviewId = crypto.randomUUID();\n\nreturn [{ json: { ...$json, reviewId: reviewId } }];"
      },
      "typeVersion": 1,
      "id": "Generar-ID-de-Revisi-n-16"
    },
    {
      "name": "Enviar Correo de Solicitud de Revisión",
      "type": "n8n-nodes-base.emailSend",
      "position": [
        2250,
        600
      ],
      "parameters": {
        "html": "<h3>Revisione Contenuto Pyragogy Handbook: {{ $json.reviewTitle }}</h3>\n<p>Ciao revisore,</p>\n<p>È stato proposto un nuovo contenuto per l'Handbook:</p>\n<hr>\n<pre>{{ $json.proposedContent }}</pre>\n<hr>\n<p><strong>Titolo:</strong> {{ $json.reviewTitle }}</p>\n<p><strong>Tags:</strong> {{ $json.reviewTags.join(', ') }}</p>\n<p>Per favore, clicca su uno dei seguenti link per approvare o rifiutare:</p>\n<p><a href=\"your_n8n_url/webhook/pyragogy/review-feedback?reviewId={{ $json.reviewId }}&status=approved\">Approva</a></p>\n<p><a href=\"your_n8n_url/webhook/pyragogy/review-feedback?reviewId={{ $json.reviewId }}&status=rejected\">Rifiuta</a></p>\n<p>Grazie!</p>",
        "text": "Ciao revisore,\n\nÈ stato proposto un nuovo contenuto per l'Handbook:\n\n---\n{{ $json.proposedContent }}\n---\n\nTitolo: {{ $json.reviewTitle }}\nTags: {{ $json.reviewTags.join(', ') }}\n\nPer favore, clicca su uno dei seguenti link per approvare o rifiutare:\n\nApprova: your_n8n_url/webhook/pyragogy/review-feedback?reviewId={{ $json.reviewId }}&status=approved\nRifiuta: your_n8n_url/webhook/pyragogy/review-feedback?reviewId={{ $json.reviewId }}&status=rejected\n\nGrazie!",
        "options": {},
        "subject": "Revisione Contenuto Pyragogy Handbook: {{ $json.reviewTitle }}",
        "toEmail": "human-reviewer@example.com",
        "fromEmail": "your-email@example.com"
      },
      "credentials": {
        "emailSend": {
          "id": "your-email-credential-id",
          "name": "Your Email Credential Name"
        }
      },
      "typeVersion": 1,
      "id": "Enviar-Correo-de-Solicitud-de-Revisi-n-17"
    },
    {
      "name": "Esperar Aprobación Humana",
      "type": "n8n-nodes-base.wait",
      "position": [
        2450,
        600
      ],
      "parameters": {
        "mode": "webhook",
        "timeout": "1h",
        "matchField": "reviewId",
        "matchValue": "={{ $json.reviewId }}",
        "webhookPath": "pyragogy/review-feedback"
      },
      "typeVersion": 1,
      "id": "Esperar-Aprobaci-n-Humana-18"
    },
    {
      "name": "División de Decisión Humana",
      "type": "n8n-nodes-base.if",
      "position": [
        2650,
        600
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.query.status === 'approved' }}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1,
      "id": "Divisi-n-de-Decisi-n-Humana-19"
    },
    {
      "name": "Guardar en handbook_entries",
      "type": "n8n-nodes-base.postgres",
      "position": [
        2850,
        500
      ],
      "parameters": {
        "table": "handbook_entries",
        "values": "={{ $json.reviewTitle || 'Untitled' }}, {{ $json.finalMarkdownContent }}, {{ $json.version || 1 }}, {{ $json.author || 'AI Village' }}, ARRAY[{{ ($json.reviewTags || []).map(t => `'${t}'`).join(',') }}], {{ $json.handbookPhase || 'final' }}, {{ $json.handbookRhythm || 'on-demand' }}",
        "columns": "title, content, version, created_by, tags, phase, rhythm",
        "options": {
          "returning": "id"
        },
        "operation": "insert"
      },
      "credentials": {
        "postgres": {
          "id": "pyragogy-postgres",
          "name": "Postgres Pyragogy DB"
        }
      },
      "typeVersion": 1,
      "id": "Guardar-en-handbook_entries-20"
    },
    {
      "name": "Preparar Datos de Contribución Aprobada",
      "type": "n8n-nodes-base.function",
      "position": [
        3050,
        500
      ],
      "parameters": {
        "functionCode": "// Registra il contributo dell'agente dopo l'approvazione umana\nconst entryId = $json.id; // ID dall'inserimento in handbook_entries\nconst agentName = 'Archivist';\nconst contributionType = 'Archiving (Approved)';\nconst details = { input: $json.proposedContent, metadata: { title: $json.reviewTitle, version: $json.version, tags: $json.reviewTags, phase: $json.handbookPhase, rhythm: $json.handbookRhythm }, reviewStatus: 'approved' };\n\n$items[0].json.contribution = { entryId, agentName, contributionType, details };\nreturn $items;"
      },
      "typeVersion": 1,
      "id": "Preparar-Datos-de-Contribuci-n-Aprobada-21"
    },
    {
      "name": "Guardar Contribución de Agente (Aprobada)",
      "type": "n8n-nodes-base.postgres",
      "position": [
        3250,
        500
      ],
      "parameters": {
        "table": "agent_contributions",
        "values": "={{ $json.contribution.entryId }}, {{ $json.contribution.agentName }}, {{ $json.contribution.contributionType }}, {{ JSON.stringify($json.contribution.details) }}",
        "columns": "entry_id, agent_name, contribution_type, details",
        "options": {},
        "operation": "insert"
      },
      "credentials": {
        "postgres": {
          "id": "pyragogy-postgres",
          "name": "Postgres Pyragogy DB"
        }
      },
      "typeVersion": 1,
      "id": "Guardar-Contribuci-n-de-Agente-Aprobada--22"
    },
    {
      "name": "Generar Ruta de Archivo GitHub",
      "type": "n8n-nodes-base.function",
      "position": [
        3450,
        500
      ],
      "parameters": {
        "functionCode": "// Genera il percorso del file GitHub con slug e timestamp per il versioning.\nconst chapterSlug = ($json.reviewTitle || 'untitled').replace(/[^a-zA-Z0-9]/g, '-').toLowerCase();\nconst timestamp = new Date().toISOString().replace(/[:.-]/g, ''); // Rimuove caratteri non validi per i nomi di file\nconst filePathWithVersion = `content/${chapterSlug}_v${timestamp}.md`;\n\nreturn [{ json: { ...$json, githubFilePath: filePathWithVersion } }];"
      },
      "typeVersion": 1,
      "id": "Generar-Ruta-de-Archivo-GitHub-23"
    },
    {
      "name": "¿GitHub Habilitado?",
      "type": "n8n-nodes-base.if",
      "position": [
        3650,
        500
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $env.GITHUB_ACCESS_TOKEN }}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1,
      "id": "-GitHub-Habilitado--24"
    },
    {
      "name": "Commit a GitHub (Aprobado)",
      "type": "n8n-nodes-base.github",
      "position": [
        3850,
        500
      ],
      "parameters": {
        "owner": "={{ $env.GITHUB_REPOSITORY_OWNER }}",
        "options": {},
        "filePath": "={{ $json.githubFilePath }}",
        "resource": "file",
        "operation": "createUpdate",
        "repository": "={{ $env.GITHUB_REPOSITORY_NAME }}",
        "fileContent": "={{ $json.finalMarkdownContent }}",
        "commitMessage": "={{ `[BOT] Add/Update Handbook: ${$json.reviewTitle} (Approved by Human)` }}",
        "authentication": "accessToken"
      },
      "credentials": {
        "githubApi": {
          "id": "pyragogy-github",
          "name": "GitHub Pyragogy"
        }
      },
      "typeVersion": 1,
      "id": "Commit-a-GitHub-Aprobado--25"
    },
    {
      "name": "Registrar Rechazo Humano",
      "type": "n8n-nodes-base.function",
      "position": [
        2850,
        700
      ],
      "parameters": {
        "functionCode": "// Registra il rifiuto umano del contenuto\nconst agentName = 'Archivist';\nconst reviewId = $json.reviewId;\nconst reviewStatus = 'rejected';\nconst reviewComments = $json.query.comments || 'Nessun commento fornito.';\nconst proposedContent = $json.proposedContent;\nconst title = $json.reviewTitle;\n\nconsole.log(`Contenuto proposto dall'Archivista (ID: ${reviewId}, Titolo: ${title}) rifiutato dall'umano. Commenti: ${reviewComments}`);\n\n// Prepara l'output per indicare il rifiuto e consentire al flusso di continuare.\nreturn [{ json: { ...$json, reviewStatus: reviewStatus, reviewComments: reviewComments, output: { message: `Archivista: Contenuto rifiutato dall'umano.`, status: 'rejected', comments: reviewComments } } }];"
      },
      "typeVersion": 1,
      "id": "Registrar-Rechazo-Humano-26"
    },
    {
      "name": "Fusionar Rutas de Archivista",
      "type": "n8n-nodes-base.merge",
      "position": [
        4050,
        600
      ],
      "parameters": {
        "mode": "mergeByPropertyName",
        "propertyName": "reviewId"
      },
      "typeVersion": 1,
      "id": "Fusionar-Rutas-de-Archivista-27"
    },
    {
      "name": "Evaluar Consenso de Junta",
      "type": "n8n-nodes-base.function",
      "position": [
        1850,
        300
      ],
      "parameters": {
        "functionCode": "// Valuta i flag 'major_issue' dagli agenti di revisione per determinare se è necessaria una rielaborazione.\nlet majorIssueCount = 0;\nlet redraftFeedback = '';\n\n// Assumi che gli output degli agenti di revisione siano accessibili tramite $node\n// (Ad esempio, se Peer Reviewer -> Sensemaking -> Prompt Engineer sono sequenziali prima di questo nodo)\n\nif ($node[\"Peer Reviewer Agent\"] && $node[\"Peer Reviewer Agent\"].json && $node[\"Peer Reviewer Agent\"].json.choices && $node[\"Peer Reviewer Agent\"].json.choices[0] && $node[\"Peer Reviewer Agent\"].json.choices[0].message && $node[\"Peer Reviewer Agent\"].json.choices[0].message.content) {\n    const peerReviewOutput = JSON.parse($node[\"Peer Reviewer Agent\"].json.choices[0].message.content);\n    if (peerReviewOutput.major_issue) majorIssueCount++;\n    redraftFeedback += `Peer Reviewer: ${peerReviewOutput.suggestions || ''}\\n`;\n}\n\nif ($node[\"Sensemaking Agent\"] && $node[\"Sensemaking Agent\"].json && $node[\"Sensemaking Agent\"].json.choices && $node[\"Sensemaking Agent\"].json.choices[0] && $node[\"Sensemaking Agent\"].json.choices[0].message && $node[\"Sensemaking Agent\"].json.choices[0].message.content) {\n    const sensemakingOutput = JSON.parse($node[\"Sensemaking Agent\"].json.choices[0].message.content);\n    if (sensemakingOutput.major_issue) majorIssueCount++;\n    redraftFeedback += `Sensemaking Agent: ${sensemakingOutput.suggestions || ''}\\n`;\n}\n\nif ($node[\"Prompt Engineer Agent\"] && $node[\"Prompt Engineer Agent\"].json && $node[\"Prompt Engineer Agent\"].json.choices && $node[\"Prompt Engineer Agent\"].json.choices[0] && $node[\"Prompt Engineer Agent\"].json.choices[0].message && $node[\"Prompt Engineer Agent\"].json.choices[0].message.content) {\n    const promptEngineerOutput = JSON.parse($node[\"Prompt Engineer Agent\"].json.choices[0].message.content);\n    if (promptEngineerOutput.major_issue) majorIssueCount++;\n    redraftFeedback += `Prompt Engineer: ${promptEngineerOutput.suggestions || ''}\\n`;\n}\n\nconst redraftNeeded = majorIssueCount >= 2; // Voto a maggioranza\n\nreturn [{ json: { ...$json, redraftNeeded: redraftNeeded, redraftFeedback: redraftFeedback } }];"
      },
      "typeVersion": 1,
      "id": "Evaluar-Consenso-de-Junta-28"
    },
    {
      "name": "Verificar si se Necesita Redacción",
      "type": "n8n-nodes-base.if",
      "position": [
        2050,
        300
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $json.redraftNeeded && $workflow.redraftLoopCount < 2 }}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1,
      "id": "Verificar-si-se-Necesita-Redacci-n-29"
    },
    {
      "name": "Manejar Redacción",
      "type": "n8n-nodes-base.function",
      "position": [
        2250,
        200
      ],
      "parameters": {
        "functionCode": "// Gestisce la logica di rielaborazione: incrementa il contatore e reindirizza al Synthesizer.\n\n$workflow.redraftLoopCount += 1; // Incrementa il contatore del ciclo di rielaborazione\n\n// Trova l'indice del Synthesizer nella sequenza degli agenti\nconst synthesizerIndex = $workflow.agentSequence.indexOf(\"Synthesizer\");\n\n// Se il Synthesizer non è il prossimo agente, imposta l'indice corrente per farlo ripartire dal Synthesizer.\n// Questo garantisce che il Synthesizer venga eseguito successivamente per la rielaborazione.\nif ($workflow.currentAgentIndex !== synthesizerIndex) {\n    $workflow.currentAgentIndex = synthesizerIndex;\n} else {\n    // Se siamo già sul Synthesizer (es. dopo il primo passaggio del loop), assicurati che l'indice vada avanti normalmente nel prossimo ciclo.\n    // Questo è un caso limite, di solito il Prepare Agent Input lo gestirà.\n}\n\n// Passa il feedback di rielaborazione come input per il Synthesizer.\n// Il nodo 'Prepare Agent Input' utilizzerà questo campo per aggiornare 'agentInput'.\nreturn [{ json: { ...$json, redraftInput: $json.output + \"\\n\\nFeedback per la rielaborazione dal Peer Review Board:\\n\" + $json.redraftFeedback } }];"
      },
      "typeVersion": 1,
      "id": "Manejar-Redacci-n-30"
    },
    {
      "name": "Procesar Salida de Agente",
      "type": "n8n-nodes-base.function",
      "position": [
        2050,
        200
      ],
      "parameters": {
        "functionCode": "// Ottieni il nome dell'agente appena eseguito\nconst agentName = $json.agentToRun;\nlet agentOutput = '';\n\n// Costruisci dinamicamente il nome del nodo per il recupero dell'output\nlet actualNodeName;\n\n// Gestione speciale per l'Archivista dopo la revisione umana\nif (agentName === 'Archivist') {\n    if ($json.query && $json.query.status === 'approved') {\n        agentOutput = { message: 'Contenuto archiviato con successo dopo l'approvazione umana.', entryId: $json.id || 'N/A', handbookMetadata: { title: $json.reviewTitle, tags: $json.reviewTags, phase: $json.handbookPhase, rhythm: $json.handbookRhythm } };\n    } else if ($json.query && $json.query.status === 'rejected') {\n        agentOutput = { message: 'Archiviazione rifiutata dall'umano.', reviewComments: $json.query.comments || 'Nessun commento' };\n    } else if ($json.reviewStatus === 'rejected') {\n        // Caso in cui la revisione umana è stata rifiutata e si proviene dal ramo di rifiuto\n        agentOutput = { message: 'Archiviazione rifiutata dall'umano (tramite ramo di rifiuto).', reviewComments: $json.reviewComments || 'Nessun commento' };\n    } else {\n        agentOutput = { message: 'Processo Archivista: Attesa revisione umana o stato inatteso.', status: 'pending_review' };\n    }\n} else {\n    // Logica originale per gli altri agenti OpenAI\n    if (agentName === 'Onboarding/Explainer') {\n        actualNodeName = 'Onboarding/Explainer Agent';\n    } else {\n        actualNodeName = agentName + ' Agent';\n    }\n\n    // Tenta in modo sicuro di recuperare l'output dal nodo determinato dinamicamente\n    // Tenta anche di analizzare il JSON se l'output dell'agente è un oggetto JSON (come per gli agenti di revisione)\n    let rawContent = '';\n    if ($node[actualNodeName] && $node[actualNodeName].json && $node[actualNodeName].json.choices && $node[actualNodeName].json.choices[0] && $node[actualNodeName].json.choices[0].message && $node[actualNodeName].json.choices[0].message.content) {\n        rawContent = $node[actualNodeName].json.choices[0].message.content;\n    } else {\n        console.warn(`Impossibile trovare l'output chat OpenAI standard per il nodo: ${actualNodeName}. Ritorno al JSON grezzo.`);\n        agentOutput = $node[actualNodeName] ? $node[actualNodeName].json : `Nessun output specifico trovato per ${agentName}`;\n    }\n\n    // Tenta di analizzare il contenuto come JSON se l'agente è un agente di revisione\n    if (agentName === 'Peer Reviewer' || agentName === 'Sensemaking Agent' || agentName === 'Prompt Engineer') {\n        try {\n            agentOutput = JSON.parse(rawContent);\n        } catch (e) {\n            console.warn(`Impossibile analizzare l'output di ${agentName} come JSON. Trattato come stringa.`);\n            agentOutput = rawContent;\n        }\n    } else {\n        agentOutput = rawContent;\n    }\n}\n\n// Registra il contributo\nconst contribution = {\n    agent: agentName,\n    output: agentOutput,\n    timestamp: new Date().toISOString()\n};\n\n// Assicurati che l'array dei contributi esista e aggiungi il nuovo contributo\nconst existingContributions = Array.isArray($json.contributions) ? $json.contributions : [];\n\n// Incrementa l'indice dell'agente per il loop (solo se non siamo in rielaborazione e questo non è un agente di revisione che porta a rielaborazione)\n// Questo è gestito dalla logica di 'Handle Redraft' che forza l'indice per il riavvio.\nif (!($json.redraftNeeded && $workflow.redraftLoopCount < 2 && agentName !== 'Synthesizer')) { // Evita doppio incremento se riavvia il Synthesizer\n    $workflow.currentAgentIndex += 1;\n}\n\n// Restituisce l'elemento aggiornato, preservando l'input originale, aggiungendo l'output corrente e tutti i contributi\nreturn [{ json: { ...$items[0].json, output: agentOutput, contributions: [...existingContributions, contribution] } }];"
      },
      "typeVersion": 1,
      "id": "Procesar-Salida-de-Agente-31"
    },
    {
      "name": "¿Slack Habilitado?",
      "type": "n8n-nodes-base.if",
      "position": [
        1250,
        500
      ],
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $env.SLACK_WEBHOOK_URL }}",
              "value2": true
            }
          ]
        }
      },
      "typeVersion": 1,
      "id": "-Slack-Habilitado--32"
    },
    {
      "name": "Notificar a Slack",
      "type": "n8n-nodes-base.slack",
      "position": [
        1450,
        500
      ],
      "parameters": {
        "text": "Pyragogy AI Village Workflow Completato!\nInput: {{ $json.body.input }}\nOutput Finale: {{ JSON.stringify($json.output) }}\nAgenti Eseguiti: {{ $workflow.agentSequence.join(', ') }}",
        "options": {},
        "webhookUrl": "={{ $env.SLACK_WEBHOOK_URL }}"
      },
      "typeVersion": 1,
      "id": "Notificar-a-Slack-33"
    },
    {
      "name": "Respuesta Final",
      "type": "n8n-nodes-base.respondToWebhook",
      "position": [
        1250,
        400
      ],
      "parameters": {
        "options": {},
        "respondWith": "json",
        "responseBody": "={{ { finalOutput: $json.output, contributions: $json.contributions, agentSequence: $workflow.agentSequence } }}"
      },
      "typeVersion": 1,
      "id": "Respuesta-Final-34"
    }
  ],
  "settings": {
    "executionOrder": "v1"
  },
  "connections": {
    "Notificar-a-Slack-33": {
      "main": [
        [
          {
            "node": "Respuesta-Final-34",
            "type": "main",
            "index": 0
          }
        ]
      ],
      "error": [
        [
          {
            "node": "Respuesta-Final-34",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Manejar-Redacci-n-30": {
      "main": [
        [
          {
            "node": "-M-s-Agentes-por-Ejecutar--5",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "-Slack-Habilitado--32": {
      "main": [
        [
          {
            "node": "Notificar-a-Slack-33",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Respuesta-Final-34",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "-GitHub-Habilitado--24": {
      "main": [
        [
          {
            "node": "Commit-a-GitHub-Aprobado--25",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Fusionar-Rutas-de-Archivista-27",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Webhook-Trigger-1": {
      "main": [
        [
          {
            "node": "Verificar-Conexi-n-a-BD-2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Agente-Resumidor-8": {
      "main": [
        [
          {
            "node": "Procesar-Salida-de-Agente-31",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Meta-Orquestador-3": {
      "main": [
        [
          {
            "node": "Analizar-Plan-de-Orquestaci-n-4",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Agente-de-Sensemaking-11": {
      "main": [
        [
          {
            "node": "Agente-Ingeniero-de-Prompt-12",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Agente-Sintetizador-9": {
      "main": [
        [
          {
            "node": "Procesar-Salida-de-Agente-31",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generar-ID-de-Revisi-n-16": {
      "main": [
        [
          {
            "node": "Enviar-Correo-de-Solicitud-de-Revisi-n-17",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Verificar-Conexi-n-a-BD-2": {
      "main": [
        [
          {
            "node": "Meta-Orquestador-3",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Registrar-Rechazo-Humano-26": {
      "main": [
        [
          {
            "node": "Fusionar-Rutas-de-Archivista-27",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "-M-s-Agentes-por-Ejecutar--5": {
      "main": [
        [
          {
            "node": "Preparar-Entrada-de-Agente-6",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "-Slack-Habilitado--32",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Agente-Revisor-Par-10": {
      "main": [
        [
          {
            "node": "Agente-de-Sensemaking-11",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Preparar-Entrada-de-Agente-6": {
      "main": [
        [
          {
            "node": "Enrutar-Agentes-con-Switch-7",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Verificar-si-se-Necesita-Redacci-n-29": {
      "main": [
        [
          {
            "node": "Manejar-Redacci-n-30",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Procesar-Salida-de-Agente-31",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Divisi-n-de-Decisi-n-Humana-19": {
      "main": [
        [
          {
            "node": "Guardar-en-handbook_entries-20",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Registrar-Rechazo-Humano-26",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Procesar-Salida-de-Agente-31": {
      "main": [
        [
          {
            "node": "-M-s-Agentes-por-Ejecutar--5",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "A-adir-Metadatos-del-Manual-14": {
      "main": [
        [
          {
            "node": "Generar-Contenido-para-Revisi-n-15",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fusionar-Rutas-de-Archivista-27": {
      "main": [
        [
          {
            "node": "Procesar-Salida-de-Agente-31",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Agente-Ingeniero-de-Prompt-12": {
      "main": [
        [
          {
            "node": "Evaluar-Consenso-de-Junta-28",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Esperar-Aprobaci-n-Humana-18": {
      "main": [
        [
          {
            "node": "Divisi-n-de-Decisi-n-Humana-19",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Evaluar-Consenso-de-Junta-28": {
      "main": [
        [
          {
            "node": "Verificar-si-se-Necesita-Redacci-n-29",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Analizar-Plan-de-Orquestaci-n-4": {
      "main": [
        [
          {
            "node": "-M-s-Agentes-por-Ejecutar--5",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Enrutar-Agentes-con-Switch-7": {
      "main": [
        [
          {
            "node": "Agente-Resumidor-8",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Agente-Sintetizador-9",
            "type": "main",
            "index": 1
          }
        ],
        [
          {
            "node": "Agente-Revisor-Par-10",
            "type": "main",
            "index": 2
          }
        ],
        [
          {
            "node": "Agente-de-Sensemaking-11",
            "type": "main",
            "index": 3
          }
        ],
        [
          {
            "node": "Agente-Ingeniero-de-Prompt-12",
            "type": "main",
            "index": 4
          }
        ],
        [
          {
            "node": "Agente-de-Onboarding-Explicador-13",
            "type": "main",
            "index": 5
          }
        ],
        [
          {
            "node": "A-adir-Metadatos-del-Manual-14",
            "type": "main",
            "index": 6
          }
        ]
      ]
    },
    "Guardar-en-handbook_entries-20": {
      "main": [
        [
          {
            "node": "Preparar-Datos-de-Contribuci-n-Aprobada-21",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generar-Ruta-de-Archivo-GitHub-23": {
      "main": [
        [
          {
            "node": "-GitHub-Habilitado--24",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Enviar-Correo-de-Solicitud-de-Revisi-n-17": {
      "main": [
        [
          {
            "node": "Esperar-Aprobaci-n-Humana-18",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Agente-de-Onboarding-Explicador-13": {
      "main": [
        [
          {
            "node": "Procesar-Salida-de-Agente-31",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Commit-a-GitHub-Aprobado--25": {
      "main": [
        [
          {
            "node": "Fusionar-Rutas-de-Archivista-27",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generar-Contenido-para-Revisi-n-15": {
      "main": [
        [
          {
            "node": "Generar-ID-de-Revisi-n-16",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Preparar-Datos-de-Contribuci-n-Aprobada-21": {
      "main": [
        [
          {
            "node": "Guardar-Contribuci-n-de-Agente-Aprobada--22",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Guardar-Contribuci-n-de-Agente-Aprobada--22": {
      "main": [
        [
          {
            "node": "Generar-Ruta-de-Archivo-GitHub-23",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  }
}
Preguntas frecuentes

¿Cómo usar este flujo de trabajo?

Copie el código de configuración JSON de arriba, cree un nuevo flujo de trabajo en su instancia de n8n y seleccione "Importar desde JSON", pegue la configuración y luego modifique la configuración de credenciales según sea necesario.

¿En qué escenarios es adecuado este flujo de trabajo?

Avanzado - Ingeniería, Inteligencia Artificial

¿Es de pago?

Este flujo de trabajo es completamente gratuito, puede importarlo y usarlo directamente. Sin embargo, tenga en cuenta que los servicios de terceros utilizados en el flujo de trabajo (como la API de OpenAI) pueden requerir un pago por su cuenta.

Información del flujo de trabajo
Nivel de dificultad
Avanzado
Número de nodos35
Categoría2
Tipos de nodos13
Descripción de la dificultad

Adecuado para usuarios avanzados, flujos de trabajo complejos con 16+ nodos

Autor
Fabrizio Terzi

Fabrizio Terzi

@org

Founder of Pyragogy.org — an open initiative exploring AI–human co-creation, ethical automation, and peer learning ecosystems. I build modular workflows for AI-driven content creation, with a focus on transparency, human-in-the-loop design, and collaborative publishing. Always learning, always building — let’s co-create the future.

Enlaces externos
Ver en n8n.io

Compartir este flujo de trabajo

Categorías

Categorías: 34