Pyragogy AI Village - 编排大师(深度架构V2)
高级
这是一个Engineering, AI领域的自动化工作流,包含 35 个节点。主要使用 If, Wait, Merge, Slack, Start 等节点,结合人工智能技术实现智能自动化。 使用GPT-4o多Agent编排和人工审核生成协作手册
前置要求
- •Slack Bot Token 或 Webhook URL
- •GitHub Personal Access Token
- •OpenAI API Key
- •HTTP Webhook 端点(n8n 会自动生成)
- •PostgreSQL 数据库连接信息
工作流预览
可视化展示节点连接关系,支持缩放和平移
无法加载工作流预览
导出工作流
复制以下 JSON 配置到 n8n 导入,即可使用此工作流
{
"meta": {
"templateCredsSetupCompleted": true
},
"name": "Pyragogy AI Village - 编排大师(深度架构 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": "开始",
"type": "n8n-nodes-base.start",
"position": [
50,
300
],
"parameters": {},
"typeVersion": 1
},
{
"name": "Webhook触发器",
"type": "n8n-nodes-base.webhook",
"position": [
250,
300
],
"webhookId": "pyragogy-master-trigger",
"parameters": {
"path": "pyragogy/process",
"options": {},
"httpMethod": "POST"
},
"typeVersion": 1
},
{
"name": "检查数据库连接",
"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
},
{
"name": "元编排器",
"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
},
{
"name": "解析编排计划",
"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
},
{
"name": "还有更多代理要运行吗?",
"type": "n8n-nodes-base.if",
"position": [
1050,
300
],
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $workflow.currentAgentIndex < $workflow.agentSequence.length }}",
"value2": true
}
]
}
},
"typeVersion": 1
},
{
"name": "准备代理输入",
"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
},
{
"name": "通过 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
},
{
"name": "摘要生成智能体",
"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
},
{
"name": "合成器代理",
"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
},
{
"name": "同行评审代理",
"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
},
{
"name": "意义建构代理",
"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
},
{
"name": "提示工程师代理",
"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
},
{
"name": "入职/解释器代理",
"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
},
{
"name": "添加手册元数据",
"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
},
{
"name": "生成待审核内容",
"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
},
{
"name": "生成审核 ID",
"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
},
{
"name": "发送审核请求邮件",
"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
},
{
"name": "等待人工批准",
"type": "n8n-nodes-base.wait",
"position": [
2450,
600
],
"parameters": {
"mode": "webhook",
"timeout": "1h",
"matchField": "reviewId",
"matchValue": "={{ $json.reviewId }}",
"webhookPath": "pyragogy/review-feedback"
},
"typeVersion": 1
},
{
"name": "人工决策分流",
"type": "n8n-nodes-base.if",
"position": [
2650,
600
],
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $json.query.status === 'approved' }}",
"value2": true
}
]
}
},
"typeVersion": 1
},
{
"name": "保存到 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
},
{
"name": "准备已批准的贡献数据",
"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
},
{
"name": "保存代理贡献(已批准)",
"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
},
{
"name": "生成 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
},
{
"name": "GitHub 已启用?",
"type": "n8n-nodes-base.if",
"position": [
3650,
500
],
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $env.GITHUB_ACCESS_TOKEN }}",
"value2": true
}
]
}
},
"typeVersion": 1
},
{
"name": "提交到 GitHub(已批准)",
"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
},
{
"name": "记录人工拒绝",
"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
},
{
"name": "合并档案管理员路径",
"type": "n8n-nodes-base.merge",
"position": [
4050,
600
],
"parameters": {
"mode": "mergeByPropertyName",
"propertyName": "reviewId"
},
"typeVersion": 1
},
{
"name": "评估委员会共识",
"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
},
{
"name": "检查是否需要重写",
"type": "n8n-nodes-base.if",
"position": [
2050,
300
],
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $json.redraftNeeded && $workflow.redraftLoopCount < 2 }}",
"value2": true
}
]
}
},
"typeVersion": 1
},
{
"name": "处理重写",
"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
},
{
"name": "处理代理输出",
"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
},
{
"name": "Slack 已启用?",
"type": "n8n-nodes-base.if",
"position": [
1250,
500
],
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $env.SLACK_WEBHOOK_URL }}",
"value2": true
}
]
}
},
"typeVersion": 1
},
{
"name": "通知 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
},
{
"name": "最终响应",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
1250,
400
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={{ { finalOutput: $json.output, contributions: $json.contributions, agentSequence: $workflow.agentSequence } }}"
},
"typeVersion": 1
}
],
"settings": {
"executionOrder": "v1"
},
"connections": {
"Notify Slack": {
"main": [
[
{
"node": "Final Response",
"type": "main",
"index": 0
}
]
],
"error": [
[
{
"node": "Final Response",
"type": "main",
"index": 0
}
]
]
},
"Handle Redraft": {
"main": [
[
{
"node": "More Agents to Run?",
"type": "main",
"index": 0
}
]
]
},
"Slack Enabled?": {
"main": [
[
{
"node": "Notify Slack",
"type": "main",
"index": 0
}
],
[
{
"node": "Final Response",
"type": "main",
"index": 0
}
]
]
},
"GitHub Enabled?": {
"main": [
[
{
"node": "Commit to GitHub (Approved)",
"type": "main",
"index": 0
}
],
[
{
"node": "Merge Archivist Paths",
"type": "main",
"index": 0
}
]
]
},
"Webhook Trigger": {
"main": [
[
{
"node": "Check DB Connection",
"type": "main",
"index": 0
}
]
]
},
"Summarizer Agent": {
"main": [
[
{
"node": "Process Agent Output",
"type": "main",
"index": 0
}
]
]
},
"Meta-Orchestrator": {
"main": [
[
{
"node": "Parse Orchestration Plan",
"type": "main",
"index": 0
}
]
]
},
"Sensemaking Agent": {
"main": [
[
{
"node": "Prompt Engineer Agent",
"type": "main",
"index": 0
}
]
]
},
"Synthesizer Agent": {
"main": [
[
{
"node": "Process Agent Output",
"type": "main",
"index": 0
}
]
]
},
"Generate Review ID": {
"main": [
[
{
"node": "Send Review Request Email",
"type": "main",
"index": 0
}
]
]
},
"Check DB Connection": {
"main": [
[
{
"node": "Meta-Orchestrator",
"type": "main",
"index": 0
}
]
]
},
"Log Human Rejection": {
"main": [
[
{
"node": "Merge Archivist Paths",
"type": "main",
"index": 0
}
]
]
},
"More Agents to Run?": {
"main": [
[
{
"node": "Prepare Agent Input",
"type": "main",
"index": 0
}
],
[
{
"node": "Slack Enabled?",
"type": "main",
"index": 0
}
]
]
},
"Peer Reviewer Agent": {
"main": [
[
{
"node": "Sensemaking Agent",
"type": "main",
"index": 0
}
]
]
},
"Prepare Agent Input": {
"main": [
[
{
"node": "Route Agents with Switch",
"type": "main",
"index": 0
}
]
]
},
"Check Redraft Needed": {
"main": [
[
{
"node": "Handle Redraft",
"type": "main",
"index": 0
}
],
[
{
"node": "Process Agent Output",
"type": "main",
"index": 0
}
]
]
},
"Human Decision Split": {
"main": [
[
{
"node": "Save to handbook_entries",
"type": "main",
"index": 0
}
],
[
{
"node": "Log Human Rejection",
"type": "main",
"index": 0
}
]
]
},
"Process Agent Output": {
"main": [
[
{
"node": "More Agents to Run?",
"type": "main",
"index": 0
}
]
]
},
"Add Handbook Metadata": {
"main": [
[
{
"node": "Generate Content for Review",
"type": "main",
"index": 0
}
]
]
},
"Merge Archivist Paths": {
"main": [
[
{
"node": "Process Agent Output",
"type": "main",
"index": 0
}
]
]
},
"Prompt Engineer Agent": {
"main": [
[
{
"node": "Evaluate Board Consensus",
"type": "main",
"index": 0
}
]
]
},
"Wait for Human Approval": {
"main": [
[
{
"node": "Human Decision Split",
"type": "main",
"index": 0
}
]
]
},
"Evaluate Board Consensus": {
"main": [
[
{
"node": "Check Redraft Needed",
"type": "main",
"index": 0
}
]
]
},
"Parse Orchestration Plan": {
"main": [
[
{
"node": "More Agents to Run?",
"type": "main",
"index": 0
}
]
]
},
"Route Agents with Switch": {
"main": [
[
{
"node": "Summarizer Agent",
"type": "main",
"index": 0
}
],
[
{
"node": "Synthesizer Agent",
"type": "main",
"index": 1
}
],
[
{
"node": "Peer Reviewer Agent",
"type": "main",
"index": 2
}
],
[
{
"node": "Sensemaking Agent",
"type": "main",
"index": 3
}
],
[
{
"node": "Prompt Engineer Agent",
"type": "main",
"index": 4
}
],
[
{
"node": "Onboarding/Explainer Agent",
"type": "main",
"index": 5
}
],
[
{
"node": "Add Handbook Metadata",
"type": "main",
"index": 6
}
]
]
},
"Save to handbook_entries": {
"main": [
[
{
"node": "Prepare Approved Contribution Data",
"type": "main",
"index": 0
}
]
]
},
"Generate GitHub File Path": {
"main": [
[
{
"node": "GitHub Enabled?",
"type": "main",
"index": 0
}
]
]
},
"Send Review Request Email": {
"main": [
[
{
"node": "Wait for Human Approval",
"type": "main",
"index": 0
}
]
]
},
"Onboarding/Explainer Agent": {
"main": [
[
{
"node": "Process Agent Output",
"type": "main",
"index": 0
}
]
]
},
"Commit to GitHub (Approved)": {
"main": [
[
{
"node": "Merge Archivist Paths",
"type": "main",
"index": 0
}
]
]
},
"Generate Content for Review": {
"main": [
[
{
"node": "Generate Review ID",
"type": "main",
"index": 0
}
]
]
},
"Prepare Approved Contribution Data": {
"main": [
[
{
"node": "Save Agent Contribution (Approved)",
"type": "main",
"index": 0
}
]
]
},
"Save Agent Contribution (Approved)": {
"main": [
[
{
"node": "Generate GitHub File Path",
"type": "main",
"index": 0
}
]
]
}
}
}常见问题
如何使用这个工作流?
复制上方的 JSON 配置代码,在您的 n8n 实例中创建新工作流并选择「从 JSON 导入」,粘贴配置后根据需要修改凭证设置即可。
这个工作流适合什么场景?
高级 - 工程, 人工智能
需要付费吗?
本工作流完全免费,您可以直接导入使用。但请注意,工作流中使用的第三方服务(如 OpenAI API)可能需要您自行付费。
相关工作流推荐
我的工作流3
多渠道客户支持自动化套件
If
Merge
Slack
+7
15 节点Elie Kattar
人工智能
AI智能助手:与Supabase存储和Google Drive文件对话
AI智能助手:与Supabase存储和Google Drive文件对话
If
Set
Wait
+20
62 节点Mark Shcherbakov
工程
🤖 WhatsApp AI个人助手:GPT-4o、记忆和日程安排功能
AI个人助手:集成GPT-4o、RAG和语音功能,使用Supabase的WhatsApp助手
If
Set
Wait
+22
76 节点Amanda Benks
人工智能
使用GPT-4.1、Outlook和Mem.ai自动化Microsoft Teams会议分析
使用GPT-4.1、Outlook和Mem.ai自动化Microsoft Teams会议分析
If
Set
Code
+19
61 节点Wayne Simpson
人力资源
使用GPT-4o、WordPress和LinkedIn发布自动化RSS内容到博客文章
使用GPT-4o、WordPress和LinkedIn发布自动化RSS内容到博客文章
If
Set
Code
+21
40 节点Immanuel
人工智能
实时Notion Todoist双向同步模板
使用Redis的Notion Todoist实时双向同步
If
Set
Code
+26
246 节点Mario
销售
工作流信息
难度等级
高级
节点数量35
分类2
节点类型13
作者
Fabrizio Terzi
@orgFounder 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.
外部链接
在 n8n.io 查看 →
分享此工作流