# Chatwoot + n8n + Dify 智能客服 > 部署日期:2026-06-24~25 · 更新:2026-06-25 --- ## 架构概览 ``` 用户 → Chatwoot(:3000) → Webhook → n8n(:5678) → Dify(:8088) → n8n → Chatwoot API → 用户 ↑ │ └──────── 机器人回复不会再触发 Webhook ──────────────┘ (Filter 过滤 sender_type ≠ "contact") ``` | 组件 | 服务器 | 端口 | 路径/管理 | |------|--------|------|-----------| | Chatwoot | aliyun2 (47.109.158.218) | 3000 | `/opt/chatwoot/`,docker-compose | | n8n | aliyun2 | 5678 | `/www/dk_project/dk_app/n8n/n8n_zZ2Y/`,宝塔 Docker | | Dify | aliyun2 | 8088 | v1.11.2,12 容器 | Chatwoot 数据库端口 5433(Postgres)/ 6380(Redis),避免与 Dify 的 5432/6379 冲突。 --- ## 一、Chatwoot 配置 ### 1.1 部署(已完成) ```bash cd /opt/chatwoot # 管理命令 docker-compose up -d # 启动 docker-compose down # 停止 docker-compose restart # ⚠️ 不重读 .env!改环境变量必须 down + up -d docker-compose ps # 查看状态 ``` 核心环境变量(`.env`):`SECRET_KEY_BASE`、`POSTGRES_PASSWORD`、`REDIS_PASSWORD`、`FRONTEND_URL` ### 1.2 SMTP 邮件配置 Chatwoot 必须配 SMTP,客服注册需邮箱验证。 **当前使用:阿里云邮件推送** | 项目 | 值 | | -------- | ------------------------- | | 发送地址 | `zlsh@smtp.zilanlife.com` | | SMTP 服务器 | `smtpdm.aliyun.com` | | 端口 | 465(SSL) | | 认证方式 | plain | | 密码 | `M9LV49ujVpxvUbC` | 备选 QQ 邮箱:`zhensolid@vip.qq.com`,SMTP 密码 `ofgfjztgxvgkbiaa`,`smtp.qq.com:465` > ⚠️ 新域名邮件可能进垃圾箱,正常现象。 ### 1.3 创建 Webhook(机器人) Chatwoot 后台 → Settings → Integrations → Webhooks → Add new webhook: 1. 将 n8n 的 **Production Webhook URL** 填入 2. 勾选事件:`message_created` 3. 保存 Chatwoot 收到用户消息 → 推送到这个 URL → n8n 接收处理。 ### 1.4 获取 API 访问令牌 Chatwoot 后台 → Settings → API Access → Generate new token: | 项目 | 值 | |------|-----| | 当前 Token | `mmFb9KQVxKFemRBQ168w3Tzb` | 用于 n8n 回复消息时调用 Chatwoot API。 --- ## 二、n8n 工作流配置 ### 节点结构 ``` Webhook(触发器) │ Respond: Immediately ← 🔴 必须!否则 Chatwoot 超时重试→死循环 │ ├─ IF: message_type == "incoming" ← 只处理用户消息 ├─ IF: private == false ← 排除私聊模式 └─ IF: conversation.status == "pending" ← 客服介入后停止 │ ▼ HTTP Request → Dify ← 调 Dify Chat API,blocking 模式 │ ▼ HTTP Request → Chatwoot ← 把 Dify 回复发回对话 ``` ### 节点详细配置 #### 节点 1:Webhook 触发器 | 配置项 | 值 | 说明 | |--------|-----|------| | HTTP Method | POST | | | Path | 自动生成 | | | **Respond** | **Immediately** | 🔴 必须!Chatwoot 5s 超时,不设会重试→循环 | | Response Code | 200 | | 生成两个 URL:**Test**(草稿测试)和 **Production**(发布后用)。 Chatwoot 里填 Production URL。 #### 节点 2-4:IF 过滤器(三个串连) | # | 条件 | 操作符 | 值 | 目的 | | --- | -------------------------------------- | ----- | ---------- | ------------- | | 2 | `{{ $json.body.message_type }}` | Equal | `incoming` | 只要用户消息,不要系统通知 | | 3 | `{{ $json.body.private }}` | Equal | `false` | 排除客服私聊模式 | | 4 | `{{ $json.body.conversation.status }}` | Equal | `pending` | 客服接手后停止机器人 | 三个都通过 → 流向 Dify。 > 🔴 注意:Webhook 的 Respond 已设为 Immediately,所以即使 Filter 拦截了消息,Chatwoot 也已拿到 200,不会重试。 #### 节点 5:HTTP Request → Dify | 配置项 | 值 | |--------|-----| | Method | POST | | URL | `http://47.109.158.218:8088/v1/chat-messages` | **Headers:** | Key | Value | |-----|-------| | Authorization | `Bearer app-ieGbmugS3yP7tzFESP56KWD7` | | Content-Type | `application/json` | **Body (JSON):** ```json { "inputs": {}, "query": "{{ $json.body.content }}", "response_mode": "blocking", "user": "{{ $json.body.sender.name }}" } ``` | 参数 | 说明 | |------|------| | `query` | 用户发来的消息文本 | | `response_mode` | `blocking` = 等 Dify 回复完再继续(简单可靠) | | `user` | 会话标识,用发送者名字区分用户 | #### 节点 6:HTTP Request → Chatwoot(回复消息) | 配置项 | 值 | |--------|-----| | Method | POST | | URL | `http://47.109.158.218:3000/api/v1/accounts/{{ $('Webhook').item.json.body.account.id }}/conversations/{{ $('Webhook').item.json.body.conversation.id }}/messages` | > ⚠️ 必须用 `$('Webhook')` 跨节点引用而非 `$json`。因为 Dify 返回后数据已变成 Dify 的响应结构,`$json.body` 里没有 Chatwoot 的 account_id。`$('Webhook')` 强制回到源头拿原始数据。 **Headers:** | Key | Value | |-----|-------| | api_access_token | `mmFb9KQVxKFemRBQ168w3Tzb` | | Content-Type | `application/json` | **Body (JSON):** ```json { "content": "{{ $json.answer.replace(/[\\s\\S]*?<\\/think>/g, '').trim() }}" } ``` `$json.answer` 是上一节点(Dify HTTP Request)返回的回复文本。正则移除 `...` 思考过程标签。 --- ## 三、Dify 配置 ### 3.1 创建聊天应用 Dify 后台(`http://47.109.158.218:8088`)→ 创建应用 → 聊天助手: 1. 选择模型(如 DeepSeek) 2. 编写系统提示词(Prompt) 3. 可选添加知识库 4. 发布应用 ### 3.2 获取 API Key 应用内 → 左侧菜单 **API 访问** → 生成密钥: | 项目 | 值 | |------|-----| | API Key | `app-ieGbmugS3yP7tzFESP56KWD7` | | API 端点 | `http://47.109.158.218:8088/v1/chat-messages` | > 这个 Key 填到 n8n 节点 5 的 Authorization 里。 --- ## 四、常见问题与排错 ### 🔴 Q1:机器人自言自语无限循环 **现象**:用户发一条消息,Dify 不停回复同一句,Chatwoot 里消息不断刷。 **原因**:n8n 回复给 Chatwoot 的消息又被 Chatwoot 当成新消息,再次触发 Webhook → 循环。 **解决步骤:** 1. Webhook 节点 → **Respond: Immediately**(Chatwoot 秒拿 200,不再重试) 2. n8n workflow **执行历史**里查看死循环的 payload → 找 `sender_type` 字段 3. Filter 节点加条件:`sender_type` ≠ `agent`(或等于 `contact`) 4. ⚠️ 注意:Chatwoot Webhook payload 里代表用户的是 `contact`,机器人回复是 `agent` 或 `bot`。**先看实际 payload 里字段名再设 Filter。** 如果你的 webhook payload 没有 `sender_type` 而是其他字段(如 `message_type` = `outgoing` / `incoming`),用它来区分。 ### 🟡 Q2:URL 里字段变成 undefined **现象**:`/accounts/undefined/conversations/...` **原因**:用了 `$json.body.account.id` 但 HTTP Request 节点拿到的 $json 不是 Webhook 的原始数据(中间有 Filter/Set 改了结构)。 **解决**:改用跨节点引用 `$('Webhook').item.json.body.account.id` ### 🟡 Q3:Dify 回复包含 `\` 标签 已在节点 6 用正则处理: ```js $json.answer.replace(/[\s\S]*?<\/think>/g, '').trim() ``` 如果 Dify 开启了思考模式(DeepSeek-R1 等),输出会带有推理过程。 ### 🟡 Q4:n8n Webhook URL 改了不生效 **原因**:n8n 里有两个 URL(Test / Production),改了 Chatwoot 里填的 URL 后要确认对应的是 n8n **当前发布**版本。 **检查**:n8n 右上角 → 确认已发布(Active),再看 Webhook 节点的 Production URL。 --- ## 五、速查 | 需要 | 去哪 | | ------------------ | ----------------------------------------------------------------- | | Chatwoot 后台 | `http://47.109.158.218:3000` | | n8n 后台 | `http://47.109.158.218:5678` | | Dify 后台 | `http://47.109.158.218:8088` | | Chatwoot API Token | `mmFb9KQVxKFemRBQ168w3Tzb` | | Dify API Key | `app-ieGbmugS3yP7tzFESP56KWD7` | | Chatwoot 重启 | `cd /opt/chatwoot && docker-compose down && docker-compose up -d` | | n8n 容器名 | `n8n_zz2y-n8n_zZ2Y-1` | --- 客户连续对话: 1、Chatwoot 需要配置自定义属性 2、n8n 工作流需要配置 code 属性和写入 dify_id