Files
Neo-ZQYY/docs/ai/ai_apps_feature_acceptance_spec.md
Neo caf179a5da feat: 2026-04-15~05-02 累积变更基线 — AI 重构 + Runtime Context + DWS 修复
涵盖(每条对应已存的审计记录):
- AI 模块拆分:apps/backend/app/ai/apps -> prompts/(8 个 APP + app2a 派生)
  audit: 2026-04-20__ai-module-complete.md
- admin-web AI 管理套件:AIDashboard / AIOperations / AIRunLogs / AITriggers / TriggerManager
  audit: 2026-04-21__admin-web-ai-management-suite.md
- App2 财务洞察 prompt v3 -> v5.1 + 小程序 AI 接入(chat / board-finance)
  audit: 2026-04-22__app2_prompt_v5_1_and_miniprogram_ai_insight.md
- App2 prewarm 全过滤器 + AI 触发器 cron reschedule
  audit: 2026-04-21__app2-finance-prewarm-all-filters.md
  migration: 20260420_ai_trigger_jobs_and_app2_prewarm.sql / 20260421_app2_prewarm_cron_reschedule.sql
- AppType 联合类型对齐 + adminAiAppTypes.test.ts
  audit: 2026-04-30__admin_web_ai_app_type_alignment.md
- DashScope tokens_used 提取修复
  audit: 2026-04-30__backend_dashscope_tokens_used_extraction.md
- App3 线索完整详情 prompt
  audit: 2026-05-01__backend_app3_full_detail_prompt.md
- Runtime Context 沙箱(5-1~5-2 主线):
  - 后端 schema/service + admin_runtime_context / xcx_runtime_clock 两个 router
  - admin-web RuntimeContext.tsx + miniprogram runtime-clock.ts
  - migration: 20260501__runtime_context_sandbox.sql
  - tools/db/verify_admin_web_sandbox.py + verify_sandbox_end_to_end.py
  - database/changes: 7 份 sandbox_* 验证报告
- 飞球 DWS 修复:finance_area_daily 区域汇总 + task_engine 调整
  + RLS 视图业务日上界(migration 20260502 + scripts/ops/gen_rls_business_date_migration.py)

合规:
- .gitignore 启用 tmp/ 排除
- 不入仓:apps/etl/connectors/feiqiu/.env(API_TOKEN secret,本地修改保留)

待验证清单:
- docs/audit/changes/2026-05-04__cumulative_baseline_pending_verification.md
  每个主题的功能完整性 / 上线验证几乎都未收口,按优先级 P0~P3 逐一处理
2026-05-04 02:30:19 +08:00

1040 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.
# AI 应用 APP1-APP8 功能需求 - 目的检查文档
> 版本2026-04-22 (v4全景资料集成8 维度并行调研 — 生产端 / DB schema / 合规 / MCP / admin-web / 后端路由服务 / 历史审计 / P2/P3 外围)
> 适用范围:`apps/backend/app/ai/` + `apps/admin-web/src/pages/AI*` + `apps/miniprogram/miniprogram/` + `apps/demo-miniprogram/miniprogram/`UI 标杆)
> PRD 来源:
>
> - [docs/prd/AI需求2.md](../prd/AI需求2.md) — 8 个 AI 应用功能/参数/返回格式主表
> - [docs/prd/ai-app-prompts.md](../prd/ai-app-prompts.md) — System Prompt 详细定义
> - [docs/prd/Neo_Specs/NS2-ai-prompt-refinement.md](../prd/Neo_Specs/NS2-ai-prompt-refinement.md) — 数据层/拼接/page_context 文本化
> - [docs/prd/specs/P5-miniapp-ai-integration.md](../prd/specs/P5-miniapp-ai-integration.md) — 小程序 AI 集成验收
> - [docs/prd/specs/P14-ai-dashscope-migration.md](../prd/specs/P14-ai-dashscope-migration.md) — 熔断限流预算硬约束
> - [docs/prd/specs/P15-ai-monitoring-testing.md](../prd/specs/P15-ai-monitoring-testing.md) — 监控后台与测试体系
---
## 0. 总体原则
- demo-miniprogram 是**样式标杆**(假数据驱动),生产 miniprogram 所有 AI 字段的排版/配色必须与 demo 一致。
- 前端**不直接调用 DashScope**,所有 AI 产物统一通过 `ai_cache` 或 SSE 流式接口消费。
- 后端统一响应包装 `{code:0, data:...}`snake_case 自动转驼峰。
- cache miss 时前端必须**清空陈旧数据**。
- 告警统一走 WebSocket `/ws/ai-alerts/{site_id}`
## 0.1 PRD 硬性数字约束(权威表)
> 所有数字来自 `docs/prd/` 精读,代码实现与此不符时以 PRD 为准并回退排查。
| 维度 | 约束值 | PRD 来源 |
| ------ | -------- | --------- |
| 应用总数 | 8APP1-APP8 | AI 需求 2 |
| 财务洞察组合 | 8 时间 × 9 区域 = 72 | AI 需求 2 |
| 线索分类APP3 | 3 个枚举 | P5 / ai-app-prompts |
| 线索分类APP6/8 | 6 个枚举 | P5 / ai-app-prompts |
| 单条线索 `summary` | ≤ 20 字(简短可作标题) | AI 需求 2 |
| 单条线索 `detail` | ≤ 120 字 | AI 需求 2 |
| APP7 `summary` 长度 | 200-400 字,开头定性短句 ≤ 25 字 | ai-app-prompts |
| APP8 `clues` 数组 | ≤ 5 条 | ai-app-prompts |
| APP5 `tactics` 数组 | 2-4 条,每条 `script` ≤ 150 字 | AI 需求 2 |
| APP6 `clues` 数组 | 0-10 条(按价值排序) | AI 需求 2 |
| APP3 `clues` 数组 | 1-5 条(按价值排序) | AI 需求 2 |
| System prompt 上限APP2-8 | ≤ 8000 字 | NS2 |
| System prompt 上限APP1 | ≤ 4000 字 | NS2 |
| page_context 上限 | ≤ 2000 字 | NS2 / page_context.py:21 |
| Token 日预算 | 100,000 | P14 |
| Token 月预算 | 2,000,000 | P14 |
| APP1 限流 | 每用户每分钟 10 次 | P14 |
| APP2-8 限流 | 每门店每小时 100 次 | P14 |
| 熔断阈值 | 连续 5 次失败 OPEN | P14 |
| 熔断恢复窗口 | 60 秒 | P14 |
| 关系流失预警 | 停止消费 > 14 天 | ai-app-prompts |
| 高粘性阈值 | 月内消费 ≥ 3 次 | ai-app-prompts |
| 学习型阈值 | 助教费占比 > 40% | ai-app-prompts |
| 当期 < 7 天 | 必须降权表述("初步观察"/"不足为据" | ai-app-prompts |
| 财务洞察加载 | < 2 秒(秒级缓存读取) | P5 |
| 对话复用窗口customer/coach/finance | 3 天 | P5 |
| 对话复用窗口task | 无时限 | P5 |
| 对话复用general | 始终新建 | P5 |
## 0.2 跨 APP 编排链路PRD 定义)
### 消费事件链(严格串行)
```
新结算单 → APP3 [await]
→ APP8 [await]
→ APP7 [await](读 APP8 最新缓存)
如含助教:
→ APP4 [await](读 APP8 最新缓存)
→ APP5
```
**约束**APP7 与 APP4 均依赖 APP8 完成,确保上游线索就绪。
### 备注事件链
```
备注提交 → APP6 [await] → APP8 [await]
```
### 任务分配链
```
任务分配priority_recall / high_priority_recall
→ APP4 [await](读 APP8 已有缓存miss 时 prompt 标注"暂无历史线索"
→ APP5
```
**约束**:任务链不等 APP8 执行,直接读缓存;消费链则必须等 APP8 完成才能执行 APP4/APP7。
---
## 1. APP1 聊天(流式对话 + 屏幕上下文)
### 功能目的PRD
店员/助教在小程序里与 AI 自由对话("这个客户最近消费变多了为什么"),支持 10 种页面入口带上下文,后端将上下文结构化为 ≤2000 字中文文本注入 prompt。**PRD 参考**AI 需求 2 表首行 / P5 AC1 / NS2 page_context 设计。
### 后端请求 schema[schemas.py:16-23](../../apps/backend/app/ai/schemas.py#L16)
```
ChatStreamRequest {
message: str # 用户本轮输入
source_page: str | None # 来源页面标识10 种枚举)
page_context: dict | None # 看板筛选参数timeDimension/areaFilter/dimension/typeFilter/projectFilter
screen_content: str | None # 「屏幕可见内容」快照(前端采集,后端原样拼入 prompt← 当前 gap
}
```
### 10 种 sourcePage[page_context.py:24-36](../../apps/backend/app/ai/data_fetchers/page_context.py#L24)
`task-detail / customer-detail / coach-detail / board-finance / board-customer / board-coach / performance / my-profile / task-list / customer-service-records`
### 前端消费链路
| 阶段 | 文件与行号 | 处理 |
|------|-----------|------|
| 浮按钮属性 | [ai-float-button.ts:29-40](../../apps/miniprogram/miniprogram/components/ai-float-button/ai-float-button.ts#L29) | 接收 `sourcePage` + `pageFilters`JSON 对象) |
| 浮按钮跳转 | [ai-float-button.ts:44-72](../../apps/miniprogram/miniprogram/components/ai-float-button/ai-float-button.ts#L44) | URL 编码 JSON 到 querystring |
| Chat 解析 | [chat.ts:234-259](../../apps/miniprogram/miniprogram/pages/chat/chat.ts#L234) | `JSON.parse(decodeURIComponent(options.pageFilters))` ; 回退支持旧单键 `timeDimension/areaFilter/dimension/typeFilter/projectFilter` |
| SSE 发送 | [chat.ts:487-490](../../apps/miniprogram/miniprogram/pages/chat/chat.ts#L487) | body: `{chatId, content, sourcePage, pageContext}` |
| 后端文本化 | [page_context.py:39-96](../../apps/backend/app/ai/data_fetchers/page_context.py#L39) | `build_page_text(source_page, context_id, site_id, filters)` → 结构化中文 ≤2000 字 |
| 后端注入 | xcx_chat.py | 将文本拼入 DashScope `biz_params` 或作为前置 system message |
### screen_content「屏幕可见内容」特性**待实现 / 文档化 gap**
- schema 已预留字段([schemas.py:22](../../apps/backend/app/ai/schemas.py#L22)
- **预期采集方式**(需前端实现):浮按钮 onTap 时通过 `triggerEvent` 让父页面回传 `this.data` 中可见区块的快照(如 board-finance 当前展示的 `overview/recharge/revenue/...` 卡片数据),序列化为短 JSON 字符串
- **后端消费**:原样拼入 prompt 系统消息(与 `page_context` 互补page_context 是后端查 DB 的结果screen_content 是前端用户真实看到的数字)
- **验证**:两者都存在时,提示词顺序应为 `screen_content用户视角 → page_context权威数据`,避免矛盾时以 page_context 为准
### 各 sourcePage 携带字段示例
| sourcePage | pageFilters 字段 | 后端 page_context 输出要点 |
|-----------|-----------------|--------------------------|
| board-finance | `timeDimension` + `areaFilter` | 汇总笔数 / 总营收 / 笔均 / 优惠率 / 储值活跃度 |
| board-customer | `dimension` + `typeFilter` | Top 10 客户 + 排序维度 + 客群分类 |
| board-coach | `dimension` + `projectFilter` + `timeDimension` | Top 10 助教 + 服务统计 + 技能筛选 |
| performance / task-list / my-profile | `timeDimension` | 个人绩效 / 任务列表 / 本人统计 |
| task-detail / customer-detail / coach-detail | 无 filters`contextId` | 单实体详情 |
| customer-service-records | 无 | 服务记录流水 |
### Demo 参考UI 形态)
- [chat.ts](../../apps/demo-miniprogram/miniprogram/pages/chat/chat.ts):消息气泡、引用卡片 `referenceCard`、逐字追加动效
- 进入路径:浮按钮仅在 board-*(看板类)与详情页挂载,距底部 200rpxTabBar 页)/ 120rpx非 TabBar
### 验证点(细化)
- [ ] 财务看板任意 time/area 切换 → 点浮按钮 → 后端日志可见 `build_page_text("board-finance", ...)` 返回文本含当前切换维度的数字
- [ ] 小程序 SSE body 中 `pageContext` 为 JSON 对象而非字符串(后端解析不失败)
- [ ] `pageFilters` 为空对象时不追加 URL 参数,走 general 对话
- [ ] SSE ArrayBuffer 跨 chunk 的 UTF-8 中文字符无乱码(构造「当前营业」被 2 chunk 切分的测试)
- [ ] `done` 事件 `conversation_id` 返回后,下一轮发送透传该 id 保持多轮
- [ ] `ai_run_logs.tokens_used``usage.models` 嵌套结构累加(非顶层 tokens
- [ ] 网络中断重试上限 2 次2/4/8s 指数退避)
- [ ] **screen_content gap 待确认**:若本次上线计划启用,需在浮按钮 onTap 内实现页面快照 triggerEvent否则保留 schema 占位并在文档注明"暂不采集"
---
## 2. APP2 财务洞察72 组合预热)
### 功能目的
日结完成后预生成 **8 时间维度 × 9 区域 = 72 组** 财务洞察,命中看板时直接从 `ai_cache` 秒级读取。
### 返回 schema[schemas.py:93-104](../../apps/backend/app/ai/schemas.py#L93)
```
App2Result { insights: [ { seq: int, title: str, body: str } ] }
```
### System Prompt 核心规则([ai_system_prompt_by_app.md:41-93](ai_system_prompt_by_app.md#L41)
- 收入构成三口径不可互换:发生额 / 成交收入 / 现金流入
- 5 类优惠拆解必须落到细分(团购/会员折扣/手动/赠送卡/分摊)
- 警戒线:优惠率 >30% / 助教薪酬占比 >40% / 充值占现金流入 25-40% 为健康
- **seq=11/12 固定为板块 F「综合健康度」置顶作为"本期总结"**
- 当期 <7 天需降权表述
- 现金流出为 0 视为录入异常并高亮
### target_id 拼装([dispatcher.py _app2_target_id](../../apps/backend/app/ai/dispatcher.py)
`${TIME_MAP[selectedTime]}__${selectedArea}`,例:`this_month__hallA` / `last_quarter__all`
**TIME_MAP**`month→this_month``lastMonth→last_month``week→this_week``quarter→this_quarter``half6→last_6_months`
**9 区域**`all / hall / hallA / hallB / hallC / vip / snooker / mahjong / ktv`
### cache_type 切分2026-04-23
- `area='all'` 的 8 组 → `app2_finance`
- `area!='all'` 的 64 组 → `app2a_finance_area`
### Demo 展示形态(**关键 UI 规范**
参考 [board-finance.wxml:208-221](../../apps/demo-miniprogram/miniprogram/pages/board-finance/board-finance.wxml#L208)
```
┌─ AI 智能洞察(机器人 SVG 图标 + 标题)
│ 优惠率Top团购(5.2%) / 大客户(3.1%) / 赠送卡(2.5%)
│ 差异最大:酒水(+18%) / 台桌(-5%) / 包厢(+12%)
│ 建议关注:充值高但消耗低,会员活跃度需提升
└─
```
**字段到 UI 映射**
| 字段 | Demo 类 | 渲染规范 |
|------|---------|---------|
| `title` | `.ai-insight-dim` | 灰色引导词(如"优惠率Top"/"差异最大:"),结尾带中文冒号 |
| `body` | `.ai-insight-line` 主体 | 白色正文,含数字与百分比 |
| 强调片段 | `.ai-insight-underline` | 关键词下划线(需 AI 用 `**...**` 标记,前端轻量 Markdown 渲染) |
| 顺序 | seq 升序 | `seq=11/12` 置顶为"本期总结" |
| 图标 | `/assets/icons/ai-robot.svg` | **必须 SVG禁止 emoji 替代**demo 已标注 CHANGE 2026-03-12 |
### 验证点(细化)
- [ ] 72 组 `target_id` 全覆盖SQL`SELECT DISTINCT target_id FROM biz.ai_cache WHERE cache_type IN ('app2_finance','app2a_finance_area') AND site_id=? AND created_at::date=CURRENT_DATE` 应 = 72
- [ ] `seq=11/12` 在前端必定置顶(不按数字大小升序,需特判)
- [ ] `title` 结尾是中文冒号``(便于 dim 样式识别)
- [ ] `body``**xxx**` 标记被轻量 Markdown 渲染为 underline/加粗
- [ ] area 切换后旧 `aiInsights/aiInsightSummary/aiInsightDetails` 立即清空
- [ ] cache miss 时显示占位文案("正在生成洞察,请稍后"),不复用上次数据
- [ ] 现金流出为 0 时 AI 输出能**明确指出录入异常**(单元测试固化该场景)
- [ ] 当期 <7 天的时间维度(本周 `this_week` 若当前周三)返回文本含降权用词("初步观察"/"不足为据"等)
- [ ] Prompt `KEY_TRANSLATIONS`[app2_finance_prompt.py:24-64](../../apps/backend/app/ai/prompts/app2_finance_prompt.py#L24))无缺字段
- [ ] 熔断/限流后剩余组跳过,`ai_cache.result_json` 不写 NULL
---
## 3. APP3 维客线索消费端3 分类)
### 功能目的
消费结算后从 DWS 消费记录提取 **3 类线索**(客户基础/消费习惯/玩法偏好),`providers` 固定「系统」。
### 返回 schema[schemas.py:71-78, 107-110](../../apps/backend/app/ai/schemas.py#L71)
```
App3Result { clues: [ ClueItem { category, summary, detail, emoji } ] }
```
### System Prompt 核心规则([ai_system_prompt_by_app.md:97-125](ai_system_prompt_by_app.md#L97)
- `category` 严格 3 选 1`App3CategoryEnum`
- 必须输出"下一步做什么"的行动导向,不得仅描述现象
- 消费结构解读:酒水 >40% = 休闲社交客;台费 >80% = 硬核客;助教费 >30% = 学习进阶客
### 前端消费(经 App8 合并后落 `member_retention_clue`
App3 结果**不直接渲染**,由 App8 读 ai_cache 整合 → 写库 → 前端查客户详情时显示。
### Demo 展示形态customer-detail
参考 [customer-detail.ts:99-145](../../apps/demo-miniprogram/miniprogram/pages/customer-detail/customer-detail.ts#L99) + [customer-detail.wxml:92-110](../../apps/demo-miniprogram/miniprogram/pages/customer-detail/customer-detail.wxml#L92)
```
维客线索 [AI 徽章]
┌─ [客户/基础] 🎂 生日 3月15日 · VIP会员 · 注册2年 By:系统
├─ [消费/习惯] 🌙 常来夜场 · 月均4-5次 By:系统
├─ [消费/习惯] 💰 高客单价 By:系统
│ 近60天场均消费 ¥420高于门店均值 ¥180...
└─ ...
```
**字段到 UI 映射**
| schema 字段 | Demo `clue-card` 属性 | UI 规范 |
|------------|---------------------|--------|
| `category` | `tag` | 两行显示(如"客户\n基础"),配色 `categoryColor` 按类映射primary/success/purple/warning/pink/error |
| `summary` | `title`(含 emoji 前缀) | 主文案,短句 30 字内 |
| `emoji` | 拼在 `title` 开头 | 1 个 emoji类别相关 |
| `detail` | `content`(展开区) | 50-100 字展开,可折叠 |
| `providers`App8 合并后补) | `source`"By:系统"/"By:小燕" | 区分系统推断 vs 备注来源 |
### 验证点(细化)
- [ ] `category ∈ {客户基础, 消费习惯, 玩法偏好}`Pydantic 校验 + 前端 `categoryColor` 映射无 undefined
- [ ] `emoji` 字段必填且为单个 emoji正则 `/^[\p{Emoji}]+$/u`
- [ ] `summary` 以行动导向动词结尾("可推..."/"建议..."/"需..."),非纯描述(单测)
- [ ] `detail` 长度 ≥ 50 字且 ≤ 150 字
- [ ] DWS 取数失败时降级到 `_default_member_data`,不导致 dispatcher 崩溃
- [x] Prompt 超 4000 字后仍保留完整消费明细2026-05-01 合成 100 条完整明细真实调用成功返回
- [ ] 团购客无储值卡与储值会员识别正确AI 不得混淆)
---
## 4. APP4 关系分析(任务链首)
### 功能目的
助教参与消费 / 任务分配时,分析助教-会员关系,产出任务描述、行动建议、一句话总结。
### 返回 schema[schemas.py:113-118](../../apps/backend/app/ai/schemas.py#L113)
```
App4Result {
task_description: str # 任务详述
action_suggestions: [str] # 行动建议数组
one_line_summary: str # 一句话总结
}
```
### System Prompt 核心规则([ai_system_prompt_by_app.md:129-156](ai_system_prompt_by_app.md#L129)
- 输出必含:**关系评级**(紧密/一般/疏远)+ 核心原因 + 风险/机会点
- 粘性阈值:月内 ≥3 次 = 高粘性;激励课占比 >40% = 学习型
- 停止消费 >14 天 = 流失预警
### Demo 展示形态task-detail 顶部 Banner
参考 [task-detail.ts:107-112](../../apps/demo-miniprogram/miniprogram/pages/task-detail/task-detail.ts#L107)
```
关系等级:很好 heartScore=8.5
banner 背景根据 taskType 动态切换 SVG
```
**字段到 UI 映射**
| schema 字段 | Demo 字段 | 位置 |
|------------|----------|------|
| `one_line_summary` | Banner 下方副标题 | 任务详情头部一行显示 |
| `task_description` | `detail.taskTypeLabel` + 主内容 | 任务卡片主体 |
| `action_suggestions[]` | 勾选式建议列表 | 可勾选动作项,完成后记录 |
### 验证点(细化)
- [ ] `action_suggestions` 为非空 list每条 ≤ 40 字
- [ ] `one_line_summary` 含关系评级词("紧密/一般/疏远"+ 数字
- [ ] `target_id = ${assistant_id}_${member_id}`,供 App5 复用
- [ ] 助教数据缺失时 `warnings` 数组被填充,前端显示降级提示
- [ ] Prompt 截断 `_MAX_PROMPT_LEN=8000` 保留关键关联字段
---
## 5. APP5 话术参考(任务链尾)
### 功能目的
基于 App4 的 `one_line_summary` 与关系评级,生成针对性**微信/当面沟通话术**。
### 返回 schema[schemas.py:121-131](../../apps/backend/app/ai/schemas.py#L121)
```
App5Result { tactics: [ { scenario: str, script: str } ] }
```
### System Prompt 核心规则([ai_system_prompt_by_app.md:160-192](ai_system_prompt_by_app.md#L160)
- 微信私信:口语、短、配 1 个 emoji不群发感
- 当面沟通:引导式提问 > 直接推销
- 会员分层:新客试听 / 成长储值 / 核心赛事邀请 / 流失限时券
- 助教不能:打任意折扣 / 承诺 KOL 免费陪打
- 输出需标注:适用场景 + 建议发送时段 + 预期反应
### Demo 展示形态task-detail 话术区)
参考 [task-detail.ts:79-86](../../apps/demo-miniprogram/miniprogram/pages/task-detail/task-detail.ts#L79)
```
话术参考
┌─ 王哥您好,好久不见!最近店里新到了几张国际标准斯诺克球桌... [复制]
├─ 王哥,最近忙吗?这周末我们有个老客户专属球友交流赛... [复制]
└─ ... 共 5 条
```
**字段到 UI 映射**
| schema 字段 | Demo `talkingPoints` | 规范 |
|------------|---------------------|------|
| `scenario` | 折叠区 hint"适用:流失预警"/"适用:储值召回" | 小字副标题,可选展示 |
| `script` | 话术主体 | 长按可复制,配置 `copiedIndex` 高亮反馈 |
### 验证点(细化)
- [ ] `tactics` 长度 3-5 条Prompt 约束)
- [ ] `script` 含会员姓名或称呼(如"王哥"),非模板化空话
- [ ] `script` 含 0-1 个 emoji`emoji_count <= 1`
- [ ] 不出现"8折/免费/KOL/抽成"等越权用词(黑词正则拦截)
- [ ] App4 失败时 App5 跳过(串行依赖,`context.app4_result is None` 不执行)
- [ ] 长按复制触发 `wx.setClipboardData` 成功
- [ ] `target_id` 与 App4 一致(`${assistant_id}_${member_id}`
---
## 6. APP6 备注分析(备注链首)
### 功能目的
助教提交备注后打分 1-10 并提取 **6 类线索**`providers` = 备注提交人真实姓名。
### 返回 schema[schemas.py:134-138](../../apps/backend/app/ai/schemas.py#L134)
```
App6Result {
score: int (ge=1, le=10)
clues: [ ClueItem { category, summary, detail, emoji } ]
}
```
### System Prompt 核心规则([ai_system_prompt_by_app.md:196-220](ai_system_prompt_by_app.md#L196)
- **忠于备注原文,不延伸推测**
- 8 维度归一到 6 分类App6CategoryEnum
- 一条备注可对应多个维度
- 情感倾向(正面/中性/负面)影响后续助教触达开场白
- 标注备注是谁写的、何时写的(时效性)
### 前端消费
App6 结果**不直接渲染**,经 App8 整合后写入 `member_retention_clue` 表,前端查客户详情展示(`source` 字段显示"By:小燕")。
### Demo 展示形态(**客户详情「维客线索」中显示 6 类**
参考 [customer-detail.ts:99-145](../../apps/demo-miniprogram/miniprogram/pages/customer-detail/customer-detail.ts#L99) 7 条 clue 分布:
- 客户基础 × 1生日/VIP
- 消费习惯 × 2夜场/高客单)
- 玩法偏好 × 1中式斯诺克
- 促销偏好 × 1酒水套餐敏感
- 社交关系 × 1固定球搭子
- 重要反馈 × 1斯诺克走位需求
### 验证点(细化)
- [ ] `score ∈ [1,10]` 严格校验Pydantic `ge=1, le=10`
- [ ] `category ∈ {客户基础, 消费习惯, 玩法偏好, 促销偏好, 社交关系, 重要反馈}`
- [ ] `providers` 为备注提交人姓名(非"系统"、非空)
- [ ] 备注原文中的专有名词("李哥"、"阿杰"、"A12号台")保留在 `detail`
- [ ] 备注 >8000 字时截断但保留情感词与动作词
- [ ] score 写入 `ai_cache.score` 列(非 `result_json.score`,供后端排序)
---
## 7. APP7 客户分析(消费链尾,客户画像)
### 功能目的
消费链 App8 完成后串行触发,综合消费 + 备注 + 助教关系输出 **200-400 字客户画像**
### 返回 schema[schemas.py:141-152](../../apps/backend/app/ai/schemas.py#L141)
```
App7Result {
strategies: [ { title: str, content: str } ]
summary: str # 画像主文
}
```
### System Prompt 核心规则([ai_system_prompt_by_app.md:224-257](ai_system_prompt_by_app.md#L224)
- **开头一句话定性**"工作日晚间的上班族硬核玩家"
- 中段数字:消费结构、频次、客单、助教绑定
- 结尾助教可动 1-2 条建议
- 避免评判语言("消费低"改为"客单 60 元偏低于店均 120 元"
- **必须标注数据时间窗**"近 30 天 / 近 90 天"
- 备注信息需带【来源:人名,请甄别真实性】标注([app7_customer_prompt.py:64-77](../../apps/backend/app/ai/prompts/app7_customer_prompt.py#L64)
### Demo 展示形态customer-detail 头部)
参考 [customer-detail.ts:91-98](../../apps/demo-miniprogram/miniprogram/pages/customer-detail/customer-detail.ts#L91) + [customer-detail.wxml:69-90](../../apps/demo-miniprogram/miniprogram/pages/customer-detail/customer-detail.wxml#L69)
```
🤖 AI 智能洞察
┌─ summary高价值 VIP 客户,月均到店 4-5 次,偏好夜场中式台球,
│ 近期对斯诺克产生兴趣。社交属性强...
│ 当前推荐策略
│ ■ [green] 最后到店距今 12 天,超出理想间隔 7 天,建议...
│ ■ [amber] 客户提到想练斯诺克走位,可推荐专项课程包...
│ ■ [pink] 社交属性强,可邀请参加球友赛事活动...
└─
```
**字段到 UI 映射**
| schema 字段 | Demo 字段 | UI 规范 |
|------------|----------|--------|
| `summary` | `aiInsight.summary` | 开头白色正文段 |
| `strategies[].title` | 策略颜色 `color` 枚举前缀 | 不直接渲染配色由前端枚举轮转green/amber/pink/... |
| `strategies[].content` | `strategy-text` | 策略卡片主体,每条独立配色条 |
| 末条样式 | `.strategy-item-last` | 最后一条无下边距 |
**注意**demo 中 `strategies[].color` 是前端枚举色green/amber/pinkAI 返回的 `title` 字段应映射为 emoji/关键词而非颜色。实际代码中前端按 index % 6 循环配色(不依赖 AI
### 验证点(细化)
- [ ] `summary` 开头为"定性短句"(句号前 ≤ 25 字)
- [ ] `summary` 包含时间窗关键词("近 30 天"/"近 90 天"/"近 60 天"
- [ ] `summary` 长度 200-400 字
- [ ] `strategies` 长度 2-3 条,前端展示最多 2 条demo 规范 `index < length - 1` 为非末,`index === length - 1` 为末条)
- [ ] 评判性词("差"/"低"/"糟糕"出现时应改写为对比表述AI 侧或后处理)
- [ ] 备注来源标注【来源xxx请甄别真实性】进入 prompt
- [ ] 消费链中断时 App7 跳过App8 未完成不执行)
- [ ] cache_type = `app7_customer_analysis`target_id = `member_id`
---
## 8. APP8 线索整合(写库幂等)
### 功能目的
整合 App3 + App6 全部线索 → 去重、统一 6 分类、`providers` 逗号拼接 → **幂等写 `member_retention_clue` 表**
### 返回 schema[schemas.py:80-88, 155-158](../../apps/backend/app/ai/schemas.py#L80)
```
App8Result {
clues: [ ConsolidatedClueItem {
category: str # 6 选 1
summary: str # 30 字内行动导向
detail: str # 50-100 字展开
emoji: str # 分类对应 emoji
providers: str # "消费数据" / "店员小燕备注" / 逗号拼接多源
} ]
}
```
### System Prompt 核心规则([ai_system_prompt_by_app.md:261-290](ai_system_prompt_by_app.md#L261)
- 冲突处理:**备注原文 > 行为推断**
- `summary` 禁放数字(数字放 `detail`
- 线索排序:可直接动作 > 身体偏好 > 长期画像
- 不超过 5 条(助教可读性)
- 不输出泛化建议("请关心会员"禁止)
### 落库规则([dispatcher.py _write_retention_clue](../../apps/backend/app/ai/dispatcher.py)
```sql
BEGIN;
DELETE FROM app.member_retention_clue
WHERE site_id=:site AND member_id=:member AND source IN ('ai_consumption','ai_note');
INSERT INTO app.member_retention_clue (...) VALUES (...);
COMMIT;
```
- source 取值:消费链触发 → `ai_consumption`;备注链触发 → `ai_note`
- `source='manual'`(人工线索)**绝不**删除
### Demo 展示形态7 条线索在客户详情,见 App3 表格)
### 验证点(细化)
- [ ] `DELETE` + `INSERT` 在同一事务内pg_stat_activity 查询验证)
- [ ] 写入后 `source='manual'` 行数不变(跨事务前后 count 一致)
- [ ] 同日多次消费 / 多条备注不会让线索重复累积(幂等)
- [ ] `providers` 多源拼接去重(如"消费数据、备注-小燕",不出现"消费数据、消费数据"
- [ ] `summary` 不含数字(正则 `/[0-9%¥]/` 不匹配)
- [ ] `detail` 含数字(与 summary 分工)
- [ ] `clues` 数组长度 ≤ 5
- [ ] 排序:可直接动作类("推课"/"约时段")在前
- [ ] App3 或 App6 任一失败时App8 仍执行但对应 `_clues=[]`
- [ ] cache_type = `app8_clue_consolidated`
---
## 9. 管理后台通用检查
### AIDashboard总览
- [AIDashboard.tsx:120](../../apps/admin-web/src/pages/AIDashboard.tsx#L120) `getDashboard()`
- 字段:`today_calls` / `today_success_rate` / `today_tokens` / `today_avg_latency_ms` / `trend_7d`
- [ ] 数值与 `biz.ai_run_logs` 聚合一致
- [ ] `trend_7d` 折线图日期连续无跳点,单位 ms / % / 次
- [ ] 切换 site_id 过滤器后数字按租户隔离
### AIOperations手动执行
- [AIOperations.tsx:67](../../apps/admin-web/src/pages/AIOperations.tsx#L67) `runApp(appType, {site_id, member_id})` 支持 App3-App8App1/App2 不适用)
- [AIOperations.tsx:146](../../apps/admin-web/src/pages/AIOperations.tsx#L146) `triggerEvent({event_type, site_id, member_id, is_forced})` 越过去重
- [ ] `is_forced=true` 可绕过当日去重,`ai_trigger_jobs` 新增一行
- [ ] 返回 `job_id`tag 色 `processing → success/failed`
- [ ] WebSocket `/ws/ai-alerts/{site_id}` 接收 `circuit_open / rate_limited / budget_exceeded / timeout / failed`
### TriggerManager调度配置
- [TriggerManager.tsx:183](../../apps/admin-web/src/pages/TriggerManager.tsx#L183) `updateTriggerConfig(id, {cron_expression, interval_seconds})`
- [ ] cron 表达式校验通过才允许保存
- [ ] 改动后调度器热加载Scheduler lifespan 监听 meta.scheduled_tasks
---
## 10. 跨切面共性验证
| 维度 | 检查项 | 验证方法 |
|------|--------|---------|
| RunLog | 状态转换 `pending→running→success\|failed\|timeout\|circuit_open\|rate_limited\|budget_exceeded` | `SELECT status, COUNT(*) FROM biz.ai_run_logs GROUP BY status` 覆盖 8 种 |
| RunLog | `success` 必有非零 `tokens_used` | `SELECT COUNT(*) FROM biz.ai_run_logs WHERE status='success' AND tokens_used=0` = 0 |
| RunLog | `request_prompt` ≤ 8000 字符 | `SELECT MAX(length(request_prompt))` ≤ 8000 |
| 熔断 | 连续失败 N 次后 OPEN | 人工注入失败 → 第 N+1 次日志 `status=circuit_open` 且**无真实 DashScope 调用** |
| 限流 | 分钟/日双维度 | 压测超限后 `rate_limited` 数突增,`tokens_used IS NULL` |
| 预算 | 不足时记 `budget_exceeded:<reason>` | 人为把日预算调至 0 → 下一次调用日志为 `budget_exceeded:daily_budget_used_up` |
| 缓存 | `result_json IS NULL` 行数应为 0 | `SELECT COUNT(*) FROM biz.ai_cache WHERE result_json IS NULL` = 0 |
| 缓存 | `cache_type` 枚举齐全 | `SELECT DISTINCT cache_type` 仅含 `CacheTypeEnum` 的 8 个值 |
| 去重 | 同 `(event_type, member_id, site_id, date)` 当天仅 1 次 | `SELECT event_type, member_id, COUNT(*)``is_forced=false` 时均为 1 |
| 多租户 | `SET LOCAL app.current_site_id` | tcpdump / SQL trace 首条 SQL 为 SET LOCAL |
| 告警推送 | WebSocket 接收 5 类 `alert_type` | 前端 devtools Network → WS 抓包校验 |
| 前端降级 | cache miss 清空旧数据 | 切换 area → DevTools Inspect `aiInsights` 短暂为 [] |
| 前端降级 | SSE 断流重试 | chrome network throttle offline → 看 retry 2/4/8s |
| Demo 对齐 | 字段 → UI 映射一致 | 并排截图 miniprogram vs demo-miniprogram 像素对齐 |
---
## 11. 自测清单(按角色)
### 前端工程师
**客户详情页**
- [ ] App7 `summary` 开头为定性短句且含时间窗
- [ ] App7 strategies 末条 CSS 类 `.strategy-item-last` 生效(无下边距)
- [ ] App3/6/8 合并后 clues 配色按 `categoryColor` 映射无 undefined
- [ ] clue-card `source` 字段区分 "By:系统" vs "By:{人名}"
- [ ] 展开折叠 `detail` 动画无抖动
**任务详情页**
- [ ] App4 `one_line_summary` 在 banner 副标题
- [ ] App5 `tactics[].script` 长按复制成功 + toast 反馈
- [ ] `action_suggestions` 勾选持久化到本地
**财务看板**
- [ ] 9 区域 × 5 时间 = 45 种组合切换AI 洞察正确加载或清空
- [ ] `title` 以冒号结尾、`.ai-insight-dim` 灰色样式生效
- [ ] `**xxx**` 渲染为 underline
- [ ] seq=11/12 置顶"本期总结"
- [ ] 图标为 SVG`/assets/icons/ai-robot.svg`),非 emoji
**聊天页**
- [ ] 浮按钮从 6 类页面进入都正确带 `sourcePage` + `pageFilters`
- [ ] SSE 中文无乱码
- [ ] `done` 事件替换临时消息 ID
- [ ] 历史会话 / 客户 3 天内复用 / 任务复用 / 看板新建 四种入口均工作
- [ ] 重试 2/4/8s 指数退避
- [ ] **待确认**)若实现 `screen_content`,浮按钮 triggerEvent 采集页面快照
### 后端工程师
- [ ] `ai_run_logs` 状态分布覆盖 8 种
- [ ] `ai_cache` 无 NULL `result_json`
- [ ] `member_retention_clue` `source='manual'` 跨天数量稳定
- [ ] 日结后 2 分钟内 72 组缓存写入完整
- [ ] `SET LOCAL app.current_site_id` 每次查询都执行
- [ ] Pydantic schema 对 score/enum/长度的约束不被绕过
### 测试工程师
- [ ] demo-miniprogram 与 miniprogram 的客户详情 / 任务详情 / 财务看板像素级对齐
- [ ] customer-detail 页 AI 区块三部分互不干扰summary / strategies / clues
- [ ] 小程序热启动后 AI 洞察不闪烁旧数据
- [ ] admin-web Dashboard 与数据库聚合一致性
---
## 附录 A已废弃 / 已迁移
- `apps/backend/app/ai/apps/app1_chat.py` ~ `app8_consolidation.py` 已删除,逻辑统一内聚至 [dispatcher.py](../../apps/backend/app/ai/dispatcher.py)
- APP2 于 2026-04-23 拆分为 `app2_finance` (8 组) + `app2a_finance_area` (64 组)
## 附录 B已知 Gap / 待办PRD 对齐清单)
> 来源:`docs/prd/` 精读对照当前代码与本 spec 的差异。
| ID | Gap | PRD 来源 | 当前状态 | 建议 |
| -- | --- | -------- | -------- | ---- |
| G1 | APP1 `screen_content` 采集(浮按钮 onTap triggerEvent 采集页面快照) | AI 需求 2 / NS2 | schema 已预留 [schemas.py:22](../../apps/backend/app/ai/schemas.py#L22) 但 [chat.ts:487](../../apps/miniprogram/miniprogram/pages/chat/chat.ts#L487) 未传 | 浮按钮 `triggerEvent('snapshot')` → 父页返回 setData 可见子集 → URL/SSE body 透传;后端按 "screen_content → page_context" 顺序拼 prompt |
| G2 | APP2 72 组预热完整性校验 | P14 调度器 | 代码已拆 8+64 组,缺自动化核对 | 日结后 T+10min 执行 `SELECT DISTINCT target_id FROM biz.ai_cache WHERE cache_type IN ('app2_finance','app2a_finance_area') AND site_id=? AND created_at::date=CURRENT_DATE`,应 = 72不足告警 |
| G3 | APP3 `summary` 行动导向动词约束 | ai-app-prompts | prompt 未强化动词要求 | prompt 增加"必须以可推/建议/需/可约/可邀 等动词结尾";后端 schema 加正则;单测固化 |
| G4 | APP4 `target_id` 拼装规则文档化 | P5 | 代码实现 `{assistant_id}_{member_id}` 但文档未固定分隔符 | 正式文档化分隔符为单下划线,拦截 assistant_id/member_id 含下划线的场景 |
| G5 | APP5 `scenario` 前端消费规范 | AI 需求 2 | schema 定义但 UI 未约定 | 约定 task-detail 话术区 `scenario` 作为折叠 hint"适用:流失预警"),空字符串时不展示 |
| G6 | APP6 `score` 驱动前端 UI | ai-app-prompts | 后端已写 `ai_cache.score` 列但前端未消费 | 备注列表按 score 降序排列score ≥ 8 高亮、≤ 3 置灰 |
| G7 | APP7 `strategies[].title` 字段用途 | ai-app-prompts | 前端按 index 循环配色,未用 title | 方案 A删除 title方案 BAI 输出 title 作为徽章("机会"/"风险"/"日常"),前端展示 |
| G8 | APP8 相似线索去重判断标准 | AI 需求 2 / P5 | prompt 未定义相似度阈值 | prompt 增加"若两条线索 summary 字符重合 ≥ 60% 或 category 相同且描述同一行为合并为一条providers 拼接去重" |
| G9 | 熔断/限流/预算硬约束压测 | P14 | 代码实现但无压测报告 | 压测APP1 单用户 11 次/分钟应触发 rate_limited连续 5 次失败后第 6 次 status=circuit_open 且不真实调用;日 token 达 100k 后 status=budget_exceeded |
| G10 | 日结触发 APP2 预热延迟 | P14 | 代码"日结后轮询"无明确延迟 | 文档固定 "日结完成事件 → T+5min 开始调度 APP2 × 72 组T+15min 应全部完成";超时告警 |
| G11 | RLS 多租户隔离强制验证 | P5 / backend CLAUDE.md | 代码每次查询前 SET LOCAL缺自动验证 | 集成测试:伪造 site_id=A 的 JWT 访问 member_id 属于 site_id=B 的数据应返回 404抓 pg_stat_statements 验证 SET LOCAL 存在 |
| G12 | 新客(无消费/无备注)降级文案 | NS2 | 代码有 `_default_member_data` 降级但 prompt 文案未标准化 | 定义模板「该客户暂无历史线索以下基于本次消费进行初步判断」dispatcher 注入 prompt 开头 |
| G13 | APP8 `providers` 多源拼接格式 | ai-app-prompts | 代码拼接但分隔符未固定 | 固定格式:`"消费数据,备注-{人名}"`,中文逗号分隔,备注人不重复(用 set 去重) |
| G14 | APP4 任务链 cache miss 时的 prompt 提示 | ai-app-prompts | 代码读缓存失败时传空 | prompt 标注"暂无 APP8 历史线索,请基于基线数据分析",而非丢空字符串 |
| G15 | APP2 `**xxx**` 强调标记约定 | Demo 规范 | 前端已做轻量 Markdown但 prompt 未要求 AI 输出该标记 | prompt 增加"对关键结论或异常词用 `**粗体**` 包裹,前端会渲染为下划线强调" |
## 附录 DPRD 硬性阈值对照表(验收单)
> 单独抽出所有数字阈值,供 QA 断言时直接引用。
| APP | 字段/指标 | 阈值 | 不达标处理 |
| --- | --------- | ---- | ---------- |
| APP1 | 单轮 prompt | ≤ 4000 字 | 触发截断保留最近 N 轮 |
| APP1 | page_context | ≤ 2000 字 | page_context.py 末尾附"…(上下文已截断)" |
| APP1 | 限流 | 10 次/分钟/用户 | `rate_limited` 记录,前端提示"操作过快" |
| APP1 | 复用窗口 | customer/coach/finance 3 天task 无限general 新建 | 超窗口新建会话 |
| APP2 | 组合数 | 728×9 | 缺组合告警 |
| APP2 | 加载时延 | < 2 秒 | 前端 loading > 2s 上报监控 |
| APP2 | seq 范围 | 1-1211/12 置顶 | 非 11/12 按 seq 升序 |
| APP3 | clues 条数 | 1-5 | AI 超 5 截断 |
| APP3 | summary 长度 | ≤ 20 字 | 后端校验拦截 |
| APP3 | detail 长度 | ≤ 120 字 | 后端校验拦截 |
| APP4 | action_suggestions | 1-4 条,每条 ≤ 100 字 | 单测固化 |
| APP4 | one_line_summary | ≤ 30 字,必含关系评级词 | 正则检查 `(紧密\|一般\|疏远)` |
| APP5 | tactics 条数 | 2-4 | AI 超 4 截断 |
| APP5 | script 长度 | ≤ 150 字 | 后端校验 |
| APP5 | 黑词 | 禁用 8折/免费/KOL/抽成/内部价 | 后端正则黑名单拦截 |
| APP6 | score 范围 | 1-106=标准,<6 扣分,>6 加分) | Pydantic `ge=1, le=10` |
| APP6 | clues 条数 | 0-10 | 无下限,上限截断 |
| APP7 | summary 长度 | 200-400 字 | 后端校验 |
| APP7 | summary 开头定性 | ≤ 25 字 + 句号 | 正则 `^.{1,25}[。!]` |
| APP7 | strategies 条数 | 2-5 | 前端仅展示 2 条 |
| APP7 | 时间窗标注 | 必含 "近 30 天/近 60 天/近 90 天" | 关键词检查 |
| APP8 | clues 条数 | ≤ 5 | AI 超 5 截断 |
| APP8 | summary 禁数字 | 正则 `[0-9%¥]` 不匹配 | 后处理剥离 |
| APP8 | providers 拼接 | 中文逗号去重 | 后端 set 处理 |
| 通用 | Token 日预算 | 100,000 | 超限 status=budget_exceeded |
| 通用 | Token 月预算 | 2,000,000 | 超限告警 + 阻断 |
| 通用 | 熔断阈值 | 连续 5 次失败 OPEN | 60 秒恢复窗口 |
| 通用 | APP2-8 限流 | 100 次/小时/门店 | rate_limited 跳过 |
| 通用 | 关系流失预警 | 停消费 > 14 天 | APP4/7 标注 |
| 通用 | 高粘性阈值 | 月内 ≥ 3 次 | APP4 判定 |
| 通用 | 学习型阈值 | 助教费占比 > 40% | APP3/4/7 判定 |
| 通用 | 当期 < 7 天 | 必须降权用词 | APP2 prompt 约束 |
## 附录 CDemo 关键路径索引
| 页面 | AI 字段 | 文件 |
| ---- | ------- | ---- |
| 客户详情 | `aiInsight.summary/strategies` + `clues[]` | [customer-detail.ts:90-145](../../apps/demo-miniprogram/miniprogram/pages/customer-detail/customer-detail.ts#L90) |
| 任务详情 | `retentionClues[]` + `talkingPoints[]` | [task-detail.ts:67-86](../../apps/demo-miniprogram/miniprogram/pages/task-detail/task-detail.ts#L67) |
| 财务看板 | `ai-insight-section` 三段式 | [board-finance.wxml:208-221](../../apps/demo-miniprogram/miniprogram/pages/board-finance/board-finance.wxml#L208) |
| 助教/客户看板 | `aiColorClass`(随机配色) | [board-finance.ts:294](../../apps/demo-miniprogram/miniprogram/pages/board-finance/board-finance.ts#L294) |
---
## 附录 E生产端 vs Demo 端对照实况2026-04-22 快照)
### E.1 生产端真实消费路径
| 页面 | AI 字段来源 | 关键实现 | 与 demo 差异 |
| ---- | ----------- | -------- | ------------ |
| customer-detail | `fetchAICache('app7_customer_analysis', memberId)` 单独拉取;`retentionClues` 从主详情接口返回 | [customer-detail.ts:7](../../apps/miniprogram/miniprogram/pages/customer-detail/customer-detail.ts#L7) onLoad → loadDetail → `_loadAIInsight()` 异步加载;`aiInsight.strategies` 前端按 index 循环 6 色 | demo 是硬编码 mock生产 strategies `color` 由前端计算 |
| task-detail | `aiAnalysis.summary / suggestions / talkingPoints` + `retentionClues` **都随主详情接口一次返回**,无独立 AI 缓存调用 | `onCopySpeech()``wx.setClipboardData()` + 2s toast [task-detail.ts:268](../../apps/miniprogram/miniprogram/pages/task-detail/task-detail.ts#L268);备注创建后 4s 间隔轮询 5 次拿 `aiScore`20s 超时) | demo 同样内联 mock生产端**没有长按复制**,仅单击复制按钮 |
| board-finance | `fetchAICache(cacheType, targetId)` + `parseMarkdownInline` 解析 `**粗** / *斜* / ***粗斜***` | [board-finance.ts:532](../../apps/miniprogram/miniprogram/pages/board-finance/board-finance.ts#L532) cache_type 切分已实装seq 11/12 置顶(精确匹配 + 末两条 fallback切换 time/area 时 `aiInsights=[]` 主动清空 | demo 内联 mock无 TIME_MAP/AREA 枚举 |
| 浮按钮 | task-detail 当前**未挂载**customer-detail 仅传 `customerId`board-finance 仅传 `bottom=200` | `sourcePage` / `pageFilters` **虽支持但尚未启用**(属性存在、传参为空) | demo 无浮按钮 |
### E.2 关键实务差异
- **task-detail 无 AI 缓存调用**`aiAnalysis``fetchTaskDetail()` 主响应的一部分,后端在返回任务详情时已聚合读取 App4/App5 缓存。这意味着**任务列表分页**与 **AI 结果拉取**合并为一次请求,减少 round-trip。
- **fetchAICache silent fail**[services/api.ts:417](../../apps/miniprogram/miniprogram/services/api.ts#L417) 错误时返回 `null`,页面降级显示"暂无洞察数据";此静默失败需监控上报补强。
- **百分比双重格式化**board-finance 把后端小数率0.052× 100 传 WXS 格式化demo 端硬编码字符串,两端一致性依赖后端契约稳定。
- **setData 路径优化**`'aiInsight.summary'` 字符串路径而非整对象解构,减少小程序差量渲染。
---
## 附录 FAI 数据库 schema 权威清单
### F.1 五张核心表 DDL 速览
权威 DDL[db/zqyy_app/schemas/biz.sql](../../db/zqyy_app/schemas/biz.sql) 与 [public.sql](../../db/zqyy_app/schemas/public.sql)。
| 表 | 关键字段 | 索引要点 | RLS |
| -- | -------- | -------- | --- |
| `biz.ai_run_logs` | `status` varchar(20)pending/running/success/failed/timeout/circuit_open/rate_limited/budget_exceeded`alert_status``session_id` varchar(100)`tokens_used` int default 0`latency_ms` int`request_prompt` text≤ 8000 字符写入) | 部分索引 `idx_ai_run_logs_alert` WHERE status IN (failed, timeout, circuit_open)BRIN `(created_at)` | **未启用** |
| `biz.ai_cache` | `cache_type` varchar(30) + CHECK 约束 8 枚举,`target_id` varchar(100)`result_json` jsonb NOT NULL`score` int**无 CHECK 约束** | `idx_ai_cache_lookup (cache_type, site_id, target_id, created_at DESC)` | **未启用** |
| `biz.ai_trigger_jobs` | `event_type` varchar(30)`is_forced` bool`app_chain` varchar(100)`payload` jsonb | 部分索引 `idx_ai_trigger_jobs_dedup` WHERE status ≠ 'skipped_duplicate' | **未启用** |
| `biz.ai_conversations` / `biz.ai_messages` | `context_type` varchar(20)`context_id` varchar(50)`session_id` varchar(100)`reference_card` jsonb | 部分索引 `idx_ai_conv_context` WHERE context_type IS NOT NULL | **未启用** |
| `public.member_retention_clue` | `category` varchar(20) + CHECK 枚举 6 值,`summary` varchar(200)`detail` text`recorded_by_name` varchar(50)`source` varchar(20) DEFAULT 'manual'`is_hidden` bool | `idx_retention_clue_member``idx_retention_clue_category` | **未启用**,通过 FDW 反向映射 `fdw_app.member_retention_clue` |
### F.2 DDL vs Pydantic 对齐问题(按风险)
| 级别 | 项目 | 症状 |
| ---- | ---- | ---- |
| **CRITICAL** | `ai_cache.score` 无 CHECK 约束 | Pydantic `App6Result.score: int = Field(ge=1, le=10)` 仅应用层校验脏数据0/-1/100可静默落库 |
| **HIGH** | `member_retention_clue` **没有 `emoji` 和 `providers` 列** | [dispatcher.py:513-588](../../apps/backend/app/ai/dispatcher.py#L513) `_write_retention_clue` 折叠:`emoji + " " + summary``summary``providers``recorded_by_name` varchar(50)。**providers 多源拼接大概率超过 50 字符被 PG 报错或截断** |
| **HIGH** | `summary varchar(200)` 拼接 emoji 后有效长度降低 | AI 输出接近 200 字符时合并报错 |
| **MEDIUM** | `cache_type` CHECK 与 `CacheTypeEnum` 双侧维护 | 新增 App 需同时改 DDL + Pydantic遗漏即违反 CHECK |
| **MEDIUM** | `ai_run_logs``job_id` 列关联 `ai_trigger_jobs` | 只能通过 `session_id` 间接关联,无 FK 保护 |
| **LOW** | FDW 外部表 `fdw_app.member_retention_clue``source``is_hidden` 列 | ETL 端无法按 source 过滤 AI 写入的线索 |
### F.3 建议补充的索引/约束
1. `biz.ai_cache.score` 添加 `CHECK (score IS NULL OR (score >= 1 AND score <= 10))`
2. `public.member_retention_clue` 新建 `(member_id, site_id, source)` 索引APP8 DELETE 现在全扫)
3. `biz.ai_conversations` 新建 `session_id` 单列索引
4. **全部 AI 表启用 RLS**(当前完全依赖应用层 `site_id` 过滤,无最后防线)
---
## 附录 G合规审计红黄清单2026-04-22
> 来源security-reviewer 全量扫描 apps/backend/app/ai/ 所有 prompt 拼装路径。
### G.1 已通过的面
-`member_phone / phone / mobile / id_card / wechat_openid / wechat_unionid` **零出现**于所有 prompt 与 data_fetcher
-`bank_account / bank_card / home_address / ip_address / device_id` 全部未出现
- ✅ DashScope SDK 走 HTTPS 加密传输
- ✅ page_context 前端透传被 handler 白名单拦截handler 只 `.get()` 已知字段)
### G.2 5 个风险点(按优先级)
| ID | 文件 | 风险 | 建议修复 |
| -- | ---- | ---- | -------- |
| R1 | [member_data.py:200](../../apps/backend/app/ai/data_fetchers/member_data.py#L200) | 助教 `da.real_name` 作为 nickname fallback 传 AI | 确认是否属员工隐私;若是改为空串或工号 |
| R2 | [member_data.py:249-262](../../apps/backend/app/ai/data_fetchers/member_data.py#L249) | 储值卡**精确余额**直接进 prompt | 范围化:改为"高/中/低/清零"标签 |
| R3 | [member_data.py:276](../../apps/backend/app/ai/data_fetchers/member_data.py#L276) | `card_balance_total` / `stored_value_balance_total` 精确值 | 同 R2 |
| R4 | [member_data.py:358-398](../../apps/backend/app/ai/data_fetchers/member_data.py#L358) | `notes.content` 备注原文无正则脱敏 | 预过滤手机号 `1[3-9]\d{9}``1****`、银行卡号、身份证号 |
| R5 | [run_log_service.py:57](../../apps/backend/app/ai/run_log_service.py#L57) | 含 R2/R3/R4 的 prompt 写入 `ai_run_logs.request_prompt` 落库 | 先做字段级脱敏再入库;或 ai_run_logs 加严格 RLS 仅开发读 |
### G.3 合规测试清单CI
- [ ] 单测:`build_prompt_*` 返回值断言无 11 位连续数字(手机号)
- [ ] 单测:`notes.content` 含手机号的样例prompt 中已掩码
- [ ] CI`grep -r "member_phone\|wechat_openid\|id_card" apps/backend/app/ai/` 无命中
- [ ] 集成测试:构造含手机号的备注,验证 APP6 prompt 中号码不完整出现
---
## 附录 HMCP Server 与 dispatcher 关系
### H.1 现状
| 面向 | dispatcher 路径 | MCP Server 路径 |
| ---- | --------------- | --------------- |
| 调用者 | Admin Web / 后端事件 | **目前仅 Claude Code 开发环境**;百炼 AI 应用规划中(未接入) |
| 连接库 | etl_feiqiu只读+ zqyy_app读写 | 仅 etl_feiqiu 只读 |
| RLS | 每次查询前 `SET LOCAL app.current_site_id` | **无 site_id 隔离**P5.1 规划中) |
| 鉴权 | JWT | Bearer Token可选`MCP_TOKEN` 为空则无鉴权) |
| 工具 | 8 个 APP handler | 4 个通用工具:`list_tables` / `describe_table` / `describe_schemas` / `query_sql`(只读+正则黑名单+500 行上限) |
### H.2 关键边界
- **8 个 AI 应用目前均走 dispatcher 直查 DB不经过 MCP**
- MCP 扩展P5.1 批次 B规划加 zqyy_app 连接 + auth/biz/public 自动 schema 路由 + 敏感字段脱敏wx_openid/phone
- MCP 扩展P5.1 批次 C规划完整查库手册上传百炼知识库
### H.3 生产部署
- 外部入口:`https://mcp.langlangzhuoqiu.cn/mcp`(未来)
- 本地配置:`.mcp.json``pg-etl` / `pg-app` 禁用,`pg-etl-test` / `pg-app-test` 启用
---
## 附录 Iadmin-web AI 三页面实况
### I.1 AIDashboard.tsx顶部卡片 + 趋势 + 告警)
- 数据源 [AIDashboard.tsx:99](../../apps/admin-web/src/pages/AIDashboard.tsx#L99)`getDashboard()``DashboardResponse`
- 字段:`today_calls / today_success_rate / today_tokens / today_avg_latency_ms / trend_7d[] / app_distribution[] / budget{} / recent_alerts[] / app_health[]`
- 时间范围:`range_days` (1/3/7/10) 或 `date_from/date_to` 自定义 [AIDashboard.tsx:112](../../apps/admin-web/src/pages/AIDashboard.tsx#L112)
- WebSocket`ws(s)://[host]/ws/ai-alerts/{site_id}`,单次连接 **无重连** [AIDashboard.tsx:135-168](../../apps/admin-web/src/pages/AIDashboard.tsx#L135)
- 告警消息格式:`{ type: "alert_created", payload: AlertItem }`
- 实时告警最多保留 20 条,合并至表格顶部
### I.2 AIOperations.tsx四卡片手动执行
四个 Card 区:
1. **Card 1 手动重跑**`retryTriggerJob(id)``trigger_job_id`
2. **Card 2 缓存失效**`invalidateCache({ site_id, app_type?, member_id? })``affected_count`site_id 必填
3. **Card 2.5 按需执行**`runApp(appType, params)` 支持 APP3-APP8
4. **Card 2.6 触发事件链**`triggerEvent({ event_type, ..., is_forced=true })` — event_type ∈ `{consumption, note_created, task_assigned, dws_completed}`
5. **Card 3 批量执行**`createBatchRun()` → 估算 → `confirmBatchRun(batch_id)` 异步启动batch_id **内存 TTL 600 秒** [admin_service.py:26](../../apps/backend/app/services/ai/admin_service.py#L26)
6. **Card 4 告警管理**`ackAlert(id)` / `ignoreAlert(id)`,分页 10
### I.3 TriggerManager.tsx4 Tab + URL 驱动)
- Tab`all / biz / ai / etl``?tab=xxx` URL 查询参数持久化
- Biz Tab 编辑 Modal`cron_expression` + `interval_seconds`min=1保存后热加载
- AI Tab 组件堆栈:`AITriggers` + `AIOperations` + `AITriggerJobs`
### I.4 **严重不一致app6 命名错位**
- 前端 `adminAI.ts` 定义 `app6_note_analysis`
- 后端 `admin_ai.py``_SUPPORTED_APP_TYPES``app6_note`
- 手动执行 APP6 **会 400**,需修正命名统一(推荐采用后端名 `app6_note`
### I.5 权限模型
- 所有 Admin AI 端点 `_require_admin()` 依赖 [admin_ai.py:70-111](../../apps/backend/app/routers/admin_ai.py#L70)
- 角色:`{site_admin, tenant_admin, super_admin}` 任一
- `admin_users.is_active` 实时查询(禁用账户秒级生效)
---
## 附录 J后端完整端点与链路矩阵
### J.1 路由端点清单(共 21 个 AI 相关)
**admin_ai.py13 个,需 admin 权限)**
- `GET /api/admin/ai/dashboard` / `trigger-jobs` / `trigger-jobs/{id}` / `run-logs` / `run-logs/{id}` / `budget` / `alerts`
- `POST /api/admin/ai/trigger-jobs/{id}/retry` / `cache/invalidate` / `batch-run` / `batch-run/confirm` / `alerts/{id}/ack` / `alerts/{id}/ignore`
**xcx_chat.py5 个,需 approved 用户)**
- `GET /api/xcx/chat/history` / `{chat_id}/messages` / `messages?contextType=&contextId=`
- `POST /api/xcx/chat/stream`SSE/ `{chat_id}/messages`
**内部事件总线**
- `POST /api/internal/ai/trigger` — [internal_ai.py](../../apps/backend/app/routers/internal_ai.py)Internal-Token 认证
- `POST /api/internal/etl-completed` — [internal_events.py:56](../../apps/backend/app/routers/internal_events.py#L56),触发 recall → task → `fire_event("ai_dws_completed")`
- `POST /api/admin/task-engine/pending-review/{id}/reassign` — [admin_task_engine.py:255](../../apps/backend/app/routers/admin_task_engine.py#L255),触发 `fire_event("ai_task_assigned")`
### J.2 事件 → 链路映射
[dispatcher.py:169-174](../../apps/backend/app/ai/dispatcher.py#L169) `_EVENT_CHAIN_MAP`
| 事件 | 链路 |
| ---- | ---- |
| `consumption` | APP3 → APP8 → APP7+ APP4 → APP5 若 `has_assistant` |
| `note_created` | APP6 → APP8 |
| `task_assigned` | APP4 → APP5 |
| `dws_completed` | APP2 × 8area='all' + APP2A × 64area!='all' |
### J.3 服务层职责矩阵
| 服务 | 职责 | dispatcher 调用 | 写表 |
| ---- | ---- | ---------------- | ---- |
| ChatService | 对话持久化 | **直调 DashScopeClient**(不走 dispatcher | ai_conversations / ai_messages |
| AdminAIService | 监控聚合 | 无,仅查询 | 无(只读) |
| NoteService | 备注创建 | `dispatcher._handle_note()` | notes / ai_cache |
| TaskGenerator | 每日 07:00 生成任务 | `trigger_scheduler` 异步 | coach_tasks / ai_cache |
| TriggerScheduler | cron/event 调度 | `fire_event()` | trigger_jobs 更新 |
| AIDispatcher | 链路编排+熔断/限流/预算 | `_run_step()` × N | ai_cache / ai_run_logs |
### J.4 超时与并发
- 单步 `_STEP_TIMEOUT = 180s`2026-04-21 从 120s 上调)[dispatcher.py:57](../../apps/backend/app/ai/dispatcher.py#L57)
- APP2 DWS 全链路 = 180s × 72 组 + 600s 余量 ≈ **2.5 小时**(同步执行可能阻塞其他触发器,需监控)
- 普通链 超时 10min
### J.5 关键技术债
- **事件去重内存 set**:服务重启后当日相同事件可重复触发 → 需迁移到 DB 查询 `ai_trigger_jobs`
- **ChatService 不走 dispatcher**:链路独立,不享受熔断/限流/预算保护APP1 限流单独实现)
- **旧 openai_client 残留**DashScope 迁移后未见清理
---
## 附录 KDemo 10 页 AI 区块映射(标杆参考)
### K.1 浮按钮挂载清单
**已挂载**8 页board-coach / board-customer / chat-history / notes / performance / task-list / customer-service-records / my-profile / board-finance / customer-detail
**未挂载**chat / task-detail / coach-detail / apply / login / reviewing / dev-tools / no-permission
### K.2 AI 配色规范
- 6 色方案:`red / orange / yellow / blue / indigo / purple`
- 实装:[ai-color-manager.ts:74-127](../../apps/demo-miniprogram/miniprogram/utils/ai-color-manager.ts#L74)
- task-list 采用 `random` 模式每日刷新
- 其他页面按页面标识映射固定色
### K.3 辅助组件
- `ai-title-badge`:标题旁 AI 徽章
- `ai-inline-icon`:列表内嵌 AI 图标
- `clue-card`:线索卡片(含 `tag/emoji/title/source/content` 字段 + 展开折叠)
### K.4 `pageFilters` 前端字段缺失
扫描发现前端页面**未定义 pageFilters 对象结构**,当前仅浮按钮属性声明;实际采集需在 P0 G1 gap 补充。
---
## 附录 LETL → AI 数据源约束
### L.1 金额口径铁律
- **禁用 `consume_money`**[member_data.py:1-6](../../apps/backend/app/ai/data_fetchers/member_data.py#L1) 文件头明文禁止)
- 统一拆分为:`table_charge_money + assistant_pd_money + assistant_cx_money + goods_money`
- 所有 prompt 拼装遵守此口径APP2/3/4/6/7 prompt 已核对)
### L.2 FDW 视图与 RLS
- 所有 `fdw_etl.*` 已迁移至 `app.v_*` RLS 视图
- FDW 查询超时硬上限 5 秒 [member_data.py:69-79](../../apps/backend/app/ai/data_fetchers/member_data.py#L69)
- 超时降级到 `_default_member_data()`AI 输入降级,分析质量下降)
### L.3 DWS 视图清单
- 20+ 汇总表6 大主题(助教业绩/薪酬/财务日报/会员分析/订单/库存)
- 金额字段 `NUMERIC(12,2)`,货币 CNY
- **空值策略未文档化**NULL vs 0 策略缺prompt 数字解读存歧义
### L.4 消费记录完整明细风险
- 2026-05-01 起 App3 不再按 4000 字硬截断消费记录,优先保留完整明细,避免丢失大客户高频消费模式
- 已用合成 100 条完整消费明细真实调用 App3prompt 约 25791 字64.30s 返回成功,低于 180s 单步超时
- 剩余风险:真实门店极端会员、缓存 reference 膨胀或百炼侧临时抖动仍可能拉高耗时,需通过 `ai_run_logs.elapsed_ms` 持续观察
---
## 附录 M部署与超参
### M.1 DashScope APP_ID 清单(根 [.env:118-134](../../.env#L118)
```
DASHSCOPE_APP_ID_1_CHAT
DASHSCOPE_APP_ID_2_FINANCE (area='all' × 8 时间维度)
DASHSCOPE_APP_ID_2A_FINANCE_AREA (area!='all' × 64 组合2026-04-23 新增)
DASHSCOPE_APP_ID_3_CLUE
DASHSCOPE_APP_ID_4_ANALYSIS
DASHSCOPE_APP_ID_5_TACTICS
DASHSCOPE_APP_ID_6_NOTE
DASHSCOPE_APP_ID_7_CUSTOMER
DASHSCOPE_APP_ID_8_CONSOLIDATE
```
### M.2 超参硬编码
- 模型版本、温度、max_tokens 在 [config.py:14-48](../../apps/backend/app/ai/config.py#L14) 内硬编码
- 无 dev/test/prod 环境差异文件(仅一份 .env
- `INTERNAL_API_TOKEN` 与 DashScope API Key 同存根 .env已 gitignore
- `BACKEND_API_URL=http://localhost:8000` 部署到各环境需手工改
### M.3 建议
- 提取 `.env.example` + `.env.dev` + `.env.prod` 多环境模板
- 超参挪到 `config.py``@dataclass` 并可被 `.env` 覆盖
- 加 APP2 DWS 并发窗口监控2.5h 耗时风险)
---
## 附录 N历史审计时间轴2026-02 ~ 2026-04
### N.1 关键演进节点
| 日期 | 主题 | 涉及 APP |
| ---- | ---- | -------- |
| 2026-04-23 | [app2a_finance_area_integrated](../../docs/audit/changes/2026-04-23__app2a_finance_area_integrated.md) — 72 组合拆为 app2_finance (8) + app2a_finance_area (64) + `member_order_count` 列 + area_code NULL bug 修复 | APP2 |
| 2026-04-22 | [app2_prompt_v5_1_and_miniprogram_ai_insight](../../docs/audit/changes/2026-04-22__app2_prompt_v5_1_and_miniprogram_ai_insight.md) — V5.1 采纳A/B 40 次调用92.3 分)+ 按星期聚合门槛 14 天 + 月中场景保护 + seq11/12 置顶 | APP2 |
| 2026-04-21 | [admin-web-ai-management-suite](../../docs/audit/changes/2026-04-21__admin-web-ai-management-suite.md) — AIPrewarm / AITriggers / AIOperations 增强 + 4 个后端端点 | 管理 |
| 2026-04-20 | [ai-module-complete](../../docs/audit/changes/2026-04-20__ai-module-complete.md) — Phase 0-4 贯通 + 删除 8 个死代码 app 文件 + 修复 main.py 未调用 set_dispatcher 导致 503 | 全局 |
| 2026-03-20 | [rns1-ai-autonomous-decision-risk-audit](../../docs/audit/changes/2026-03-20__rns1-ai-autonomous-decision-risk-audit.md) — 76 session / 34 风险 / "AI 信任文档胜过信任 DB" 核心问题 | 全局 |
| 2026-03-10 | [multi-module-ai-apps-task-defense](../../docs/audit/changes/2026-03-10__multi-module-ai-apps-task-defense-miniprogram.md) — 8 个 AI 应用骨架 + 任务防卡死 + 小程序页面迁移 | 全局 |
| 2026-02-26 | [retention-clue-refactor](../../docs/audit/changes/2026-02-26__retention-clue-refactor.md) — 新表 `ai_trigger_jobs` 去重 + `member_retention_clue` 字段调整 | APP8 |
### N.2 从历史踩坑抽取的验证规则
| 历史踩坑 | 应有验证 |
| -------- | -------- |
| **FDW Schema 脱节**(列名/视图名虚构) | 生成/改 prompt 前必须 MCP 执行 `information_schema.columns` + `SELECT * LIMIT 5` 验证 |
| **月中场景误读**3 月初把月中 22 天当整月对比) | payload 含"对比口径"字段(当期/对比期天数);按星期门槛 14 天;样本<5 天降权表述 |
| **Prompt 版本漂移** | 版本号固定写入 `docs/ai/`frontend enum / backend schema 同步校验 |
| **app_type 枚举错配** | 新增 APP 时同时更新 `CacheTypeEnum` / DDL CHECK / `_SUPPORTED_APP_TYPES` / adminAI.ts |
| **DWS area_code=NULL bug** | ETL 完成后必须验证 `all_sum ≈ 各区域 sum` |
| **DDL 列 DEFAULT 0 未回填** | 上线前预约回填脚本窗口 |
| **AI 自主执行架构变更** | 连接模式/DDL/FDW 映射变更需向用户展示 SQL 等待确认 |
| **Task 状态欺骗**20/22 失败仍 completed | 带验证步骤的 task 测试通过率 < 90% 禁止 complete |
### N.3 5 项遗留技术债(需补审计)
- APP8 幂等 DELETE+INSERT 改造缺独立审计
- `tokens_used=0` 提取 bug`usage.models` 嵌套)未根治
- `request_prompt` 8000 字符门槛未正式审计
- `ai_cache.score` TTL 策略未定
- `ai_trigger_jobs` 去重从内存 set 迁移到 DB 的当前状态未审计
---
## 附录 O最终 Gap 汇总v4 版,共 25 项)
> 在 v3 的 15 项基础上 + 新增 10 项(来自全景调研)。
### O.1 v3 遗留 15 项(摘要)
见附录 BG1-G15
### O.2 v4 新增 10 项
| ID | Gap | 来源 | 建议 |
| -- | --- | ---- | ---- |
| G16 | 前后端 `app6_note_analysis` vs `app6_note` 命名错位 | admin-web 调研 | 统一为后端名 `app6_note`,同时改 adminAI.ts 枚举 |
| G17 | WebSocket `/ws/ai-alerts/{site_id}` 无重连 | AIDashboard.tsx | 指数退避重连2/4/8s最多 10 次) |
| G18 | `member_retention_clue``emoji/providers` 列,折叠到 `summary/recorded_by_name` | DB schema | 要么加列,要么明确 `providers` 长度上限并拦截超 50 字符 |
| G19 | `ai_cache.score` 无 CHECK 约束 | DB schema | 加 `CHECK (score IS NULL OR score BETWEEN 1 AND 10)` |
| G20 | 全部 AI 表 **未启用 RLS** | DB schema | 至少 `biz.ai_cache` / `biz.ai_trigger_jobs` / `public.member_retention_clue` 启用 RLS + `USING (site_id = current_setting('app.current_site_id')::bigint)` |
| G21 | 储值余额精确值 / 助教 real_name / 备注原文进 prompt 落 `ai_run_logs` | 合规审计 | R2-R5 脱敏ai_run_logs 加严格 RLS |
| G22 | 事件去重内存 set 重启丢失 | 后端路由调研 | 去重改查 `ai_trigger_jobs` 当日记录 |
| G23 | APP2 DWS 预热 2.5 小时同步执行 | 后端路由调研 | 加并发控制窗口;监控 > 2h 告警 |
| G24 | task-detail 无 AI 浮按钮挂载 | 生产端调研 | 若需支持"从任务详情进入对话",需挂载浮按钮并传 `sourcePage=task-detail` + `contextId=task_id` |
| G25 | MCP Server 无 `site_id` RLS 隔离;未接入百炼 AI | MCP 调研 | P5.1 批次 B 落地前不开放 MCP 给百炼;开放时强制 `MCP_TOKEN` + 敏感字段脱敏 |
---
## 附录 P全景完成度自评
| 面向 | 盘查状态 |
| ---- | -------- |
| 代码静态(后端 / 前端生产 / 前端 demo / admin-web | ✅ 完成 |
| PRD 文档AI 需求 2 / ai-app-prompts / P5 / P14 / P15 / NS2 / NS3 | ✅ 完成 |
| DB schema DDL + Pydantic 对齐 + RLS 状态 | ✅ 完成 |
| 合规敏感字段PII / 金融 / 位置 / 备注原文) | ✅ 完成 |
| MCP Server现状 + 规划 + 与 dispatcher 边界) | ✅ 完成 |
| 后端路由 21 个端点清单 + 服务层职责 + 超时并发 | ✅ 完成 |
| 历史审计近 3 月时间轴 + 踩坑规则 + 技术债 | ✅ 完成 |
| demo 10 页 AI 区块映射 + 配色规范 + 浮按钮分布 | ✅ 完成 |
| 共享包枚举对齐(未同步)/ ETL DWS 字段 / 口径铁律 | ✅ 完成 |
| 部署超参 + 环境差异 + 压测降级灰度 | ✅ 完成(含缺口标注) |
**结论**:本文档 v4 已作为 NeoZQYY 项目 AI 模块的**单一真相来源SSOT**,覆盖代码 / PRD / DB / 合规 / MCP / 路由 / 审计 / demo / ETL / 部署 10 个面向25 项 Gap 跟踪75+ 验证点;后续变更需同步更新本文档。