前言
每個工程師都有一些「每天都在做但其實可以自動化」的事情。收到客戶信件要轉到 Slack、每週要整理報表、PR 合併後要更新文件……這些事情一件一件做不累,但加起來每週可能吃掉你好幾個小時。
n8n 是一個開源的工作流自動化平台,有點像 Zapier 但可以自己架、不用付訂閱費。更重要的是,它最近加入了 AI 節點,讓你可以在自動化流程中串接 LLM,做一些以前「只有人能做」的判斷和處理。
這篇文章分享我用 n8n 建立的幾個實用自動化案例,從安裝到實際的 workflow 設計。
安裝部署
Docker Compose 部署(推薦)
# docker-compose.yml
services:
n8n:
image: n8nio/n8n
container_name: n8n
restart: always
ports:
- "5678:5678"
environment:
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=admin
- N8N_BASIC_AUTH_PASSWORD=your-secure-password
- N8N_HOST=localhost
- N8N_PORT=5678
- N8N_PROTOCOL=http
- GENERIC_TIMEZONE=Asia/Taipei
- TZ=Asia/Taipei
# 啟用社群節點(可選)
- N8N_COMMUNITY_PACKAGES_ALLOW_TOOL_USAGE=true
volumes:
- n8n_data:/home/node/.n8n
# 如果要跟本地的 Ollama 通訊
extra_hosts:
- "host.docker.internal:host-gateway"
# 搭配 PostgreSQL 做持久化(正式環境建議)
postgres:
image: postgres:16-alpine
container_name: n8n-db
restart: always
environment:
- POSTGRES_DB=n8n
- POSTGRES_USER=n8n
- POSTGRES_PASSWORD=n8n-password
volumes:
- postgres_data:/var/lib/postgresql/data
volumes:
n8n_data:
postgres_data:
# 啟動
docker compose up -d
# 打開瀏覽器
open http://localhost:5678
# 查看日誌
docker compose logs -f n8n
搭配 Ollama 的完整部署
# docker-compose.yml(n8n + Ollama 一起部署)
services:
n8n:
image: n8nio/n8n
container_name: n8n
restart: always
ports:
- "5678:5678"
environment:
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=admin
- N8N_BASIC_AUTH_PASSWORD=your-secure-password
- GENERIC_TIMEZONE=Asia/Taipei
volumes:
- n8n_data:/home/node/.n8n
depends_on:
- ollama
ollama:
image: ollama/ollama
container_name: ollama
restart: always
ports:
- "11434:11434"
volumes:
- ollama_data:/root/.ollama
volumes:
n8n_data:
ollama_data:
Workflow 設計基礎
n8n 核心概念
Trigger (觸發) → Node (處理) → Node (處理) → ... → Output (輸出)
常用 Trigger:
- Webhook: 接收 HTTP 請求
- Schedule: 定時執行(cron)
- Email Trigger: 收到郵件時
- Telegram Trigger: 收到訊息時
常用 Node:
- HTTP Request: 呼叫外部 API
- Code: 執行 JavaScript/Python
- IF: 條件分支
- Switch: 多路分支
- AI Agent: LLM 處理
- Set: 設定/轉換資料
第一個 Workflow:定時健康檢查
{
"name": "Service Health Check",
"nodes": [
{
"name": "Schedule Trigger",
"type": "n8n-nodes-base.scheduleTrigger",
"parameters": {
"rule": {
"interval": [{ "field": "minutes", "minutesInterval": 5 }]
}
}
},
{
"name": "Check Services",
"type": "n8n-nodes-base.httpRequest",
"parameters": {
"url": "http://your-api.com/health",
"method": "GET",
"options": {
"timeout": 5000
}
}
},
{
"name": "Check Status",
"type": "n8n-nodes-base.if",
"parameters": {
"conditions": {
"number": [{
"value1": "={{ $json.statusCode }}",
"operation": "notEqual",
"value2": 200
}]
}
}
},
{
"name": "Send Alert",
"type": "n8n-nodes-base.slack",
"parameters": {
"channel": "#alerts",
"text": "Service is DOWN! Status: {{ $json.statusCode }}"
}
}
]
}
實戰案例
案例 1:AI 郵件分類與回覆
這是我最常用的 workflow。收到客戶郵件後,AI 自動分類並草擬回覆。
// n8n Code Node:郵件分類邏輯 // 這個 Code 節點在收到郵件後執行, stream: false, options: { temperature: 0.1 } }) });const email = $input.first().json;
// 呼叫 Ollama 做郵件分類 const response = await fetch('http://ollama:11434/api/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'llama3.1', prompt:
分類以下郵件。只回覆一個分類標籤。可能的分類:
- bug_report(Bug 回報)
- feature_request(功能需求)
- general_inquiry(一般詢問)
- urgent(緊急問題)
郵件主旨:${email.subject} 郵件內容:${email.text}
分類:
const result = await response.json(); const category = result.response.trim().toLowerCase();
return [{ json: { ...email, ai_category: category, processed_at: new Date().toISOString() } }];
// n8n Code Node:AI 草擬回覆, stream: false, options: { temperature: 0.5, num_predict: 500 } }) });const email = $input.first().json;
const response = await fetch('http://ollama:11434/api/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'llama3.1', prompt:
你是一位客服工程師。請根據以下客戶郵件草擬一份專業友善的回覆。 用繁體中文回覆。分類:${email.ai_category} 客戶主旨:${email.subject} 客戶內容:${email.text}
回覆草稿:
const result = await response.json();
return [{ json: { original_email: email, draft_reply: result.response, needs_review: email.ai_category === 'urgent' } }];
案例 2:PR 合併後自動更新 Changelog
// Webhook Trigger 接收 GitHub webhook (pull_request event), stream: false, options: { temperature: 0.2 } }) });const payload = $input.first().json;
// 只處理 merged 的 PR if (payload.action !== 'closed' || !payload.pull_request.merged) { return []; }
const pr = payload.pull_request;
// 用 AI 生成 changelog 條目 const response = await fetch('http://ollama:11434/api/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'llama3.1', prompt:
根據以下 Pull Request 資訊,生成一條簡潔的 changelog 條目。 格式:- [類型] 描述 (#PR號) 類型可以是:feat, fix, docs, refactor, test, chorePR 標題:${pr.title} PR 描述:${pr.body} PR 號碼:#${pr.number} 變更的檔案:${pr.changed_files} 個
Changelog 條目:
const changelogEntry = (await response.json()).response.trim();
return [{ json: { pr_number: pr.number, pr_title: pr.title, changelog_entry: changelogEntry, merged_at: pr.merged_at, author: pr.user.login } }];
案例 3:每日技術新聞摘要
// Schedule Trigger: 每天早上 8:00, stream: false, options: { temperature: 0.3, num_predict: 1000 } }) });// Step 1: 抓取 Hacker News 前 10 則 const hnResponse = await fetch( 'https://hacker-news.firebaseio.com/v0/topstories.json' ); const topIds = (await hnResponse.json()).slice(0, 10);
const stories = []; for (const id of topIds) { const storyRes = await fetch(
https://hacker-news.firebaseio.com/v0/item/${id}.json); const story = await storyRes.json(); stories.push({ title: story.title, url: story.url ||https://news.ycombinator.com/item?id=${id}, score: story.score, comments: story.descendants || 0 }); }// Step 2: 用 AI 生成中文摘要 const storyList = stories .map((s, i) =>
${i + 1}. ${s.title} (${s.score} points)) .join('\n');const summaryRes = await fetch('http://ollama:11434/api/generate', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'llama3.1', prompt:
以下是今天 Hacker News 的熱門文章。 請用繁體中文為每一則寫一句話的摘要,並標注跟後端工程相關的文章。${storyList}
摘要:
const summary = (await summaryRes.json()).response;
// Step 3: 組裝 Slack 訊息 const slackMessage =
<em>:newspaper: 今日技術新聞摘要</em>\n\n${summary}\n\n+ stories.map(s =>• <${s.url}|${s.title}> (${s.score} pts)).join('\n');return [{ json: { message: slackMessage, story_count: stories.length, generated_at: new Date().toISOString() } }];
案例 4:文件變更通知
// Webhook: 接收 Git push event
const payload = $input.first().json;
const commits = payload.commits || [];
// 找出改了文件的 commit
const docChanges = commits.filter(commit => {
const allFiles = [
...(commit.added || []),
...(commit.modified || []),
];
return allFiles.some(f =>
f.endsWith('.md') || f.includes('docs/') || f.includes('README')
);
});
if (docChanges.length === 0) {
return []; // 沒有文件變更,不通知
}
// 整理變更清單
const changes = docChanges.map(commit => ({
message: commit.message,
author: commit.author.name,
files: [...(commit.added || []), ...(commit.modified || [])]
.filter(f => f.endsWith('.md') || f.includes('docs/')),
timestamp: commit.timestamp
}));
return [{
json: {
changes,
repo: payload.repository.full_name,
branch: payload.ref.replace('refs/heads/', ''),
notification_text: 文件更新:${changes.length} 個 commit 修改了文件
}
}];
進階技巧
錯誤處理
// 在 Code 節點中做完善的錯誤處理
try {
const response = await fetch('http://ollama:11434/api/generate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
model: 'llama3.1',
prompt: $input.first().json.prompt,
stream: false,
}),
signal: AbortSignal.timeout(30000), // 30 秒超時
});
if (!response.ok) {
throw new Error(Ollama API 回傳 ${response.status});
}
const result = await response.json();
return [{ json: { success: true, response: result.response } }];
} catch (error) {
// 記錄錯誤但不中斷 workflow
console.error('AI 處理失敗:', error.message);
return [{
json: {
success: false,
error: error.message,
fallback: '(AI 處理失敗,需要人工處理)'
}
}];
}
環境變數管理
# .env 檔案
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=change-me
OLLAMA_BASE_URL=http://ollama:11434
SLACK_WEBHOOK_URL=https://hooks.slack.com/services/xxx/yyy/zzz
GITHUB_TOKEN=ghp_xxxxxxxxxxxxx
# docker-compose.yml 中引用
services:
n8n:
env_file:
- .env
environment:
- N8N_BASIC_AUTH_USER=${N8N_BASIC_AUTH_USER}
- N8N_BASIC_AUTH_PASSWORD=${N8N_BASIC_AUTH_PASSWORD}
效能與安全注意事項
- API 限流:呼叫外部 API 時要注意 rate limit,在 n8n 中可以用 Wait 節點做間隔
- 資料保留:n8n 預設會保留所有執行紀錄,時間久了硬碟會爆。設定
EXECUTIONS_DATA_MAX_AGE=168(小時)自動清理
- Webhook 安全:對外的 webhook 要加驗證。GitHub webhook 可以用 secret token 驗證簽章
- LLM 回應不穩定:AI 的輸出不是 100% 可預測的。重要的自動化流程,AI 的輸出最好只做「建議」,不要直接當「決策」
小結
n8n + AI 是一個很強大的組合。n8n 負責「什麼時候做」和「怎麼串」,AI 負責「理解」和「判斷」。以前很多需要人工介入的自動化流程,現在都可以用 AI 節點來填補那個「需要思考」的缺口。
我的建議是:
- 先從簡單的非 AI 自動化開始,熟悉 n8n 的 workflow 概念
- 加入 AI 節點處理「需要判斷」的步驟,比如分類、摘要、草擬回覆
- 永遠保留人工 review 的環節,至少在初期不要讓 AI 全自動執行關鍵操作
- 監控和記錄,建立 dashboard 追蹤自動化的執行狀況
延伸閱讀:
- n8n 官方文件:https://docs.n8n.io
- n8n 社群 workflow 範本庫
- Zapier vs n8n 比較
- Make (Integromat) — 另一個自動化平台