Files
chatwoot-ai-agent/README.md
T
GreatQiu 91104e58cf v1.8: FastAdmin chathub-addon — register/plan/payment/member-center + 5 channel bindings
- New fastadmin/chathub/ (11 files, 204K): user-facing FastAdmin ThinkPHP 5 addon
- _markOrderPaid() now calls _provisionAsync() on empty embed_code (closes 'paid but no code' gap)
- New reprovision() action — user-initiated resource rebuild
- payReturn() smart redirect: 3 branches (just_paid / provisioning / pending / fallback)
- status badge updated with 'provisioning' state (blue)
- _initialize() whitelist expanded: reprovision (user) + payNotify/payReturn (public webhook)
- 5 chathub_* tables (tenant/log/order/channel_account/gateway_log) + MIGRATIONS.md

Bugfixes during E2E:
- payNotify HTTP 500: tenant.status ENUM missing 'provisioning' value (DBA migration)
- payNotify HTTP 500: chathub_log.status='received' (not in ENUM) — changed to 'success'
- TP5 method signature: function reprovision(\$ids) does not read query string — use \$this->request->param('ids')
2026-06-05 14:20:00 +08:00

311 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Chatwoot AI Agent — 多租户 AI 自动回复系统
基于 Chatwoot ActionCable WebSocket 的实时 AI 客服系统,支持多租户、人工/AI 无缝切换、自动开通。
## 架构概览
```
┌─────────────────────────────────────────────────────────────────┐
│ QwenPaw Agent │
│ ┌─────────────────────┐ ┌──────────────────────┐ │
│ │ WS Agent │ │ Provision Server │ │
│ │ (WebSocket 长连接) │ │ (HTTP API :5566) │ │
│ │ • 接收实时消息 │ │ • 自动开通租户 │ │
│ │ • AI 自动回复 │ │ • 创建 Inbox/Team │ │
│ │ • 人工/AI 切换 │ │ • 创建 AI Agent │ │
│ │ • 5s 防抖 + 重试 │ │ • 写入路由配置 │ │
│ │ • 多 Inbox 路由 │ └──────────┬───────────┘ │
│ └─────────┬─────────────┘ │ │
│ │ │ │
│ ┌─────────▼───────────────────────────▼───────────────────┐ │
│ │ Platform Gateway13 文件,1437 LOC │ │
│ │ Amazon │ 京东 │ 淘宝 │ 拼多多 │ 抖音 — 统一接口 │ │
│ │ AES-256-GCM 凭证加密 · 限流/熔断/缓存 · 6 种错误路径 │ │
│ └──────────────────────────────────────────────────────────┘ │
└───────────────────────────────────────────────────────────────────┘
│ WebSocket (wss) │ HTTP API
▼ ▼
┌────────────────┐ ┌──────────────────┐
│ Chatwoot │ │ FastAdmin │
│ (自托管客服系统)│◄───────│ (PHP 管理后台) │
└────────────────┘ └──────────────────┘
```
## 组件说明
### 1. WS Agent (`chatwoot_ws_agent.py`)
WebSocket 长连接实时 AI 客服,**1147 行**。
**核心技术:**
- **ActionCable WebSocket** — 通过 Chatwoot RoomChannel 实时接收消息事件,零轮询
- **多 Inbox 路由** — 根据 `inbox_id` 分发到不同 AI Agent,支持 30+ 租户并发
- **人工 ↔ AI 无缝切换** — 通过消息 ID 追踪 (`ai_sent_msg_ids`) + 对话 Pending 机制 (`_ai_pending_convs`) 精准区分 AI 回复和人工回复,不误判
- **15 分钟人工超时** — 人工回复后 AI 自动回避;超过 15 分钟无人工回复,AI 自动接回
- **会议模式** — 三人实时通信(开发团队 Inbox),消息同时转发多个 AI,`[SKIP]` 机制避免重复回复
- **状态持久化** — 每 30 秒保存到 JSON 文件,重启/崩溃后自动恢复(1 小时内快照安全兜底)
- **热加载配置** — `inboxes.json` 文件变化自动检测,无需重启进程
- **Metrics 监控** — 记录 WebSocket 连接状态、每个 Inbox 的 AI 回复成功率和响应时间
- **Graceful Shutdown** — SIGTERM 信号处理器,退出时保存状态和指标
- **内存保护** — `ai_sent_msg_ids``processed_ids` 定期清理,上限 10000 条
**AI 回复流程:**
```
客户发消息 → WS 接收 → is_human_active? → 跳过/回复
call_qwenpaw_ai() → subprocess.qwenpaw agents chat
send_reply() → Chatwoot API (以 User 身份发送)
metrics.record_reply() 记录性能指标
```
### 2. Provision Server (`provision_server.py`)
HTTP API 服务,**555 行**,用于自动开通租户。
**端点:**
| 路径 | 方法 | 说明 |
|------|------|------|
| `/health` | GET | 健康检查 |
| `/provision` | POST | 创建租户(Inbox + 团队 + Agent + 路由配置) |
| `/suspend` | POST | 暂停租户(改名 + 清 Website URL + 关闭欢迎语) |
| `/activate` | POST | 恢复租户(恢复原名 + 重设 Website URL |
**安全性:**
- 所有 POST 端点需 `X-API-Key` 头部认证
- 幂等性支持:`Idempotency-Key` 头部,5 分钟 TTL,返回真实响应(含正确 HTTP 状态码)
- 输入验证:name/domain/email/channel 格式校验
- Session 管理:自动检测 `expiry` 过期,提前 1 小时自动续期
- Chatwoot API 401 自动重试(3 次)
**幂等性机制:**
```python
# 使用字典存储 {key: response} + 线程锁,避免竞态
# 5 分钟自动过期
_IDEMPOTENT_RESULTS: dict[str, dict] = {}
_IDEMPOTENT_LOCK = threading.Lock()
_IDEMPOTENT_TTL = 300
```
### 3. 控制脚本 (`chatwoot_ws_ctl.sh`)
进程管理脚本,包含 PID 验证(`/proc/PID/cmdline` 防复用)。
## 消息路由配置 (`inboxes.json`)
```json
{
"1": {
"name": "GreatQiu",
"type": "web_widget",
"target_agent": "sourcing-agent",
"system_prompt": "你是专业的外贸采购代理...",
"prompt_template": "客户 '{sender_name}' 发来消息:\n{customer_msg}\n\n请回复..."
},
"7": {
"name": "HALO Blog",
"type": "web_widget",
"target_agent": "halo-blog-agent",
"system_prompt": "你是安防弱电专家...",
"prompt_template": "..."
}
}
```
## 人工/AI 切换机制详解
### 核心原理
AI 使用 **User Session** 发消息(不让客户察觉是 AI),通过追踪消息 ID 区分:
1. **AI 回复追踪**`track_sent_message(msg_id)` 将 ID 存入 `ai_sent_msg_ids`
2. **竞态防护**`_ai_pending_convs` 在 API 调用前标记对话为 Pending`try/finally` 保证清理
3. **人工检测**WS 事件到达时,检查 `is_ai_sent_message(msg_id)``conv_id in _ai_pending_convs`,任一命中则忽略
4. **超时恢复**:人工最后回复后 15 分钟无响应 → AI 自动接回
5. **状态恢复**:客户将对话改为 Pending → AI 立即恢复
```
时间线:
客户发消息 ──→ AI 回复 ──→ 人工介入 ──→ 人工离开 ──→ 15分钟超时 ──→ AI 接回
↑ ↑ ↑
ai_sent_msg_ids mark_human_active() human_active 过期
加入 conv_id conv_id 加入 conv_id 被清理
```
## Platform Gateway(电商平台 API 集成)
WS Agent 内置 `gateway/` 库,在生成 AI prompt 前自动查询电商平台数据(商品价格、库存等),将结果注入上下文。
### 支持的平台
| 平台 | 协议 | 签名算法 |
|------|------|---------|
| Amazon | PA-API 5 | AWS4-HMAC-SHA256 |
| 京东 | 联盟 API | MD5 |
| 淘宝 | TOP API | MD5 |
| 拼多多 | DDK API | MD5 |
| 抖音 | 开放平台 | HMAC-SHA256 |
### 6 种错误路径
`no_creds`(静默降级)→ `error` / `timeout` / `rate_limited` / `breaker_open``success`
### 配置
```bash
export GATEWAY_ENABLED=1 # 默认开启,0=关闭
export CHATHUB_DB_HOST=localhost # MySQL 存储凭证
export CHATHUB_DB_USER=root
export CHATHUB_DB_PASS=your-password
export GATEWAY_AES_KEY=32-byte-base64-key # AES-256-GCM 加密凭证
```
详见 `gateway/ARCHITECTURE.md`
## 快速开始
```bash
# 1. 安装依赖
pip install -r requirements.txt
# 2. 配置环境变量(详见 .env.example
export CW_BASE=https://your-chatwoot.com
export CW_EMAIL=admin@example.com
export CW_PASSWORD=your-password
# 3. 登录 Chatwoot 获取 session
python3 chatwoot_ws_agent.py --renew
# 4. 启动 WS Agent
python3 chatwoot_ws_agent.py &
# 5. (可选)启动 Provision Server
python3 provision_server.py
```
## 环境变量
### WS Agent 必需
| 变量 | 说明 |
|------|------|
| `CW_BASE` | Chatwoot 服务器地址 |
| `CW_EMAIL` | 管理员账号邮箱 |
| `CW_PASSWORD` | 管理员密码 |
| `CW_PUBSUB_TOKEN` | Chatwoot ActionCable pubsub token(首次运行自动获取) |
### Provision Server 必需
| 变量 | 说明 |
|------|------|
| `CW_BASE` | Chatwoot 服务器地址 |
| `CW_ADMIN_EMAIL` | 管理员邮箱 |
| `CW_ADMIN_PASSWORD` | 管理员密码 |
| `CW_PLATFORM_TOKEN` | Chatwoot Platform API Token |
| `CHATHUB_API_KEY` | API 密钥(默认: chathub-default-key-change-me |
## 文件结构
```
chatwoot-ai-agent/
├── chatwoot_ws_agent.py # WebSocket AI Agent(核心,1294 行)
├── provision_server.py # HTTP 开通服务(555 行)
├── start_provision_v2.sh # Provision Server 环境变量 wrapper
├── chatwoot_ws_ctl.sh # 进程管理脚本
├── start_agent.sh # 启动脚本(旧,推荐用 supervisor
├── gateway/ # Platform Gateway 库(5 平台 API 集成)
│ ├── __init__.py # 入口 + 6 种错误路径统一处理
│ ├── base.py # 基础通道抽象类 + 限流/熔断
│ ├── amazon.py # Amazon PA-API 5AWS4-HMAC-SHA256
│ ├── jd.py # 京东联盟(MD5 签名)
│ ├── taobao.py # 淘宝 TOP APIMD5 签名)
│ ├── pdd.py # 拼多多 DDKMD5 签名)
│ ├── tiktok.py # 抖音开放平台(HMAC-SHA256
│ ├── router.py # 渠道路由 + 缓存
│ ├── credentials.py # 凭证管理(MySQL 读取)
│ ├── crypto.py # AES-256-GCM 加密/解密
│ ├── breaker.py # 熔断器 + 限流器
│ ├── cache.py # LRU 缓存(60s TTL
│ ├── loop.py # 异步事件桥接(BackgroundLoop
│ └── ARCHITECTURE.md # 199 行设计文档
├── .env.example # 环境变量模板
├── requirements.txt # Python 依赖
├── chatwoot_auth.example.json # Session 认证文件模板
├── inboxes.example.json # 路由配置模板
├── fastadmin/ # FastAdmin 用户端 PHP 插件 (v1.8+)
│ └── chathub/ # 用户注册/付费/会员中心 + 渠道绑定 UI
│ ├── controller/Index.php # 2108 行 (HTML 内联, 14 个 action)
│ ├── model/ChathubTenant.php
│ ├── config.php # 17 个后台可填配置项
│ ├── install.sql # 5 张表 (tenant/log/order/channel_account/gateway_log)
│ ├── MIGRATIONS.md # v1.0 → v1.6 schema 升级脚本
│ ├── assets/css|js/ # 样式 + 表格初始化
│ └── README.md # 安装 + 路由表 + 生命周期
└── .gitignore
```
## FastAdmin 用户端 (`fastadmin/chathub/`)
PHP 前端 (ThinkPHP 5 / FastAdmin addon),为整个系统提供用户面:注册 → 选套餐 → 支付 → 会员中心 → 渠道绑定。所有 HTML 都在 `controller/Index.php` 里以 PHP heredoc 形式内联,不依赖 FastAdmin 模板引擎。
### 路由表
| URL | Auth | 用途 |
|---|---|---|
| `/addons/chathub/index/landing` | public | 落地页 |
| `/addons/chathub/index/register` | public | 注册 + 选套餐 |
| `/addons/chathub/index/login` / `doLogin` / `logout` | public | 登录登出 |
| `/addons/chathub/index/my` | user (session) | 会员中心 (租户列表 + 嵌入代码 + 重新开通) |
| `/addons/chathub/index/channelList` / `channelAuth` / `channelCallback` | user | 5 平台渠道绑定 |
| `/addons/chathub/index/reprovision` | user | 用户主动补建资源 |
| `/addons/chathub/index/payAlipay` / `payWechat` | user | 发起支付 |
| `/addons/chathub/index/payNotify` | public (webhook) | 支付宝/微信异步回调 (验签 + 开户) |
| `/addons/chathub/index/payReturn` | public (webhook) | 支付宝/微信同步跳转 (3 分支 smart redirect) |
### 与 chathub-provision 的契约
`payNotify``register` 调用 `chathub-provision` 服务的 `/provision` 端点:
```http
POST {provision_server_url}/provision
Headers: X-API-Key, Idempotency-Key, Content-Type: application/json
Body: {"name": "...", "domain": "...", "email": "...", "type": "web_widget", "max_agents": 3}
```
`Idempotency-Key``reprov-{tenant_id}-{md5(domain|channel_type)}` 计算,允许 PHP 重试时 chathub-provision 端去重 (5 分钟 TTL)。
### 状态机
```
register → status='provisioning' ─┬─ success → status='active' (embed_code 写入)
└─ failure → status='pending' (用户点"重新开通")
payNotify → _markOrderPaid() + _provisionAsync() (若 embed_code 为空)
└─ success → status='active'
reprovision → 用户主动重试
```
详见 `fastadmin/chathub/README.md`
## 版本历史
| 版本 | 说明 |
|------|------|
| v1.0 | 初始 WebSocket 版本,支持基本 AI 回复 |
| v1.1 | Amazon API 集成,人工/AI 切换修复 |
| v1.2 | 热加载配置架构 |
| v1.3 | 代码清理优化,Metrics 监控 |
| v1.4 | 多租户架构,Provision Server,状态持久化,安全性重构 |
| v1.5 | 消息防抖(5s 累积合并),AI 错误重试(指数退避)|
| v1.6 | Platform Gateway 库——Amazon/JD/Taobao/PDD/TikTok 5 平台统一 API 集成 |
| v1.7 | 对话上下文(`--session-id`)+ 对话摘要 + 客户画像 + WebSocket 指数退避重连 |
| v1.8 | FastAdmin 用户端 PHP 插件 (`fastadmin/chathub/`) ——注册/选套餐/支付/会员中心/5 渠道绑定, 支付完成后自动调 chathub-provision 补建资源, 修复 `payNotify` HTTP 500 (ENUM schema + log status bug), 新增 `reprovision` 用户主动重试 |
## 许可证
**GNU Affero General Public License v3 (AGPL-3.0)**
本许可证要求:如果您修改了代码并向用户提供服务(包括通过网络提供),您必须公开您的修改。
选择 AGPL v3 的原因:
- 与 Chatwoot(本系统依赖的客服平台)保持许可证一致
- 防止竞争对手直接复制本代码提供相同商业服务
- 如果您需要闭源或商业许可,请联系作者