# P15：AI 监控后台 + 测试重建 + 回填

> 状态：Draft | 创建日期：2026-03-21 | 依赖：P14（DashScope 迁移 + 调度器完善）
> 前置条件：P14 全部完成并验证通过后，方可开始 P15 实施

---

## 一、执行摘要

P14 完成 SDK 迁移和调度器修复后，本 PRD 覆盖三个方面：
1. admin-web AI 监控后台（运行总览、调度状态、调用明细、手动操作）
2. 测试体系重建（旧脚本归档、新全链路测试、属性测试更新）
3. 历史数据回填（半年内活跃会员，<100 人）

### 问题覆盖

本 PRD 覆盖 `docs/reports/2026-03-21__ai_module_issues.md` 中的：
- P2-1：App5 话术缺分类
- P2-2：MCP 无健康检查
- P2-4：全链路测试不完整
- P3-3：Prompt 版本管理（仅监控展示部分）

---

## 二、admin-web AI 监控后台

### 2.1 技术基础

- 框架：React + Vite + Ant Design（已有 8 个页面，加页面即可）
- 认证：JWT Bearer Token，复用现有 admin 权限体系
- API 前缀：`/api/admin/ai/*`
- 权限：系统管理员全量可见，不脱敏，点开详情看原文
- 门店筛选：支持按 `site_id` 筛选查看

### 2.2 页面规划

#### 页面 1：AI 运行总览（Dashboard）

| 区域 | 内容 | 数据来源 |
|------|------|---------|
| 顶部统计卡片 | 今日调用次数、成功率、Token 消耗、平均延迟 | `ai_run_logs` 聚合 |
| 趋势图 | 近 7 天调用量 + 成功率折线图 | `ai_run_logs` 按日聚合 |
| App 分布 | 各 App 调用占比饼图 | `ai_run_logs.app_type` 分组 |
| Token 预算 | 日/月预算使用进度条 | `ai_run_logs.tokens_used` 聚合 |
| 告警列表 | 最近失败/超时/熔断事件 | `ai_run_logs` WHERE status IN ('failed','timeout') |

#### 页面 2：调度状态

| 区域 | 内容 | 数据来源 |
|------|------|---------|
| 触发任务列表 | 分页表格：事件类型、会员、状态、执行链、耗时 | `ai_trigger_jobs` |
| 筛选器 | event_type / status / site_id / 日期范围 | — |
| 操作列 | 查看详情、手动重跑 | — |
| 去重统计 | 今日跳过的重复事件数 | `ai_trigger_jobs` WHERE status='skipped_duplicate' |

#### 页面 3：调用明细

| 区域 | 内容 | 数据来源 |
|------|------|---------|
| 调用记录表格 | app_type、trigger_type、member_id、tokens、延迟、状态 | `ai_run_logs` |
| 筛选器 | app_type / status / trigger_type / site_id / 日期范围 | — |
| 详情抽屉 | 点击行展开：完整 prompt、完整 response、error_message | `ai_run_logs` 单条 |

#### 页面 4：手动操作

| 功能 | 说明 |
|------|------|
| 手动重跑 | 选择 App + 会员 + 门店，触发单次执行（标记 `is_forced=true`） |
| 缓存失效 | 按 App / 会员 / 门店批量将 `ai_cache.status` 设为 `invalidated` |
| 成本二次确认 | 批量操作前展示预估调用次数和 Token 消耗，点"确认执行"后才真正执行 |
| 告警确认/忽略 | 对失败告警标记"已确认"或"忽略" |

### 2.3 后端 API 清单

```
GET  /api/admin/ai/dashboard          — 总览统计数据
GET  /api/admin/ai/trigger-jobs       — 调度任务列表（分页 + 筛选）
GET  /api/admin/ai/trigger-jobs/:id   — 调度任务详情
POST /api/admin/ai/trigger-jobs/:id/retry  — 手动重跑
GET  /api/admin/ai/run-logs           — 调用记录列表（分页 + 筛选）
GET  /api/admin/ai/run-logs/:id       — 调用记录详情（含完整 prompt/response）
POST /api/admin/ai/cache/invalidate   — 批量缓存失效
GET  /api/admin/ai/budget             — Token 预算使用情况
POST /api/admin/ai/batch-run          — 批量执行（需二次确认）
POST /api/admin/ai/batch-run/confirm  — 确认批量执行
GET  /api/admin/ai/alerts             — 告警列表
POST /api/admin/ai/alerts/:id/ack     — 确认告警
POST /api/admin/ai/alerts/:id/ignore  — 忽略告警
```

### 2.4 成本二次确认流程

```
管理员选择批量操作（如：回填 50 个会员）
  → POST /api/admin/ai/batch-run
    → 后端计算：50 会员 × 5 App = 250 次调用，预估 ~500K tokens
    → 返回 { batch_id, estimated_calls: 250, estimated_tokens: 500000 }
  → 前端展示确认弹窗："本次将执行 250 次 AI 调用，预估消耗 50 万 tokens，确认执行？"
  → 管理员点"确认执行"
    → POST /api/admin/ai/batch-run/confirm { batch_id }
    → 后端异步执行
```

---

## 三、测试体系重建

### 3.1 旧测试脚本归档

以下 4 个脚本移至 `_archived/` 目录：

| 脚本 | 原位置 | 归档位置 |
|------|---------|---------|
| `ai_full_chain_test.py` | `scripts/ops/` | `scripts/ops/_archived/` |
| `test_chat_e2e.py` | `scripts/ops/` | `scripts/ops/_archived/` |
| `test_chat_ai_quality.py` | `scripts/ops/` | `scripts/ops/_archived/` |
| `test_bailian_apps.py` | `scripts/ops/` | `scripts/ops/_archived/` |
| `test_bailian_single.py` | `scripts/ops/` | `scripts/ops/_archived/` |
| `test_bailian_full.py` | `scripts/ops/` | `scripts/ops/_archived/` |
| `_run_ai_tests_remaining.py` | `scripts/ops/` | `scripts/ops/_archived/` |
| `test_ai_bailian.py` | `apps/backend/tests/` | `apps/backend/tests/_archived/` |
| `2026-03-21__ai_full_chain_test.md` | `docs/reports/` | `docs/reports/_archived/` |

归档时保留文件内容不变，仅移动位置。审计记录（`docs/audit/`）不删除，只停止引用。

### 3.2 新全链路测试

#### 测试覆盖矩阵

| 测试场景 | 覆盖 App | 验证点 |
|---------|---------|--------|
| App1 对话（10 种入口） | App1 | SSE 流式、session_id 双轨、对话复用规则 |
| App2 定时预生成 | App2 | 8 个时间维度、缓存写入、JSON 合法性 |
| 消费事件触发链 | App3→App8→App7 | 事件传播、缓存写入、业务表写入 |
| 助教消费触发链 | App3→App8→App7→App4→App5 | 完整链路、助教关联 |
| 备注事件触发链 | App6→App8 | 备注分析、线索整合 |
| 任务分配触发链 | App4→App5 | 关系分析、话术生成 |
| 缓存命中 | App2~8 | 缓存有效期内直接返回、过期后重新生成 |
| 缓存失效 | App2~8 | admin-web 手动失效后重新生成 |
| 熔断触发 | 任意 App | 连续 5 次失败→熔断→半开→恢复 |
| Token 预算超限 | 任意 App | 超限后正确降级 |
| 失败记录 | 任意 App | `ai_run_logs` 记录完整、error_message 有值 |
| 后台可见性 | 全部 | admin-web 能看到所有运行记录和详情 |
| JSON 兜底 | App2~8 | 非法 JSON 重试 3 次 |
| 幂等验证 | App8 | 同一 member 同一天重复触发只执行一次 |
| 内容质量 | App2~8 | 返回内容包含必要字段、格式正确 |

#### App1 的 10 种入口

1. general（通用对话，始终新建）
2. customer_detail（客户详情页）
3. coach_detail（助教详情页）
4. task_detail（任务详情页）
5. finance_overview（财务总览页）
6. member_list（会员列表页）
7. settlement_detail（结算详情页）
8. note_detail（备注详情页）
9. dashboard（首页仪表盘）
10. report（报表页）

### 3.3 属性测试更新

更新 `tests/` 下的 Hypothesis 属性测试：

| 属性测试 | 验证不变量 |
|---------|-----------|
| 熔断器状态机 | 状态转换合法性：closed→open→half_open→closed/open |
| Token 预算计算 | 日/月聚合值 = 各条记录 tokens_used 之和 |
| 去重逻辑 | 相同 (event_type, member_id, site_id, date) 只产生一条非 skipped 记录 |
| 缓存过期 | expires_at 过期的缓存 status 必须为 expired |
| App8 幂等 | 同一 member 同一天的 member_retention_clue 只有一组记录 |
| session_id 重建 | 重建后的 messages 数组与本地 ai_messages 一致 |
| 限流计数 | 窗口内请求数不超过上限 |

---

## 四、历史数据回填

### 4.1 回填范围

半年内（2025-09-21 ~ 2026-03-21）三者并集：
- 有消费记录的 member
- 有备注记录的 member
- 有任务变更的 member

预估规模：<100 会员。

门店范围：`site_id = 2790685415443269`（写死）。

### 4.2 回填策略

- 执行方式：专门脚本（`scripts/ops/ai_backfill.py`）
- 分批执行：每批 10 个会员，批间间隔 5 秒
- 断点续跑：脚本记录已完成的 member_id 列表到本地文件，失败后从断点继续
- 不备份现有数据
- App8 写业务表（`member_retention_clue`）：DELETE + INSERT，事务包裹

### 4.3 回填调用链

每个会员执行：
```
App3（维客线索）→ App8（线索整理）→ App7（客户分析）
  如有助教关联：→ App4（关系分析）→ App5（话术参考）
如有备注：→ App6（备注分析）→ App8（再次整合）
```

### 4.4 成本预估

- 100 会员 × 平均 5 个 App = 500 次调用
- 每次约 2000 tokens → 总计约 100 万 tokens
- 执行前通过 admin-web 二次确认

---

## 五、数据保留策略

| 数据类型 | 保留策略 | 清理方式 |
|---------|---------|---------|
| App1 对话记录（ai_conversations + ai_messages） | 永久保留，不自动删除 | — |
| App2~8 缓存（ai_cache） | 每个 App 保留最新 20,000 条 | 定时任务：按 created_at 排序，超出部分 DELETE |
| AI 运行记录（ai_run_logs） | 保留 90 天 | 定时任务：DELETE WHERE created_at < now() - 90 days |
| 调度记录（ai_trigger_jobs） | 保留 90 天 | 同上 |

清理定时任务建议每日凌晨 03:00 执行。

---

## 六、后端 API 变更汇总

### 新增路由文件

`apps/backend/app/routers/admin_ai.py`

### 新增 Service

`apps/backend/app/services/ai/admin_service.py` — 聚合查询、批量操作、告警管理

### 数据库查询优化

- `ai_run_logs` 的 Dashboard 聚合查询需要按日分区或添加 BRIN 索引
- `ai_trigger_jobs` 的去重查询已有复合索引（§P14 六.2）

---

## 七、问题修复映射

| 问题编号 | 优先级 | 描述 | 本 PRD 对应章节 |
|---------|--------|------|----------------|
| P2-1 | P2 | App5 话术缺分类 | 回填时验证 App5 输出包含分类字段 |
| P2-2 | P2 | MCP 无健康检查 | admin-web Dashboard 展示各 App 最近调用状态 |
| P2-4 | P2 | 全链路测试不完整 | §3.2（新全链路测试） |
| P3-3 | P3 | Prompt 版本管理 | admin-web 展示当前 App 配置（只读） |

---

## 八、收尾标准流程

### 8.1 DDL 迁移

本 PRD 无新增表（P14 已创建）。如有字段调整：
1. 编写增量迁移脚本
2. 测试库验证
3. 合并基线

### 8.2 BD 手册更新

- 更新 `docs/database/BD_Manual_ai_tables.md`：补充 admin API 相关的查询模式说明

### 8.3 文档同步

| 文档 | 更新内容 |
|------|---------|
| `apps/admin-web/README.md` | 新增 AI 监控页面说明 |
| `apps/backend/README.md` | 新增 admin AI API 说明 |
| `docs/DOCUMENTATION-MAP.md` | 新增条目 |
| `docs/prd/ai-app-prompts.md` | 确认环境变量已全部更新 |

### 8.4 属性测试

- 新增 §3.3 中列出的 7 个属性测试
- 确保所有属性测试通过

### 8.5 最终检查点

- [ ] admin-web AI 监控 4 个页面功能正常
- [ ] 手动重跑功能正常，标记 forced
- [ ] 缓存失效功能正常
- [ ] 成本二次确认流程完整
- [ ] 旧测试脚本已归档到 `_archived/`
- [ ] 新全链路测试覆盖 15 个场景
- [ ] 属性测试全部通过
- [ ] 回填脚本执行完成，<100 会员数据完整
- [ ] 数据保留定时任务配置完成
- [ ] 所有文档已同步更新

---

## 九、风险与缓解

| 风险 | 影响 | 缓解措施 |
|------|------|---------|
| 回填触发大量 API 调用 | 成本突增 | 分批执行 + admin-web 二次确认 |
| 回填中途失败 | 部分会员数据不完整 | 断点续跑机制 |
| admin-web 查询大量 ai_run_logs 性能差 | 页面卡顿 | 分页 + 索引 + 90 天保留策略 |
| 全链路测试依赖真实百炼 API | 测试不稳定 | Mock 模式 + 真实 API 模式双轨 |

---

## 十、不在本 PRD 范围

- 多门店支持（BACKLOG）
- 消息队列（单独 PRD）
- Prompt 版本管理的编辑功能（BACKLOG，本轮仅展示）
- 前端刷新机制（P1-5）
- FDW 数据一致性（P1-6）
- 企业微信/邮件告警推送（后续迭代）
