69일차 실습·강의 자료 추가 (정답본 제외)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mcchaeandClaude Opus 5.5 committed 2026-10-20 19:00:48 +09:00
1 parent aa6cc1c71a
commit 17242dc34a
44 files changed
+5500

No files matched your search

@@ -0,0 +1,204 @@
{
"name": "69 Writer (reference · Lab 01 example 2)",
"nodes": [
{
"name": "Writer webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
0,
300
],
"parameters": {
"httpMethod": "POST",
"path": "my-writer",
"responseMode": "responseNode",
"options": {}
}
},
{
"name": "Has facts?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
200,
300
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "c0",
"leftValue": "={{ $json.body.facts }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
}
},
{
"name": "Reject empty",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
420,
500
],
"parameters": {
"respondWith": "json",
"responseBody": "{ \"error\": \"facts are required\" }",
"options": {
"responseCode": 400
}
}
},
{
"name": "Writer agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 3.1,
"position": [
420,
300
],
"parameters": {
"promptType": "define",
"text": "=요청: {{ $('Writer webhook').first().json.body.request || 'IT 공지' }}\n사실:\n{{ $('Writer webhook').first().json.body.facts }}{{ $('Writer webhook').first().json.body.feedback ? '\\n\\n이전 초안:\\n' + $('Writer webhook').first().json.body.previous_draft + '\\n\\n검토 의견 (모두 고칠 것):\\n' + $('Writer webhook').first().json.body.feedback : '' }}",
"options": {
"systemMessage": "You are the writer of company IT notices, in Korean. Write one notice from the facts only. First line: a title in [square brackets]. Then 3 to 6 short lines. Copy every date and time exactly as written in the facts (for example 10월 23일, 22:00~24:00). Do not add promises, compensation, dates, people or contacts that are not in the facts. If reviewer feedback is given, rewrite the previous draft so that every point of the feedback is fixed, and change nothing else. Fixing never means adding: when you remove a phone number or e-mail address, do not put any other number, extension or address in its place. Return only the notice text.",
"maxIterations": 2
}
}
},
{
"name": "OpenAI chat model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [
380,
520
],
"parameters": {
"model": {
"__rl": true,
"mode": "id",
"value": "gpt-4.1-mini"
},
"options": {
"temperature": 0
}
},
"credentials": {
"openAiApi": {
"id": "openAiCourse0001",
"name": "OpenAI (.env)"
}
}
},
{
"name": "Build draft",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
680,
300
],
"parameters": {
"jsCode": "// The writer's contract: { draft }.\nconst draft = String($json.output ?? '').trim().replace(/^```\\w*\\s*|```$/g, '').trim();\nreturn [{ json: { draft } }];"
}
},
{
"name": "Send result",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
900,
300
],
"parameters": {
"respondWith": "json",
"responseBody": "={{ $json }}",
"options": {}
}
}
],
"connections": {
"Writer webhook": {
"main": [
[
{
"node": "Has facts?",
"type": "main",
"index": 0
}
]
]
},
"Has facts?": {
"main": [
[
{
"node": "Writer agent",
"type": "main",
"index": 0
}
],
[
{
"node": "Reject empty",
"type": "main",
"index": 0
}
]
]
},
"OpenAI chat model": {
"ai_languageModel": [
[
{
"node": "Writer agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Writer agent": {
"main": [
[
{
"node": "Build draft",
"type": "main",
"index": 0
}
]
]
},
"Build draft": {
"main": [
[
{
"node": "Send result",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
}
}
@@ -0,0 +1,522 @@
{
"name": "69 Notice team (reference · Lab 02 example 2)",
"nodes": [
{
"name": "Notice webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
0,
300
],
"parameters": {
"httpMethod": "POST",
"path": "my-notice",
"responseMode": "responseNode",
"options": {}
}
},
{
"name": "Has facts?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
200,
300
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "c0",
"leftValue": "={{ $json.body.facts }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
}
},
{
"name": "Reject empty",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
420,
520
],
"parameters": {
"respondWith": "json",
"responseBody": "{ \"error\": \"facts are required\" }",
"options": {
"responseCode": 400
}
}
},
{
"name": "Write draft",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
420,
300
],
"parameters": {
"method": "POST",
"url": "http://localhost:5678/webhook/my-writer",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ request: $('Notice webhook').first().json.body.request, facts: $('Notice webhook').first().json.body.facts }) }}",
"options": {
"timeout": 60000
}
}
},
{
"name": "Review draft",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
640,
300
],
"parameters": {
"method": "POST",
"url": "http://localhost:5678/webhook/my-reviewer",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ facts: $('Notice webhook').first().json.body.facts, draft: $json.draft }) }}",
"options": {
"timeout": 60000
}
}
},
{
"name": "Passed?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
1080,
300
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "c0",
"leftValue": "={{ $json.passed }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
}
},
{
"name": "Approved",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1300,
160
],
"parameters": {
"assignments": {
"assignments": [
{
"id": "a0",
"name": "route",
"value": "게시 가능",
"type": "string"
},
{
"id": "a1",
"name": "published",
"value": true,
"type": "boolean"
},
{
"id": "a2",
"name": "rounds",
"value": 1,
"type": "number"
},
{
"id": "a3",
"name": "notice",
"value": "={{ $('Write draft').first().json.draft }}",
"type": "string"
},
{
"id": "a4",
"name": "codes",
"value": "={{ [] }}",
"type": "array"
}
]
},
"options": {}
}
},
{
"name": "Revise draft",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1300,
400
],
"parameters": {
"method": "POST",
"url": "http://localhost:5678/webhook/my-writer",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ request: $('Notice webhook').first().json.body.request, facts: $('Notice webhook').first().json.body.facts, previous_draft: $('Write draft').first().json.draft, feedback: $('Review draft').first().json.issues.map(i => i.code + ': ' + i.detail).join('\\n') }) }}",
"options": {
"timeout": 60000
}
}
},
{
"name": "Review again",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1520,
400
],
"parameters": {
"method": "POST",
"url": "http://localhost:5678/webhook/my-reviewer",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ facts: $('Notice webhook').first().json.body.facts, draft: $json.draft }) }}",
"options": {
"timeout": 60000
}
}
},
{
"name": "Passed again?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
1740,
400
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "c0",
"leftValue": "={{ $json.passed }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
}
},
{
"name": "Approved after revision",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1960,
300
],
"parameters": {
"assignments": {
"assignments": [
{
"id": "a0",
"name": "route",
"value": "수정 후 게시 가능",
"type": "string"
},
{
"id": "a1",
"name": "published",
"value": true,
"type": "boolean"
},
{
"id": "a2",
"name": "rounds",
"value": 2,
"type": "number"
},
{
"id": "a3",
"name": "notice",
"value": "={{ $('Revise draft').first().json.draft }}",
"type": "string"
},
{
"id": "a4",
"name": "codes",
"value": "={{ [] }}",
"type": "array"
},
{
"id": "a5",
"name": "first_codes",
"value": "={{ $('Review draft').first().json.codes }}",
"type": "array"
}
]
},
"options": {}
}
},
{
"name": "Needs person",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1960,
520
],
"parameters": {
"assignments": {
"assignments": [
{
"id": "a0",
"name": "route",
"value": "담당자 확인",
"type": "string"
},
{
"id": "a1",
"name": "published",
"value": false,
"type": "boolean"
},
{
"id": "a2",
"name": "rounds",
"value": 2,
"type": "number"
},
{
"id": "a3",
"name": "notice",
"value": "={{ $('Revise draft').first().json.draft ?? '' }}",
"type": "string"
},
{
"id": "a4",
"name": "codes",
"value": "={{ $json.codes ?? [] }}",
"type": "array"
},
{
"id": "a5",
"name": "first_codes",
"value": "={{ $('Review draft').first().json.codes }}",
"type": "array"
}
]
},
"options": {}
}
},
{
"name": "Send result",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
2600,
300
],
"parameters": {
"respondWith": "json",
"responseBody": "={{ $json }}",
"options": {}
}
}
],
"connections": {
"Notice webhook": {
"main": [
[
{
"node": "Has facts?",
"type": "main",
"index": 0
}
]
]
},
"Has facts?": {
"main": [
[
{
"node": "Write draft",
"type": "main",
"index": 0
}
],
[
{
"node": "Reject empty",
"type": "main",
"index": 0
}
]
]
},
"Write draft": {
"main": [
[
{
"node": "Review draft",
"type": "main",
"index": 0
}
]
]
},
"Review draft": {
"main": [
[
{
"node": "Passed?",
"type": "main",
"index": 0
}
]
]
},
"Passed?": {
"main": [
[
{
"node": "Approved",
"type": "main",
"index": 0
}
],
[
{
"node": "Revise draft",
"type": "main",
"index": 0
}
]
]
},
"Revise draft": {
"main": [
[
{
"node": "Review again",
"type": "main",
"index": 0
}
]
]
},
"Review again": {
"main": [
[
{
"node": "Passed again?",
"type": "main",
"index": 0
}
]
]
},
"Passed again?": {
"main": [
[
{
"node": "Approved after revision",
"type": "main",
"index": 0
}
],
[
{
"node": "Needs person",
"type": "main",
"index": 0
}
]
]
},
"Needs person": {
"main": [
[
{
"node": "Send result",
"type": "main",
"index": 0
}
]
]
},
"Approved": {
"main": [
[
{
"node": "Send result",
"type": "main",
"index": 0
}
]
]
},
"Approved after revision": {
"main": [
[
{
"node": "Send result",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
}
}
@@ -0,0 +1,237 @@
{
"name": "69 Reviewer (reference · Lab 02 start = Lab 01 challenge)",
"nodes": [
{
"name": "Review webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
0,
300
],
"parameters": {
"httpMethod": "POST",
"path": "my-reviewer",
"responseMode": "responseNode",
"options": {}
}
},
{
"name": "Has facts and draft?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
200,
300
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "c0",
"leftValue": "={{ $json.body.facts }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
}
},
{
"id": "c1",
"leftValue": "={{ $json.body.draft }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
}
},
{
"name": "Reject empty",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
420,
500
],
"parameters": {
"respondWith": "json",
"responseBody": "{ \"error\": \"facts and draft are required\" }",
"options": {
"responseCode": 400
}
}
},
{
"name": "Rule checks",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
420,
300
],
"parameters": {
"jsCode": "// Rules that must give the same answer every time are checked in code, not by the LLM.\nconst body = $('Review webhook').first().json.body;\nconst facts = String(body.facts ?? '');\nconst draft = String(body.draft ?? '');\nconst squeeze = text => text.replace(/\\s+/g, '');\nconst tokens = [...new Set(facts.match(/\\d{1,2}월\\s?\\d{1,2}일|\\d{1,2}:\\d{2}/g) ?? [])];\nconst missing = tokens.filter(token => !squeeze(draft).includes(squeeze(token)));\nconst issues = [];\nif (missing.length) issues.push({ code: 'missing_fact', detail: missing.join(', ') });\nconst personal = draft.match(/01[016789]-?\\d{3,4}-?\\d{4}|[\\w.+-]+@[\\w-]+\\.[\\w.]+/g) ?? [];\nif (personal.length) issues.push({ code: 'personal_info', detail: personal.join(', ') });\nif (draft.length > 600) issues.push({ code: 'too_long', detail: `${draft.length}자 (600자 이하)` });\nreturn [{ json: { rule_issues: issues } }];"
}
},
{
"name": "Claim check agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 3.1,
"position": [
640,
300
],
"parameters": {
"promptType": "define",
"text": "=사실(facts):\n{{ $('Review webhook').first().json.body.facts }}\n\n공지 초안(draft):\n{{ $('Review webhook').first().json.body.draft }}",
"options": {
"systemMessage": "You check a Korean IT notice draft against its facts. List every statement in the draft that promises, schedules or states something that is not in the facts: compensation, gifts, new dates or times, extra services, people or contacts. Anything that appears in the facts, even reworded, shortened or partly left out, is supported (a contact person named in the facts is supported). Polite phrases (thanks, apologies, asking for understanding, '문의 바랍니다') are not claims. Return only JSON, no code fence: {\"unsupported\": [\"exact sentence from the draft\"]}",
"maxIterations": 2
}
}
},
{
"name": "OpenAI chat model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [
600,
520
],
"parameters": {
"model": {
"__rl": true,
"mode": "id",
"value": "gpt-4.1-mini"
},
"options": {
"temperature": 0
}
},
"credentials": {
"openAiApi": {
"id": "openAiCourse0001",
"name": "OpenAI (.env)"
}
}
},
{
"name": "Build review",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
880,
300
],
"parameters": {
"jsCode": "// The reviewer's contract: { passed, codes, issues }.\nconst raw = String($json.output ?? '').trim().replace(/^```(json)?\\s*|```$/g, '');\nlet unsupported = [];\ntry { unsupported = JSON.parse(raw).unsupported ?? []; } catch (error) { unsupported = []; }\n// Check the LLM's verdict too: a sentence whose words all appear in the facts is supported.\nconst facts = String($('Review webhook').first().json.body.facts ?? '');\nconst words = sentence => sentence.match(/[가-힣A-Za-z0-9]{2,}/g) ?? [];\nunsupported = unsupported.map(String).filter(Boolean)\n .filter(sentence => words(sentence).some(word => !facts.includes(word)));\nconst issues = [...$('Rule checks').first().json.rule_issues];\nif (unsupported.length) issues.push({ code: 'unsupported_claim', detail: unsupported.join(' / ') });\nreturn [{ json: { passed: issues.length === 0, codes: issues.map(issue => issue.code), issues } }];"
}
},
{
"name": "Send result",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
1100,
300
],
"parameters": {
"respondWith": "json",
"responseBody": "={{ $json }}",
"options": {}
}
}
],
"connections": {
"Review webhook": {
"main": [
[
{
"node": "Has facts and draft?",
"type": "main",
"index": 0
}
]
]
},
"Has facts and draft?": {
"main": [
[
{
"node": "Rule checks",
"type": "main",
"index": 0
}
],
[
{
"node": "Reject empty",
"type": "main",
"index": 0
}
]
]
},
"Rule checks": {
"main": [
[
{
"node": "Claim check agent",
"type": "main",
"index": 0
}
]
]
},
"OpenAI chat model": {
"ai_languageModel": [
[
{
"node": "Claim check agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Claim check agent": {
"main": [
[
{
"node": "Build review",
"type": "main",
"index": 0
}
]
]
},
"Build review": {
"main": [
[
{
"node": "Send result",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
}
}
@@ -0,0 +1,674 @@
{
"name": "69 Notice team (reference · Lab 03 example 2)",
"nodes": [
{
"name": "Notice webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
0,
300
],
"parameters": {
"httpMethod": "POST",
"path": "my-notice",
"responseMode": "responseNode",
"options": {}
}
},
{
"name": "Has facts?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
200,
300
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "c0",
"leftValue": "={{ $json.body.facts }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
}
},
{
"name": "Reject empty",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
420,
520
],
"parameters": {
"respondWith": "json",
"responseBody": "{ \"error\": \"facts are required\" }",
"options": {
"responseCode": 400
}
}
},
{
"name": "Write draft",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
420,
300
],
"parameters": {
"method": "POST",
"url": "http://localhost:5678/webhook/my-writer",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ request: $('Notice webhook').first().json.body.request, facts: $('Notice webhook').first().json.body.facts }) }}",
"options": {
"timeout": 60000,
"response": {
"response": {
"neverError": true
}
}
}
}
},
{
"name": "Review draft",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
640,
300
],
"parameters": {
"method": "POST",
"url": "http://localhost:5678/webhook/my-reviewer",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ facts: $('Notice webhook').first().json.body.facts, draft: $json.draft }) }}",
"options": {
"timeout": 60000,
"response": {
"response": {
"neverError": true
}
}
}
}
},
{
"name": "Reviewed?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
860,
300
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "c0",
"leftValue": "={{ typeof $json.passed === 'boolean' }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
}
},
{
"name": "Review unavailable",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1080,
560
],
"parameters": {
"assignments": {
"assignments": [
{
"id": "a0",
"name": "route",
"value": "담당자 확인",
"type": "string"
},
{
"id": "a1",
"name": "published",
"value": false,
"type": "boolean"
},
{
"id": "a2",
"name": "reason",
"value": "검토자가 응답하지 않아 게시하지 않습니다.",
"type": "string"
},
{
"id": "a3",
"name": "notice",
"value": "={{ $('Write draft').first().json.draft ?? '' }}",
"type": "string"
},
{
"id": "a4",
"name": "codes",
"value": "={{ [] }}",
"type": "array"
},
{
"id": "a5",
"name": "steps",
"value": "={{ [\"writer\", \"reviewer\"] }}",
"type": "array"
}
]
},
"options": {}
}
},
{
"name": "Passed?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
1080,
300
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "c0",
"leftValue": "={{ $json.passed }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
}
},
{
"name": "Approved",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1300,
160
],
"parameters": {
"assignments": {
"assignments": [
{
"id": "a0",
"name": "route",
"value": "게시 가능",
"type": "string"
},
{
"id": "a1",
"name": "published",
"value": true,
"type": "boolean"
},
{
"id": "a2",
"name": "rounds",
"value": 1,
"type": "number"
},
{
"id": "a3",
"name": "notice",
"value": "={{ $('Write draft').first().json.draft }}",
"type": "string"
},
{
"id": "a4",
"name": "codes",
"value": "={{ [] }}",
"type": "array"
},
{
"id": "a5",
"name": "steps",
"value": "={{ [\"writer\", \"reviewer\"] }}",
"type": "array"
}
]
},
"options": {}
}
},
{
"name": "Revise draft",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1300,
400
],
"parameters": {
"method": "POST",
"url": "http://localhost:5678/webhook/my-writer",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ request: $('Notice webhook').first().json.body.request, facts: $('Notice webhook').first().json.body.facts, previous_draft: $('Write draft').first().json.draft, feedback: $('Review draft').first().json.issues.map(i => i.code + ': ' + i.detail).join('\\n') }) }}",
"options": {
"timeout": 60000,
"response": {
"response": {
"neverError": true
}
}
}
}
},
{
"name": "Review again",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4.2,
"position": [
1520,
400
],
"parameters": {
"method": "POST",
"url": "http://localhost:5678/webhook/my-reviewer",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "={{ JSON.stringify({ facts: $('Notice webhook').first().json.body.facts, draft: $json.draft }) }}",
"options": {
"timeout": 60000,
"response": {
"response": {
"neverError": true
}
}
}
}
},
{
"name": "Passed again?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
1740,
400
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "c0",
"leftValue": "={{ $json.passed === true }}",
"rightValue": "",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
}
},
{
"name": "Approved after revision",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1960,
300
],
"parameters": {
"assignments": {
"assignments": [
{
"id": "a0",
"name": "route",
"value": "수정 후 게시 가능",
"type": "string"
},
{
"id": "a1",
"name": "published",
"value": true,
"type": "boolean"
},
{
"id": "a2",
"name": "rounds",
"value": 2,
"type": "number"
},
{
"id": "a3",
"name": "notice",
"value": "={{ $('Revise draft').first().json.draft }}",
"type": "string"
},
{
"id": "a4",
"name": "codes",
"value": "={{ [] }}",
"type": "array"
},
{
"id": "a5",
"name": "first_codes",
"value": "={{ $('Review draft').first().json.codes }}",
"type": "array"
},
{
"id": "a6",
"name": "steps",
"value": "={{ [\"writer\", \"reviewer\", \"writer\", \"reviewer\"] }}",
"type": "array"
}
]
},
"options": {}
}
},
{
"name": "Needs person",
"type": "n8n-nodes-base.set",
"typeVersion": 3.4,
"position": [
1960,
520
],
"parameters": {
"assignments": {
"assignments": [
{
"id": "a0",
"name": "route",
"value": "담당자 확인",
"type": "string"
},
{
"id": "a1",
"name": "published",
"value": false,
"type": "boolean"
},
{
"id": "a2",
"name": "rounds",
"value": 2,
"type": "number"
},
{
"id": "a3",
"name": "notice",
"value": "={{ $('Revise draft').first().json.draft ?? '' }}",
"type": "string"
},
{
"id": "a4",
"name": "codes",
"value": "={{ $json.codes ?? [] }}",
"type": "array"
},
{
"id": "a5",
"name": "first_codes",
"value": "={{ $('Review draft').first().json.codes }}",
"type": "array"
},
{
"id": "a6",
"name": "steps",
"value": "={{ [\"writer\", \"reviewer\", \"writer\", \"reviewer\"] }}",
"type": "array"
}
]
},
"options": {}
}
},
{
"name": "Send result",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
2600,
300
],
"parameters": {
"respondWith": "json",
"responseBody": "={{ $json }}",
"options": {}
}
}
],
"connections": {
"Notice webhook": {
"main": [
[
{
"node": "Has facts?",
"type": "main",
"index": 0
}
]
]
},
"Has facts?": {
"main": [
[
{
"node": "Write draft",
"type": "main",
"index": 0
}
],
[
{
"node": "Reject empty",
"type": "main",
"index": 0
}
]
]
},
"Write draft": {
"main": [
[
{
"node": "Review draft",
"type": "main",
"index": 0
}
]
]
},
"Review draft": {
"main": [
[
{
"node": "Reviewed?",
"type": "main",
"index": 0
}
]
]
},
"Reviewed?": {
"main": [
[
{
"node": "Passed?",
"type": "main",
"index": 0
}
],
[
{
"node": "Review unavailable",
"type": "main",
"index": 0
}
]
]
},
"Passed?": {
"main": [
[
{
"node": "Approved",
"type": "main",
"index": 0
}
],
[
{
"node": "Revise draft",
"type": "main",
"index": 0
}
]
]
},
"Revise draft": {
"main": [
[
{
"node": "Review again",
"type": "main",
"index": 0
}
]
]
},
"Review again": {
"main": [
[
{
"node": "Passed again?",
"type": "main",
"index": 0
}
]
]
},
"Passed again?": {
"main": [
[
{
"node": "Approved after revision",
"type": "main",
"index": 0
}
],
[
{
"node": "Needs person",
"type": "main",
"index": 0
}
]
]
},
"Needs person": {
"main": [
[
{
"node": "Send result",
"type": "main",
"index": 0
}
]
]
},
"Review unavailable": {
"main": [
[
{
"node": "Send result",
"type": "main",
"index": 0
}
]
]
},
"Approved": {
"main": [
[
{
"node": "Send result",
"type": "main",
"index": 0
}
]
]
},
"Approved after revision": {
"main": [
[
{
"node": "Send result",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
}
}
@@ -0,0 +1,204 @@
{
"name": "69 Single agent notice (reference · Lab 03 comparison)",
"nodes": [
{
"name": "Single webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 2,
"position": [
0,
300
],
"parameters": {
"httpMethod": "POST",
"path": "my-single",
"responseMode": "responseNode",
"options": {}
}
},
{
"name": "Has facts?",
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
200,
300
],
"parameters": {
"conditions": {
"options": {
"caseSensitive": true,
"leftValue": "",
"typeValidation": "strict",
"version": 2
},
"conditions": [
{
"id": "c0",
"leftValue": "={{ $json.body.facts }}",
"rightValue": "",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
}
}
],
"combinator": "and"
},
"options": {}
}
},
{
"name": "Reject empty",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
420,
500
],
"parameters": {
"respondWith": "json",
"responseBody": "{ \"error\": \"facts are required\" }",
"options": {
"responseCode": 400
}
}
},
{
"name": "Notice agent",
"type": "@n8n/n8n-nodes-langchain.agent",
"typeVersion": 3.1,
"position": [
420,
300
],
"parameters": {
"promptType": "define",
"text": "=요청: {{ $('Single webhook').first().json.body.request || 'IT 공지' }}\n사실:\n{{ $('Single webhook').first().json.body.facts }}",
"options": {
"systemMessage": "You are the writer of company IT notices, in Korean. Write one notice from the facts only. First line: a title in [square brackets]. Then 3 to 6 short lines. Copy every date and time exactly as written in the facts (for example 10월 23일, 22:00~24:00). Do not add promises, compensation, dates, people or contacts that are not in the facts. If reviewer feedback is given, rewrite the previous draft so that every point of the feedback is fixed, and change nothing else. Fixing never means adding: when you remove a phone number or e-mail address, do not put any other number, extension or address in its place. Never include phone numbers or e-mail addresses. Keep the notice under 600 characters. Return only the notice text.",
"maxIterations": 2
}
}
},
{
"name": "OpenAI chat model",
"type": "@n8n/n8n-nodes-langchain.lmChatOpenAi",
"typeVersion": 1.2,
"position": [
380,
520
],
"parameters": {
"model": {
"__rl": true,
"mode": "id",
"value": "gpt-4.1-mini"
},
"options": {
"temperature": 0
}
},
"credentials": {
"openAiApi": {
"id": "openAiCourse0001",
"name": "OpenAI (.env)"
}
}
},
{
"name": "Build result",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
680,
300
],
"parameters": {
"jsCode": "const notice = String($json.output ?? '').trim();\nreturn [{ json: { route: '게시 가능', published: true, rounds: 1, notice, codes: [], steps: ['single'] } }];"
}
},
{
"name": "Send result",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1.1,
"position": [
900,
300
],
"parameters": {
"respondWith": "json",
"responseBody": "={{ $json }}",
"options": {}
}
}
],
"connections": {
"Single webhook": {
"main": [
[
{
"node": "Has facts?",
"type": "main",
"index": 0
}
]
]
},
"Has facts?": {
"main": [
[
{
"node": "Notice agent",
"type": "main",
"index": 0
}
],
[
{
"node": "Reject empty",
"type": "main",
"index": 0
}
]
]
},
"OpenAI chat model": {
"ai_languageModel": [
[
{
"node": "Notice agent",
"type": "ai_languageModel",
"index": 0
}
]
]
},
"Notice agent": {
"main": [
[
{
"node": "Build result",
"type": "main",
"index": 0
}
]
]
},
"Build result": {
"main": [
[
{
"node": "Send result",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1"
}
}