# 实施计划：P15 — AI 监控后台 + 测试重建 + 回填

> 权威参考：实施过程中如遇细节不明确，应优先查阅 PRD 原文：
> - `docs/prd/specs/P15-ai-monitoring-testing.md` — P15 完整设计（Dashboard、调度状态、调用明细、手动操作、测试重建、回填、数据保留）

## 概述

按依赖关系分阶段实施六个模块：DDL 迁移 → 旧脚本归档 → 后端 API（模块 A）→ 数据保留（模块 E）→ 回填脚本（模块 D）→ 测试重建（模块 C）→ 前端页面（模块 B）→ 文档收尾（模块 F）→ 收尾。

后端使用 Python（FastAPI + Pydantic），前端使用 TypeScript（React + Vite + Ant Design），属性测试使用 Hypothesis。

## 任务

### 阶段一：DDL 迁移

- [x] 1. DDL 迁移 — ai_run_logs 新增 alert_status + BRIN 索引
  - [x] 1.1 创建迁移脚本 `db/zqyy_app/migrations/2026-03-23__p15_ai_monitoring.sql`
    - ALTER TABLE `biz.ai_run_logs` ADD COLUMN `alert_status` VARCHAR(20) DEFAULT NULL
    - ADD CONSTRAINT `chk_ai_run_logs_alert_status` CHECK (alert_status IN ('pending', 'acknowledged', 'ignored'))
    - CREATE INDEX `idx_ai_run_logs_alert` ON biz.ai_run_logs(alert_status, created_at DESC) WHERE status IN ('failed', 'timeout', 'circuit_open')（部分索引）
    - CREATE INDEX `idx_ai_run_logs_created_brin` ON biz.ai_run_logs USING BRIN (created_at) WITH (pages_per_range = 32)
    - UPDATE 回填：已有 status IN ('failed','timeout','circuit_open') 的记录设置 alert_status='pending'
    - 编写回滚脚本（逆序 DROP INDEX + DROP CONSTRAINT + DROP COLUMN）
    - _需求: A8.4, F1.1_

### 阶段二：旧测试脚本归档（模块 C1）

- [x] 2. 归档旧测试脚本
  - [x] 2.1 移动 7 个 `scripts/ops/` 旧脚本到 `scripts/ops/_archived/`
    - `ai_full_chain_test.py`、`test_chat_e2e.py`、`test_chat_ai_quality.py`、`test_bailian_apps.py`、`test_bailian_single.py`、`test_bailian_full.py`、`_run_ai_tests_remaining.py`
    - _需求: C1.1_

  - [x] 2.2 移动后端测试和报告到归档目录
    - `apps/backend/tests/test_ai_bailian.py` → `apps/backend/tests/_archived/test_ai_bailian.py`
    - `docs/reports/2026-03-21__ai_full_chain_test.md` → `docs/reports/_archived/2026-03-21__ai_full_chain_test.md`
    - 保留文件内容不变，仅移动位置
    - _需求: C1.2, C1.3, C1.4, C1.5_

### 阶段三：后端 API（模块 A）

- [x] 3. 后端 Schema — Admin AI
  - [x] 3.1 创建 `apps/backend/app/schemas/admin_ai.py`
    - 定义 Dashboard 相关：`DashboardResponse`、`DailyTrend`、`AppDistItem`、`BudgetInfo`、`AlertItem`、`AppHealthItem`
    - 定义调度任务相关：`TriggerJobListResponse`、`TriggerJobItem`、`TriggerJobDetailResponse`、`RetryResponse`
    - 定义调用记录相关：`RunLogListResponse`、`RunLogItem`、`RunLogDetailResponse`
    - 定义缓存失效：`CacheInvalidateRequest`（site_id 必填）、`CacheInvalidateResponse`
    - 定义 Token 预算：`BudgetResponse`
    - 定义批量执行：`BatchRunRequest`、`BatchRunEstimate`、`BatchRunConfirm`、`BatchRunConfirmResponse`
    - 定义告警：`AlertListResponse`、`AlertActionResponse`
    - _需求: A1.1, A2.1, A4.1, A5.1, A6.1, A7.1, A8.1_

- [x] 4. 后端服务层 — AdminAIService
  - [x] 4.1 创建 `apps/backend/app/services/ai/admin_service.py`
    - 实现 `AdminAIService` 类，注入 `AIDispatcher` 和 `BudgetTracker`
    - 实现 Dashboard 聚合方法：`get_dashboard()`、`_get_today_stats()`、`_get_7d_trend()`、`_get_app_distribution()`、`_get_app_health()`
    - 实现调度任务方法：`list_trigger_jobs()`、`get_trigger_job()`、`retry_trigger_job()`（创建 is_forced=true 的新 trigger_job）
    - 实现调用记录方法：`list_run_logs()`、`get_run_log()`（含完整 prompt/response，不脱敏）
    - 实现缓存失效方法：`invalidate_cache()`（将匹配记录 status 设为 invalidated）
    - 实现 Token 预算方法：`get_budget()`（复用 BudgetTracker）
    - 实现批量执行方法：`estimate_batch()`（生成 batch_id 存入内存 dict，TTL 10min）、`confirm_batch()`（取出参数异步执行）、`_cleanup_expired_batches()`
    - 实现告警管理方法：`list_alerts()`、`ack_alert()`、`ignore_alert()`
    - _需求: A1.1, A1.2, A1.3, A1.4, A1.5, A1.6, A1.7, A2.1, A2.2, A2.3, A2.4, A3.1, A3.2, A3.3, A4.1, A4.2, A4.3, A4.4, A5.1, A5.2, A5.3, A6.1, A6.2, A7.1, A7.2, A7.3, A7.4, A8.1, A8.2, A8.3, A8.4_

- [x] 5. 后端路由 — Admin AI API
  - [x] 5.1 创建 `apps/backend/app/routers/admin_ai.py`
    - 实现 13 个端点，全部使用 `Depends(require_admin)` JWT 认证：
    - `GET /api/admin/ai/dashboard` — 总览统计（支持 site_id 筛选）
    - `GET /api/admin/ai/trigger-jobs` — 调度任务分页列表（筛选：event_type, status, site_id, date_from, date_to）
    - `GET /api/admin/ai/trigger-jobs/{job_id}` — 调度任务详情
    - `POST /api/admin/ai/trigger-jobs/{job_id}/retry` — 手动重跑
    - `GET /api/admin/ai/run-logs` — 调用记录分页列表（筛选：app_type, status, trigger_type, site_id, date_from, date_to）
    - `GET /api/admin/ai/run-logs/{log_id}` — 调用记录详情
    - `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/{log_id}/ack` — 确认告警
    - `POST /api/admin/ai/alerts/{log_id}/ignore` — 忽略告警
    - _需求: A1.1, A2.1, A2.4, A3.1, A4.1, A4.3, A5.1, A6.1, A7.1, A7.3, A8.1, A8.2, A8.3, A9.1, A9.2, A9.3_

  - [x] 5.2 在 `apps/backend/app/main.py` 中注册 admin_ai router
    - _需求: A9.1_

  - [x] 5.3 创建 `apps/backend/tests/unit/test_admin_ai_router.py`
    - 测试 13 个端点的请求/响应格式
    - 测试 JWT 认证拒绝（无 Token → 401）
    - 测试参数校验（无效日期格式 → 422、缺少 site_id → 422）
    - _需求: A9.1, A9.2_

  - [x] 5.4 创建 `apps/backend/tests/unit/test_admin_ai_service.py`
    - 测试 AdminAIService 各方法的边界条件（空数据集、单条记录）
    - 测试 batch_id 过期清理逻辑
    - 测试告警状态转换（pending → acknowledged / ignored）
    - _需求: A7.4, A8.4_

- [x] 6. 检查点 — 模块 A 后端 API 验证
  - 确保 Admin AI API 所有端点正常工作，单元测试通过，ask the user if questions arise.

### 阶段四：数据保留与清理（模块 E）

- [x] 7. 数据清理服务
  - [x] 7.1 创建 `apps/backend/app/services/ai/cleanup_service.py`
    - 实现 `AICleanupService` 类
    - `_cleanup_run_logs()` — DELETE FROM ai_run_logs WHERE created_at < now() - 90 days
    - `_cleanup_trigger_jobs()` — DELETE FROM ai_trigger_jobs WHERE created_at < now() - 90 days
    - `_cleanup_cache()` — 遍历 App2~App8（7 个类型），每个保留最新 20,000 条，删除超出部分
    - `run_cleanup()` — 依次执行三步清理，返回各步骤删除记录数
    - 永久保留 App1 对话记录（ai_conversations + ai_messages），不清理
    - _需求: E1.1, E1.2, E1.3, E1.4_

  - [x] 7.2 注册清理定时任务
    - 在 `scheduled_tasks` 表中注册清理任务，每日凌晨 03:00 执行
    - 清理任务调用 `AICleanupService.run_cleanup()`
    - 记录每次清理结果到日志
    - _需求: E2.1, E2.2, E2.3_

  - [x] 7.3 创建 `apps/backend/tests/unit/test_ai_cleanup_service.py`
    - 测试清理服务边界条件（无数据、刚好 20000 条、跨日边界）
    - _需求: E1.2, E1.3_

### 阶段五：回填脚本（模块 D）

- [x] 8. 历史数据回填脚本
  - [x] 8.1 创建 `scripts/ops/ai_backfill.py`
    - 实现 `AIBackfillRunner` 类
    - `query_active_members()` — 查询半年内（2025-09-21 ~ 2026-03-21）活跃会员（消费 ∪ 备注 ∪ 任务变更三者并集）
    - `load_checkpoint()` / `save_checkpoint()` — 断点续跑（checkpoint 文件：`scripts/ops/_ai_backfill_checkpoint.json`）
    - `run_member()` — 对单个会员执行调用链：App3→App8→App7；如有助教关联→App4→App5；如有备注→App6→App8
    - `run()` — 主入口：加载断点 → 查询会员 → 分批执行（每批 10 人，间隔 5 秒）→ 保存断点
    - `print_estimate()` — 执行前输出预估信息（会员数、预估调用次数、预估 Token 消耗）
    - App8 写入 `member_retention_clue` 使用 DELETE + INSERT 事务包裹
    - 验证 App5 输出包含分类字段（覆盖 P2-1 修复）
    - 支持 `--dry-run`、`--batch-size`、`--interval` CLI 参数
    - 写死 `site_id = 2790685415443269`
    - _需求: D1.1, D1.2, D1.3, D1.4, D1.5, D1.6, D1.7, D2.1, D2.3_

  - [x] 8.2 创建 `apps/backend/tests/unit/test_backfill_script.py`
    - 测试 checkpoint 文件读写（空文件、已有数据）
    - 测试空会员列表处理
    - 测试批次划分逻辑
    - _需求: D1.4_

- [x] 9. 检查点 — 模块 A + E + D 后端验证
  - 确保后端 API、清理服务、回填脚本所有测试通过，ask the user if questions arise.

### 阶段六：测试重建（模块 C2 + C3）

- [x] 10. 属性测试更新 — PRD §3.3 指定的 7 个属性（Property 1-7）
  - [x] 10.1 更新 `tests/test_circuit_breaker_props.py`
    - **Property 1: 熔断器状态机 — 状态转换合法性**（P14 Property 6 更新）
    - 增强测试覆盖，确保 P14 迁移后熔断器在新 DashScope 调用链中行为一致
    - **验证: 需求 C3.1**

  - [x] 10.2 更新 `tests/test_budget_tracker_props.py`
    - **Property 2: Token 预算计算 — 日/月聚合不变量**（P14 Property 8 更新）
    - 验证 AdminAIService 的聚合查询与 BudgetTracker 的聚合结果一致
    - **验证: 需求 C3.2, A6.2**

  - [x] 10.3 更新 `tests/test_dispatcher_props.py`
    - **Property 3: 去重逻辑 — 唯一性不变量**（P14 Property 12 更新）
    - 验证 admin-web 手动重跑（is_forced=true）正确绕过去重检查
    - **验证: 需求 C3.3, A3.2**

  - [x] 10.4 新增/更新 `tests/test_cache_service_props.py`
    - **Property 4: 缓存过期 — expires_at 过期状态一致性**（P15 新增）
    - 验证 expires_at < now() 时 status 为 expired，过期记录不返回为有效缓存
    - **验证: 需求 C3.4**

  - [x] 10.5 更新 `tests/test_app8_idempotent.py`
    - **Property 5: App8 幂等 — member_retention_clue 唯一性**（P14 Property 13 更新）
    - 验证回填脚本中的 App8 写入同样满足幂等性
    - **验证: 需求 C3.5**

  - [x] 10.6 新增/更新 `tests/test_session_props.py`
    - **Property 6: session_id 重建 — round-trip 属性**（P15 新增）
    - 验证重建后的 messages 数组与本地 ai_messages 一致（内容和顺序均匹配）
    - **验证: 需求 C3.6**

  - [x] 10.7 更新 `tests/test_rate_limiter_props.py`
    - **Property 7: 限流计数 — 窗口内请求数上限**（P14 Property 7 更新）
    - 确保限流器在高并发回填场景下行为正确
    - **验证: 需求 C3.7**

- [x] 11. 属性测试新增 — P15 监控后台属性（Property 8-14）
  - [x] 11.1 创建 `tests/test_admin_ai_dashboard_props.py`
    - **Property 8: Dashboard 聚合正确性**
    - 验证 today_calls、today_success_rate、today_tokens、today_avg_latency_ms、trend_7d、app_distribution 与直接 SQL 聚合结果一致
    - **验证: 需求 A1.1, A1.2, A1.3, A1.6**

  - [x] 11.2 创建 `tests/test_admin_ai_alert_props.py`
    - **Property 9: 告警筛选与状态转换**
    - 验证告警列表仅包含 status IN ('failed','timeout','circuit_open') 的记录
    - 验证 ack → acknowledged、ignore → ignored 状态转换
    - **验证: 需求 A8.1, A8.2, A8.3, A8.4**

  - [x] 11.3 创建 `tests/test_admin_ai_cache_props.py`
    - **Property 10: 缓存失效精确性**
    - 验证匹配条件的记录 status 变为 invalidated，不匹配的保持不变
    - **验证: 需求 A5.1, A5.2, A5.3**

  - [x] 11.4 创建 `tests/test_admin_ai_batch_props.py`
    - **Property 11: 批量执行预估公式**
    - 验证 estimated_calls = len(app_types) × len(member_ids)，estimated_tokens = estimated_calls × avg_tokens_per_call
    - **验证: 需求 A7.1, A7.2**

  - [x] 11.5 创建 `tests/test_admin_ai_pagination_props.py`
    - **Property 12: 分页与筛选正确性**
    - 验证 items 均匹配筛选条件、len(items) <= page_size、total 正确
    - **验证: 需求 A2.1, A2.2, A4.1, A4.2, A1.6**

  - [x] 11.6 创建 `tests/test_backfill_props.py`
    - **Property 13: 回填断点续跑 round-trip**
    - 验证 checkpoint 文件序列化/反序列化 round-trip 一致性
    - **验证: 需求 D1.4**

  - [x] 11.7 创建 `tests/test_ai_cleanup_props.py`
    - **Property 14: 数据保留不变量**
    - 验证清理后 ai_run_logs/ai_trigger_jobs 无 90 天前记录、每个 App 的 ai_cache ≤ 20000、ai_conversations/ai_messages 不减少
    - **验证: 需求 E1.1, E1.2, E1.3, E1.4**

- [x] 12. 全链路测试 — 15 个场景（模块 C2）
  - [x] 12.1 创建 `apps/backend/tests/integration/test_ai_full_chain.py`
    - 实现 Mock/Real 双轨模式（`AI_TEST_MODE` 环境变量控制）
    - Mock 模式：mock `Application.call()` 返回预设 JSON
    - Real 模式：调用真实百炼 API（需要 `DASHSCOPE_API_KEY`）
    - 实现 15 个测试场景：
      1. `test_app1_10_entries` — App1 对话 10 种入口（SSE 流式 + session_id 双轨持久化）
      2. `test_app2_pregenerate` — App2 定时预生成 8 维度
      3. `test_consumption_chain` — 消费事件链 App3→App8→App7
      4. `test_coach_consumption_chain` — 助教消费链 App3→App8→App7→App4→App5
      5. `test_note_chain` — 备注事件链 App6→App8
      6. `test_task_chain` — 任务分配链 App4→App5
      7. `test_cache_hit` — 缓存命中（有效期内直接返回）
      8. `test_cache_invalidation` — 缓存失效后重新生成
      9. `test_circuit_breaker_flow` — 熔断触发→半开→恢复
      10. `test_budget_exceeded` — Token 预算超限降级
      11. `test_failure_logging` — 失败记录完整性
      12. `test_admin_visibility` — admin-web API 可见性
      13. `test_json_fallback` — JSON 兜底重试 3 次
      14. `test_app8_idempotent` — App8 幂等验证
      15. `test_content_quality` — 内容质量（必要字段+格式）
    - _需求: C2.1, C2.2, C2.3, C2.4, C2.5, C2.6, C2.7, C2.8, C2.9, C2.10, C2.11, C2.12, C2.13, C2.14, C2.15, C2.16_

- [x] 13. 检查点 — 测试体系验证
  - 运行 Monorepo 属性测试：`cd C:\Project\NeoZQYY && pytest tests/ -v`
  - 运行全链路测试（Mock 模式）：`cd apps/backend && pytest tests/integration/test_ai_full_chain.py -v`
  - 确保 Property 1-14 全部通过，15 个全链路场景 Mock 模式通过，ask the user if questions arise.

### 阶段七：前端页面（模块 B）

- [x] 14. 前端 API 层
  - [x] 14.1 创建 `apps/admin-web/src/api/adminAI.ts`
    - 封装 Dashboard API：`getDashboard(siteId?)`
    - 封装调度任务 API：`getTriggerJobs(params)`、`getTriggerJobDetail(id)`、`retryTriggerJob(id)`
    - 封装调用记录 API：`getRunLogs(params)`、`getRunLogDetail(id)`
    - 封装缓存管理 API：`invalidateCache(data)`
    - 封装 Token 预算 API：`getBudget()`
    - 封装批量执行 API：`createBatchRun(data)`、`confirmBatchRun(batchId)`
    - 封装告警 API：`getAlerts(params)`、`ackAlert(id)`、`ignoreAlert(id)`
    - _需求: B1.1, B2.1, B3.1, B4.1_

- [x] 15. 前端页面 — AI Dashboard
  - [x] 15.1 创建 `apps/admin-web/src/pages/AIDashboard.tsx`
    - 顶部：门店筛选 Select + 刷新按钮
    - 第一行：4 个 Statistic 卡片（今日调用次数、成功率、Token 消耗、平均延迟）
    - 第二行左：7 天趋势折线图（调用量 + 成功率）
    - 第二行右：App 调用占比饼图
    - 第三行左：Token 预算进度条（日/月各一个 Progress）
    - 第三行右：App 健康状态列表（Badge 标记状态）
    - 第四行：告警列表 Table + App 配置信息（只读 Descriptions）
    - _需求: B1.1, B1.2, B1.3, B1.4_

- [x] 16. 前端页面 — 调度状态
  - [x] 16.1 创建 `apps/admin-web/src/pages/AITriggerJobs.tsx`
    - 顶部筛选器行：event_type Select + status Select + site_id Select + DatePicker.RangePicker
    - 统计行：今日去重跳过数 Statistic
    - 主体：Table 分页表格（事件类型、会员、状态 Tag、执行链、耗时、操作列）
    - 操作列：查看详情 → Modal、手动重跑 → Popconfirm
    - _需求: B2.1, B2.2, B2.3, B2.4, B2.5_

- [x] 17. 前端页面 — 调用明细
  - [x] 17.1 创建 `apps/admin-web/src/pages/AIRunLogs.tsx`
    - 顶部筛选器行：app_type + status + trigger_type + site_id + DatePicker.RangePicker
    - 主体：Table 分页表格（app_type、trigger_type、member_id、tokens、延迟、状态 Tag）
    - 点击行：Drawer 抽屉展示完整 prompt（代码块）、完整 response（代码块）、error_message
    - _需求: B3.1, B3.2, B3.3_

- [x] 18. 前端页面 — 手动操作
  - [x] 18.1 创建 `apps/admin-web/src/pages/AIOperations.tsx`
    - Card 1 — 手动重跑：App Select + member_id Input + site_id Select + 执行 Button
    - Card 2 — 缓存失效：app_type Select + member_id Input + site_id Select + 失效 Button + 结果 message
    - Card 3 — 批量执行：app_types Checkbox.Group + member_ids TextArea + site_id Select → 预估 → 确认弹窗 Modal（成本二次确认流程）
    - Card 4 — 告警管理：Table（告警列表）+ 每行"确认"/"忽略" Button
    - _需求: B4.1, B4.2, B4.3, B4.4, B4.5_

- [x] 19. 前端路由与侧边栏
  - [x] 19.1 修改 `apps/admin-web/src/App.tsx`
    - 新增 4 个路由：`/ai/dashboard`、`/ai/trigger-jobs`、`/ai/run-logs`、`/ai/operations`
    - 侧边栏新增"AI 监控"菜单组，包含 4 个子菜单项
    - _需求: B1.1, B2.1, B3.1, B4.1_

- [x] 20. 检查点 — 前端页面验证
  - 确保所有前端组件渲染正常，API 调用层工作正确，ask the user if questions arise.

### 阶段八：收尾

- [x] 21. 前后端联调与集成验证
  - [x] 21.1 启动后端服务，使用测试库验证各端点完整请求-响应链路
    - 验证 13 个 admin AI API 端点 JSON 响应结构与 Schema 定义一致
    - 验证 JWT 认证在真实请求中生效（无 Token → 401）
    - 验证 site_id 筛选在 Dashboard 和列表端点中正常工作
  - [x] 21.2 前端联调验证
    - 确认 Dashboard 页面能正确调用 API 并渲染统计卡片、趋势图、饼图、预算进度
    - 确认调度状态页面筛选、分页、手动重跑正常工作
    - 确认调用明细页面 Drawer 展示完整 prompt/response
    - 确认手动操作页面批量执行二次确认流程、告警确认/忽略正常
    - 验证空数据/降级场景下前端不崩溃

- [x] 22. 数据库变更审计与 DDL 合并
  - [x] 22.1 审计本次实现中对数据库的所有改动
    - 检查新增字段（ai_run_logs.alert_status）、新增索引（部分索引 + BRIN 索引）、新增定时任务记录
  - [x] 22.2 执行迁移脚本到测试库（`test_zqyy_app`）
    - 验证 alert_status 字段和索引已正确创建（使用 BD 手册中的验证 SQL）
  - [x] 22.3 合并到主 DDL 基线文件
    - ai_run_logs 变更 → `docs/database/ddl/zqyy_app__biz.sql`
  - [x] 22.4 验证回滚脚本可执行（任务 1 中已编写）

- [x] 23. BD 手册更新
  - [x] 23.1 更新 `docs/database/BD_Manual_ai_tables.md`
    - 补充 `alert_status` 字段说明（值域、默认值、CHECK 约束）
    - 补充部分索引 `idx_ai_run_logs_alert` 说明
    - 补充 BRIN 索引 `idx_ai_run_logs_created_brin` 说明
    - 补充 admin API 相关的查询模式说明（Dashboard 聚合、告警筛选、分页查询）
    - 包含：字段明细、约束与索引、验证 SQL（≥3 条）、回滚策略
    - _规范: db-docs.md_
    - _需求: F2.4_

- [x] 24. 文档同步更新
  - [x] 24.1 更新后端文档
    - 在 `apps/backend/docs/API-REFERENCE.md` 新增 admin_ai 路由模块文档（13 个端点）
    - 更新 `apps/backend/README.md` 新增 admin AI API 说明
    - _需求: F2.2_
  - [x] 24.2 更新 admin-web 文档
    - 在 `apps/admin-web/README.md` 新增 AI 监控页面说明（4 个页面）
    - _需求: F2.1_
  - [x] 24.3 更新文档地图
    - 在 `docs/DOCUMENTATION-MAP.md` 新增 P15 相关条目（BD 手册、Spec、API 文档）
    - _规范: doc-map.md_
    - _需求: F2.3_
  - [x] 24.4 确认 `docs/prd/ai-app-prompts.md` 环境变量已全部更新（P14 遗留确认）
    - _需求: F2.5_

- [x] 25. 最终检查点 — 全量验证
  - 运行 Monorepo 属性测试：`cd C:\Project\NeoZQYY && pytest tests/ -v`
  - 运行后端单元测试：`cd apps/backend && pytest tests/ -v`
  - 运行全链路测试（Mock 模式）：`cd apps/backend && pytest tests/integration/test_ai_full_chain.py -v`
  - 确保所有属性测试（Property 1-14）和单元测试全部通过
  - 确保 DDL 迁移已合并到主基线
  - 确保 BD 手册已同步更新
  - 确保 API 文档、后端 README、admin-web README、文档地图均已更新
  - 确保前端页面连接真实后端运行正常（Dashboard + 调度状态 + 调用明细 + 手动操作）
  - 确保旧测试脚本已归档到 `_archived/` 目录
  - ask the user if questions arise.

- [x] 26. 服务清理
  - [x] 26.1 关闭浏览器、停止后端和前端服务、清理资源
    - 停止 uvicorn 后端进程（controlPwshProcess stop）
    - 停止前端开发服务器（controlPwshProcess stop）

## 备注

- 标记 `*` 的子任务为可选（属性测试 / 单元测试），可跳过以加速 MVP
- 每个任务引用了具体的需求编号以确保可追溯性（A1-A9 后端 API、B1-B4 前端页面、C1-C3 测试重建、D1-D2 回填、E1-E2 数据保留、F1-F2 查询优化与文档）
- 属性测试验证 14 个正确性属性（Property 1-14）：Property 1-7 为 PRD §3.3 指定（5 个 P14 更新 + 2 个 P15 新增），Property 8-14 为 P15 监控后台新增
- 单元测试验证具体边界条件和错误处理
- 全链路测试覆盖 15 个场景，支持 Mock/Real 双轨模式
- 检查点任务确保增量验证，避免问题累积（任务 6、9、13、20、25）
- 后端使用 Python（FastAPI + Pydantic + Hypothesis），前端使用 TypeScript（React + Vite + Ant Design）
- 收尾阶段遵循 `spec-closing-checklist.md`（全栈类 Spec，步骤 1-6 全覆盖）：
  - 步骤 1（最终测试）→ 任务 25
  - 步骤 2（前后端联调）→ 任务 21
  - 步骤 3（DDL 合并）→ 任务 22
  - 步骤 4（BD 手册）→ 任务 23
  - 步骤 5（文档同步）→ 任务 24
  - 步骤 6（服务清理）→ 任务 26
