소스 검색

vault backup: 2026-06-25 16:59:01

zhensolid 3 주 전
부모
커밋
c8476d3b01
1개의 변경된 파일269개의 추가작업 그리고 48개의 파일을 삭제
  1. 269 48
      chatwoot+n8n+dify.md

+ 269 - 48
chatwoot+n8n+dify.md

@@ -1,48 +1,269 @@
-一 部署系统
-
-二 chatwoot 配置
-必须配置邮箱,因为客服注册,需要使用邮箱认证
-例如:阿里云邮件推送和 QQ 邮箱
-# 阿里云邮箱
-阿里云:`https://dm.console.aliyun.com/`
-发送地址:`zlsh@smtp.zilanlife.com`
-SMTP 密码:`M9LV49ujVpxvUbC`
-SMTP服务地址: smtpdm.aliyun.com ,SMTP服务端口号:25或80或465(SSL加密)。
-# QQ 邮箱
-SMTP 密码:ofgfjztgxvgkbiaa
-
-三 n8n 配置
-
-工作流配置:
-使用 webhook 生成地址 作为 chatwoot 的机器人地址,分测试和正式版 
-生成 一个过滤
-{{ $json.body.message_type }} 等于incoming  说明:避免机器人自己聊天
-{{ $json.body.private }} 等于false   说明:避免客服的私聊模式进入工作流
-{{ $json.body.conversation.status }} 等于 pending  说明:当客服介入聊天后,工作流停止
-
-生成 HTTP Request  关联 dify
-Method 选择 post
-URL 选择 http://47.109.158.218:8088/v1/chat-messages 说明:这个是 dify 的聊天信息
-开启Send Headers 
-设置Authorization 为Bearer app-ieGbmugS3yP7tzFESP56KWD7  说明 这个是 dify 的聊天api-key
-设置Content-Type 为application/json 
-开启Send Body
-选择 json
-配置 json
-```
-{{ { "inputs": {}, "query": $json.body.content, "response_mode": "blocking", "user": $json.body.account.name } }}
-```
-
-生成 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` 
-说明:这个是 chatwoot 的聊天信息,其中`{{ $('Webhook').item.json.body.account.id }}`是跨表获取的 webhook 第一次传入的参数
-开启Send Headers 
-设置api_access_token 为mmFb9KQVxKFemRBQ168w3Tzb 说明 这个是 chatwoot 的访问令牌
-设置Content-Type 为application/json 
-开启Send Body
-Body Content Type为JSON
-Specify Body为Using Fields Below
-配置content为{{ $json.answer }}
-
-四 dify 配置
+# 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(/<think>[\\s\\S]*?<\\/think>/g, '').trim() }}"
+}
+```
+
+`$json.answer` 是上一节点(Dify HTTP Request)返回的回复文本。正则移除 `<think>...</think>` 思考过程标签。
+
+---
+
+## 三、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 回复包含 `\<think\>` 标签
+
+已在节点 6 用正则处理:
+```js
+$json.answer.replace(/<think>[\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` |
+
+---
+
+## 关联笔记
+
+- [[家庭网络配置手册]]
+- [[服务器清单]]