Files
crm-ai/docs/deploy-wangliang.md
T

384 lines
21 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.
# crm-ai 在 wangliang 工作区的部署记录
> 部署时间:2026-10-23 | 部署位置:`/data/dev/wangliang/crm-ai` | 代码版本:`main @ 1ae25f1`
>
> 本文件记录这套栈**实际跑成什么样**、和仓库默认配置差在哪、怎么运维、以及还缺哪些凭据。
> 环境相关的临时信息写在这里,通用约定见 `/data/dev/_shared/NOTES.md`。
## 1. 交付摘要
一套自包含的客服 + CRM 栈(与仓库 `docker-compose.yml` 一致,Compose 项目名 `crm-new`):
| 组件 | 容器 | 宿主端口 | 状态 |
|---|---|---|---|
| LibreDesk v2.9.0-rc.10(渠道/工作台) | `crmnew-libredesk` | `9000` | ✅ healthy |
| Twenty CRM v2.30.1(server) | `crmnew-twenty-server` | `3100` | ✅ healthy |
| Twenty worker | `crmnew-twenty-worker` | — | ✅ running |
| bridge(业务工具 / RPC 集成) | `crmnew-bridge` | `4100` | ✅ healthy |
| PostgreSQL 18 + pgvector | `crmnew-postgres` | `127.0.0.1:5440` | ✅ healthy |
| Redis 8.8 | `crmnew-redis` | `127.0.0.1:6390` | ✅ healthy |
| IMAP ID 注入代理(163 专用) | `crmnew-imap-proxy` | `127.0.0.1:1993` | ✅ running |
数据卷:`crm-new_pg-data`、`crm-new_redis-data`、`crm-new_twenty-data`(`docker compose down` 不会删)。
## 2. 访问地址与账号
| 入口 | 地址 | 账号 |
|---|---|---|
| LibreDesk(工作台 / 帮助中心 / widget) | https://wangliang-crm.dev.gaia888.com | `System` /密码见下方命令 |
| Twenty CRM | https://wangliang-twenty.dev.gaia888.com | `admin@yehwang.com` /密码见下方命令 |
| bridge(调试/集成) | https://wangliang-crmbridge.dev.gaia888.com | 请求头 `X-Tools-Key`,值见下方命令 |
```bash
cd /data/dev/wangliang/crm-ai
grep '^LIBREDESK_SYSTEM_USER_PASSWORD=' .env # LibreDesk System 密码
grep '^TWENTY_ADMIN_PASSWORD=' .env # Twenty 管理员密码
grep '^TOOLS_SHARED_KEY=' .env # bridge 工具密钥
```
> 密钥只存在 `.env`(600)与 `libredesk/config/config.toml`(600),两者都已被 `.gitignore` 排除,**不要提交**。
## 3. 与仓库默认配置的差异(重要)
| 项 | 仓库默认 | 本环境 | 原因 |
|---|---|---|---|
| `LIBREDESK_IMAGE` | `libredesk/libredesk:v2.9.0-rc.10` | `libredesk/libredesk:v2.9.0-rc.10-**arm64**` | 宿主是 ARM64,`v2.9.0-rc.10` 只有 amd64;官方另发了 `-arm64` 同版本 tag,实测原生 `linux/arm64` 运行 |
| `LIBREDESK_BASE_URL` | `http://localhost:9000` | `https://wangliang-crm.dev.gaia888.com` | 走宿主 nginx 的 HTTPS |
| `TWENTY_SERVER_URL` | `http://192.168.31.61:3100` | `https://wangliang-twenty.dev.gaia888.com` | 前端 `REACT_APP_SERVER_BASE_URL` 用的就是它(已核对页面内联配置) |
| `RECOMMEND_URL` | 测试服地址 | `jsonrpc://wangliang-dev:9503` | 用 **本工作区** 的 product-service |
| `ORDER_API_URL` | 测试服地址 | `jsonrpc://wangliang-dev:9505` | trading-service |
| `INVENTORY_API_URL` | 测试服地址 | `jsonrpc://wangliang-dev:9509` | eta-service |
| `MEMBER_RPC_URL` | 测试服地址 | `jsonrpc://wangliang-dev:9506` | member-service |
| `IDENTITY_DEV_MODE` | `false` | `true` | dev 环境方便 widget 联调;**上生产前改回 `false`** |
| `PRODUCT_TIMEOUT_MS` | 未透传(bridge 默认 15s) | `30000`(compose 增加透传) | 冷门长查询会偶发 >15s,超时会降级成无价格的站点地图结果,模型易编价 |
| `public-gateway` / `cloudflared` | compose 里默认启动 | **未启动** | 已有宿主 nginx 直出 HTTPS,不需要 Cloudflare 临时隧道 |
| bridge 网络 | 只挂 `crm-new_default` | 额外接入 `spug-net` | 直连 `wangliang-dev:9503/9505/9506/9509`,去掉对宿主 socat 转发的依赖 |
RPC 走 **容器网络直连**:compose 把 `bridge` 同时接入外部网络 `spug-net`(与 `wangliang-dev` 同网络),
直接按容器名解析到 `wangliang-dev`,不再依赖宿主 `socat` 24503-24509 转发。容器重建后 DNS 名不变,
比 socat 需要按 IP 重建更稳。宿主侧的 `conf/dev/rpc-forward.sh` 仍保留(供宿主/不在 spug-net 的容器使用),
但本栈已不依赖它。
## 4. 已完成初始化
### LibreDesk
- `System` 账号可登录(浏览器实测落到 `/inboxes/assigned`)。
- **AI Provider 已配置**:DashScope `qwen-plus-latest`(completion)+ `text-embedding-v4` 1024 维(embedding),
`configure-ai.js` 的两项连通性测试均通过。密钥只存在 LibreDesk 库内与 `.env`(600),未写入文档。
- 助理指令已加固(见第 9 节):`search_products` 必须用 2-5 个词的关键词、不得编造价格/商品,`price=null` 时明确说“暂不可用”。
- **知识库已接入**:`bridge` 库 `kb_docs` 594 行(523 parent / 71 child,来自 `deploy/seeds/kb_docs.sql.gz`),
`RAG_PG_URL` 已配置,`search_kb` 实测命中最低起订量/退换货等 FAQ,AI 能据此回答。
- `app.root_url = https://wangliang-crm.dev.gaia888.com`(`scripts/set-public-url.js`)。
- 10 个 bridge 工具已注册(`scripts/register-tools.js`):`search_products / search_orders / search_inventory /
search_kb / search_email_history / collect_info / send_verification_code / verify_email_code / handoff_to_human /
create_opportunity`。
- 默认助理 `Yehwang Assistant`(#1,10 个工具、引用与 handoff 开启)。
- Live chat Inbox `Website`(#1,uuid `ad25d7a4-722d-4561-aebf-9d8f60952d09`)。
- Automation:Live chat 规则 1 已关闭(AI Assistant);新会话由 bridge webhook 接管。
- **2026-10-23 切流完成**:
- bridge webhook `Bridge AI` 已创建并启用(`message.created` → `http://bridge:4100/libredesk/webhook`)
- LibreDesk API key 已生成并写入 `.env`
- `Wholesale Support` 团队(id=1,Round robin,System 为成员)
- WhatsApp 规则改为 `assign_team=Wholesale Support`,启用
- `IDENTITY_CORS_ORIGIN` 改为 `https://wangliang-crm.dev.gaia888.com`
- bridge AI 端到端已验证,`bridge_ai_metrics` 开始有数据
### Twenty
- Workspace `Yehwang`:`2ff5a56b-95d8-40e1-85cf-529ad3a70ed7`(`ACTIVE`)。
- API key 已生成并写入 `.env` 的 `TWENTY_API_KEY`;GraphQL `companies` 查询验证通过。
- 浏览器实测可登录,首次进入 `/install-apps`(Twenty 的新工作区引导页,正常现象)。
### bridge
- `/healthz` 正常,10 个工具就绪。
- 实测 `search_products` 直接命中**本工作区真实商品数据**(如 `Stainless Steel Y-Chain Necklace` / `$6.73` /
`product_id=11079`),说明 `wangliang-dev:9503` 的 JSONRPC 直连链路是通的(工具调用 925ms,source=catalog)。
- `search_orders` 未验证身份时按设计返回 `verification_required`。
## 5. 运维命令
```bash
cd /data/dev/wangliang/crm-ai
docker compose ps # 状态
docker compose logs -f --tail=100 libredesk
docker compose logs -f --tail=100 twenty-server
docker compose logs -f --tail=100 bridge
docker compose start # 启动已有容器
docker compose stop # 停止(保留数据)
docker compose restart libredesk # 重启单个服务
# 改 .env 后(bridge 必做;libredesk 也要重建才会读到新 env)
docker compose up -d --force-recreate bridge
docker compose up -d --force-recreate libredesk
# 改 LibreDesk 配置模板后
node scripts/render-config.js && docker compose up -d --force-recreate libredesk
# 备份(DB + Twenty 自带数据都在 PG 里;卷里是上传文件)
mkdir -p backups
docker exec crmnew-postgres pg_dump -U crmnew -Fc libredesk > backups/libredesk-$(date +%F).dump
docker exec crmnew-postgres pg_dump -U crmnew -Fc twenty > backups/twenty-$(date +%F).dump
docker exec crmnew-postgres pg_dump -U crmnew -Fc bridge > backups/bridge-$(date +%F).dump
# 恢复
docker exec -i crmnew-postgres pg_restore -U crmnew -d libredesk --clean --if-exists < backups/libredesk-YYYY-MM-DD.dump
```
所有容器都是 `restart: unless-stopped`,Docker 守护进程重启后会自己拉起来。
## 6. 域名反向代理
宿主 nginx 新增了**独立**配置文件 `/etc/nginx/conf.d/zz-wangliang-crm.conf`:
- `wangliang-crm.dev.gaia888.com` → `127.0.0.1:9000`
- `wangliang-twenty.dev.gaia888.com` → `127.0.0.1:3100`
- `wangliang-crmbridge.dev.gaia888.com` → `127.0.0.1:4100`
- 80 → 308 跳 HTTPS;证书复用 `*.dev.gaia888.com`(通配一级域名,三个域名都被覆盖)。
它**没有改** `devstack.conf` 的 map/正则,因此不影响其它工作区。
改完后的自检 / 重载(query 用户没有 root,用容器代跑):
```bash
docker run --rm --privileged --pid=host redis:7-alpine \
sh -c 'nsenter -t 1 -m -u -i -n -p /usr/sbin/nginx -t'
docker run --rm --privileged --pid=host redis:7-alpine \
sh -c 'nsenter -t 1 -m -u -i -n -p /usr/sbin/nginx -s reload'
```
回滚(只需删掉这个文件并 reload,其它站点不受影响):
```bash
docker run --rm -v /etc/nginx/conf.d:/c redis:7-alpine rm -f /c/zz-wangliang-crm.conf
# 再执行上面的 nginx -t / -s reload
```
## 7. 尚未配置(需要真实凭据,不是代码问题)
| 项 | 现状 | 补法 |
|---|---|---|
| ~~AI Provider(DashScope)~~ | ✅ **已配置**(qwen-plus-latest / text-embedding-v4),端到端已验证 | 换 key:`DASHSCOPE_API_KEY=sk-xxx node scripts/configure-ai.js` |
| **SMTP / 163 邮件** | `SMTP_*` 为空,OTP 与邮件渠道不可用 | 填 `.env` 后 `node scripts/configure-email.js` + `node scripts/add-email-inbox.js` |
| ~~知识库 RAG(search_kb)~~ | ✅ **已配置**:快照导入 `bridge` 库(594 行),`RAG_PG_URL` 已指向,`search_kb` 可用 | 更新:`FORCE=1 bash scripts/import-kb-snapshot.sh` 或 `node scripts/sync-kb-online.js --local-only --force` |
| **WhatsApp 渠道** | `WA_*` 已存在,inbox 已存在;规则已指向人工团队 | 仍需确认 Meta callback 是否指向测试域名,并补测试号/模板 |
| **public-gateway / cloudflared** | 未启动 | 需要 Cloudflare 临时隧道时再 `docker compose up -d public-gateway cloudflared` |
> `IDENTITY_DEV_MODE=true` 只适合 dev;上生产前改回 `false`。
## 8. 验证记录(2026-10-23)
```text
$ curl -o /dev/null -w '%{http_code}' https://wangliang-crm.dev.gaia888.com/health # 200
$ curl -o /dev/null -w '%{http_code}' https://wangliang-twenty.dev.gaia888.com/healthz # 200
$ curl -o /dev/null -w '%{http_code}' https://wangliang-crmbridge.dev.gaia888.com/healthz # 200
$ curl -s https://wangliang-crmbridge.dev.gaia888.com/healthz
{"ok":true,"service":"crm-tools","tools":[10 个],"sites":["www","us","eu"]}
$ curl -s -X POST http://127.0.0.1:4100/tools/search_products -H 'X-Tools-Key: …' -d '{"query":"stainless steel necklace"}'
{"ok":true,"products":[{"name":"Stainless Steel Y-Chain Necklace","price":"$6.73","product_id":11079}]}
```
AI 端到端(`node scripts/e2e-widget.js "Show me 3 stainless steel necklaces with prices and stock."`):
助手调用 `search_products`(612ms,source=catalog)并返回真实数据 —— Y-Chain `$6.73`/237、Necklace `$6.85`/186、
Necklace Star `$5.70`/110,与 RPC 直查一致(首次调试时的编价问题见第 9 节)。
浏览器(Playwright 真 Chrome,1440×900):
- LibreDesk 登录成功 → `/inboxes/assigned`(截图 `docs/screenshots/wangliang-libredesk-inbox.png`)
- Twenty 登录成功 → `/install-apps` 引导页(截图 `docs/screenshots/wangliang-twenty-onboarding.png`)
- 回归:`wangliang-www` 仍 200,`wangliang-agent` 仍 302(宿主 nginx 未被改坏)
资源占用:约 1.9 GB 内存(Twenty worker 约 1.0 GB + server 0.6 GB + PG/Redis/bridge/LibreDesk 约 0.3 GB)。
## 9. 上线当天修的一个 AI 质量问题(编造价格)
首次用 `e2e-widget.js` 问“3 条不锈钢项链的价格和库存”时,助手返回了三款**不存在的商品**和虚高价格。
排查(临时把 LibreDesk `log_level` 调到 `debug`)定位到:
1. 模型把用户**整句**(含 "under 10 dollars" 等)直接当成 `search_products` 的 query;
2. 这类长查询偶发 >15s(bridge 的 `PRODUCT_TIMEOUT_MS` 默认 15s),超时后走降级:只返回名字、`price=null`;
3. 模型面对 `price=null` 时自行编了价格(LibreDesk 的 guardrails 只是提示词,不是硬拦截)。
修复(已落地并验证):
- `docker-compose.yml` 给 bridge 透传 `PRODUCT_TIMEOUT_MS`,`.env` 设为 `30000`;
- 助理的 instructions 追加严格规则(`scripts/setup-defaults.js` 同步):短关键词查询、工具输出是唯一事实来源、
`price=null`/`stock=null` 时必须说“暂不可用”并建议转人工,禁止估算/换算币种。
- 复测:同一条问题,工具调用 612ms 返回真实商品,回复价格/库存与 RPC 完全一致。
已改为 bridge agent 回复,输出护栏、Citation、飞轮、质量指标都生效;LibreDesk 内置 AI 仅保留作回滚。
验证完已把 `log_level` 恢复为 `info`。
## 10. 数据面初始化(5.1 KB / 5.2 sitemap / 5.3 质量表)
### 5.3 质量表 ✅
`node scripts/quality-report.js 1`:自动建表成功。
```text
table_exists = bridge_ai_metrics;切流后已有数据
{"summary":{"runs":0,"errors":0,...},"tools":[],"hours":1}
```
✅ 切流后 `bridge_ai_metrics` 由 bridge agent 写入;当前已有 Live chat AI 运行记录。
### 5.2 全量 sitemap ✅
官网 `sitemap.xml` 受阿里云 WAF 的 JS 挑战保护(直接 fetch 返回 4KB 挑战 HTML、0 个 `<loc>`)。
处理:用真 Chrome(Playwright)过挑战后抓取,缓存到 `var/sitemap-{www,us}.xml`(已 gitignore),
并给 `scripts/build-site-map.js` 增加本地 XML 回退(`var/sitemap-<site>.xml` 或 `SITEMAP_XML_<SITE>`)。
```text
[site-map] www: total=32710, products=32070, nav=640
[site-map] us: total=22644, products=22324, nav=320
```
表 `bridge_site_map_urls` 已核对:
```text
site | total | products
us | 22644 | 22324
www | 32710 | 32070
```
刷新方式(sitemap 会变):先用浏览器重新抓 `var/sitemap-{www,us}.xml`,再 `node scripts/build-site-map.js www us`。
直接跑脚本在 WAF 下会明确报错「返回 0 个 <loc>」,不会静默写空表。
### 5.1 旧 KB(products.kb_docs)✅ 已用最新代码解决
拉取 `origin/main` 之前,本机确实没有旧 `products.kb_docs`:`upgrade-kb-schema.js` 报
`password authentication failed for user "postgres"`,`rebuild-kb-index.sh` 报 `No such container: crm-postgres`
(这两个脚本是旧栈专用,新版仓库已不依赖它们)。
最新代码(`f526696` / `81b49c9`)提供了「无旧 bridge」的标准路径,已按 `docs/go-live.md` 6.3 执行:
1. 导入仓库内置快照:`bash scripts/import-kb-snapshot.sh`
→ 导入到 `crmnew-postgres/bridge`,实测 **367 行(296 parent / 71 child)**,与文档预期一致;
后续第二批上游把快照升级到 **594 chunks(523 parent / 71 child)**,用 `FORCE=1 bash scripts/import-kb-snapshot.sh` 重导;
2. `.env` 增加 `RAG_PG_URL=postgres://crmnew:***@postgres:5432/bridge`,`docker compose up -d --build --force-recreate bridge`;
3. 修了一个上游接线 bug:`server.js` 调 `createTools(cfg)` 时没传 `retriever/verification/client`,
导致 HTTP 工具入口的 `search_kb` / `send_verification_code` / `handoff_to_human` 永远返回 `not_configured`。
现已按 `agent.js` 的方式创建并传入(本地提交 `fix(bridge): HTTP 工具入口注入 retriever/verification/client`)。
4. 验证:
- `search_kb "minimum order value"` → 命中 `What is the minimum order value?`(score 0.733,`source_urls` 含 `order.html`);
- AI 端到端 → 回答 `The minimum order value is €30`,内容与 `kb_docs` parent 记录一致;
5. 新索引器实测:`RAG_PG_URL=postgres://...@127.0.0.1:5440/bridge node scripts/sync-kb-online.js --local-only --force`
→ `parents=296 children=71`,自动备份 `kb_docs_v20261023033455`,`search_kb` 仍正常。
维护方式:
```bash
# 从 bridge/kb/*.md 重建(宿主机执行,RAG 用 127.0.0.1:5440)
RAG_PG_URL=postgres://crmnew:<password>@127.0.0.1:5440/bridge \
node scripts/sync-kb-online.js --local-only --force
# 在线同步官网 FAQ(建议每周一次;官网有 WAF,失败就退回 --local-only)
RAG_PG_URL=postgres://crmnew:<password>@127.0.0.1:5440/bridge \
node scripts/sync-kb-online.js
```
## 11. 最新代码(origin/main merge `a1d1de0` + `589e5f9`)怎么运行
最新 `main` 新增了 KB 快照/在线同步、环境差异文档,并去掉了 5.1 对旧栈的依赖。本机已合并并落地。
### 日常更新
```bash
cd /data/dev/wangliang/crm-ai
git pull --ff-only # 或 merge origin/main
docker compose config -q
docker compose up -d
docker compose up -d --build --force-recreate bridge # bridge 代码变更时必须
docker compose ps
```
`bootstrap.sh` 只用于全新环境;已有环境重跑会覆盖 `.env`/配置,**不要跑**。
### 数据准备(对应 `docs/go-live.md` 6.3)
| 任务 | 命令 | 本机结果 |
|---|---|---|
| KB 快照导入 | `FORCE=1 bash scripts/import-kb-snapshot.sh` | ✅ 594 行(523/71) |
| KB 本地重建 | `RAG_PG_URL=...127.0.0.1:5440/bridge node scripts/sync-kb-online.js --local-only --force` | ✅ 296/71 + 备份 |
| KB 在线同步 | `RAG_PG_URL=... node scripts/sync-kb-online.js` | 可选(官网 WAF,失败用 `--local-only`) |
| 全量 sitemap | `node scripts/build-site-map.js www us` | ✅ 55,354 行(见 5.2) |
| 质量报表 | `node scripts/quality-report.js 1` | ✅ 表已建(0 行,原因见 5.3) |
### 环境差异(`docs/environments.md`)
最新文档明确:`rpc-tunnel.sh` / `public-https.sh` 只用于本地联调;测试/生产直接用内网 RPC 地址 + 正式域名 HTTPS。
本机现状正好符合:
- RPC 走 `spug-net` 直连 `wangliang-dev:950x`,**没有**运行 `rpc-tunnel.sh`;
- HTTPS 由宿主 nginx + `*.dev.gaia888.com` 证书提供,**没有**用 Cloudflare Quick Tunnel。
### 第二批(merge `589e5f9`):质量/观测/KB 版本
新增:Prometheus `/metrics`、RAG reranker、citation checker、三级升级、A/B 实验、`agent_trace_steps`(`TRACE_ENABLED`)、
`flywheel_samples`、`chat-log`,以及 `kb-audit.js` / `kb-version.js` / `eval-retrieval.js` / `build-site-kb.js`。
KB 快照同步升级到 594 chunks。
```bash
# 重导新版 KB 快照
FORCE=1 bash scripts/import-kb-snapshot.sh
# 观测与质量
curl -s http://127.0.0.1:4100/metrics | head
PW=$(grep '^POSTGRES_PASSWORD=' .env | cut -d= -f2)
RAG_PG_URL="postgres://crmnew:${PW}@127.0.0.1:5440/bridge" node scripts/kb-audit.js
RAG_PG_URL="postgres://crmnew:${PW}@127.0.0.1:5440/bridge" node scripts/eval-retrieval.js
RAG_PG_URL="postgres://crmnew:${PW}@127.0.0.1:5440/bridge" node scripts/kb-version.js list
```
本机实测:
- `kb-audit`:594 行(523/71)、`empty_answers=0`、`missing_tsv=0`;
- `eval-retrieval`:**9/9 PASS**;
- `kb-version list`:能看到 `kb_docs_v20261023033455` 备份与 `kb_sync_runs` 记录;
- AI 端到端:退款/VAT 问题均按 KB 内容回答(如 VAT refund 给出 `helpdesk@yehwang.com` 与 proof of export 流程)。
⚠️ 两点范围说明:
1. `/metrics` 的 `bridge_ai_*` 计数只在 **bridge agent 路径**(webhook 编排)里累加;LibreDesk 内置 AI 不走这条路径,
所以当前 `/metrics` 主要只有 uptime/RSS,`bridge_ai_metrics` 也仍是 0 行。
2. `build-site-kb.js` 直接 fetch 官网 sitemap,和 `build-site-map.js` 一样会被 WAF 挑战页挡住;
如需从本地 sitemap 重新生成 `bridge/kb/11-site-map.md`,要先给脚本加本地 XML 回退(`build-site-map.js` 已有同款逻辑)。
本地在这一批上补了两个小修复(未推送):
- `search_kb` 在没有 reranker 时会忽略 `limit`(上游改为先取 8 条给 reranker)→ 现在无 reranker 时尊重 `limit`;
- parent/child 可能命中同一篇 → 按 `question+answer` 去重后再重排;
- 同时把 `createLLM + createReranker` 接进 HTTP 工具入口,让 LibreDesk 直接调 `search_kb` 也能享受重排。
## 11. 2026-10-23 后续配置
### Live chat / bridge AI
- bridge webhook `Bridge AI`:`message.created` → `http://bridge:4100/libredesk/webhook`(启用)
- LibreDesk 规则 1(AI Assistant):关闭
- bridge API key:已生成并写入 `.env`
- bridge AI e2e:最低起订量问题回复正常,`bridge_ai_metrics` 已有数据
- `IDENTITY_CORS_ORIGIN`:已从 `*` 改为 `https://wangliang-crm.dev.gaia888.com`
### 人工团队
- `Wholesale Support`(id=1,Round robin)
- 成员:
- System(id=1)
- agent1@yehwang.com(id=11)
- agent2@yehwang.com(id=12)
- 两个 agent 的临时密码写在测试服 `.agent-credentials`(600,已在 `.gitignore` 忽略)
- 登录验证:两个 agent 均 200
### Email
- SMTP 已使用本地 163 凭据配置
- Email Inbox `Email 163`(id=3,IMAP 走 `imap-proxy:1993`)
- Automation #3:email → `assign_team=Wholesale Support`
- ⚠️ 本机测试服和本地共用同一个 163 邮箱,两个 LibreDesk 会同时轮询,可能重复/抢占邮件;长期测试建议用独立测试邮箱
### WhatsApp
- `WA_INBOX_ID=2`
- `PUBLIC_BASE_URL=https://wangliang-crm.dev.gaia888.com`
- 规则 #2:WhatsApp → `assign_team=Wholesale Support`
- **Meta callback 尚未改到测试域名**:当前 Meta app 的回调仍可能指向本机 Cloudflare Quick Tunnel;一个 Meta App 只能有一个 callback,切到测试域名后本机 WhatsApp 会停止接收。要同时保留两边,需要独立 Meta App/测试号。