Files
crm-ai/README.md
T
2026-10-23 10:35:28 +08:00

11 KiB
Raw Permalink Blame History

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/帮助中心)

快速开始

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 安装

<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 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:

{
  "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/runbook.md(启动/停止/日志/备份/升级/回滚/排错)。

测试

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)保持运行,可并行对照,互不影响