Files
gochat/docs/qa/2026-07-19-cdp-full-user-function-test-plan.md
T
2026-07-27 15:50:18 +08:00

1099 lines
62 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.
# 2026-07-19 GoChat CDP 全量用户功能测试计划
> 创建日期:2026-07-19
> 最后更新:2026-07-20(Monday, July 20, 2026)
> 适用仓库:`/home/rogee/Projects/gochat`
> 当前基线:你已手动启动 `backend + frontend + fake channel`,下一步要基于 CDP 做真实用户点击路径验收
> 关联文档:
> - [docs/qa/2026-07-15-cdp-user-function-test-plan.md](/home/rogee/Projects/gochat/docs/qa/2026-07-15-cdp-user-function-test-plan.md)
> - [docs/qa/2026-07-17-cdp-full-user-function-test-plan.md](/home/rogee/Projects/gochat/docs/qa/2026-07-17-cdp-full-user-function-test-plan.md)
> - [docs/qa/2026-07-18-cdp-click-only-full-test-plan.md](/home/rogee/Projects/gochat/docs/qa/2026-07-18-cdp-click-only-full-test-plan.md)
> - [docs/qa/reports/2026-07-15-cdp-user-function-report.md](/home/rogee/Projects/gochat/docs/qa/reports/2026-07-15-cdp-user-function-report.md)
说明:
- 本文件是截至 Monday, July 20, 2026 这轮 GoChat CDP 用户功能验收的当前唯一主计划。
- 2026-07-15 / 2026-07-17 / 2026-07-18 三份文档保留为历史演进稿与执行上下文,不再作为新的主执行基线。
这份文档作为 2026-07-19 之后这轮 CDP 用户验收的当前执行基线。目标不是再写一份泛泛 QA 说明,而是把三件事一次定清楚:
1. 哪些页面和子页面必须纳入点击式测试。
2. 每个页面至少要验证哪些特性功能点,不能只看“能打开”。
3. 哪些数据和 AI 测试支撑不补齐前,不能宣称“全量实际功能测试完成”。
## 0. 本轮直接执行摘要
为了让后续接 CDP 和逐页实测时不需要再从全文里二次提炼,这里先把本轮结论压成一页:
### 0.1 现在就能开始做的
- 复用你当前已经启动的 `backend + frontend + fake channel`。
- 先接入 Chrome CDP,按真实点击路径做 dashboard 内部页面验收。
- 非 AI 页面先做 `可达 / 可见 / 可载 / 可用 / 可回 / 可观测` 六项检查。
- 证据统一沉淀到:
- [docs/qa/reports/2026-07-15-cdp-user-function-report.md](/home/rogee/Projects/gochat/docs/qa/reports/2026-07-15-cdp-user-function-report.md)
### 0.2 这轮不能混淆的边界
- 当前三服务足够支撑:
- 登录
- dashboard 主壳
- fake 会话主链路
- 联系人 / 公司 / settings 首轮点击验收
- widget / help center / campaigns 首屏与入口检查
- 当前三服务还不够支撑:
- Captain / Copilot / Playground / Embeddings 的真实 AI 成功链路
- 报表、CSAT、SLA、Audit 的完整实际功能验证
- 多坐席协作、mentions、团队分配、容量策略等需要额外数据的场景
- 另有一类问题不能混进“缺数据”:
- 如果页面主链路 API 已稳定出现 `429`
- 或 `/cable` 握手持续 `429`,导致全局 `离线的`
- 这类要优先记为运行时/限流/鉴权缺陷,而不是简单记成“样本不够”
### 0.3 开始“全量实际功能测试”前必须补齐的东西
- `fake_01` inbox 与 fake webhook 回流必须真正打通
- 至少 1 个额外可登录 agent
- 一组多状态会话池(open / pending / resolved / snoozed / assigned)
- 一组非空 CRM / labels / teams / attributes 数据
- 一套本地 deterministic `fake:ai`
### 0.4 `fake:ai` 的本轮定位
- 参考现有 `channels/fake` 的工程组织方式新增 `channels/fake-ai`
- 不先改 GoChat provider 抽象,直接复用现有 `openai_compatible`
- 首版只要优先打通:
- `POST /v1/chat/completions`
- `POST /v1/embeddings`
- `GET /health`
- 基础请求观测接口
- 但流式 SSE 不能省,否则 Copilot / Playground / 会话内 AI task 仍只能算半测
## 1. 当前已知运行前提
你已经手动启动:
```bash
pnpm dev:backend
pnpm dev:frontend
GOCHAT_WEBHOOK_URL=http://127.0.0.1:3000/webhooks/fake/fake_01 FAKE_AUTO_REPLY=true pnpm fake:start
```
当前默认地址矩阵:
| 服务 | 地址 | 用途 |
|---|---|---|
| frontend | `http://127.0.0.1:3036` | dashboard / widget / public 页面 |
| backend | `http://127.0.0.1:3000` | API / webhook / websocket / AI 任务后端 |
| fake channel | `http://127.0.0.1:9100` | 外部客户消息模拟、GoChat 出站消息回收、auto reply 回流 |
| Chrome CDP | `http://127.0.0.1:9222` | 浏览器接管与点击式取证 |
当前仓库里已经确认的事实:
| 项 | 当前状态 | 说明 |
|---|---|---|
| 根脚本 | 已有 `fake:start` / `fake:dev` / `fake:test` | fake message 平台已可运行 |
| workspace | 仅包含 `frontend` 与 `channels/fake` | 目前没有 `channels/fake-ai` |
| fake webhook 接口 | 已有 `/webhooks/fake/:identifier` | fake channel 可以回流到 GoChat |
| LLM provider | 已支持 `openai_compatible` | 可直接接本地 OpenAI-compatible 假服务 |
| AI 请求路径 | `{baseURL}/chat/completions`、`{baseURL}/embeddings` | `fake:ai` 不需要先改 provider 抽象 |
因此本轮测试边界要分成两层:
1. 非 AI 功能:现在就可以开始做 click-only CDP 实测。
2. AI 功能:先补 `fake:ai`,再进入“全量实际功能测试”。
### 1.1 本轮已复核的 checkout 事实
为了避免计划继续沿用旧假设,这里把这次实际对过代码和脚本的结论单独固定下来:
| 类别 | 已复核事实 | 结论 |
|---|---|---|
| 根脚本 | `package.json` 当前只有 `fake:start` / `fake:dev` / `fake:test`,`dev:all` 也只并发 `backend + frontend + fake` | 目前仓库里还没有现成的 `fake:ai` 脚本,需要新增 |
| fake channel 形态 | fake message 平台已经独立在 `channels/fake` | 适合作为 `channels/fake-ai` 的工程骨架参考 |
| fake channel 默认 webhook | `channels/fake/src/index.ts` 默认指向 `http://127.0.0.1:3000/webhooks/fake/fake_inbox_1` | 本轮若使用 `fake_01`,必须显式通过环境变量或 `POST /api/config` 覆盖,否则会出现 fake 健康正常但 GoChat 不建会话的假失败 |
| fake channel 控制面 | 已有 `POST /api/send`、`GET /api/messages`、`GET /api/status`、`POST /api/reset`、`POST /api/config`、`GET /health`、`POST /receive` | 会话 E2E、出站回收、自动回流、批量造数都可以围绕现有 fake control surface 设计,不需要再额外补一套消息注入工具 |
| OpenAI-compatible provider | `backend/internal/llm/openai_provider.go` 当前会把 `baseURL` 去掉尾部 `/`,然后实际调用 `POST {baseURL}/chat/completions`、流式 `POST {baseURL}/chat/completions`、`POST {baseURL}/embeddings` | `fake:ai` 只要实现 OpenAI-compatible 最小面即可,不需要先改 GoChat provider 抽象 |
| 流式能力 | 当前 provider 已有 `ChatCompletionStream`,按 SSE 解析 chunk | 如果会话回复框、Playground 或 Copilot 走流式,`fake:ai` 不能只做非流式 JSON |
| Captain / Copilot 路由面 | 当前后端已存在 assistants / responses / documents / scenarios / custom_tools / copilot_threads / tasks 等 Captain API | AI 页面覆盖可以按真实页面族拆,不需要先补路由 |
### 1.1.1 本轮计划涉及到的关键代码锚点
为了让后面补数据、接 `fake:ai`、接 CDP 时都能直接落到代码位置,而不是继续靠口头记忆,这里把本轮最关键的 checkout 锚点再固定一遍:
| 主题 | 代码位置 | 本轮用途 |
|---|---|---|
| 根脚本入口 | `package.json` | 确认当前只有 `fake:start` / `fake:dev` / `fake:test`,还没有 `fake:ai` 启动脚本 |
| workspace 成员 | `pnpm-workspace.yaml` | 确认当前 workspace 只纳入 `frontend` 与 `channels/fake`,后续若落地 `channels/fake-ai` 需要补到这里 |
| fake 默认 webhook | `channels/fake/src/index.ts` | 确认默认仍是 `http://127.0.0.1:3000/webhooks/fake/fake_inbox_1`,不是这轮要测的 `fake_01` |
| fake 控制面 | `channels/fake/src/server.ts` | 对照现有 `POST /api/send`、`POST /api/config`、`POST /api/reset`、`GET /api/status`、`GET /api/messages` 是否足够支撑造数与回流观察 |
| OpenAI-compatible provider 入口 | `backend/internal/llm/provider_manager.go` | 确认当前 chat / embedding provider 已接受 `openai_compatible` |
| chat/embedding 实际请求路径 | `backend/internal/llm/openai_provider.go` | 确认 `fake:ai` 至少要兼容 `POST /v1/chat/completions`、流式 `POST /v1/chat/completions`、`POST /v1/embeddings` |
| smoke seed 对象 | `backend/cmd/gochat/main.go` | 确认当前 seed 已准备 smoke account、website inbox、help center、crm、sla、captain 等基线对象 |
| smoke feature flags | `backend/cmd/gochat/main.go` | 确认 `campaigns`、`captain_integration_v2`、`help_center`、`reports`、`team_management`、`channel_email`、`channel_voice` 等入口默认应处于开启基线 |
### 1.2 当前 smoke seed 能直接提供的基线
执行前如果本地数据过脏或过空,仍然建议先跑一次:
```bash
cd backend
go run ./cmd/gochat seed
```
按当前 `backend/cmd/gochat/main.go` 的 smoke seed,实现上已经会直接准备出下面这些基线对象:
| 类别 | 当前 seed 已覆盖 | 适合先验证什么 |
|---|---|---|
| 管理员与账户 | `admin@gochat.local / changeme`、`Test Account` | 登录、dashboard 主壳、General/Profile/Account 基线 |
| Website inbox | `Test Website Inbox`,含 `website_token`、欢迎语、CSAT、email collect | widget、pre-chat、campaign、website inbox settings |
| Voice inbox | `Smoke Voice Inbox`(当前为 `twilio_sms` 型 smoke inbox) | voice/twilio 相关设置页、降级态、渠道多样性 |
| CRM 基线 | `Smoke Customer`、`Smoke Company` | 联系人/公司详情、会话侧栏联动 |
| 会话与消息 | 1 条 open conversation,含 incoming / outgoing / CSAT template 消息 | 会话详情基线、消息渲染基线、CSAT 展示基线 |
| Help Center 基线 | `Smoke Help Center`、1 个 category、1 篇 published article | portal、article、public help center 基线 |
| SLA / 权限基线 | 1 条 `Smoke SLA`、1 条 custom role、1 条 capacity policy | SLA 页面、权限页面、assignment policy 基线 |
| Captain 基线 | 1 个 `Smoke Captain` assistant、1 条 Captain message、1 个 agent bot | Captain 列表/详情首屏、conversation 内 Captain 渲染 |
这组 smoke seed 足够让我们先开始“页面能否挂载、入口是否通、基础 CRUD 是否可点”的第一轮 CDP 检查;但它还不够支撑“全量实际功能测试完成”。后文第 6 节列出的缺口,默认都是在这组 seed 基线上继续补的,而不是从零开始。
另外有两个这次已经对过代码、执行时很容易误判的点,建议直接记住:
- 当前 smoke account 会一次性打开大量 feature flags,包括 `campaigns`、`captain_integration_v2`、`custom_tools`、`help_center`、`inbox_view`、`reports`、`sla`、`team_management`、`channel_email`、`channel_voice` 等,所以首轮 CDP 的目标应是验证“这些入口是否真的可挂载、可加载、可操作”,而不是先怀疑入口没显示是产品未开功能。
- 当前 smoke conversation 的 `labels` 只是会话表上的字符串值 `vip`,不是完整的 labels 配置数据集;因此如果标签列表页、标签筛选器或标签报表看起来“有一个标签能显示”,也不能把它算作 labels 功能已完成真实验证。
### 1.3 当前可立即开测 vs 必须补前置
为了让后续执行不至于一上来就把 AI/报表/高级后台全部点成一片 `blocked`,建议先把页面族按“现在就能开始”和“必须补前置”拆清楚:
| 页面族 | 当前状态 | 说明 |
|---|---|---|
| 登录 / dashboard 主壳 / 一级导航稳定性 | 可立即开测 | 现有 `backend + frontend` 已满足 |
| 会话主链路(列表、详情、回复、fake 入站/出站/auto reply) | 可立即开测,但依赖 `fake_01` inbox 建档 | 如果 fake 配置仍指向默认 `fake_inbox_1`,要先纠正 |
| 联系人 / 公司 / 搜索 / 通知 | 可开始首屏与基础跳转 | 但若 contacts/companies 太少,分页/搜索/合并只能记 `partial` 或 `blocked` |
| Settings / Agents / Teams / Labels / Attributes / Inboxes | 可开始首屏与基础 CRUD | 多数页面可先测渲染与保存;成员绑定、团队联动依赖第二个 agent |
| Reports / CSAT / SLA / Audit | 需要更多真实数据后再做“全量实际功能” | 没有 metrics 样本时只能做挂载验证 |
| Campaigns / Widget / Public Help Center | 需要 website inbox 与 portal 内容更完整 | launcher 可测不等于用户链路已通过 |
| Captain / Copilot / Playground / Embeddings | 需要 `fake:ai` 后再进入真实功能测试 | 当前最多只能做页面渲染、表单、错误态 |
建议首轮执行顺序固定为:
1. 登录与 dashboard 主壳
2. fake 会话主链路
3. 联系人 / 公司 / 搜索 / 通知
4. Settings 基础 CRUD
5. 补数据
6. Reports / Campaigns / Widget / Help Center
7. `fake:ai` 接入后再跑 Captain / Copilot / Playground / Embeddings
## 2. 强约束
### 2.1 导航约束
- 登录页、widget/public 根入口允许直接打开。
- 登录后的 GoChat 内部页面一律通过真实 UI 点击进入。
- 不允许把 `/app/accounts/:id/...` 深链当成页面通过证据。
- 如果只能靠手输 URL 才能进入,记为入口或路由组织问题,不算通过。
### 2.2 页面判定约束
每个页面至少同时满足以下 6 项,才可判为 `pass`:
1. 可达:通过真实 UI 路径点击进入。
2. 可见:主区域不是空壳,不是只变 URL。
3. 可载:关键 API 2xx,首屏数据成功加载。
4. 可用:至少 1~3 个核心操作真实执行成功。
5. 可回:返回列表、刷新、再次进入时状态一致。
6. 可观测:console、network、runtime exception 已留证。
以下情况不能算通过:
- URL 改了,但主区域没切换。
- 主区域只显示 `离线的`、空白、Vue update error、Unhandled promise error。
- 页面主链路接口 4xx/5xx,且无明确降级提示。
## 3. 执行前检查
| 检查项 | 动作 | 通过标准 |
|---|---|---|
| backend health | `GET /health` | 200 |
| frontend login | 打开 `/app/login` | 登录页正常渲染 |
| fake health | `GET http://127.0.0.1:9100/health` | `status=ok` |
| seed 基线 | 必要时执行 `cd backend && go run ./cmd/gochat seed` | 至少能登录并看到 smoke 基础对象 |
| fake config 对齐 | `POST http://127.0.0.1:9100/api/config` 或读取当前 fake 状态 | 确认 webhook 实际指向 `/webhooks/fake/fake_01`;不要沿用代码默认的 `fake_inbox_1` |
| fake inbox 绑定 | 确认 GoChat 内存在 `fake_01` 对应 fake inbox | fake 入站能建会话 |
| CDP attach | `GET http://127.0.0.1:9222/json/version` | 能拿到 `webSocketDebuggerUrl` |
| Node tooling | `command -v npx` | `npx` 可用 |
| 登录态 | 通过 UI 登录 | 能进入 dashboard |
| websocket | 登录后观察 `/cable` | 无持续 401/403/重连风暴 |
| 报告落点 | 确认 report 文件存在 | 本轮证据统一追加到既有 report |
### 3.1 基于当前 fake channel 的最小造数配方
当前这轮不需要先新写消息注入脚本。`channels/fake` 已经提供了足够的控制面,可以直接拿来准备 CDP 首轮数据:
| fake 接口 | 作用 | 适合准备什么 |
|---|---|---|
| `POST /api/send` | 模拟客户发首条消息到 GoChat | 批量建 open 会话、补联系人最近消息 |
| `POST /api/reply` | 模拟客户针对某条消息回复 | 验证 reply chain、引用回复、同会话回流 |
| `POST /api/close` | 模拟渠道侧结束会话 | 验证会话结束事件、关闭后的 UI 状态 |
| `POST /api/typing` | 模拟客户输入中 | 验证 typing indicator |
| `POST /api/agent/online` / `POST /api/agent/offline` | 模拟坐席在线状态观测 | 在线状态、presence、分配辅助验证 |
| `GET /api/messages` | 查看 GoChat 出站消息是否回流 fake | 验证 agent 回复、自动回复、模板出站 |
| `POST /api/reset` | 清空 fake 内存态 | 每批次回归前重置观察面板 |
| `GET /api/status` | 查看 fake 当前状态与计数 | 开测前确认平台不是脏态 |
建议固定先跑一次:
```bash
curl -X POST http://127.0.0.1:9100/api/reset
curl http://127.0.0.1:9100/api/status
```
然后至少灌入一条首消息,确认 `fake_01` 主链路已通:
```bash
curl -X POST http://127.0.0.1:9100/api/send \
-H 'Content-Type: application/json' \
-d '{
"inbox_identifier":"fake_01",
"sender_id":"cdp_customer_001",
"sender_name":"CDP Customer 001",
"content":"你好,这是一条 CDP 首轮基线消息"
}'
```
再用 dashboard 回复一次后,通过:
```bash
curl http://127.0.0.1:9100/api/messages
```
确认 fake 已收到 GoChat 出站消息。这样可以先锁定“入站、坐席回复、fake 回收出站”三段链路都成立,再继续更大范围页面测试。
需要注意:
- fake channel 最适合先批量造 `open` 会话基线。
- `pending / resolved / snoozed / assigned / labeled` 更适合在 dashboard 里通过真实 UI 操作流转出来,这样数据准备本身也能顺手成为 CDP 功能验证的一部分。
- 如果 `GET /api/messages` 始终空,而 dashboard 看起来“已经回复成功”,优先回查 fake inbox 建档、webhook identifier、以及当前 fake config 是否仍指向默认 `fake_inbox_1`。
### 3.2 当前已知运行时风险(不要误记为缺数据)
这部分用于约束后续报告判定口径。以下现象如果在 Monday, July 20, 2026 这一轮继续复现,应优先按“产品缺陷”记账,而不是按“缺数据”处理:
| 风险项 | 当前已见现象 | 影响页面族 | 推荐判定 |
|---|---|---|---|
| websocket 握手异常 | `/cable` 持续 `429`,主壳或子页伴随 `离线的` | 会话、通知、Copilot、所有依赖实时状态的页面 | `fail` 或 `partial pass with runtime issue` |
| Contacts 主链路异常 | `GET /api/v1/accounts/:id/contacts* => 429` | 联系人列表、active/segment/label 子页 | `fail`,不要记成“只是联系人太少” |
| Companies 主链路异常 | `GET /api/v1/accounts/:id/companies* => 429` | 公司列表与详情入口 | `fail` |
| Reports 主链路异常 | `/api/v2/accounts/:id/reports*`、`/live_reports*`、`/agents`、`/cache_keys` 出现 `429` | 报告总览、agent/team/label/inbox 子页 | `fail`,不要误记成“指标样本不足” |
| Campaigns 主链路异常 | `GET /api/v1/accounts/:id/campaigns => 429` | live chat / sms / whatsapp campaigns | `fail` |
| Help Center 主链路异常 | `GET /api/v1/accounts/:id/portals => 429` | portal、articles、categories、settings | `fail` |
| Copilot Config 主链路异常 | `GET /api/v1/accounts/:id/copilot/config => 429` | Copilot 配置、provider 保存前置读取 | 至少记 `partial pass`,不能算真实配置链路通过 |
执行时统一按下面规则处理:
1. 如果页面只是空态,但关键 API 是 `200`,且当前数据确实不足,可记 `blocked: dataset missing`。
2. 如果页面首屏关键 API 是 `429/4xx/5xx`,即使 UI 壳层还在,也优先记 `fail` 或 `partial pass with runtime issue`。
3. 如果 URL 已切换,但主区叠加 `离线的`、空白壳、Vue update error,且同时伴随 `/cable` 或主链路 API `429`,优先归类为壳层/运行时缺陷。
## 4. CDP 统一取证格式
每个页面或子页至少记录:
- page group
- click path
- url
- expected
- observed
- api
- console
- verdict
每个页面族默认至少要有:
1. 1 个首屏加载用例
2. 1~3 个核心操作用例
3. 1 个刷新/返回一致性用例
4. 1 个异常、空态或边界态用例
### 4.1 每个页面都要套用的测试切面
为了避免后面执行时只测“能打开”,这里再把页面级测试切面固定下来。后续不管是 dashboard、settings、help center 还是 Captain 页面,都至少按下面维度套一次:
| 页面类型 | 必测切面 |
|---|---|
| 列表页 | 首屏加载、筛选、搜索、分页、排序、空态、列表项跳转 |
| 详情页 | 详情字段、关联对象、返回路径、刷新回显、跨模块跳转 |
| 表单页 | 必填校验、默认值、保存、错误提示、再次进入回显 |
| 配置页 | toggle / select / textarea / secret 输入、保存成功 toast、刷新后仍生效 |
| 弹窗/抽屉 | 打开、关闭、遮罩点击、取消、确认、成功后列表或详情联动 |
| 实时页 | websocket 更新、无需刷新出现新内容、重复事件不应脏写 |
| 文件/附件页 | 上传、失败提示、删除、二次进入状态一致 |
| AI 结果页 | loading、成功文本、超时、重试、错误态、可观测请求摘要 |
| 受限页 | 无权限时的降级态、禁用态、引导文案,而不是静默空白 |
建议执行时,每个页面至少从下面 10 个问题里勾掉 6 个以上:
1. 入口是否只能靠真实点击进入?
2. 首屏是否加载了正确 API,而不是只切 URL?
3. 页面主文案、主按钮、主列表是否真实出现?
4. 至少 1 个新增/编辑/删除/保存类核心动作是否成功?
5. 成功后 toast、列表、详情或计数是否同步更新?
6. 失败时是否给出明确错误,而不是静默失败?
7. 刷新或返回后状态是否保持一致?
8. 是否出现 `离线的`、空白壳、Unhandled promise、Vue update error?
9. 关键网络请求是否是预期方法与路径,且返回 2xx?
10. 这个页面是否仍缺少数据,导致“看起来能用,实际上没测到功能”?
### 4.2 页面类型到最小动作的收口模板
上面的测试切面是在问“这个页面有没有活过来”;这里再补一层“不同类型页面至少该跑什么动作”,避免后续只做打开检查,没有把真实功能点跑实。
| 页面类型 | 至少要跑的动作 |
|---|---|
| 列表页 | 首屏加载、搜索、筛选、排序(若有)、分页/加载更多、点行进入详情、空态、错误态、刷新一致性 |
| 详情页 | 基础信息、关联对象、从详情发起编辑、返回列表、跨模块跳转回来、刷新回显一致 |
| 新建/编辑表单页 | 必填校验、格式校验、默认值、保存成功、保存失败提示、取消后不脏写、再次打开回显 |
| 弹窗/抽屉 | 打开、关闭、Esc/遮罩关闭、提交后主页面同步、失败后输入不丢失、重复打开不残留旧状态 |
| 消息/实时页 | 入站、出站、typing、状态流转、附件/富消息、掉线降级、刷新前后同一对象状态一致 |
| 配置/集成页 | 当前配置回显、secret 遮罩、保存、测试连接(若有)、错误配置提示、刷新后仍生效 |
| 报表/图表页 | 时间范围切换、维度/筛选切换、图表与列表同步、drilldown、导出/下载(若有)、空态/非空态 |
| Public / Widget 页 | 首屏渲染、入口打开、表单校验、提交、dashboard 回流验证、重新进入恢复路径 |
| AI 页 | success、timeout、rate_limit、malformed、stream、retry、历史恢复、请求观测接口回收 |
执行时建议按下面顺序收口:
1. 先用第 4.1 节确认页面已经真实挂载。
2. 再用本节确认这个页面类型对应的关键动作已经测到。
3. 如果关键动作因为缺数据或缺 `fake:ai` 跑不起来,直接记 `blocked`,不要把“页面能打开”记成 `pass`。
## 5. 页面覆盖矩阵
### 5.0 页面覆盖与源码落点对照
为了避免执行过程中只按左侧导航肉眼扫,而漏掉某些实际已存在的页面族,后续 CDP 执行时建议同时对照下面这些前端路由源文件:
| 页面组 | 主要路由文件 / 模块落点 | 说明 |
|---|---|---|
| Dashboard 主壳 | `frontend/app/javascript/dashboard/routes/index.js` | 登录后统一路由入口与保护逻辑 |
| 会话工作台 | `frontend/app/javascript/dashboard/routes/dashboard/conversation/conversation.routes.js` | 包含 home、inbox、label、team、mentions、unattended、participating、custom view/folder 等主链路 |
| Inbox View | `frontend/app/javascript/dashboard/routes/dashboard/inbox/routes.js` | inbox-view 列表与详情双栏视图 |
| 联系人 | `frontend/app/javascript/dashboard/routes/dashboard/contacts/routes.js` | contacts、segments、labels、active、detail |
| 公司 | `frontend/app/javascript/dashboard/routes/dashboard/companies/routes.js` | companies 列表与详情 |
| Campaigns | `frontend/app/javascript/dashboard/routes/dashboard/campaigns/campaigns.routes.js` | ongoing、one_off、live_chat、sms、whatsapp |
| Help Center | `frontend/app/javascript/dashboard/routes/dashboard/helpcenter/` | portals、articles、categories、settings、search |
| Settings / Attributes | `frontend/app/javascript/dashboard/routes/dashboard/settings/attributes/attributes.routes.js` | custom attributes 三类定义 |
| Settings / Canned | `frontend/app/javascript/dashboard/routes/dashboard/settings/canned/canned.routes.js` | canned responses |
| Settings / SLA | `frontend/app/javascript/dashboard/routes/dashboard/settings/sla/sla.routes.js` | SLA policy 配置页 |
| Settings / Automation | `frontend/app/javascript/dashboard/routes/dashboard/settings/automation/` | automation 规则条件与动作编排 |
| Captain | `frontend/app/javascript/dashboard/routes/dashboard/captain/`、`frontend/app/javascript/dashboard/routes/dashboard/settings/captain/` | assistants、documents、responses、scenarios、playground、provider config |
### 5.1 登录与 dashboard 主壳
| 页面组 | 入口 | 必测功能点 |
|---|---|---|
| 登录页 | `/app/login` | 邮箱/密码输入、回车提交、错误密码提示、loading、登录成功跳转 |
| dashboard 主壳 | 登录后默认页 | 左侧菜单、顶部栏、账号上下文、主区切换、刷新恢复 |
| 壳层稳定性 | 一级菜单切换 | URL 与主区同步更新,不出现 `离线的`/空白壳 |
### 5.2 会话工作台
| 页面组 | 入口 | 必测功能点 |
|---|---|---|
| 会话总览 | 左侧“会话” | open/pending/resolved/snoozed 列表、计数、分页、切换 |
| Inbox 维度会话 | 会话侧栏 inbox 分组 | inbox 过滤、列表切换、详情联动 |
| Label 维度会话 | 会话侧栏 label 分组 | 标签过滤、计数、详情打开 |
| Team 维度会话 | 会话侧栏 team 分组 | 团队过滤、分配联动 |
| Custom View / Folder 维度会话 | 会话侧栏自定义视图 | 视图过滤、保存视图、从视图进入详情后返回一致性 |
| Mentions / Unattended / Participating | 左侧特殊入口 | 对应列表、计数、切换稳定性 |
| 单会话详情 | 从列表点进会话 | 时间线、状态、优先级、标签、assignee、team、联系人/公司侧栏 |
| 回复能力 | 会话编辑区 | 发文本、private note、resolve/reopen、snooze、assign agent、assign team、加标签 |
| 富消息能力 | 会话编辑区 | 附件、预览、粘贴、模板或富消息渲染 |
| 实时链路 | fake channel + 会话页 | fake 入站、客服回复、fake 回收出站、auto reply 回流、无刷新更新 |
### 5.3 Inbox View
| 页面组 | 入口 | 必测功能点 |
|---|---|---|
| Inbox View 列表 | Inbox View 入口 | 空态/非空态、筛选切换、主列表渲染 |
| Inbox View 详情 | 从 Inbox View 打开会话 | 列表与详情同步、返回路径稳定、主区真实切换 |
### 5.4 联系人与公司
| 页面组 | 入口 | 必测功能点 |
|---|---|---|
| 联系人列表 | 左侧“联系人” | 列表加载、搜索、分页、segment/label/active 过滤 |
| 联系人详情 | 列表点进详情 | 基础资料、自定义属性、标签、最近会话、最近活动 |
| 联系人操作 | 联系人页按钮/弹窗 | 新建、编辑、删除、合并、加标签 |
| 公司列表 | 左侧“公司” | 列表、搜索、分页、计数 |
| 公司详情 | 列表点进详情 | 基本信息、关联联系人、会话、备注、自定义属性 |
| CRM 联动 | 会话 ↔ 联系人 ↔ 公司 | 从会话侧栏跳联系人/公司,再回会话,状态不丢 |
### 5.5 全局搜索与通知
| 页面组 | 入口 | 必测功能点 |
|---|---|---|
| 全局搜索 | 顶部搜索入口 | conversations/messages/contacts/articles tab 切换、关键词高亮、跳转 |
| 搜索异常态 | 搜索接口异常时 | 有错误提示,不静默失败 |
| 通知中心 | 右上通知入口 | 列表加载、已读/未读、跳回目标会话或页面 |
### 5.6 Reports
| 页面组 | 入口 | 必测功能点 |
|---|---|---|
| Overview | 左侧“报告”默认页 | 指标卡、筛选器、时间范围 |
| Conversations / Agents / Teams / Labels / Inboxes | 报告各子页 | 图表、筛选、列表、drilldown |
| CSAT | 报告 → CSAT | metrics、分布、评论、空态/非空态 |
| SLA | 报告 → SLA | metrics、列表、筛选、下载 |
| Bot / Live Reports | 报告相关子页 | feature gate、空态、列表或图表渲染 |
### 5.7 Settings:账户、组织、权限
| 页面组 | 入口 | 必测功能点 |
|---|---|---|
| General / Account | 设置 → General | 账户名、语言、时区、保存与刷新回显 |
| Profile | 设置 → Profile | 昵称、签名、头像、通知偏好 |
| Security / MFA / SSO | 设置相关子页 | 表单渲染、保存校验、受限能力降级提示 |
| Agents | 设置 → Agents | 列表、创建/邀请、编辑、重置密码、状态 |
| Teams | 设置 → Teams | 列表、新建、编辑、成员绑定 |
| Custom Roles | 设置 → Custom Roles | 列表、权限矩阵、创建/编辑、绑定用户 |
| Assignment Policy | 设置 → Assignment Policy | policy 列表、创建、编辑、inbox/agent 绑定 |
| Agent Capacity | Assignment Policy 子页 | 容量策略创建、编辑、保存回显 |
### 5.8 Settings:Inbox 与渠道
| 页面组 | 入口 | 必测功能点 |
|---|---|---|
| Inbox 列表 | 设置 → Inboxes | 列表完整、类型徽标、入口稳定 |
| Website inbox | 具体 inbox 设置页 | 基础配置、欢迎语、营业时间、CSAT、预聊天表单、锁单会话 |
| Email inbox | Email inbox 设置页 | provider/SMTP/IMAP 表单、校验、降级态 |
| API inbox | API inbox 设置页 | token、endpoint、复制、回显 |
| Voice/Twilio | 对应 inbox 设置页 | voice 配置、缺配置时错误提示或降级态 |
| Fake inbox | fake inbox 设置页 | identifier/webhook 回显、channel 健康联动 |
| Inbox members / collaborators | inbox 成员页 | agent 绑定、移除、回显 |
### 5.9 Settings:提效工具与规则编排
| 页面组 | 入口 | 必测功能点 |
|---|---|---|
| Labels | 设置 → Labels | 列表、创建、编辑、删除 |
| Custom Attributes | 设置 → Attributes | contact/conversation/company 三类定义 CRUD |
| Canned Responses | 设置 → Canned Responses | 列表、创建、编辑、插入编辑器 |
| Macros | 设置 → Macros | 列表、创建、编辑、执行宏后会话变化 |
| Automation | 设置 → Automation | event/condition/action 表单、create/edit/clone/delete |
| Conversation Workflow | 设置相关页 | 配置项渲染、保存、回显 |
| SLA Policies | 设置 → SLA | 列表、创建、编辑、删除、命中展示 |
### 5.10 Settings:Integrations、Audit、Billing
| 页面组 | 入口 | 必测功能点 |
|---|---|---|
| Integrations Root | 设置 → Integrations | 卡片渲染、入口可点 |
| Webhooks | Integrations → Webhooks | 列表、创建、编辑、删除、测试 |
| Dashboard Apps | Integrations → Apps | 列表、创建、删除、iframe 装载 |
| Slack / Linear / Notion / Shopify 等 | 对应子页 | 配置页可见、错误态、保存/测试 |
| Audit Logs | 设置 → Audit Logs | 列表、筛选、真实操作写入可见 |
| Billing | 设置 → Billing | 页面加载、配额、enterprise 降级态 |
### 5.11 Campaigns、Help Center、Widget、Public
| 页面组 | 入口 | 必测功能点 |
|---|---|---|
| Live Chat / SMS / WhatsApp Campaigns | 左侧“Campaigns” | 列表、创建、编辑、启停、依赖 inbox 校验 |
| Portal 列表 | 左侧“帮助中心” | portal 列表、切换、空态 |
| Articles | portal 内文章页 | 列表、tab、过滤、进入编辑 |
| New/Edit Article | 文章新建/编辑入口 | 新建、编辑、保存、预览 |
| Categories | portal 分类页 | 列表、新建、编辑、排序 |
| Locales | portal locales 页 | locale 列表、切换、多语言入口 |
| Portal Settings | portal settings 页 | 设置项加载、保存、异常态 |
| Widget Init | website widget 根入口 | 首屏渲染、launcher 打开 |
| Pre-chat form | widget 内流程 | 字段展示、必填校验、提交建会话 |
| Widget Messaging | widget 会话页 | 客户发首条消息、客服回复、回流验证 |
| Public CSAT | public csat 链接 | 页面可打开、评分提交、后台有回流证据 |
| Public Help Center | public portal 链接 | portal 首页、分类、文章、搜索 |
### 5.12 Captain / Copilot / AI 页面
| 页面组 | 入口 | 必测功能点 |
|---|---|---|
| Captain Config | 设置 → Captain / Copilot Config | provider 配置、feature toggle、model 保存、test config |
| Assistants | Captain 助手列表 | 列表、创建、编辑、删除 |
| Assistant Settings | 单助手 settings 页 | 基础信息、guardrails、response guidelines、保存回显 |
| Assistant Inboxes | 助手 inboxes 页 | 绑定/解绑 inbox |
| Documents | 助手 documents 页 | URL/PDF 创建、sync 状态、失败态、删除 |
| Responses / FAQs | 助手 responses 页 | 列表、pending 审核、approve/reject、搜索 |
| Scenarios | 助手 scenarios 页 | 列表、创建、编辑、启停、示例导入 |
| Custom Tools | 工具页 | 列表、创建、测试、启用禁用 |
| Playground | 助手 playground 页 | 发 prompt、接收稳定响应、错误态 |
| Conversation 内 AI Tasks | 会话页 AI 入口 | summarize、rewrite、reply suggestion、label suggestion、follow_up |
| Copilot Threads | Copilot 面板 | 创建 thread、发消息、接收回复、刷新恢复历史 |
说明:
- 在没有 `fake:ai` 前,上面这些页面只能做“路由、表单、空态、错误态”验证。
- 只有接入本地 deterministic AI provider 后,才能把 Playground、Tasks、Copilot、Document Embedding 记为真实功能通过。
## 6. 还需要补充的、会影响全量实际测试的数据
### 6.1 P0:不补就会大面积 blocked
| 缺口 | 最低要求 | 直接影响 |
|---|---|---|
| 第二个可登录 agent | 1 个 | 分配、协作、在线状态、mentions、跨坐席实时 |
| `fake_01` inbox 真正建档 | 1 个 | fake webhook 主链路、消息回流 |
| 多状态会话池 | 至少 8 条 | open / pending / resolved / snoozed / unattended / assigned 视图 |
| 更丰富消息类型 | text / private note / attachment / template | 会话详情、时间线、编辑器、下载 |
| website inbox 完整配置 | 1 套 | widget / pre-chat / live chat campaign |
| 本地 `fake:ai` | 1 套可观测 `openai_compatible` 假服务 | Captain / Copilot / Playground / Embedding |
### 6.2 P1:不补就只能看空态或半成品态
| 缺口 | 最低要求 | 直接影响 |
|---|---|---|
| Contacts | 8~12 条 | CRM 列表、搜索、分页、合并 |
| Companies | 3~5 条 | 公司列表、详情、关联联系人 |
| Labels | 5 条以上 | 过滤、标签设置、报表 |
| Teams | 2 条以上 | 团队分配、团队报表 |
| Custom attributes | 三类各 2 条定义 | 表单、筛选、详情侧栏 |
| Canned responses | 3 条 | 编辑器插入、列表 CRUD |
| Macros | 3 条 | 会话执行宏 |
| Automation rules | 3 条 | 列表、编辑、条件动作校验 |
| Notifications | 5 条以上 | 通知列表与跳转 |
| Audit logs | 5 条以上 | 审计页真实内容 |
### 6.3 P2:不补就无法完成高级页验证
| 缺口 | 最低要求 | 直接影响 |
|---|---|---|
| Email inbox | 1 个 | email channel 配置页 |
| API inbox | 1 个 | API channel 页面与 token 展示 |
| Campaign 样本 | live chat / sms / whatsapp 各 1 条 | campaign 列表、编辑、启停 |
| Help Center 丰富数据 | categories 2+、articles 5+、locales 2+ | 多语言与分类切换 |
| CSAT responses | 5 条以上 | CSAT 页面、public submit 回流 |
| Applied SLA data | 3 条以上 | SLA metrics / download |
| Dashboard app | 1 个 | iframe app 页面 |
### 6.4 建议一次性补齐的数据包
| 数据包 | 推荐下限 |
|---|---:|
| 管理员 | 1 |
| 普通 agent | 2 |
| custom role 用户 | 1 |
| fake inbox | 1 (`fake_01`) |
| website inbox | 1 |
| api inbox | 1 |
| email inbox | 1 |
| voice/twilio inbox | 1 |
| open 会话 | 4 |
| pending 会话 | 2 |
| resolved 会话 | 2 |
| snoozed 会话 | 2 |
| unattended / participating / mention 命中会话 | 各 1 |
| 附件消息 | 2 |
| private note | 2 |
| labels | 5 |
| teams | 2 |
| contacts | 10 |
| companies | 4 |
| canned responses | 3 |
| macros | 3 |
| automation rules | 3 |
| custom attributes | 三类各 2 |
| notifications | 5 |
| audit logs | 5 |
| csat responses | 5 |
| applied sla | 3 |
| help center portal | 1 |
| categories | 2 |
| published articles | 3~5 |
| campaigns | live chat / sms / whatsapp 各 1 |
| captain assistant | 1 |
| captain documents | 3(syncing / synced / failed) |
| captain responses | 5 |
| captain scenarios | 2 |
| captain custom tools | 1 |
### 6.5 页面组与数据依赖速查表
| 页面组 | 至少需要的数据 | 不满足时常见假象 |
|---|---|---|
| 登录 / dashboard 主壳 | 1 个可登录管理员 | 只能停在登录页,后续页面全部 blocked |
| 会话工作台 | `fake_01` inbox、8+ 会话、不同 status、2 个 agent | 列表能开但过滤、分配、实时链路都是空壳 |
| 联系人 / 公司 | 10 contacts、4 companies、labels、custom attributes | 页面能进,但搜索、分页、联动、详情都接近空态 |
| 搜索 / 通知 | conversations、contacts、articles、notifications | 搜索永远空结果,通知中心只有空态 |
| Reports | 多状态会话、teams、labels、csat、applied sla | 图表能渲染但没有真实指标,无法验证 drilldown |
| Settings / Agents / Teams / Roles | 2 agents、2 teams、1 custom role 用户 | 列表可见但无法验证成员绑定、权限边界 |
| Settings / Inboxes / Channels | fake / website / api / email / voice inbox 各至少 1 套 | 只能看到部分渠道页,无法覆盖配置差异 |
| 自动化与提效工具 | canned responses、macros、automation rules、custom attributes | CRUD 可测性不足,执行链路难以验证 |
| Help Center / Public | 1 portal、2 categories、3+ published articles、2 locales | public 页可打开,但分类、多语言、搜索都不成立 |
| Widget / Campaigns | website inbox、campaign 样本、基础 contact / conversation | widget 只能看 launcher,无法验证建会话与触达 |
| Captain / Copilot / AI | assistant、documents、responses、scenarios、custom tools、`fake:ai` | 页面只剩渲染/空态,不能证明 AI 主链路可用 |
### 6.6 数据补齐来源建议
为了避免后续一边跑 CDP 一边临时决定“这批数据到底该怎么造”,建议按下面的来源优先级准备:
| 数据/能力 | 推荐来源 | 原因 | 备注 |
|---|---|---|---|
| 管理员、基础 account、website inbox、help center smoke 对象 | `cd backend && go run ./cmd/gochat seed` | 当前仓库已有最稳定的 smoke 基线 | 适合作为第一层底座,不建议手工从零建;但要记得它只给 1 条 open conversation,且标签只是字符串 `vip` |
| `fake_01` inbox | GoChat 后台 UI 建档 | 需要和当前 `GOCHAT_WEBHOOK_URL=/webhooks/fake/fake_01` 精确对齐 | 要实际核对 identifier、channel type、回流是否打通 |
| 多状态 conversations / messages | `pnpm fake:start` + fake 平台批量发消息 | 最贴近真实入站链路,也最利于 CDP 留证 | 建议按 open / pending / resolved / snoozed 分批造数 |
| 第二个 agent、teams、custom role 用户 | GoChat 后台 UI 创建 | 这些本身就是待测功能的一部分 | 既能补数据,也能顺便验证 CRUD |
| contacts / companies / labels / canned responses / macros / custom attributes | GoChat 后台 UI 为主 | 列表、表单、回显、搜索都需要真实跑一遍 | 数量不够时再考虑补 seed/脚本 |
| notifications / audit logs / csat / applied sla | 真实操作回灌优先 | 这类页面最怕“有数据但来源不真实” | 可在前面批次执行时顺手沉淀 |
| campaigns 样本 | GoChat 后台 UI 创建 | 能同时覆盖依赖校验、表单保存、列表回显 | 至少 live chat / sms / whatsapp 各 1 条 |
| help center categories / articles / locales | GoChat 后台 UI 创建 | 页面入口、编辑器、public 渲染都要验证 | smoke seed 只够首屏,不够全量切换 |
| Captain assistant / documents / responses / scenarios / tools | Captain UI 创建 | 能顺便验证 AI 后台页面本身 | 真实成功链路仍依赖 `fake:ai` |
| `fake:ai` | 新增 `channels/fake-ai` | 这是本轮唯一建议新增的测试底座 | 不建议继续把 AI 假服务塞进 `channels/fake` |
补齐顺序建议固定成:
1. `seed`
2. `fake_01` inbox
3. 第二个 agent + teams
4. fake channel 批量 conversations
5. CRM / labels / canned / macros / attributes
6. reports / csat / sla / audit 补回灌
7. `fake:ai`
其中第 2 步建议直接显式做一次 fake 配置对齐,避免把默认值带进测试:
```bash
curl -X POST http://127.0.0.1:9100/api/config \
-H 'Content-Type: application/json' \
-d '{
"webhook_url":"http://127.0.0.1:3000/webhooks/fake/fake_01",
"auto_reply":true
}'
```
然后再通过 fake 平台发一条测试消息,确认当前 inbox identifier 真的是 `fake_01`,而不是代码默认的 `fake_inbox_1`。
### 6.7 开始“全量实际功能测试”前的阻断清单
下面这张表建议直接作为开测前 checklist 使用。凡是标记为 `blocked` 的项,如果没补齐,就不要把对应页面族记成“真实功能已通过”:
| 检查项 | 最低标准 | 未满足时应如何标记 |
|---|---|---|
| 管理员登录 | 能稳定登录 `admin@gochat.local` 或等价管理员账号 | `blocked: auth baseline missing` |
| 第二个 agent | 至少 1 个额外可登录 agent | `blocked: collaboration baseline missing` |
| `fake_01` inbox | fake webhook 建会话、agent 回复回 fake、auto reply 回同会话 | `blocked: message e2e baseline missing` |
| 会话池 | 至少覆盖 open / pending / resolved / snoozed | `blocked: conversation state coverage missing` |
| CRM 数据 | contacts 8+、companies 3+ | `blocked: crm dataset too thin` |
| 标签/团队/属性 | labels 5+、teams 2+、三类 attributes 各 2+ | `blocked: filtering and settings dataset missing` |
| website inbox | widget/pre-chat 能真实建会话 | `blocked: widget baseline missing` |
| reports 数据 | CSAT、SLA、notifications、audit 至少有可见样本 | `blocked: reports dataset missing` |
| Help Center 数据 | portal/category/article/locale 至少各有 1 组真实对象 | `blocked: help-center dataset missing` |
| `fake:ai` | `/v1/chat/completions`、`/v1/embeddings` 可调用,且有请求观测接口 | `blocked: ai runtime baseline missing` |
建议执行时把页面 verdict 分成三种,而不是只写 pass/fail:
- `pass`:页面和核心功能已完成真实链路验证。
- `fail`:页面或功能本身有缺陷。
- `blocked`:页面本身未必坏,但因为缺前置数据/假服务,当前不能判定真实功能通过。
### 6.8 不要误判成“缺数据”的现有缺陷族
为了避免后续越测越乱,这里再把当前已经暴露出的非数据类问题单独列一次。它们不是靠补 contacts、companies、articles 或 `fake:ai` 就能自然消失的:
| 缺陷族 | 当前表现 | 为什么不能算“缺数据” |
|---|---|---|
| `/cable` 全局 `429` | 登录后多个页面都可能带 `离线的` 覆盖层 | 这是实时连接或限流问题,不是样本数量问题 |
| contacts / companies / reports / campaigns / portals / copilot config 请求 `429` | 页面能切路由,但主区同时出现空态、残留壳层或异常文案 | 关键 API 已经失败,说明当前是运行时或鉴权/限流问题 |
| Vue update error / Unhandled promise | URL 已更新但主区未真实挂载 | 这是壳层、store、router 或异常处理缺陷,不是数据不足 |
| “空态和非空态同时出现” | 列表卡片渲染了,但空态提示也保留在同页 | 这是前端状态管理或条件渲染问题,不是单纯缺少样本 |
因此后续报告里建议把“缺数据”与“运行时缺陷”拆开写:
- 缺数据:`blocked: dataset missing`
- 假服务未就绪:`blocked: ai runtime baseline missing`
- 运行时问题:`fail: runtime 429` / `fail: websocket degraded` / `fail: shell mount error`
## 7. 数据补齐策略
### 7.1 先用 seed 打底
```bash
cd backend
go run ./cmd/gochat seed
```
### 7.2 再补 fake channel 所需真实 inbox
最少需要把 `fake_01` 这个 webhook 标识对应到 GoChat 内的 fake inbox 配置,确保:
- fake 发出的入站消息能建会话
- dashboard 发出的出站消息能回到 `/receive`
- auto reply 回来的消息能附着到同一会话
### 7.3 把数据分三类准备
| 类别 | 数据 | 建议方式 |
|---|---|---|
| 主链路数据 | admin / 2 agents / fake inbox / website inbox / 基础 conversations | 执行前一次性准备 |
| CRUD 数据 | labels / teams / macros / canned / custom attributes | 在 CDP 首轮过程中边测边创建 |
| 报表数据 | csat / applied_sla / notifications / audit logs | 定向 seed 或通过真实操作回灌 |
## 8. AI 测试支撑:`fake:ai`
### 8.1 为什么需要单独补 `fake:ai`
当前仓库的 AI 运行时已支持 `openai_compatible` provider,实际会打:
- `/chat/completions`
- `/embeddings`
所以不需要先改 GoChat 的 provider 抽象,优先补一个本地 OpenAI-compatible 假服务即可。
如果没有本地 deterministic AI provider,Captain/Copilot 测试会卡在:
- 依赖真实第三方 key
- 响应不可复现
- 超时、429、stream 中断、tool call 失败无法稳定覆盖
### 8.2 `fake:ai` 的建议工程形态
建议新增 `channels/fake-ai`,并与现有 `channels/fake` 保持同构:
| 项 | 建议 |
|---|---|
| 目录 | `channels/fake-ai` |
| 根脚本 | `fake:ai`、`fake:ai:dev`、`fake:ai:test` |
| workspace | 把 `channels/fake-ai` 纳入 `pnpm-workspace.yaml` |
| 控制面 | `/health`、`/api/config`、`/api/reset`、`/api/status`、`/api/requests` |
| 核心接口 | `POST /v1/chat/completions`、`POST /v1/embeddings` |
建议后续本地服务矩阵统一成:
```bash
pnpm dev:backend
pnpm dev:frontend
pnpm fake:start
pnpm fake:ai
```
### 8.3 `fake:ai` 的最小接口与兼容约束
| 方法 | 路径 | 用途 |
|---|---|---|
| `GET` | `/health` | 健康检查 |
| `POST` | `/v1/chat/completions` | Playground / Copilot / AI task;同时要支持普通 JSON 返回与 `stream=true` 的 SSE chunk 返回 |
| `POST` | `/v1/embeddings` | document embedding / reindex |
这里不建议把流式支持当成“后面再加”的可选项。原因是当前 `backend/internal/llm/openai_provider.go` 已经实现了 `ChatCompletionStream`,所以只做非流式 JSON 会让 Playground、Copilot 或会话内 AI task 的一部分路径继续停留在“未真实验证”状态。
embedding 第一版也建议直接返回与当前 GoChat 默认配置兼容的固定维度向量,优先按 `1536` 实现。这样能避免出现“`/v1/embeddings` 已可调用,但 Copilot config test、Captain document sync 或向量入库仍因维度不匹配而失败”的假 blocker。
同时建议把流式返回格式也固定下来,避免后面 `fake:ai` 已经在返回 SSE,但 GoChat 仍然解析不到有效 chunk:
- 返回头至少包含 `Content-Type: text/event-stream`
- 每个 chunk 使用标准 SSE `data: ...` 行
- `data` 内容直接放 OpenAI-compatible JSON chunk
- 结束时发送 `data: [DONE]`
也就是说,`fake:ai` 的流式接口要兼容当前 provider 对下面这种结构的预期:
```text
data: {"choices":[{"index":0,"delta":{"content":"hello"}}]}
data: {"choices":[{"index":0,"delta":{"content":" world"}}]}
data: [DONE]
```
如果后续只返回自定义 event name、只返回纯文本 token,或缺少 `[DONE]`,那么本地服务虽然“看起来有流式输出”,但在 GoChat 里仍会表现成 Copilot/Playground 一直 pending 或最终空结果。
### 8.3.1 建议直接固定的最小响应样例
为了让 `fake:ai` 的实现与后续 CDP 验证都尽量少走弯路,建议把最小返回样例也直接固定下来。
普通 chat completion 建议至少返回:
```json
{
"id": "chatcmpl-fake-001",
"object": "chat.completion",
"created": 1784428800,
"model": "fake-gpt-4o-mini",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "这是一个稳定的 fake:ai 响应。"
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 12,
"completion_tokens": 10,
"total_tokens": 22
}
}
```
流式 chat completion 建议至少返回:
```text
data: {"id":"chatcmpl-fake-002","object":"chat.completion.chunk","created":1784428801,"model":"fake-gpt-4o-mini","choices":[{"index":0,"delta":{"role":"assistant"},"finish_reason":null}]}
data: {"id":"chatcmpl-fake-002","object":"chat.completion.chunk","created":1784428801,"model":"fake-gpt-4o-mini","choices":[{"index":0,"delta":{"content":"这是"},"finish_reason":null}]}
data: {"id":"chatcmpl-fake-002","object":"chat.completion.chunk","created":1784428801,"model":"fake-gpt-4o-mini","choices":[{"index":0,"delta":{"content":"流式回复。"},"finish_reason":null}]}
data: {"id":"chatcmpl-fake-002","object":"chat.completion.chunk","created":1784428801,"model":"fake-gpt-4o-mini","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
```
embedding 建议至少返回:
```json
{
"object": "list",
"data": [
{
"object": "embedding",
"index": 0,
"embedding": [0.001, 0.002, 0.003]
}
],
"model": "fake-text-embedding-3-small",
"usage": {
"prompt_tokens": 8,
"total_tokens": 8
}
}
```
注意:
- 第一版文档里示例向量可以只写 3 个值,但实际 `fake:ai` 实现建议按配置生成固定维度,例如 `1536`。
- `message.content`、流式 `delta.content`、`usage`、`finish_reason` 都建议保留,避免 Captain / Copilot / Playground 某条路径因为字段缺失而出现“服务已通但前端仍不可用”的假失败。
- 如果后续要覆盖 tool call,建议在 `tool_call` 场景里额外返回 `tool_calls` 字段,而不是污染 `success_text` 的默认响应。
### 8.4 `fake:ai` 推荐场景开关
| 场景 | 用途 |
|---|---|
| success_text | 正常文本回复 |
| success_stream | 正常流式输出 |
| tool_call | 验证 custom tool / search_documentation |
| empty | 空回复降级 |
| malformed | 非法结构错误提示 |
| timeout | loading / cancel / retry |
| rate_limit | 429 提示 |
| unauthorized | provider 配置错误提示 |
| embedding_success | 正常 embedding |
| embedding_fail | reindex / document failed 态 |
### 8.5 推荐控制接口
| 方法 | 路径 | 用途 |
|---|---|---|
| `POST` | `/api/scenario` | 切换当前场景 |
| `POST` | `/api/reset` | 清空调用历史 |
| `GET` | `/api/requests` | 观察收到的 prompt / embedding 请求 |
| `GET` | `/api/status` | 当前模式、计数、最近错误 |
| `POST` | `/api/config` | 默认模型、延迟、stream 开关、默认场景 |
`GET /api/requests` 建议至少记录:
- timestamp
- route
- scenario
- model
- messages / input 摘要
- response status
- latency
- tool calls / stream chunk 数
- 最近一次 error
### 8.6 推荐接入模板
建议默认约定:
```text
FAKE_AI_PORT=9110
FAKE_AI_BASE_URL=http://127.0.0.1:9110/v1
FAKE_AI_API_KEY=fake-ai-key
FAKE_AI_DEFAULT_MODEL=fake-gpt-4o-mini
FAKE_AI_DEFAULT_EMBED_MODEL=fake-text-embedding-3-small
FAKE_AI_EMBED_DIMENSIONS=1536
```
然后在 Copilot/Captain 配置中:
- provider 选 `openai_compatible`
- base URL 指向 `http://127.0.0.1:9110/v1`
- API key 填固定占位值即可
### 8.7 `fake:ai` 与现有 `fake channel` 的关系
为了降低心智负担,建议 `fake:ai` 直接复用 `channels/fake` 的工程组织思路:
| 维度 | `channels/fake` | 建议中的 `channels/fake-ai` |
|---|---|---|
| 角色 | 模拟外部消息渠道 | 模拟外部 LLM / embedding provider |
| 目标 | 让消息主链路可重复、可观测 | 让 AI 主链路可重复、可观测 |
| 健康检查 | `/health` | `/health` |
| 配置入口 | `/api/config` | `/api/config` |
| 状态/观测 | `/api/status`、回流消息观察 | `/api/status`、`/api/requests` |
| 重置能力 | 可重置消息状态 | 可重置请求历史与场景 |
| 回放价值 | 复现客户入站/客服出站/auto reply | 复现 success / timeout / 429 / malformed / tool_call |
这样后面无论是人工调试还是 CDP 自动验收,都可以把两类假服务当作同一风格的本地测试底座:
- `fake channel` 负责“消息是否真的通”
- `fake:ai` 负责“AI 调用是否真的通”
### 8.8 AI 页面与 `fake:ai` 场景对照表
为了让后续执行不是“把 AI 页点一遍就算过”,这里把建议场景和页面绑定起来:
| AI 页面/链路 | 至少要跑的 `fake:ai` 场景 | 通过标准 |
|---|---|---|
| Captain provider config | `success_text`、`unauthorized` | 保存成功;错误 key 时有明确失败提示 |
| Assistant Playground | `success_text`、`timeout`、`rate_limit` | 能看到成功回复、超时 loading/重试、429 提示 |
| Copilot thread / message | `success_text`、`success_stream`、`malformed` | 新 thread 可收到回复;流式时逐步渲染;坏结构时有错误态 |
| Conversation AI Tasks | `success_text`、`rate_limit`、`timeout` | summarize / rewrite / suggestion 有结果或明确失败提示 |
| Document create + sync | `embedding_success`、`embedding_fail` | 文档状态可从 syncing 到 synced/failed,并有可见错误 |
| Responses / FAQ approve flow | `success_text` | AI 生成结果可见,审批/拒绝后列表回显正确 |
| Custom tool chain | `tool_call`、`malformed` | 工具调用结果可见;坏工具结果不应打崩页面 |
| Scenario / auto reply 验证 | `success_text`、`empty` | 命中特定场景时会产生可解释输出;空响应有降级路径 |
建议 `fake:ai` 每个场景都支持固定响应模板,这样同一条 CDP 用例多次重跑时,页面文案、状态流转、请求摘要都能稳定复现。
### 8.9 `fake:ai` 首版完成标准
为了避免后续 `fake:ai` 做成“接口存在,但 GoChat 还是测不起来”的半成品,建议首版直接以下面标准验收:
| 验收项 | 最低标准 |
|---|---|
| 健康检查 | `GET /health` 返回 200 |
| OpenAI-compatible chat | `POST /v1/chat/completions` 支持普通 JSON |
| OpenAI-compatible stream | `stream=true` 时返回标准 SSE chunk,并以 `[DONE]` 结束 |
| OpenAI-compatible embeddings | `POST /v1/embeddings` 返回固定维度向量,默认建议 `1536` |
| 场景切换 | 可通过控制接口切到 `success_text / success_stream / timeout / rate_limit / unauthorized / malformed / embedding_success / embedding_fail / tool_call` |
| 观测能力 | `GET /api/requests` 至少能看到 route、scenario、model、status、latency、请求摘要 |
| 重置能力 | `POST /api/reset` 可清空历史,便于重复跑同一条 CDP 用例 |
| GoChat 接入 | Captain/Copilot 选 `openai_compatible`,把 `base_url` 指向本地 `http://127.0.0.1:9110/v1` 后,可稳定完成至少 1 条 Playground 成功回复和 1 条 Embedding 成功请求 |
如果只满足“能回一个普通 JSON 文本”,但还没有流式、embedding、观测接口,那么它最多算 `fake:ai` 原型,不建议把 AI 页面族记为“可开始全量实际功能测试”。
## 9. 推荐执行批次
### 批次 A:主壳与消息链路
- 登录
- dashboard 主壳
- 会话列表 / 详情
- fake 入站 / 出站 / auto reply
- contacts / companies / search / notifications
### 批次 B:settings 与后台页面
- agents / teams / custom roles
- inbox / labels / attributes / canned / macros
- automation / assignment policy / sla / reports
- integrations / audit / billing / help center
### 批次 C:widget / public
- widget config
- pre-chat
- public csat
- public help center
- campaign 触达验证
### 批次 D:AI
- 先接 `fake:ai`
- 再跑 captain settings / assistants / documents / responses / scenarios / tools / playground / copilot / task actions
### 9.1 每个批次的进入 / 退出标准
为了避免后续执行时“上一批次根本没打稳,就继续往下点”,建议给每个批次加一个最小 gate:
| 批次 | 进入条件 | 退出标准 |
|---|---|---|
| A:主壳与消息链路 | backend / frontend / fake channel 正常;管理员可登录;`fake_01` inbox 已建档 | 一级导航点击后主区真实切换;至少 1 条 fake 入站建会话成功;至少 1 条 agent 出站被 fake 回收;至少 1 条 auto reply 回附到同会话 |
| B:settings 与后台页面 | A 已通过;第二个 agent、teams、labels、contacts/companies 基础数据已补齐 | Agents / Teams / Inboxes / Labels / Attributes / Assignment Policy / Reports 至少都完成首屏 + 1 个核心操作验证 |
| C:widget / public | website inbox、portal/category/article 至少各有 1 套真实对象 | widget 能从 pre-chat 建会话;public help center 可完成 portal → category → article 点击链路;public CSAT 可提交 |
| D:AI | `fake:ai` 已启动;Captain/Copilot 使用 `openai_compatible` 指向本地 `base_url`;至少 1 个 assistant 已配置 | Playground、Copilot、至少 2 个 conversation AI task、1 个 document embedding 链路完成 success/error 双场景验证 |
如果某一批次的进入条件没满足,报告里建议优先写成 `blocked`,而不是继续堆积大量“页面空态/无数据/接口没配置”的伪失败。
## 10. 本轮结论
如果目标是“每个页面的特性功能点都做详细测试”,建议按下面的定义推进:
1. 这轮先以当前文档为 CDP 主计划。
2. 先用 `seed + fake channel` 打通非 AI 主流程。
3. 先补齐 `fake_01 inbox + 第二个 agent + 多状态会话 + CRM/标签/团队数据`。
4. 优先把当前已知的 `/cable` 与多模块 `429` 问题从“缺数据”里剥离出来,按运行时缺陷单独记录。
5. 单独补一个 `fake:ai`,通过 `openai_compatible` 接到当前 Copilot/Captain。
6. 在 `fake:ai` 未接入前,AI 页面只能算“渲染级验证”,不能算“全量实际功能通过”。
## 11. 下一步建议
建议按性价比最高的顺序继续:
1. 先补 `fake_01 inbox + 第二个 agent + 多状态 conversations`
2. 开始批次 A 前,先把当前已知 `429` 风险作为单独运行时检查项
3. 再开始批次 A 的 click-only CDP 实测
4. 同时立一个小实现项:落地 `fake:ai`
5. 后续所有证据统一追加到 [docs/qa/reports/2026-07-15-cdp-user-function-report.md](/home/rogee/Projects/gochat/docs/qa/reports/2026-07-15-cdp-user-function-report.md)
## 12. 建议直接拆出的前置准备工单
为了避免后续 CDP 执行时一边点一边发现基础数据不够,建议先把缺口直接拆成下面几类工单:
| 工单 | 目标产出 | 完成标准 |
|---|---|---|
| 数据基线包 | admin / 2 agents / custom role user / contacts / companies / labels / teams / 会话池 | 登录后关键页面都不是纯空态,至少能覆盖列表、详情、筛选、分配 |
| fake inbox 工单 | `fake_01` inbox 已在 GoChat 内建档并可稳定回流 | fake 入站建会话、坐席回复回 fake、auto reply 附着同一会话 |
| website/widget 工单 | 1 套可用 website inbox + pre-chat 配置 | widget 能真实建会话,不只是 launcher 可见 |
| 报表回灌工单 | csat / sla / audit logs / notifications / campaigns 样本 | reports、audit、campaign 页不是空壳,能验证 drilldown/筛选 |
| Help Center 内容包 | portal / categories / articles / locales | public portal、文章搜索、分类、多语言可实际点击验证 |
| `fake:ai` 工单 | 本地 OpenAI-compatible 假服务 + 观测接口 | Captain/Copilot/Playground/Embeddings 可以做真实主链路测试 |
如果要进一步压缩首轮准备量,推荐按下面顺序执行:
1. `fake_01 inbox`
2. 第二个 agent
3. 多状态 conversations + contacts/companies
4. website inbox + widget
5. `fake:ai`
6. reports / help center / campaigns 补样本
### 12.1 建议直接排期的执行清单
为了让这份计划文档能直接转成实施顺序,下面把建议拆成 7 个最小前置项。后续无论是继续让我补数据、补 `fake:ai`,还是直接开始 CDP 点击验收,都建议按这个顺序推进:
| 顺序 | 前置项 | 产出物 | 不完成时会卡住什么 |
|---|---|---|---|
| 1 | 对齐 `fake_01` inbox | GoChat 内有 1 个真实 fake inbox,identifier 与 `GOCHAT_WEBHOOK_URL=/webhooks/fake/fake_01` 一致 | fake 入站会话、agent 出站回流、auto reply 链路都会变成假失败 |
| 2 | 准备第二个 agent | 至少 1 个额外可登录 agent 账号 | mentions、assign、participating、team 协作、在线状态无法做真实验证 |
| 3 | 补多状态会话池 | open/pending/resolved/snoozed 各有真实样本 | 会话筛选、报表、通知、SLA 大量页面只能看到空态 |
| 4 | 补 CRM 与筛选数据 | contacts、companies、labels、teams、custom attributes、notifications 至少达到本计划下限 | contacts/companies/search/filters 看起来能打开,但其实没有测到真实能力 |
| 5 | 补 website/widget 基线 | 1 套完整 website inbox + pre-chat 配置 + 至少 1 条真实 widget 建会话样本 | widget、campaign、public CSAT 只能做渲染级检查 |
| 6 | 补 reports/help center 样本 | csat、applied sla、audit logs、portal/category/article/locale、campaign 样本 | reports/help center 只能停留在空态或首屏壳层 |
| 7 | 落地 `fake:ai` | 本地 `channels/fake-ai` 服务,至少支持 `/v1/chat/completions`、`/v1/embeddings`、`/api/requests` | Captain/Copilot/Playground/Embeddings 不能算真实 E2E |
### 12.2 `fake:ai` 首版建议验收口径
为了避免后续把 `fake:ai` 做成“接口在,但页面还是测不起来”的半成品,建议直接按下面口径验收首版:
| 验收项 | 最低标准 |
|---|---|
| Chat completion | `POST /v1/chat/completions` 同时支持普通 JSON 与 `stream=true` 的 SSE |
| Embeddings | `POST /v1/embeddings` 返回固定维度向量,默认建议 `1536` |
| 场景切换 | 至少可切 `success_text`、`success_stream`、`timeout`、`rate_limit`、`unauthorized`、`embedding_success`、`embedding_fail` |
| 请求观测 | `GET /api/requests` 能看到最近请求摘要、响应状态、延迟、场景 |
| 配置切换 | `POST /api/config` 可调整默认模型、默认场景、是否流式、人工延迟 |
| 重跑能力 | `POST /api/reset` 能清掉历史请求,便于重复跑同一条 CDP 用例 |
| GoChat 接通证明 | Copilot/Captain 以 `openai_compatible` 指向本地 `base_url` 后,至少能稳定完成 1 条 Playground 成功响应和 1 条 embedding 成功请求 |