Skip to Content

26d - 错误处理模式

本文是《AI Agent 实战手册》第 26 章第 4 节。 上一节:26c-工作流蓝图集 | 下一节:26e-Webhook与高级集成

概述

生产环境中的 AI 工作流不可避免地会遇到 API 超时、速率限制、模型幻觉、数据格式异常等各类故障。本节系统讲解 n8n 和 Make.com 的错误处理机制,覆盖死信队列(Dead Letter Queue)、指数退避(Exponential Backoff)、降级路径(Fallback/Degradation Path)和告警(Alerting)四大核心模式,帮助你构建能在故障中自愈、在异常中降级、在失败后告警的生产级工作流。


1. 错误处理基础:故障分类与策略选择

1.1 AI 工作流常见故障类型

故障类型典型表现频率严重度推荐策略
瞬时网络错误连接超时、DNS 解析失败自动重试 + 指数退避
API 速率限制HTTP 429 Too Many Requests中高指数退避 + 队列限流
模型服务不可用HTTP 500/502/503模型降级 + 备用提供商
认证过期HTTP 401/403告警 + 人工介入
数据格式异常JSON 解析失败、字段缺失数据验证 + 降级路径
LLM 输出异常幻觉、格式不符、拒绝回答中高输出验证 + 重试/降级
上游服务变更API 字段变更、Schema 漂移告警 + 死信队列
资源耗尽内存溢出、磁盘满极高告警 + 熔断

1.2 错误处理决策树

工作流执行出错 ├── 错误是否为瞬时故障?(网络超时、429、5xx) │ ├── 是 → 自动重试(指数退避) │ │ ├── 重试成功 → 继续执行 │ │ └── 重试耗尽 → 进入死信队列 + 告警 │ └── 否 → 错误是否可降级? │ ├── 是 → 执行降级路径(备用模型/缓存/默认值) │ │ └── 降级成功 → 继续执行(标记降级) │ └── 否 → 错误是否为数据问题? │ ├── 是 → 记录到死信队列 + 跳过当前项 │ └── 否 → 终止执行 + 紧急告警

1.3 错误处理工具与服务

工具/服务用途价格适用场景
n8n Error Trigger全局错误捕获与告警免费(内置节点)所有 n8n 工作流
n8n Error Workflow指定工作流的错误处理链免费(内置功能)生产工作流错误路由
n8n Retry on Fail节点级自动重试免费(内置功能)瞬时故障自动恢复
Make.com Error Handler模块级错误处理路由免费(内置功能)Make 场景错误分支
Make.com Break 指令不完整执行队列 + 自动重试免费(内置功能)类死信队列模式
Make.com Incomplete Executions失败执行存储与重放免费(内置功能)手动/自动重试失败任务
Slack API告警通知渠道免费(基础)/ Pro $8.75/人/月团队实时告警
PagerDuty事件管理与升级$21/用户/月起关键业务告警升级
Sentry错误追踪与聚合免费层可用 / Team $26/月错误趋势分析
Grafana + Prometheus监控仪表板免费(自托管)/ Cloud 免费层可用工作流健康度监控
Supabase / PostgreSQL错误日志持久化免费层可用 / Pro $25/月死信队列数据存储
Google Sheets轻量错误日志免费小规模错误追踪
Redis重试状态与计数器免费(自托管)/ Cloud $5/月起分布式重试状态管理
BetterStack (Logtail)日志聚合与告警免费层 1GB/月 / Pro $25/月集中式日志管理

💡 价格信息截止日期:2025 年 7 月。请以各服务官方最新定价为准。


2. n8n 错误处理机制详解

2.1 n8n 错误处理三层架构

n8n 提供三个层级的错误处理机制,从细粒度到全局依次为:

┌─────────────────────────────────────────────┐ │ 第三层:Error Workflow(全局错误工作流) │ │ ┌─────────────────────────────────────────┐ │ │ │ 第二层:Error Output(节点错误输出分支) │ │ │ │ ┌─────────────────────────────────────┐ │ │ │ │ │ 第一层:Retry on Fail(节点自动重试)│ │ │ │ │ └─────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────┘ │ └─────────────────────────────────────────────┘
层级机制作用范围触发条件典型用途
第一层Retry on Fail单个节点节点执行失败瞬时故障自动恢复
第二层Error Output单个节点的下游分支节点执行失败且重试耗尽降级路径、错误数据路由
第三层Error Workflow整个工作流工作流执行失败(未被捕获)全局告警、日志记录

2.2 第一层:Retry on Fail(节点级自动重试)

配置方法

在任意节点的 Settings 面板中启用:

  1. 打开节点设置 → Settings 标签
  2. 开启 Retry on Fail
  3. 配置参数:
    • Max Tries:最大重试次数(建议 2-5 次)
    • Wait Between Tries (ms):重试间隔(毫秒)

操作步骤

步骤 1:识别需要重试的节点

优先为以下类型的节点启用重试:

  • HTTP Request 节点(外部 API 调用)
  • 数据库操作节点(Postgres、MySQL、MongoDB)
  • 第三方服务节点(Slack、Gmail、Notion)
  • AI/LLM 节点(OpenAI、Anthropic、Google AI)
步骤 2:配置重试参数
{ "retryOnFail": true, "maxTries": 3, "waitBetweenTries": 2000 }

推荐配置表

节点类型Max TriesWait Between (ms)说明
HTTP Request(一般 API)32000覆盖大部分瞬时故障
OpenAI / Anthropic 节点35000LLM 服务恢复较慢
数据库写入21000避免重复写入
邮件发送23000防止重复发送
Webhook 回调32000确保通知送达
步骤 3:注意幂等性

⚠️ 关键提醒:启用重试前,必须确认操作是幂等的(重复执行不会产生副作用)。对于非幂等操作(如创建订单、发送邮件),需要在业务逻辑中加入去重机制。

// n8n Code 节点:幂等性检查示例 const itemId = $input.item.json.id; const processedKey = `processed_${itemId}`; // 检查是否已处理(使用 n8n 静态数据或外部存储) const staticData = $getWorkflowStaticData('global'); if (staticData[processedKey]) { return []; // 已处理,跳过 } // 标记为已处理 staticData[processedKey] = new Date().toISOString(); return $input.item;

2.3 第二层:Error Output(节点错误输出分支)

Error Output 是 n8n 中实现降级路径的核心机制。当节点失败时(且重试耗尽),错误数据会被路由到专门的错误输出分支,而不是终止整个工作流。

配置方法

  1. 选中目标节点
  2. 在节点设置中找到 On Error 选项
  3. 将其从默认的 Stop Workflow 改为 Output Error Data
  4. 节点底部会出现红色的错误输出端口
  5. 从错误输出端口连接后续的降级处理节点

错误输出数据结构

当节点失败时,错误输出会包含以下数据:

{ "error": { "message": "Request failed with status code 429", "name": "NodeApiError", "description": "Rate limit exceeded", "httpCode": "429" }, "pairedItem": { "item": 0 } }

典型应用模式

[HTTP Request: 主 API] ├── ✅ 成功输出 → [后续处理节点] └── ❌ 错误输出 → [Switch: 错误分类] ├── 429 → [Wait 节点] → [HTTP Request: 重试] ├── 401 → [Slack: 认证过期告警] ├── 500 → [HTTP Request: 备用 API] └── 其他 → [Google Sheets: 记录错误] → [Slack: 通用告警]

操作步骤:构建错误分类路由

步骤 1:设置错误输出

将目标节点的 On Error 设置为 Output Error Data

步骤 2:添加 Switch 节点进行错误分类
// Switch 节点条件配置 // 条件 1:速率限制 {{ $json.error.httpCode === "429" }} // 条件 2:认证失败 {{ $json.error.httpCode === "401" || $json.error.httpCode === "403" }} // 条件 3:服务端错误 {{ parseInt($json.error.httpCode) >= 500 }} // 条件 4:其他错误(Fallthrough)
步骤 3:为每种错误类型配置处理逻辑
  • 429 速率限制:添加 Wait 节点(动态等待时间),然后重试
  • 401/403 认证失败:发送紧急告警,终止执行
  • 5xx 服务端错误:切换到备用服务或缓存数据
  • 其他错误:记录日志,发送通用告警

2.4 第三层:Error Workflow(全局错误工作流)

Error Workflow 是 n8n 的全局安全网。当工作流中任何未被捕获的错误发生时,指定的 Error Workflow 会被触发。

配置方法

  1. 创建一个新的工作流,以 Error Trigger 节点开始
  2. 在需要监控的工作流中,打开 Workflow Settings
  3. Error Workflow 下拉菜单中选择刚创建的错误工作流

Error Trigger 节点输出数据

{ "execution": { "id": "231", "url": "https://your-n8n.com/execution/231", "retryOf": null, "error": { "message": "Request failed with status code 500", "name": "NodeApiError" }, "lastNodeExecuted": "HTTP Request", "mode": "trigger" }, "workflow": { "id": "15", "name": "AI 内容管线" } }

操作步骤:构建集中式错误告警工作流

步骤 1:创建 Error Workflow
[Error Trigger] → [Code: 错误分级] → [Switch: 严重度路由] ├── 🔴 Critical → [PagerDuty: 创建事件] + [Slack: #alerts-critical] ├── 🟡 Warning → [Slack: #alerts-warning] └── 🟢 Info → [Google Sheets: 错误日志]
步骤 2:实现错误分级逻辑
// Code 节点:错误严重度分级 const error = $input.item.json.execution.error; const workflow = $input.item.json.workflow; const errorMessage = error.message || ''; let severity = 'info'; let emoji = '🟢'; // Critical:认证失败、数据库连接失败、支付相关 if ( errorMessage.includes('401') || errorMessage.includes('403') || errorMessage.includes('ECONNREFUSED') || workflow.name.toLowerCase().includes('payment') || workflow.name.toLowerCase().includes('billing') ) { severity = 'critical'; emoji = '🔴'; } // Warning:速率限制、超时、5xx else if ( errorMessage.includes('429') || errorMessage.includes('timeout') || errorMessage.includes('ETIMEDOUT') || errorMessage.includes('500') || errorMessage.includes('502') || errorMessage.includes('503') ) { severity = 'warning'; emoji = '🟡'; } return { severity, emoji, workflow_name: workflow.name, workflow_id: workflow.id, error_message: errorMessage, execution_id: $input.item.json.execution.id, execution_url: $input.item.json.execution.url, timestamp: new Date().toISOString() };
步骤 3:配置 Slack 告警消息

提示词模板:Slack 告警消息格式

${emoji} *工作流错误告警* *工作流*:${workflow_name}(ID: ${workflow_id}) *严重度*:${severity} *错误信息*:${error_message} *执行 ID*:${execution_id} *时间*:${timestamp} <${execution_url}|查看执行详情>
步骤 4:将 Error Workflow 绑定到所有生产工作流

💡 最佳实践:创建一个通用的 Error Workflow,然后在所有生产工作流的设置中统一指向它。这样可以集中管理告警逻辑,避免每个工作流重复配置。


3. Make.com 错误处理机制详解

3.1 Make.com 五大错误处理指令

Make.com 提供五种错误处理指令(Error Handler Directives),每种对应不同的故障恢复策略:

指令行为执行状态后续模块适用场景
Rollback立即停止执行,回滚所有操作❌ 错误不执行默认行为,数据一致性要求高
Commit立即停止执行,提交已完成操作✅ 成功不执行部分成功可接受的场景
Ignore忽略错误,跳过后续模块✅ 成功不执行非关键操作,允许跳过
Resume提供替代输出,继续执行✅ 成功继续执行降级路径,使用默认值
Break存入不完整执行队列,可自动重试⚠️ 警告不执行类死信队列,延迟重试

3.2 错误处理指令详解

Rollback(回滚)

行为:停止场景执行,回滚所有已执行的操作(如果服务支持事务回滚)。

配置方法

  1. 右键点击可能出错的模块
  2. 选择 Add error handler
  3. 在错误路由中添加 Rollback 指令模块

适用场景

  • 金融交易:转账失败时必须回滚
  • 数据同步:部分同步会导致数据不一致
  • 批量操作:一个失败则全部回滚
[模块 A: 扣款] → [模块 B: 发货] → [模块 C: 通知] └── ❌ Error Handler → [Rollback] (模块 A 的扣款也会被回滚)

Commit(提交)

行为:停止场景执行,但保留已完成操作的结果。

适用场景

  • 批量处理中,部分成功是可接受的
  • 日志记录类操作,已写入的日志无需回滚

Ignore(忽略)

行为:忽略当前模块的错误,跳过该模块后续的所有模块,场景标记为成功。

适用场景

  • 非关键的通知发送(如 Slack 消息发送失败不影响核心流程)
  • 可选的数据增强步骤
[获取数据] → [AI 摘要生成] → [发送 Slack 通知] └── ❌ Error Handler → [Ignore] (通知失败不影响整体流程)

Resume(恢复)

行为:提供一个替代输出值,场景使用该替代值继续执行后续模块。这是实现降级路径的核心指令。

配置方法

  1. 添加 Error Handler
  2. 在错误路由中添加 Resume 指令模块
  3. 在 Resume 模块中配置替代输出(硬编码的默认值或从其他来源获取的备用数据)

适用场景

  • AI 摘要生成失败时,使用原文前 200 字作为替代
  • 图片生成失败时,使用默认占位图
  • 翻译失败时,保留原文
[AI 摘要生成] ├── ✅ 成功 → [后续处理](使用 AI 摘要) └── ❌ Error Handler → [Text: 截取原文前200字] → [Resume](使用截取文本作为替代输出) → [后续处理](使用替代摘要继续)

Break(中断 + 不完整执行队列)

行为:将当前执行存入”不完整执行”(Incomplete Executions)队列,可配置自动重试。这是 Make.com 中最接近死信队列的机制。

配置前提

  1. 进入场景设置
  2. 开启 Allow storing of incomplete executions → 设为 Yes

配置参数

  • Automatically complete execution:是否自动重试
    • Yes:自动重试(类似自动重放死信队列)
    • No:手动处理(需要人工在 Incomplete Executions 面板中重放)
  • Number of consecutive attempts:自动重试次数(1-10)
  • Interval between attempts:重试间隔(分钟)

适用场景

  • 第三方 API 临时不可用,需要稍后重试
  • 批量处理中某些项目失败,需要单独重试
  • 需要人工审查后决定是否重试
[HTTP Request: 调用外部 API] └── ❌ Error Handler → [Break] ├── 自动重试:3 次,间隔 15 分钟 └── 重试耗尽 → 留在 Incomplete Executions 队列 → 人工在面板中查看并处理

3.3 Make.com 错误处理指令选择决策表

场景推荐指令原因
支付/转账失败Rollback必须保证数据一致性
批量邮件发送,个别失败Ignore个别失败不影响整体
AI 生成失败,有备用方案Resume使用默认值继续流程
外部 API 临时不可用Break存入队列稍后重试
日志写入失败Commit已完成的操作无需回滚
关键数据同步失败Break + 告警重试 + 通知人工介入

3.4 Make.com 错误处理操作步骤

步骤 1:为关键模块添加 Error Handler

  1. 右键点击目标模块
  2. 选择 Add error handler
  3. 系统会在该模块下方创建一条错误路由(虚线连接)

步骤 2:在错误路由中添加处理逻辑

错误路由中可以添加任意模块(如发送通知、记录日志),最后以一个指令模块结束:

[目标模块] └── ❌ Error Route → [Slack: 发送错误通知] → [Google Sheets: 记录错误日志] → [Resume / Break / Ignore / Rollback / Commit]

步骤 3:配置 Break 指令的自动重试

  1. 进入场景 Settings(齿轮图标)
  2. Allow storing of incomplete executionsYes
  3. Automatically complete executionYes
  4. Number of consecutive attempts3
  5. Interval between attempts15(分钟)

⚠️ 注意:Break 指令的重试间隔是固定的(不支持指数退避)。如果需要指数退避效果,需要通过自定义逻辑实现(见第 4 节)。


4. 指数退避(Exponential Backoff)

4.1 指数退避原理

指数退避是一种渐进式重试策略:每次重试的等待时间按指数增长,避免在服务恢复期间产生”重试风暴”。

公式

等待时间 = min(base_delay × 2^(attempt - 1) + random_jitter, max_delay)

示例(base_delay = 1s, max_delay = 60s):

重试次数等待时间(无抖动)等待时间(含抖动)
第 1 次1 秒1.0 - 1.5 秒
第 2 次2 秒2.0 - 3.0 秒
第 3 次4 秒4.0 - 6.0 秒
第 4 次8 秒8.0 - 12.0 秒
第 5 次16 秒16.0 - 24.0 秒
第 6 次32 秒32.0 - 48.0 秒
第 7 次60 秒(上限)60.0 秒(上限)

💡 为什么需要随机抖动(Jitter)? 当多个工作流同时遇到同一个 API 的速率限制时,如果它们都在完全相同的时间点重试,会再次同时触发限制。添加随机抖动可以分散重试时间,降低碰撞概率。

4.2 n8n 中实现指数退避

n8n 内置的 Retry on Fail 使用固定间隔,不支持指数退避。需要通过 Code 节点 + Wait 节点手动实现。

方案一:Loop + Wait 模式(推荐)

[触发器] → [设置重试参数] → [Loop] → [HTTP Request: API 调用] ├── ✅ 成功 → [跳出循环] → [后续处理] └── ❌ 错误输出 → [Code: 计算退避时间] → [IF: 是否超过最大重试次数?] ├── 是 → [跳出循环] → [错误处理/死信队列] └── 否 → [Wait: 动态等待] → [回到循环开始]

操作步骤

步骤 1:初始化重试参数

使用 Set 节点设置初始参数:

{ "attempt": 0, "maxAttempts": 5, "baseDelay": 1000, "maxDelay": 60000 }
步骤 2:实现退避时间计算
// Code 节点:计算指数退避等待时间 const attempt = $input.item.json.attempt + 1; const baseDelay = $input.item.json.baseDelay || 1000; const maxDelay = $input.item.json.maxDelay || 60000; // 指数退避 + 随机抖动 const exponentialDelay = baseDelay * Math.pow(2, attempt - 1); const jitter = Math.random() * exponentialDelay * 0.5; const waitTime = Math.min(exponentialDelay + jitter, maxDelay); return { attempt, maxAttempts: $input.item.json.maxAttempts, baseDelay, maxDelay, waitTime: Math.round(waitTime), waitTimeSeconds: Math.round(waitTime / 1000), error: $input.item.json.error };
步骤 3:配置 Wait 节点
  1. 添加 Wait 节点
  2. Resume 设置为 After Time Interval
  3. Amount 使用表达式:{{ $json.waitTimeSeconds }}
  4. Unit 设置为 Seconds
步骤 4:配置循环退出条件

使用 IF 节点判断:

条件:{{ $json.attempt >= $json.maxAttempts }} ├── true → 退出循环,进入错误处理 └── false → 继续循环,回到 API 调用

方案二:Sub-Workflow 递归模式

对于复杂场景,可以将重试逻辑封装为独立的子工作流:

[主工作流] → [Execute Sub-Workflow: 带退避的 API 调用] ├── 输入:url, method, body, attempt, maxAttempts ├── 内部逻辑: │ [HTTP Request] │ ├── ✅ 成功 → 返回结果 │ └── ❌ 失败 → [计算退避] → [Wait] → [递归调用自身] └── 输出:API 响应或最终错误

4.3 Make.com 中实现指数退避

Make.com 的 Break 指令只支持固定间隔重试。要实现指数退避,需要使用以下模式:

方案:Clone + Sleep + Resume 模式

[模块 A: API 调用] └── ❌ Error Handler → [Set Variable: retry_count + 1] → [Router] ├── 条件:retry_count <= max_retries │ → [Sleep: 动态计算时间] │ → [模块 A 的克隆: 重试 API 调用] │ ├── ✅ 成功 → [Resume](使用重试结果继续) │ └── ❌ 失败 → [回到 Router] └── 条件:retry_count > max_retries → [Slack: 告警通知] → [Google Sheets: 记录到死信队列] → [Ignore / Break]

操作步骤

步骤 1:设置重试计数器

在场景开始处添加 Set Variable 模块:

  • 变量名:retry_count
  • 初始值:0
步骤 2:计算动态等待时间

在错误路由中添加 Set Variable 模块:

变量名:wait_seconds 值:{{min(pow(2, retry_count) * 1 + floor(random * pow(2, retry_count) * 0.5), 60)}}
步骤 3:添加 Sleep 模块

使用 Tools > Sleep 模块:

  • 延迟时间:{{wait_seconds}}
步骤 4:克隆原始模块并重试
  1. 复制(克隆)原始 API 调用模块
  2. 将克隆模块放在 Sleep 之后
  3. 克隆模块成功时,使用 Resume 指令将结果传回主路径

4.4 指数退避提示词模板

你是一个 n8n 工作流错误处理专家。请为以下场景设计指数退避重试逻辑: **场景描述**:[描述你的 API 调用场景,例如"调用 OpenAI API 生成文章摘要"] **预期错误类型**:[例如"429 速率限制、500 服务器错误、网络超时"] **最大重试次数**:[例如 5] **基础延迟**:[例如 1 秒] **最大延迟**:[例如 60 秒] 请提供: 1. 完整的 n8n 节点拓扑图(ASCII 格式) 2. Code 节点中的退避时间计算代码 3. 各节点的关键配置参数 4. 重试耗尽后的处理建议

5. 死信队列(Dead Letter Queue)

5.1 死信队列概念

死信队列(DLQ)是一种消息处理模式:当消息经过多次重试仍无法成功处理时,将其转移到一个专门的”死信”存储中,供后续人工审查或自动重放。

在 n8n/Make.com 的上下文中,“死信”指的是经过所有重试和降级策略后仍然失败的工作流执行数据。

5.2 n8n 死信队列实现

n8n 没有内置的死信队列功能,但可以通过以下模式实现:

架构设计

[生产工作流] → [节点执行失败] → [重试耗尽] → [Error Output / Error Workflow] → [DLQ Writer: 写入死信队列] ├── 存储到 Postgres/Supabase 表 ├── 或存储到 Google Sheets └── 或存储到 Redis List [DLQ 处理工作流](独立工作流) → [Schedule Trigger: 定时扫描] → [读取 DLQ 中的待处理项] → [逐项重试] ├── ✅ 成功 → 标记为已处理 └── ❌ 失败 → 更新重试计数 ├── 未超限 → 保留在队列中 └── 已超限 → 标记为永久失败 + 告警

操作步骤

步骤 1:创建死信队列存储表

使用 Supabase/PostgreSQL:

CREATE TABLE dead_letter_queue ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, workflow_id VARCHAR(50) NOT NULL, workflow_name VARCHAR(255) NOT NULL, execution_id VARCHAR(50), node_name VARCHAR(255), error_message TEXT, error_code VARCHAR(20), input_data JSONB, retry_count INTEGER DEFAULT 0, max_retries INTEGER DEFAULT 5, status VARCHAR(20) DEFAULT 'pending', -- pending, retrying, resolved, dead created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW(), next_retry_at TIMESTAMP, resolved_at TIMESTAMP ); -- 索引优化 CREATE INDEX idx_dlq_status ON dead_letter_queue(status); CREATE INDEX idx_dlq_next_retry ON dead_letter_queue(next_retry_at) WHERE status = 'pending'; CREATE INDEX idx_dlq_workflow ON dead_letter_queue(workflow_id);
步骤 2:在错误处理中写入 DLQ

在 Error Workflow 或 Error Output 分支中添加 Postgres 节点:

INSERT INTO dead_letter_queue (workflow_id, workflow_name, execution_id, node_name, error_message, error_code, input_data, next_retry_at) VALUES ($1, $2, $3, $4, $5, $6, $7::jsonb, NOW() + INTERVAL '15 minutes')

参数映射:

  • $1{{ $json.workflow.id }}
  • $2{{ $json.workflow.name }}
  • $3{{ $json.execution.id }}
  • $4{{ $json.execution.lastNodeExecuted }}
  • $5{{ $json.execution.error.message }}
  • $6{{ $json.execution.error.httpCode || 'UNKNOWN' }}
  • $7{{ JSON.stringify($json.originalInput || {}) }}
步骤 3:创建 DLQ 处理工作流
[Schedule Trigger: 每 15 分钟] → [Postgres: 查询待处理项] SELECT * FROM dead_letter_queue WHERE status = 'pending' AND next_retry_at <= NOW() ORDER BY created_at ASC LIMIT 10 → [Loop Over Items] → [Code: 构建重试请求] → [HTTP Request: 重试原始操作] ├── ✅ 成功 │ → [Postgres: UPDATE status='resolved', resolved_at=NOW()] └── ❌ 失败 → [Code: 更新重试计数和下次重试时间] → [IF: retry_count >= max_retries?] ├── 是 → [Postgres: UPDATE status='dead'] │ → [Slack: 永久失败告警] └── 否 → [Postgres: UPDATE retry_count, next_retry_at]
步骤 4:DLQ 重试时间计算
// Code 节点:计算下次重试时间(指数退避) const retryCount = $input.item.json.retry_count + 1; const baseMinutes = 15; const maxMinutes = 1440; // 24 小时 const delayMinutes = Math.min( baseMinutes * Math.pow(2, retryCount - 1), maxMinutes ); const nextRetryAt = new Date( Date.now() + delayMinutes * 60 * 1000 ).toISOString(); return { ...$input.item.json, retry_count: retryCount, next_retry_at: nextRetryAt, delay_minutes: delayMinutes };

5.3 Make.com 死信队列实现

Make.com 的 Incomplete Executions(不完整执行)功能本身就是一种内置的死信队列机制。

内置方案:Incomplete Executions

工作原理

  1. 当使用 Break 指令时,失败的执行数据会被存入 Incomplete Executions 队列
  2. 可以配置自动重试(次数和间隔)
  3. 重试耗尽后,数据保留在队列中供人工处理
  4. 在 Make.com 控制台的 Incomplete Executions 面板中可以查看、编辑和重放

配置步骤

  1. 场景设置 → Allow storing of incomplete executionsYes
  2. 场景设置 → Sequential processingYes(推荐,避免并发冲突)
  3. 在需要 DLQ 的模块上添加 Error Handler → Break
  4. 配置 Break 的自动重试参数

限制

  • 不完整执行有存储上限(取决于 Make.com 套餐)
  • 自动重试间隔是固定的,不支持指数退避
  • 无法通过 API 批量管理不完整执行

增强方案:外部 DLQ 存储

对于需要更灵活控制的场景,可以将失败数据写入外部存储:

[目标模块] └── ❌ Error Handler → [HTTP Request: POST 到 Supabase/Airtable] Body: { "scenario_id": "{{scenario.id}}", "scenario_name": "{{scenario.name}}", "error": "{{error.message}}", "input_data": {{toJSON(input)}}, "timestamp": "{{now}}" } → [Slack: 发送 DLQ 入队通知] → [Ignore](让场景继续处理其他项目)

5.4 DLQ 监控仪表板

关键指标

指标计算方式告警阈值说明
队列深度COUNT WHERE status=‘pending’> 50积压过多需要关注
入队速率过去 1 小时新增数量> 20/小时可能存在系统性问题
平均处理时间AVG(resolved_at - created_at)> 4 小时处理效率下降
永久失败率COUNT(status=‘dead’) / COUNT(*)> 10%需要排查根因
重试成功率COUNT(status=‘resolved’) / COUNT(retried)< 60%重试策略可能需要调整

监控查询示例

-- DLQ 概览仪表板 SELECT status, COUNT(*) as count, AVG(retry_count) as avg_retries, MIN(created_at) as oldest_item, MAX(created_at) as newest_item FROM dead_letter_queue WHERE created_at >= NOW() - INTERVAL '24 hours' GROUP BY status; -- 按工作流分组的失败统计 SELECT workflow_name, COUNT(*) as total_failures, COUNT(*) FILTER (WHERE status = 'resolved') as resolved, COUNT(*) FILTER (WHERE status = 'dead') as permanent_failures, ROUND( COUNT(*) FILTER (WHERE status = 'resolved')::numeric / NULLIF(COUNT(*), 0) * 100, 1 ) as resolution_rate_pct FROM dead_letter_queue WHERE created_at >= NOW() - INTERVAL '7 days' GROUP BY workflow_name ORDER BY total_failures DESC;

6. 降级路径(Fallback / Degradation Path)

6.1 降级策略概览

降级路径是指当主要服务不可用时,自动切换到备用方案以保证工作流继续运行(可能以降低的质量或功能)。

降级策略描述质量影响实现复杂度适用场景
模型降级主模型不可用时切换到备用模型LLM 调用
提供商降级主提供商不可用时切换到备用提供商API 调用
缓存降级使用缓存的历史结果中高数据查询、AI 生成
默认值降级使用预设的默认值非关键字段填充
功能降级跳过非关键功能步骤可选增强步骤
人工降级转交人工处理关键业务决策

6.2 n8n 降级路径实现

模式一:LLM 模型降级链

当主 LLM 不可用时,自动切换到备用模型:

[输入数据] → [OpenAI: GPT-4.1](主模型) ├── ✅ 成功 → [输出验证] → [后续处理] └── ❌ Error Output → [Anthropic: Claude Sonnet](备用模型 1) ├── ✅ 成功 → [输出验证] → [后续处理] └── ❌ Error Output → [Google AI: Gemini 2.5 Flash](备用模型 2) ├── ✅ 成功 → [输出验证] → [后续处理] └── ❌ Error Output → [缓存/默认值降级] → [后续处理(标记降级)]

操作步骤

步骤 1:配置主模型节点
  1. 添加 OpenAI 节点(或 HTTP Request 调用 OpenAI API)
  2. 设置 On ErrorOutput Error Data
  3. 配置 Retry on Fail:2 次,间隔 3000ms
步骤 2:配置备用模型节点
  1. 从主模型的错误输出连接 Anthropic 节点
  2. 使用相同的 prompt(通过表达式引用原始输入)
  3. 同样设置 On ErrorOutput Error Data
步骤 3:配置最终降级
// Code 节点:最终降级逻辑 const originalInput = $input.item.json.originalText || ''; // 使用简单的文本截取作为降级摘要 const fallbackSummary = originalInput.substring(0, 200) + '...'; return { summary: fallbackSummary, model_used: 'fallback_truncation', is_degraded: true, degradation_reason: 'All LLM providers unavailable', timestamp: new Date().toISOString() };
步骤 4:在后续处理中标记降级状态
// 在输出中添加降级标记 const isDegraded = $input.item.json.is_degraded || false; const modelUsed = $input.item.json.model_used || 'gpt-4.1'; return { ...$input.item.json, metadata: { model_used: modelUsed, is_degraded: isDegraded, processed_at: new Date().toISOString() } };

模式二:缓存降级

[输入数据] → [Code: 生成缓存键] → [Redis/Supabase: 查询缓存] → [IF: 缓存命中?] ├── 是 → [使用缓存数据](跳过 API 调用) └── 否 → [API 调用] ├── ✅ 成功 → [Redis/Supabase: 写入缓存] → [后续处理] └── ❌ 失败 → [使用默认值] → [后续处理(标记降级)]

模式三:功能降级(跳过非关键步骤)

[核心数据处理] → [AI 情感分析](可选增强) ├── ✅ 成功 → [合并结果] └── ❌ Error Output → [Set: sentiment = "unknown"] → [合并结果] → [AI 关键词提取](可选增强) ├── ✅ 成功 → [合并结果] └── ❌ Error Output → [Set: keywords = []] → [合并结果] → [写入数据库](核心操作,不降级)

6.3 Make.com 降级路径实现

使用 Resume 指令实现降级

[OpenAI: 生成摘要] ├── ✅ 成功 → [后续处理] └── ❌ Error Handler → [Router] ├── 路径 1:尝试备用模型 │ → [HTTP Request: Anthropic API] │ ├── ✅ 成功 → [Resume](使用备用结果) │ └── ❌ Error Handler → [路径 2] └── 路径 2:使用默认值 → [Text: 截取原文前200字] → [Resume](使用默认值)

使用 Router 实现条件降级

[HTTP Request: 主 API] └── ❌ Error Handler → [Set Variable: error_type = {{error.code}}] → [Router] ├── 条件:error_type = 429(速率限制) │ → [Sleep: 30秒] → [HTTP Request: 重试] → [Resume] ├── 条件:error_type >= 500(服务端错误) │ → [HTTP Request: 备用 API] → [Resume] └── 默认路径 → [Slack: 告警] → [Ignore]

6.4 降级路径提示词模板

你是一个 [n8n/Make.com] 工作流架构师。请为以下 AI 工作流设计完整的降级路径: **工作流描述**:[描述你的工作流,例如"从 RSS 抓取文章 → AI 生成摘要 → 翻译 → 发布到 Slack"] **关键节点**:[列出不能失败的核心节点] **可降级节点**:[列出可以降级的非关键节点] **可用的备用服务**:[例如"备用 LLM: Claude, Gemini; 备用通知: Email"] 请提供: 1. 每个可降级节点的降级策略(模型降级/缓存降级/默认值降级) 2. 降级后的数据标记方案(如何标识降级数据) 3. 降级触发条件和恢复条件 4. 完整的节点拓扑图

7. 告警系统(Alerting)

7.1 告警分级体系

级别名称触发条件通知渠道响应时间
P0🔴 紧急核心业务流程完全中断PagerDuty + 电话 + Slack< 15 分钟
P1🟠 严重关键功能降级或部分不可用Slack #alerts-critical + 邮件< 1 小时
P2🟡 警告非关键功能异常或性能下降Slack #alerts-warning< 4 小时
P3🟢 信息可预期的错误或需要关注的趋势日志记录 + 周报汇总下个工作日

7.2 n8n 告警实现

方案一:集中式 Error Workflow 告警

这是最推荐的方案,所有工作流共享一个告警工作流:

[Error Trigger] → [Code: 错误分级 + 去重检查] → [Switch: 按严重度路由] ├── P0 紧急 │ → [PagerDuty: 创建事件] │ → [Slack: #alerts-critical(@channel)] │ → [Postgres: 记录错误日志] ├── P1 严重 │ → [Slack: #alerts-critical] │ → [Gmail: 发送告警邮件] │ → [Postgres: 记录错误日志] ├── P2 警告 │ → [Slack: #alerts-warning] │ → [Postgres: 记录错误日志] └── P3 信息 → [Postgres: 记录错误日志]

告警去重逻辑

避免同一错误在短时间内产生大量重复告警:

// Code 节点:告警去重 const error = $input.item.json.execution.error; const workflow = $input.item.json.workflow; // 生成去重键:工作流ID + 错误消息的哈希 const dedupeKey = `${workflow.id}_${error.message}`.substring(0, 100); // 检查最近 30 分钟内是否已发送过相同告警 const staticData = $getWorkflowStaticData('global'); const now = Date.now(); const cooldownMs = 30 * 60 * 1000; // 30 分钟冷却期 if (staticData[dedupeKey] && (now - staticData[dedupeKey]) < cooldownMs) { // 在冷却期内,跳过告警但仍记录日志 return { ...formatError($input.item.json), alert_suppressed: true, suppression_reason: 'Duplicate within cooldown period' }; } // 更新最后告警时间 staticData[dedupeKey] = now; // 清理过期的去重记录(避免内存泄漏) for (const key of Object.keys(staticData)) { if (staticData[key] < now - cooldownMs * 2) { delete staticData[key]; } } return { ...formatError($input.item.json), alert_suppressed: false }; function formatError(data) { const error = data.execution.error; const workflow = data.workflow; let severity = 'P3'; let emoji = '🟢'; // P0:支付、认证、数据库连接 if ( workflow.name.match(/payment|billing|order/i) || (error.message && error.message.match(/ECONNREFUSED|connection.*refused/i)) ) { severity = 'P0'; emoji = '🔴'; } // P1:认证过期、持续性 5xx else if ( error.message && error.message.match(/401|403|unauthorized/i) ) { severity = 'P1'; emoji = '🟠'; } // P2:速率限制、超时 else if ( error.message && error.message.match(/429|timeout|ETIMEDOUT|rate.?limit/i) ) { severity = 'P2'; emoji = '🟡'; } return { severity, emoji, workflow_name: workflow.name, workflow_id: workflow.id, error_message: error.message || 'Unknown error', execution_id: data.execution.id, execution_url: data.execution.url, timestamp: new Date().toISOString() }; }

方案二:定时错误汇总报告

除了实时告警,还应该有定期的错误汇总报告:

[Schedule Trigger: 每天 09:00] → [Postgres: 查询过去 24 小时错误统计] → [Code: 生成报告] → [Slack: 发送每日错误摘要]
每日错误报告查询
SELECT workflow_name, COUNT(*) as error_count, COUNT(DISTINCT error_message) as unique_errors, MIN(created_at) as first_occurrence, MAX(created_at) as last_occurrence, ARRAY_AGG(DISTINCT error_code) as error_codes FROM dead_letter_queue WHERE created_at >= NOW() - INTERVAL '24 hours' GROUP BY workflow_name ORDER BY error_count DESC LIMIT 20;
报告格式模板
📊 *每日工作流错误报告* 📅 ${date} *概览* • 总错误数:${totalErrors} • 受影响工作流:${affectedWorkflows} • 已自动恢复:${autoResolved}(${autoResolvedPct}%) • 需要人工处理:${manualRequired} *Top 5 错误工作流* ${top5List} *DLQ 状态* • 队列深度:${queueDepth} • 永久失败:${deadCount} • 待重试:${pendingCount} <${dashboardUrl}|查看完整仪表板>

7.3 Make.com 告警实现

内置告警功能

Make.com 提供内置的执行通知:

  1. 进入场景设置
  2. Notifications 部分:
    • Warning 通知:场景执行出现警告时发送
    • Error 通知:场景执行失败时发送
  3. 通知会发送到 Make.com 账户关联的邮箱

自定义告警增强

内置通知功能有限,建议在错误路由中添加自定义告警:

[目标模块] └── ❌ Error Handler → [Slack: 发送告警] Channel: #automation-alerts Message: "⚠️ 场景 '{{scenario.name}}' 执行失败 模块: {{failedModule.name}} 错误: {{error.message}} 时间: {{formatDate(now; 'YYYY-MM-DD HH:mm:ss')}}" → [Google Sheets: 记录错误日志] Spreadsheet: "Automation Error Log" Row: [{{now}}, {{scenario.name}}, {{error.message}}, {{error.type}}] → [Break / Resume / Ignore]

7.4 告警通知渠道配置

Slack 告警配置(n8n)

步骤 1:创建 Slack App 和 Webhook
  1. 访问 api.slack.com/apps 
  2. 创建新 App → From scratch
  3. 启用 Incoming Webhooks
  4. 添加 Webhook 到目标频道
  5. 复制 Webhook URL
步骤 2:在 n8n 中配置 Slack 节点
  1. 添加 Slack 节点
  2. 配置凭证(OAuth 或 Webhook)
  3. 设置消息格式:
{ "blocks": [ { "type": "header", "text": { "type": "plain_text", "text": "${emoji} 工作流错误告警 [${severity}]" } }, { "type": "section", "fields": [ { "type": "mrkdwn", "text": "*工作流:*\n${workflow_name}" }, { "type": "mrkdwn", "text": "*严重度:*\n${severity}" }, { "type": "mrkdwn", "text": "*错误信息:*\n${error_message}" }, { "type": "mrkdwn", "text": "*时间:*\n${timestamp}" } ] }, { "type": "actions", "elements": [ { "type": "button", "text": { "type": "plain_text", "text": "查看执行详情" }, "url": "${execution_url}" } ] } ] }

邮件告警配置

对于 P0/P1 级别的告警,建议同时发送邮件:

[Switch: P0 或 P1] → [Gmail: 发送告警邮件] To: [ops-team@company.com] Subject: "[${severity}] 工作流告警: ${workflow_name}" Body: " 工作流 '${workflow_name}' 执行失败。 错误信息:${error_message} 执行 ID:${execution_id} 时间:${timestamp} 请立即查看:${execution_url} "

PagerDuty 集成(P0 级别)

对于最高优先级的告警,集成 PagerDuty 实现电话/短信通知和值班升级:

[Switch: P0] → [HTTP Request: PagerDuty Events API v2] Method: POST URL: https://events.pagerduty.com/v2/enqueue Body: { "routing_key": "[你的 PagerDuty Integration Key]", "event_action": "trigger", "payload": { "summary": "工作流 '${workflow_name}' 严重故障", "severity": "critical", "source": "n8n-automation", "component": "${workflow_name}", "custom_details": { "error_message": "${error_message}", "execution_url": "${execution_url}" } } }

7.5 告警提示词模板

你是一个自动化运维专家。请为以下 [n8n/Make.com] 工作流设计完整的告警体系: **工作流清单**: [列出你的生产工作流,例如: - AI 内容管线(每日运行) - 线索评分(Webhook 触发) - 数据同步(每小时运行)] **团队规模**:[例如"3 人团队"] **值班制度**:[例如"工作日 9-18 点有人值班"] **现有通知渠道**:[例如"Slack、邮件、企业微信"] 请提供: 1. 告警分级标准(P0-P3)及每级的触发条件 2. 每级告警的通知渠道和响应时间要求 3. 告警去重和抑制策略 4. 每日/每周错误汇总报告的内容和格式 5. 值班升级流程

8. AI 工作流专属错误处理

8.1 LLM 输出验证

AI 工作流中最常见的”隐性错误”是 LLM 输出不符合预期格式。这类错误不会触发 HTTP 错误码,但会导致下游节点解析失败。

常见 LLM 输出问题

问题表现检测方法处理策略
JSON 格式错误输出不是有效 JSONJSON.parse 尝试解析重试(换 prompt)或正则提取
字段缺失缺少必需字段Schema 验证重试或使用默认值
内容幻觉生成虚假信息事实核查(可选)标记为待审核
拒绝回答”I cannot…” 类回复关键词检测调整 prompt 重试
输出过长/过短超出预期长度范围长度检查重试或截断
语言错误输出语言不符合要求语言检测重试(强调语言要求)

n8n LLM 输出验证节点

// Code 节点:LLM 输出验证 const llmOutput = $input.item.json.text || $input.item.json.output || ''; const validationResult = { raw_output: llmOutput, is_valid: true, errors: [], parsed_data: null }; // 1. 检查是否为空 if (!llmOutput || llmOutput.trim().length === 0) { validationResult.is_valid = false; validationResult.errors.push('LLM 输出为空'); return validationResult; } // 2. 尝试 JSON 解析(如果期望 JSON 输出) try { // 尝试从 markdown 代码块中提取 JSON const jsonMatch = llmOutput.match(/```(?:json)?\s*([\s\S]*?)```/); const jsonStr = jsonMatch ? jsonMatch[1].trim() : llmOutput.trim(); validationResult.parsed_data = JSON.parse(jsonStr); } catch (e) { validationResult.is_valid = false; validationResult.errors.push(`JSON 解析失败: ${e.message}`); } // 3. 检查必需字段(根据你的 schema 调整) if (validationResult.parsed_data) { const required = ['title', 'summary', 'category']; for (const field of required) { if (!validationResult.parsed_data[field]) { validationResult.is_valid = false; validationResult.errors.push(`缺少必需字段: ${field}`); } } } // 4. 检查拒绝回答 const refusalPatterns = [ /I (?:cannot|can't|am unable to)/i, /I'm sorry,? (?:but )?I/i, /as an AI/i, /I don't have (?:access|the ability)/i ]; for (const pattern of refusalPatterns) { if (pattern.test(llmOutput)) { validationResult.is_valid = false; validationResult.errors.push('LLM 拒绝回答'); break; } } // 5. 长度检查 if (llmOutput.length < 50) { validationResult.errors.push('输出过短(< 50 字符)'); } if (llmOutput.length > 10000) { validationResult.errors.push('输出过长(> 10000 字符)'); } return validationResult;

验证失败后的处理流程

[LLM 调用] → [Code: 输出验证] → [IF: is_valid?] ├── ✅ 有效 → [后续处理] └── ❌ 无效 → [Code: 构建修正 prompt] → [LLM 调用: 重试(含修正指令)] → [Code: 二次验证] → [IF: 二次验证通过?] ├── ✅ 通过 → [后续处理] └── ❌ 失败 → [降级处理 / DLQ]

8.2 AI Agent 循环超时保护

n8n 的 AI Agent 节点可能陷入无限循环(Agent 反复调用工具但无法完成任务)。需要设置超时保护:

// 在 AI Agent 节点的 System Message 中添加超时约束 const systemMessage = ` 你是一个高效的 AI 助手。请遵守以下约束: 1. 最多使用 5 次工具调用来完成任务 2. 如果 3 次工具调用后仍无法完成,请返回当前最佳结果并说明原因 3. 不要重复调用相同的工具(除非参数不同) 4. 如果遇到错误,最多重试 2 次,然后报告错误 `;

Agent 执行监控

[AI Agent 节点] → [Code: 检查执行统计] // 检查 Agent 的工具调用次数和执行时间 const agentOutput = $input.item.json; const toolCalls = agentOutput.steps?.length || 0; if (toolCalls > 10) { // Agent 可能陷入循环 return { warning: 'Agent 工具调用次数过多', tool_calls: toolCalls, needs_review: true }; }

8.3 速率限制智能管理

n8n 速率限制处理模式

[批量数据输入] → [Split In Batches: 每批 5 个] → [Wait: 1 秒(批次间延迟)] → [HTTP Request: API 调用] ├── ✅ 成功 → [收集结果] └── ❌ Error Output → [IF: 是 429 错误?] ├── 是 → [Code: 解析 Retry-After 头] │ → [Wait: 动态等待] │ → [HTTP Request: 重试] └── 否 → [其他错误处理]

解析 Retry-After 头

// Code 节点:解析速率限制响应 const error = $input.item.json.error; const headers = error.headers || {}; // 尝试从 Retry-After 头获取等待时间 let waitSeconds = 60; // 默认等待 60 秒 if (headers['retry-after']) { const retryAfter = headers['retry-after']; if (/^\d+$/.test(retryAfter)) { waitSeconds = parseInt(retryAfter); } else { // Retry-After 可能是日期格式 const retryDate = new Date(retryAfter); waitSeconds = Math.max( Math.ceil((retryDate.getTime() - Date.now()) / 1000), 1 ); } } // 添加 10% 的安全余量 waitSeconds = Math.ceil(waitSeconds * 1.1); return { wait_seconds: waitSeconds, original_error: error.message };

实战案例:生产级 AI 内容管线的完整错误处理

案例背景

构建一个每日自动运行的 AI 内容管线:从 RSS 源抓取文章 → AI 生成中文摘要 → 发布到 Slack 和 Notion。需要处理各种可能的故障场景。

完整工作流拓扑

[Schedule Trigger: 每天 08:00] → [RSS Feed Read: 多源抓取] ├── ✅ 成功 → [Merge + Filter: 去重] └── ❌ Error Output → [Slack: RSS 源不可用告警] → [Stop] → [Loop Over Items] → [Code: 幂等性检查(跳过已处理文章)] → [IF: 已处理?] ├── 是 → [跳过] └── 否 → [OpenAI: 生成摘要](Retry: 3次, 5000ms) ├── ✅ 成功 → [Code: 输出验证] │ ├── ✅ 有效 → [继续] │ └── ❌ 无效 → [Anthropic: 备用模型重试] │ ├── ✅ 成功 → [继续] │ └── ❌ 失败 → [Code: 文本截取降级] → [继续(标记降级)] └── ❌ Error Output → [Switch: 错误分类] ├── 429 → [Code: 指数退避] → [Wait] → [重试] ├── 401 → [Slack: 认证过期告警] → [DLQ 写入] └── 其他 → [Anthropic: 备用模型] ├── ✅ 成功 → [继续] └── ❌ 失败 → [DLQ 写入] → [Slack: 发送摘要](On Error: Output Error Data) ├── ✅ 成功 → [继续] └── ❌ Error Output → [Log: Slack 发送失败] → [继续(不阻塞)] → [Notion: 创建页面](On Error: Output Error Data) ├── ✅ 成功 → [继续] └── ❌ Error Output → [DLQ 写入] → [Code: 标记为已处理] → [Code: 生成执行摘要] → [Slack: 发送每日处理报告] [Error Workflow: 全局告警] → [Error Trigger] → [Code: 错误分级 + 去重] → [Switch: 路由] ├── P0/P1 → [Slack: #alerts-critical] + [邮件] └── P2/P3 → [Slack: #alerts-warning] + [日志] [DLQ 处理工作流: 每 30 分钟] → [Schedule Trigger] → [Postgres: 查询待重试项] → [Loop: 逐项重试] → [更新状态]

案例分析

关键设计决策

  1. 三层防御:节点重试(第一层)→ 模型降级(第二层)→ 死信队列(第三层)
  2. 非关键步骤不阻塞:Slack 发送失败不影响 Notion 写入和后续处理
  3. 幂等性保证:通过文章 URL 哈希去重,避免重复处理
  4. 降级标记:降级数据会被标记,方便后续人工审查
  5. 集中式告警:所有工作流共享一个 Error Workflow,统一管理告警逻辑
  6. DLQ 自动重试:失败数据不会丢失,定时自动重试

错误处理覆盖率

故障场景处理方式数据是否丢失
RSS 源不可用告警 + 终止否(下次执行会抓取)
OpenAI API 429指数退避重试
OpenAI API 不可用切换到 Anthropic
所有 LLM 不可用文本截取降级否(降级质量)
Slack 发送失败记录日志,继续执行摘要不丢失
Notion 写入失败写入 DLQ,稍后重试
认证过期紧急告警 + DLQ

避坑指南

❌ 常见错误

  1. 没有任何错误处理就上线生产

    • 问题:工作流静默失败,数据丢失,直到用户投诉才发现
    • 正确做法:至少配置一个全局 Error Workflow 发送 Slack/邮件告警,这是最低限度的生产要求
  2. 对非幂等操作启用无限重试

    • 问题:邮件发送、支付扣款等操作被重复执行,导致用户收到多封邮件或被多次扣款
    • 正确做法:对非幂等操作限制重试次数(最多 1-2 次),并在业务逻辑中加入去重机制(如唯一请求 ID)
  3. 重试间隔设置为固定值且过短

    • 问题:当 API 返回 429 时,固定 1 秒间隔的重试会持续触发速率限制,形成”重试风暴”
    • 正确做法:使用指数退避策略,并解析 Retry-After 响应头来确定等待时间
  4. 忽略 LLM 输出验证

    • 问题:LLM 返回格式错误的 JSON 或拒绝回答,下游节点解析失败导致整个工作流崩溃
    • 正确做法:在 LLM 调用后添加输出验证节点,检查 JSON 格式、必需字段、拒绝回答等
  5. 告警没有去重和分级

    • 问题:一个 API 持续返回 500 错误,每分钟触发一次告警,团队被告警淹没后开始忽略所有告警
    • 正确做法:实现告警去重(相同错误 30 分钟内只告警一次)和分级(P0-P3),避免告警疲劳
  6. Make.com 中不了解 Break 和 Ignore 的区别

    • 问题:对关键操作使用 Ignore 导致数据丢失,或对非关键操作使用 Break 导致不完整执行队列堆积
    • 正确做法:关键操作用 Break(保留数据可重试),非关键操作用 Ignore(跳过不影响主流程)
  7. 死信队列没有监控和清理机制

    • 问题:DLQ 中的数据越积越多,永远没人处理,最终变成数据黑洞
    • 正确做法:设置 DLQ 深度告警(超过阈值时通知),配置自动重试,定期清理永久失败的记录
  8. 降级路径没有标记降级状态

    • 问题:使用默认值或截取文本替代 AI 生成结果,但下游无法区分正常数据和降级数据
    • 正确做法:在降级数据中添加 is_degraded: truedegradation_reason 字段,方便后续审查和统计

✅ 最佳实践

  1. 遵循”洋葱模型”:从内到外依次配置节点重试 → 错误输出分支 → 全局 Error Workflow,形成多层防御
  2. 区分瞬时故障和持久故障:瞬时故障(网络抖动、429)自动重试,持久故障(认证过期、API 变更)立即告警
  3. 为每个生产工作流配置 Error Workflow:这是最基本的生产要求,确保任何未捕获的错误都能被发现
  4. 使用指数退避而非固定间隔重试:避免重试风暴,对 API 提供商友好
  5. 实现死信队列:确保失败数据不丢失,可以在问题修复后重放
  6. 定期审查 DLQ 和错误日志:每周审查一次,识别系统性问题和趋势
  7. 测试错误处理路径:不要只测试正常路径,主动模拟各种故障场景验证错误处理是否正常工作
  8. 记录错误处理决策:在工作流的 Notes 中记录为什么选择某种错误处理策略,方便团队成员理解和维护

相关资源与延伸阅读

官方文档

社区模板与工作流

深度教程

相关概念

监控与告警工具


参考来源


📖 返回 总览与导航 | 上一节:26c-工作流蓝图集 | 下一节:26e-Webhook与高级集成

Last updated on