chore: CRM-NEW baseline
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
# crm-new
|
||||
|
||||
**完全新建**的一套 CRM 客服栈:LibreDesk(平台 + 内置 AI)+ Twenty(CRM)+ bridge(业务工具/集成)。
|
||||
与旧项目 `/home/dev/code/crm/crm-ai-demo` 完全隔离:独立目录、独立 Postgres/Redis、独立端口,不迁旧数据。
|
||||
|
||||
## 当前状态(2026-10-21)
|
||||
|
||||
| 组件 | 状态 |
|
||||
|---|---|
|
||||
| LibreDesk v2.9.0-rc.10 | ✅ 运行中 :9000(System 密码见 `.env`) |
|
||||
| Twenty v2.30.1(全新实例) | ✅ 运行中 :3100,数据库迁移完成(71 张表) |
|
||||
| bridge | ✅ 运行中 :4100,4 个工具,单测 18/18 通过 |
|
||||
| Postgres / Redis | ✅ :5440 / :6390 healthy |
|
||||
| AI Provider | ✅ 已配置(DashScope `qwen-plus-latest` + `text-embedding-v4` 1024 维),测试通过 |
|
||||
| 知识库向量 | ✅ 108 篇文章已索引(embeddings=108) |
|
||||
| AI Tools | ✅ 4 个工具已注册(search_products / search_orders / search_inventory / create_opportunity) |
|
||||
| RAG/引用验证 | ✅ 已验证:问题命中文章并返回编号引用 |
|
||||
| Live chat Inbox | ✅ `Website`(uuid `10fd5207-d235-4c49-beaa-a65eed0ddd0e`) |
|
||||
| AI Assistant | ✅ `Yehwang Assistant`(4 个工具、引用开启、handoff 开启、max_turns=8) |
|
||||
| Automation | ✅ 新会话自动分配给 Assistant(仅 Website inbox) |
|
||||
| 端到端验证 | ✅ widget 发起 → 自动分配 → AI 回复(带文章引用);商品问题已触发 bridge `search_products` |
|
||||
| Twenty Workspace | ✅ 已创建(admin@yehwang.com,workspace 已 ACTIVE) |
|
||||
| Twenty API Key | ✅ 已在库内生成并写入 `.env`,GraphQL 验证通过 |
|
||||
| 商机写入 | ✅ `create_opportunity` 实测创建 company/person/opportunity/note/task |
|
||||
| 业务 RPC | ✅ 测试服 24503/24505/24506/24509 外网被封,已用 SSH 隧道(`scripts/rpc-tunnel.sh`) |
|
||||
| 邮件通知(OTP) | ✅ 163 SMTP 已配置,实测验证码邮件已送达 163 收件箱 |
|
||||
| Email 渠道 | ✅ 163 收件(imap-proxy 绕过 Unsafe Login)+ AI 回复邮件(模板)已实测 |
|
||||
| IMAP 代理 | ✅ `services/imap-id-proxy`(LibreDesk 不发 IMAP ID,163 会拒绝 SELECT) |
|
||||
| widget JWT | ✅ bridge `/identity` 签发;登录用户端到端验证通过(`search_orders` verified=true) |
|
||||
| 知识库 | ✅ 帮助中心 `yehwang-help`:9 个 collection / 108 篇文章(公开)→ http://192.168.31.61:9000/hc/yehwang-help/en |
|
||||
|
||||
旧栈 `crm/crm-ai-demo` 不受影响,两套可并行对照。
|
||||
|
||||
## 架构
|
||||
|
||||
```
|
||||
访客(Web widget / Email / WhatsApp)
|
||||
│
|
||||
▼
|
||||
LibreDesk :9000 ── 渠道、会话、坐席工作台、内置 AI Assistant、snippets RAG、OTP 验证
|
||||
│ AI 调用自定义 HTTP tools
|
||||
▼
|
||||
bridge :4100 ── 商品/订单/库存查询 + Twenty 商机写入(身份校验在服务端二次执行)
|
||||
│
|
||||
▼
|
||||
Twenty :3100 ── 公司/联系人/商机/备注/任务
|
||||
```
|
||||
|
||||
与旧栈的区别:**AI 编排交给 LibreDesk 内置 assistant**,bridge 收缩为业务工具/集成服务。
|
||||
知道这样会丢失旧桥的评估/飞轮/情绪升级等能力,后续按需在 bridge 里补"质量旁路"(webhook 消费)。
|
||||
|
||||
## 组件与端口
|
||||
|
||||
| 组件 | 地址 | 镜像/实现 |
|
||||
|---|---|---|
|
||||
| LibreDesk | http://192.168.31.61:9000 | `libredesk:v2.9.0-rc.10`(含 WhatsApp) |
|
||||
| Twenty | http://192.168.31.61:3100 | `twentycrm/twenty:v2.30.1`(全新实例) |
|
||||
| bridge | http://192.168.31.61:4100 | Node 24,自研 |
|
||||
| Postgres | 127.0.0.1:5440 | pgvector/pg18,库 `libredesk` / `twenty` / `bridge` |
|
||||
| Redis | 127.0.0.1:6390 | redis:8.8 |
|
||||
|
||||
## 目录
|
||||
|
||||
```
|
||||
crm-new/
|
||||
├── docker-compose.yml # 全栈编排
|
||||
├── .env / .env.example # 全栈配置(.env 自动生成密钥)
|
||||
├── infra/init-databases.sql # 首次初始化三个库
|
||||
├── libredesk/ # LibreDesk 配置模板/渲染结果/uploads(legacy/ 为旧单机模式归档)
|
||||
├── twenty/ # Twenty 部署说明(不需要源码)
|
||||
├── bridge/ # 业务工具服务(原 tools)+ 单测
|
||||
│ └── config/sites.json # inbox ID → 站点 profile(tenant/语言/类目地图)
|
||||
├── services/imap-id-proxy/ # IMAP ID 注入代理(163 专用)
|
||||
├── scripts/
|
||||
│ ├── bootstrap.sh # 生成 .env + 渲染配置 + 启动 + 健康等待
|
||||
│ ├── render-config.js
|
||||
│ ├── register-tools.js # 把 4 个工具注册进 LibreDesk(支持 session/API key)
|
||||
│ ├── configure-ai.js # 配置 DashScope Completion + Embedding
|
||||
│ ├── twenty-bootstrap.js # 库里创建 Twenty workspace + API key(不走 UI)
|
||||
│ ├── configure-email.js # 配置 163 SMTP(通知/OTP 邮件)
|
||||
│ ├── add-email-inbox.js # 配置 Email Inbox + 默认外发模板 + Automation
|
||||
│ ├── e2e-widget-jwt.js # 端到端验证 widget JWT + 订单工具
|
||||
│ ├── rpc-tunnel.sh # 测试服 JSON-RPC 端口 SSH 隧道(bridge 依赖)
|
||||
│ ├── public-https.sh # Cloudflare Tunnel 公网 HTTPS + 白名单网关
|
||||
│ ├── set-public-url.js # 更新 LibreDesk app.root_url
|
||||
│ ├── import-kb-helpcenter.js # 旧 KB -> 帮助中心文章(幂等)
|
||||
│ ├── verify-rag.js # 临时助手 + preview,验证检索/引用
|
||||
│ └── smoke.sh
|
||||
├── web/ # 站点接入:ai-snippet.js + demo.html(widget JWT 演示)
|
||||
├── docs/ # 架构、LibreDesk 配置、接口对照、迁移说明、运维手册
|
||||
│ ├── go-live.md # 上线文档(范围/切流/验收/监控/回滚)
|
||||
│ ├── human-routing.md # Email/WhatsApp 人工团队分配
|
||||
│ └── plan-a.md # 方案 A:bridge 接管 AI 编排进度
|
||||
└── deploy/nginx.conf.example
|
||||
services/public-gateway/ # 公网路径白名单反向代理(仅 webhook/帮助中心)
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
```bash
|
||||
cd /home/dev/code/crm-new
|
||||
|
||||
# 1) 启动全栈(首次 Twenty 迁移 1-3 分钟)
|
||||
bash scripts/bootstrap.sh
|
||||
|
||||
# 2) 冒烟
|
||||
bash scripts/smoke.sh
|
||||
```
|
||||
|
||||
启动完成后:
|
||||
|
||||
1. **LibreDesk**(`System`,密码见 `.env` 的 `LIBREDESK_SYSTEM_USER_PASSWORD`)
|
||||
- `Admin → AI → Providers`:Completion 用 DashScope `qwen-plus-latest`,Embedding 用 `text-embedding-v4`(1024 维)
|
||||
- 建 Live chat / Email / WhatsApp inbox
|
||||
- 建 AI Assistant + Automation(new_conversation → assign assistant)
|
||||
- 生成 API key 填 `.env`,然后 `node scripts/register-tools.js`
|
||||
2. **Twenty**(首次访问需注册 workspace)
|
||||
- 注册后在 `Settings → Developers → API Keys` 创建 key,连同 workspaceId 填进 `.env`,再 `docker compose up -d --force-recreate bridge`
|
||||
3. 回到 LibreDesk 的 Assistant 里勾选 4 个工具,验证一条会话能查单 + 建商机
|
||||
|
||||
## Widget 安装
|
||||
|
||||
```html
|
||||
<script>
|
||||
window.LibredeskSettings = {
|
||||
baseURL: 'http://192.168.31.61:9000',
|
||||
inboxID: '10fd5207-d235-4c49-beaa-a65eed0ddd0e'
|
||||
};
|
||||
</script>
|
||||
<script async src="http://192.168.31.61:9000/widget.js"></script>
|
||||
```
|
||||
|
||||
登录用户用服务端签的 `userJWT` 绑定身份(见 `docs/libredesk-setup.md` 第 6 节)。
|
||||
|
||||
## 站点接入(widget)
|
||||
|
||||
```bash
|
||||
bash scripts/serve-web.sh # http://192.168.31.61:8088/demo.html
|
||||
```
|
||||
|
||||
- `web/ai-snippet.js`:设置 `window.YehwangChatConfig` 后引入;有 `siteJWT` 时先调 bridge `/identity` 换 widget JWT
|
||||
- `web/demo.html`:匿名 / 登录身份两种模式的联调页
|
||||
- 生产:`IDENTITY_DEV_MODE=false`,由站点注入 `siteJWT`;CORS 白名单加真实域名
|
||||
|
||||
## 多站点路由(bridge)
|
||||
|
||||
`bridge/config/sites.json` 把 LibreDesk inbox 映射到站点 profile:
|
||||
|
||||
```json
|
||||
{
|
||||
"defaultSite": "www",
|
||||
"languageIds": { "en": 1, "nl": 2, "de": 3, "es": 4, "fr": 5, "it": 6, "tr": 7, "cn": 10 },
|
||||
"profiles": {
|
||||
"www": { "site": "www", "tenantId": 2, "language": "en", "searchUrl": "https://www.yehwang.com/search.html?q=", "siteMap": "site-map.json" },
|
||||
"us": { "site": "us", "tenantId": 2, "language": "en", "searchUrl": "https://us.yehwang.com/search.html?q=", "siteMap": "site-map-us.json" }
|
||||
},
|
||||
"inboxes": { "1": "www", "2": "www", "3": "www" }
|
||||
}
|
||||
```
|
||||
|
||||
- 工具调用时按 `X-Libredesk-Inbox-ID` 选 profile
|
||||
- RPC 请求会带 `reqContext`:当前默认 `tenantId=2`;不强制币种,金额按 RPC 返回原样展示,`languageId` 按 profile 的 `language` code 映射(`DEFAULT_TENANT_ID` / `DEFAULT_LANGUAGE_ID` 可覆盖)
|
||||
- 商品搜索走真实 RPC(带 `_class` 的 `ReqContext`/`DeviceInfoDto`),RPC 不返回 URL 时退化到官网搜索链接
|
||||
- 商品兜底用对应站点的 site-map 和搜索地址
|
||||
- 商机备注会记录 `Site / Tenant / inbox`
|
||||
- 可用 `SITES_JSON` 环境变量整体覆盖
|
||||
|
||||
## 工具清单(bridge)
|
||||
|
||||
| 工具 | 需要验证 | 作用 |
|
||||
|---|---|---|
|
||||
| `search_kb` | 否 | 旧 pgvector 知识库检索(政策/FAQ),vector-only |
|
||||
| `search_products` | 否 | 商品搜索:价格/库存/图片/product_id/URL;中文词自动映射英文类目 |
|
||||
| `search_orders` | **是** | 订单列表/详情(状态、商品、金额、地址、物流) |
|
||||
| `search_inventory` | 否 | 库存/ETA(优先用 `product_id`) |
|
||||
| `search_email_history` | **是** | 已验证客户自己的邮件历史(旧 `kb_mail_docs`,向量检索,邮箱强制 scope) |
|
||||
| `collect_info` | 否 | 跨轮采集 lead 槽位(company/contact/need/quantity/budget/closeDate) |
|
||||
| `handoff_to_human` | 否 | 私聊备注 + 分配人工用户 |
|
||||
| `send_verification_code` | 否 | 匿名客户邮箱验证码(163 SMTP) |
|
||||
| `verify_email_code` | 否 | 校验验证码,成功后 30 分钟内可查私有数据 |
|
||||
| `create_opportunity` | **是** | 合并槽位后写 Twenty 商机 + Note + 3 天 Task |
|
||||
|
||||
> `collect_info` 槽位存 Redis DB 3(key `bridge:lead:<conversation-uuid>`,TTL 24h);Redis 不可用时退回进程内存并告警。
|
||||
|
||||
## 运维
|
||||
|
||||
上线清单见 [docs/go-live.md](docs/go-live.md),常用命令见 [docs/runbook.md](docs/runbook.md)(启动/停止/日志/备份/升级/回滚/排错)。
|
||||
|
||||
## 测试
|
||||
|
||||
```bash
|
||||
cd bridge && npm test
|
||||
```
|
||||
|
||||
## 发送前输出护栏
|
||||
|
||||
- 禁止声称已下单/已发邮件/已生成订单号/PDF/已通知团队(没有对应工具)
|
||||
- 回复中的 URL 只允许来自本轮工具结果 + 静态白名单(`/cart`、`/order.html`)
|
||||
- 价格/库存数量必须出现在工具结果里,否则替换为“无法确认”提示
|
||||
- 命中后自动重写:删除虚假动作句子、替换未验证价格/库存、移除未验证 URL,保留商品/订单等有效内容
|
||||
- 只有重写后没有有效内容时才回退到固定安全话术
|
||||
- 命中记录到 `audit.output_guard`(`blocked` + `rewritten`),并打 `agent: output_guard` 日志
|
||||
|
||||
## 关键约束
|
||||
|
||||
- **本机无法直连 Docker Hub**:镜像走 `docker.1panel.live` 前缀(`.env` 已按此配置)
|
||||
- **WhatsApp 仅 v2.9 RC 支持**,需要 Meta Cloud API 凭据;RC 版供联调,不直接上生产
|
||||
- **LibreDesk AI 仍是 beta**,建议低流量验证
|
||||
- 旧栈(Chatwoot/bridge/Twenty :3000)保持运行,可并行对照,互不影响
|
||||
Reference in New Issue
Block a user