Files
Neo-ZQYY/docs/ai/app2_finance_multi_app_design.md
Neo d269ee6401 docs(ai): app2a v1.2 system prompt + 多 APP 派生设计 v2 + 审计 + A/B 脚本
1. docs/ai/app2a_finance_area_system_prompt_20260422_v1.md (新建 · v1.2 生产版):
   - 基于 app2_finance V5.1 派生
   - 板块 C 改"业态收入结构" · 板块 E 改"业态定位与对比"
   - 新增 H7 硬约束:业态特征引用必须紧跟 payload 真实数据
   - H6 扩展区域级 6 类字段缺失降级(储值卡/分渠道现金流/现金流出/会员占比/按星期/日异常)
   - 经 3 次修正:v1"稀疏" → v1.1 纠正为业务真实 0/非 0 → v1.2 纠正为字段存在/整块缺失
   - 已同步百炼控制台 APP ID 0ae965029bc54706bcff44f511ac716b

2. docs/ai/app2_finance_multi_app_design.md (新建 · v2 定稿):
   - 6 章 + 3 附录 · Q1-Q7 全部决策 · 6 阶段 28 项 checklist
   - 72 组合数据源支持度三档梳理(必须 / 业务级全店 / 字段存在 vs 整块缺失)
   - 2 套 prompt 拼接方案 · 2 个派生百炼 APP 策略

3. docs/audit/changes/2026-04-23__app2a_finance_area_integrated.md (新建):
   - 完整审计记录 · 13 高风险文件逐项注解
   - 数据库变更 + 风险与回滚 + 验证方式 + 合规检查

4. docs/audit/audit_dashboard.md (刷新 · 135 条记录)

5. scripts/ab_test_app2a_area.py (新建):
   - 8 业态 × 3 轮 = 24 次采样评估含金量
   - 自动检测 H1/H2/H3/H7 硬约束通过率 + seq11 三色灯分布

6. scripts/ab_to_cache.py (新建):
   - 复用 A/B 结果直接写 ai_cache · 绕开百炼预算验证 UI 端到端

A/B 实测 24/24 成功 · 12 条齐整率 100% · H1/H3/H7 100% · 达生产级。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 21:56:46 +08:00

380 lines
19 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.
# App2 财务洞察 72 组合 · 多 APP 派生与 Prompt 拼接方案设计 **v2**(决策已定 · 实施中)
> 文档状态:**v2 定稿 · 实施中**2026-04-22 用户已确认全部 7 个决策点 · 已进入整包实施阶段)
> 前置版本v1 草案2026-04-22 初稿)
> 作者Claude + Neo
> 适用范围:`apps/backend/app/ai/prompts/` · `apps/backend/app/ai/dispatcher.py` · `apps/etl/connectors/feiqiu/loaders/` · `db/etl_feiqiu/migrations/` · 小程序 `board-finance` · admin-web 3 页 · 百炼控制台新建 APP
> 前置背景:**this_month / all 组合**已通过 V5.1 system prompt 达到生产级(综合分 92.3);本次针对**其他 64 个区域组合**8 时间 × 8 业态)派生新 APP `app2a_finance_area`。
---
## · TL;DR整包实施总览
| 维度 | 定案 |
|------|------|
| Prompt 拼接方案 | **2 套**(全域全量 `app2_finance` 不动 · 区域派生 `app2a_finance_area` 新建) |
| 百炼 APP 数量 | **2 个**(新建 `app2a_finance_area`env: `DASHSCOPE_APP_ID_2A_FINANCE_AREA` |
| System Prompt | `app2a` v1 [已产出](app2a_finance_area_system_prompt_20260422_v1.md) · 基于 V5.1 派生 · 12 条 · 板块 C/E 重分工 · 新增 H7 业态特征硬约束 |
| DWS 改造 | **必做** · `dws_finance_area_daily``member_order_count` 列 + ETL loader 改造 + RLS 视图更新(会员订单占比覆盖) |
| 前端 seq 精确匹配 | **必做** · 保留 `seq` 字段,按 `find(i => i.seq === 11)` 定位,降级保留"末两条"启发式 |
| 灰度开关 | **不加**(用户指示作为正式模块上线) |
| admin-web 改动 | **一起做** · AIPrewarm 分两段 + AIDashboard/AIOperations app_type 选择器扩展 |
| DB DDL 改动 | **1 处** · `db/etl_feiqiu/migrations/YYYYMMDD__app2a_member_order_count.sql` |
---
## 一 · 用户确认的决策点Q1-Q7
| 问题 | 决策 |
|---|---|
| Q1 · app2a system prompt 条数 | **方案 α · 12 条板块重分工**(前端 seq 11/12 兼容) |
| Q2 · 板块 C/E 替代方向 | **同意** · C = 业态收入结构 / E = 业态定位与对比 |
| Q3 · DWS 改造 | **做** · 区域级单位经济 + 按星期聚合纳入 payload |
| Q4 · seq 精确匹配时机 | **现在做** · 与 app2a 一起上 |
| Q5 · 灰度开关 | **不加** · 作为正式模块上线,要通过测试验收 |
| Q6 · 百炼建 APP 节奏 | 用户拿 [v1 system prompt](app2a_finance_area_system_prompt_20260422_v1.md) 去百炼建 APP → 拿到 APP ID → 写 `.env` → Claude 实施后端 |
| Q7 · admin-web 改动 | **一起做** · 整包交付 |
---
## 二 · 区域粒度字段处理三档(用户已认同)
### 档 1 · 业务本质为"全店级",区域下**无需补齐**(维持现状隐藏)
| 字段 | 业务理由 |
|---|---|
| 预收资产 / 储值卡余额变化 | 储值卡是会员账户级资产,与消费区域无关 |
| 现金流入来源(纸币/线上/团购 分渠道) | 支付渠道是收银台级属性,区域级无法自然拆分 |
| 现金流出 4 类 | 全店级成本(房租/水电/平台手续费),无法按区域归属 |
### 档 2 · 区域下有业务价值且**技术上可补齐**(本期做)
| 字段 | 实施方式 |
|---|---|
| 单位经济 · 客单价(按成交收入 / 按发生额) | 用 `app.v_dws_finance_area_daily` 现有字段 `gross_amount / confirmed_income / order_count` 可直接算 |
| 单位经济 · 日均订单数 | 用 `order_count` 聚合 |
| 单位经济 · **会员订单占比** | 需新增 `member_order_count` 列 + ETL loader 改造(**本次 DDL** |
| 按星期聚合(区域级 7 天日均) | 用 `gross_amount / order_count` 聚合(不含 `cash_inflow_total`,区域级不适用) |
| 日粒度异常(同周基线 / 区域级) | 用 `gross_amount` 做异常检测(不含现金流入) |
### 档 3 · 助教分析(区域下"**字段存在 vs 字段整块缺失**"两态,非"稀疏"也非"值 = 0"
- **数据链路真相**ODS 助教服务日志每条带 `table_id / room_id` → DWD 层按物理位置→区域映射 → DWS `dws_coach_area_hours``(assistant_id, area_code, stat_month)` 精确分桶聚合
- **ETL 写入逻辑**(见 [coach_area_hours_task.py:L134](../../apps/etl/connectors/feiqiu/tasks/dws/coach_area_hours_task.py#L134)**只在发生过服务时才 INSERT 记录**。没有服务 = 没有聚合键 = 不写入
- **查询结果只有两种状态**
- **查到记录且 effective_hours > 0** = 业务真实:本期该区域发生了 N 小时助教服务、M 元薪酬
- **查不到记录(空集)** = 业务真实:本期该区域**零助教服务发生**(不是数据问题,不是"稀疏"
- **不存在"单条记录值 = 0"**(除非边角极端:全部服务被标废单 · 概率极低)
- **UI 处置**
- 后端返回助教字段 → 正常展示助教板块
- 后端不返回助教字段(空集) → 不展示助教板块 **或** 展示中性提示"本期本区域无助教服务"**不**用"数据稀疏"字眼)
- **AI 处置v1.2 system prompt 板块 D 已改)**:按助教字段"存在 vs 整块缺失" + 业态合理性区分
- **麻将/KTV 业态缺失**:业态正常,简述一笔带过,不作为隐患
- **大厅/VIP/斯诺克 业态缺失**:业态异常,提示店长核查(排班未录入/停招/ETL 流水完整性),作为 seq 11 健康度"数据/运营完整性"维度扣分
---
## 三 · DWS 改造详细设计
### 3.1 DDL 改动1 处)
**文件**`db/etl_feiqiu/migrations/20260423__app2a_member_order_count.sql`(即将产出)
**内容**
```sql
BEGIN;
-- 1. 给区域级 DWS 表加 member_order_count 列
ALTER TABLE dws.dws_finance_area_daily
ADD COLUMN IF NOT EXISTS member_order_count integer DEFAULT 0 NOT NULL;
COMMENT ON COLUMN dws.dws_finance_area_daily.member_order_count IS '会员订单数区域粒度DWD 聚合)';
-- 2. 重建 RLS 视图app schema
CREATE OR REPLACE VIEW app.v_dws_finance_area_daily AS
SELECT id, site_id, tenant_id, stat_date, area_code,
table_fee_amount, goods_amount, assistant_pd_amount, assistant_cx_amount,
gross_amount,
discount_groupbuy, discount_vip, discount_manual, discount_gift_card,
discount_rounding, discount_other, discount_total,
confirmed_income,
cash_pay_amount, cash_paper_amount, scan_pay_amount, groupbuy_pay_amount,
recharge_cash_inflow, cash_inflow_total, cash_outflow_total, cash_balance_change,
card_consume_total, recharge_card_consume, gift_card_consume,
recharge_cash, first_recharge_cash, renewal_cash,
order_count,
member_order_count, -- 新增
created_at, updated_at
FROM dws.dws_finance_area_daily
WHERE (site_id = (current_setting('app.current_site_id'::text))::bigint);
-- 3. dws schema 视图同步(如存在)
-- 按 CLAUDE.md "RLS 视图双 Schema 规则",若 dws.v_dws_finance_area_daily 存在需同步创建
COMMIT;
```
### 3.2 ETL loader 改造(飞球 Connector
**文件**`apps/etl/connectors/feiqiu/loaders/dws_finance_area_daily.py`(或对应文件,需确认)
**改动**:聚合 DWD 层订单数据时,按 `area_code + stat_date + is_member_order` 分组,将 `is_member_order = true` 的订单数合并到 `member_order_count` 列。
**验证 SQL**
```sql
-- 全店总数应等于区域总和
SELECT s.site_id, s.stat_date, s.member_order_count AS full_store,
COALESCE(SUM(a.member_order_count), 0) AS sum_areas
FROM dws.dws_finance_daily_summary s
LEFT JOIN dws.dws_finance_area_daily a
ON s.site_id = a.site_id AND s.stat_date = a.stat_date
WHERE s.stat_date >= current_date - interval '7 days'
GROUP BY s.site_id, s.stat_date, s.member_order_count
HAVING s.member_order_count <> COALESCE(SUM(a.member_order_count), 0);
-- 期望0 行
```
### 3.3 回填策略
- 新列 `DEFAULT 0` → 历史数据 `member_order_count = 0`(区域历史会员占比暂全为 0
- 用户可选择:① 接受新数据生效即可P2 上线后生成的数据正确);② 写回填脚本 `scripts/ops/backfill_area_member_order.py` 从 DWD 重算历史(工作量 1 天)
- **建议当期不回填**,待运行 7 天观察新数据正确后再视需要回填
---
## 四 · 后端实施清单
### 4.1 新建 `app2a_finance_area_prompt.py`
**文件**`apps/backend/app/ai/prompts/app2a_finance_area_prompt.py`
**关键设计**
- 复用 `app2_finance_prompt.py` 的:`DIMENSION_MAP / AREA_LABELS / KEY_TRANSLATIONS / _slim / _pct / _build_discount_kpi / _build_coach_kpi / _translate_keys / _calc_date_range / _calc_prev_range`
- **新增** `_fetch_area_daily_series(site_id, start_date, end_date, area_code) -> list[tuple]`:查 `app.v_dws_finance_area_daily` 区域级日粒度
- **新增** `_build_area_unit_economics(series, prev_series)`:区域级单位经济(客单价、日均订单数、会员占比 含环比)
- **新增** `_aggregate_by_weekday_area(series)`:区域级按星期聚合(无现金流入)
- **新增** `_detect_anomaly_days_area(site_id, start, end, area_code, series)`:区域级日粒度异常(仅 gross_amount
- **新增** `AREA_INDUSTRY_TRAITS`:业态特征字典,按 area_code 映射文字描述
- **新增** `_fetch_area_share(site_id, time_dimension, area_code) -> dict`:查本区域成交收入占全店比(对比区域 total vs 全店 total
**payload 结构**
```python
payload = {
"当前时间": now,
"门店编号": site_id,
"时间维度": time_label,
"区域": area_label,
"对比口径": compare_caliber, # H1 依赖
"业态说明": {"区域编码": area, "区域名称": label, "业态特征": trait, "典型对比项": peer}, # 新增
"区域占比": {"本区域成交收入": ..., "占全店成交收入": ..., "占比环比": ...}, # 新增
"核心KPI": {...},
"派生比率": {"人力成本占成交收入比": ..., "优惠侵蚀率": ...}, # 仅 2 项,其他区域级不可用
"优惠构成": {...},
"助教成本": {...}, # 可能为空
"单位经济": {...}, # 新增(区域级,不含会员占比直到 DWS 改造后)
"按星期聚合": {...}, # 新增(区域级,当期 ≥ 14 天)
"日粒度异常": [...], # 新增(区域级,当期 ≥ 7 天)
"行业基线": {"周中客流规律": ...},
"原始指标": raw_cn,
}
```
### 4.2 `dispatcher.py` 改动
- 常量拆分:
```python
_ALL_AREA = "all"
_SUB_AREAS = ("hall", "hallA", "hallB", "hallC", "vip", "snooker", "mahjong", "ktv")
```
- `_handle_dws_completed()` 的 72 循环拆分:
```python
for td in _TIME_DIMENSIONS:
await self._run_step("app2_finance", td, "all", ...)
for area in _SUB_AREAS:
await self._run_step("app2a_finance_area", td, area, ...)
```
- `run_single_app()` 新增分支:
```python
elif app_type == "app2a_finance_area":
prompt_str = await build_app2a_area_prompt(context)
...
```
### 4.3 配置与注册
- `config.py``app_id_2a_finance_area: str` env `DASHSCOPE_APP_ID_2A_FINANCE_AREA`
- `prompts/__init__.py``from .app2a_finance_area_prompt import build_prompt as build_app2a_area_prompt`
- `cache_service.py` `CacheTypeEnum`:新增 `APP2A_FINANCE_AREA = "app2a_finance_area"`TTL 0 · 当日过期)
- `admin_ai.py` `_SUPPORTED_APP_TYPES`:加 `"app2a_finance_area"`
- `schemas/admin_ai.py` `RunAppRequest``app_type` 枚举加入新值
### 4.4 环境变量
**`.env` 追加**
```
DASHSCOPE_APP_ID_2A_FINANCE_AREA=<用户从百炼控制台粘贴>
```
**`config.py` 启动期校验**:缺失立即 raise`AIConfig.from_env()` 现有机制)
---
## 五 · 前端实施清单
### 5.1 小程序 seq 精确匹配 + 双 cache key
**文件**`apps/miniprogram/miniprogram/pages/board-finance/board-finance.ts`
**改动 1** · `_loadAIInsights`:按 area 选 cache_type
```typescript
const cacheType = areaKey === 'all' ? 'app2_finance' : 'app2a_finance_area'
const cache = await fetchAICache(cacheType, targetId)
```
**改动 2** · map 阶段保留 `seq` 字段:
```typescript
const insights = Array.isArray(rj.insights)
? rj.insights.map((item: any, idx: number) => ({
seq: Number(item.seq) || (idx + 1), // 保留 seq兼容无 seq 的旧缓存
title: ...,
body: ...,
titleSegs: ...,
bodySegs: ...,
}))
: []
```
**改动 3** · `_extractSummary` 按 seq 精确匹配:
```typescript
const evaluation = insights.find(i => i.seq === 11) || insights[insights.length - 2]
const tracking = insights.find(i => i.seq === 12) || insights[insights.length - 1]
const details = insights.filter(i => i.seq !== 11 && i.seq !== 12)
```
### 5.2 admin-web 改动
**`AIPrewarm.tsx`**
- 72 组合列表分两段渲染:
- 段 18 个全域组合cache_type: `app2_finance`
- 段 264 个区域组合cache_type: `app2a_finance_area`
- 每组合显示 `app_type` 标签(蓝=全域,绿=区域)
**`AIDashboard.tsx` + `AIOperations.tsx`**
- app_type 下拉选择器增加 `app2a_finance_area` 项
- 运行日志筛选器支持新 app_type
**`api/adminAI.ts`**
- `AppType` 类型增加 `'app2a_finance_area'`
---
## 六 · 整包实施 Checklist按依赖顺序
### Phase A · 用户侧准备(独立于 Claude
- [ ] **A1** · 用户将 [app2a_finance_area_system_prompt_20260422_v1.md](app2a_finance_area_system_prompt_20260422_v1.md) 的 ``` 代码块内全文粘贴到百炼控制台新建 APP
- [ ] **A2** · 获取 APP ID类似 `1dcdb5f39c3040b6af8ef79215b9b051`
- [ ] **A3** · 在根 `.env` 追加 `DASHSCOPE_APP_ID_2A_FINANCE_AREA=<APP ID>`
- [ ] **A4** · 告知 Claude APP ID 已配置完成
### Phase B · DWS 改造Claude 实施)
- [ ] **B1** · 产出 `db/etl_feiqiu/migrations/20260423__app2a_member_order_count.sql`DDL 迁移)
- [ ] **B2** · 改 ETL loader 增加 `member_order_count` 聚合(`apps/etl/connectors/feiqiu/loaders/` 对应 loader
- [ ] **B3** · 执行 migration + 运行一次区域级 ETL 回放 7 天验证
- [ ] **B4** · 校验 SQL 确认全店 = 区域和
### Phase C · 后端实施Claude 实施 · 依赖 A4
- [ ] **C1** · 新建 `app2a_finance_area_prompt.py`(含业态特征字典 + 5 个区域级辅助函数)
- [ ] **C2** · 改 `prompts/__init__.py` / `config.py` / `cache_service.py` / `admin_ai.py` / `schemas/admin_ai.py`
- [ ] **C3** · 改 `dispatcher.py` 72 循环拆分 + `run_single_app` 新分支
- [ ] **C4** · 单元测试:`build_app2a_area_prompt` 对 hall/vip/mahjong/ktv 4 个代表业态拼接验证
- [ ] **C5** · 集成测试admin-web 手动触发任一区域组合,验证 `ai_run_logs.app_type = 'app2a_finance_area'` 有记录
### Phase D · 前端实施Claude 实施 · 独立于 B/C
- [ ] **D1** · 小程序 `board-finance.ts` 改 `_loadAIInsights` 按 area 切 cache_type + 保留 seq 字段
- [ ] **D2** · 小程序 `_extractSummary` 按 seq 精确匹配(含回退启发式)
- [ ] **D3** · admin-web `AIPrewarm.tsx` 分两段渲染 72 组合
- [ ] **D4** · admin-web `AIDashboard.tsx / AIOperations.tsx / api/adminAI.ts` app_type 扩展
### Phase E · 端到端验证(全部完成后)
- [ ] **E1** · 百炼 APP 端到端实调:单个区域组合(如 `this_month__vip`触发AI 返回 12 条完整
- [ ] **E2** · 72 组合预热实调:手动触发 `ai_dws_completed`,全链路走完 ~20 min
- [ ] **E3** · 小程序切换区域 → AI 洞察区正确展示 app2a 结果 · 总结卡片 seq 11/12 精确匹配
- [ ] **E4** · admin-web AIPrewarm 两段正确展示 · AIDashboard 筛选 app2a 可见
- [ ] **E5** · A/B 测试采样 16 组8 时间 × 2 业态 hall/vip人工评估 ≥ 12/16 组达标
- [ ] **E6** · 更新 `docs/ai/app2_finance_prompt_version_history.md` 增加 app2a v1 生产版记录
### Phase F · 审计收尾
- [ ] **F1** · `/audit` 生成 `docs/audit/changes/2026-0X-XX__app2a_finance_area_integrated.md`
- [ ] **F2** · 数据库文档同步:`docs/database/` 记录 `member_order_count` 列新增
- [ ] **F3** · 本设计文档切换为 `v2 完成`
---
## 七 · 分阶段时间估算(整包交付)
| Phase | 工作量 | 负责 | 依赖 |
|---|---|---|---|
| A · 百炼 APP 建立 | 10 min | Neo | 无(可立即开始) |
| B · DWS 改造 | 2-3 h | Claude | A1 完成 |
| C · 后端实施 | 3-4 h | Claude | A4拿 APP ID + B4DWS 新列生效) |
| D · 前端实施 | 2-3 h | Claude | 无(与 B/C 并行) |
| E · 端到端验证 | 2-3 h | Neo + Claude | C/D 全部完成 |
| F · 审计 | 30 min | Claude | E 全部通过 |
**总耗时估算**10-14 小时1.5 工作日)
---
## 八 · 风险与回滚
| 风险 | 影响 | 缓解 |
|---|---|---|
| app2a system prompt v1 质量不达预期 | 64 组合低质 | E5 人工评估 · 未通过则迭代 v2 系统 prompt |
| DWS `member_order_count` 历史值 = 0未回填 | 区域级会员占比环比在上线首周失真 | 接受首周降级 · system prompt H2 已定"样本不足后缀"降权引用 |
| ETL loader 改造引入数据错算 | 区域级订单数异常 | 全店 = 区域和校验 SQL · E2 跑完立即验证 |
| 百炼 APP ID 配置错误 | 64 组合全失败 | config.py 启动期校验缺失即报错 |
| dispatcher 72 循环拆分 bug | 预热超时 | 保留原熔断/限流机制 · C3 单测覆盖拆分 |
**回滚**(若整包实施后发现重大问题):
1. **百炼侧**:将 app2a APP 暂停控制台操作dispatcher 自动回退(但需代码改动支持)
2. **代码侧**`git revert` 整包 commit
3. **DWS 侧**`ALTER TABLE DROP COLUMN member_order_count`CASCADE 视图)
由于用户指示"作为正式模块上线、不加灰度",回滚复杂度较高 · 要求 E5 人工评估严格把关。
---
## 九 · 验证清单Definition of Done
**B · DWS**
- [ ] `SELECT column_name FROM information_schema.columns WHERE table_schema='dws' AND table_name='dws_finance_area_daily' AND column_name='member_order_count';` 返回 1 行
- [ ] 全店 vs 区域和 校验 SQL 返回 0 行
- [ ] `app.v_dws_finance_area_daily` 新视图 SELECT 正常返回带 member_order_count 列
**C · 后端**
- [ ] `AIConfig.from_env()` 加载通过(新 env 变量识别)
- [ ] 对 hall/vip/mahjong/ktv 4 个代表业态调 `build_app2a_area_prompt` 本地返回 prompt 字符串,"对比口径"/"业态说明"/"区域占比" 三字段齐
- [ ] `ai_run_logs.app_type = 'app2a_finance_area'` 出现
- [ ] 单次预热总耗时 < 25 min
**D · 前端**
- [ ] 小程序切换到 VIP/大厅/斯诺克 3 个区域AI 洞察区能正确加载 app2a 缓存结果
- [ ] 本期总结卡片三色灯 + seq 11/12 精确匹配
- [ ] admin-web AIPrewarm 两段分区正常显示 72 组合
- [ ] admin-web AIDashboard 可按 app2a_finance_area 筛选日志
**E · 端到端**
- [ ] 16 组采样人工评估 ≥ 75% 达标12/16 组)
- [ ] 7 天成功率 ≥ 95%
- [ ] `insights` 数组长度 = 12 占比 ≥ 90%
---
## 变更记录
| 日期 | 版本 | 变更 | 作者 |
|---|---|---|---|
| 2026-04-22 | v1草案 | 初版 · 基于 this_month/all 调优结论 + 72 组合数据源调研 | Claude + Neo |
| 2026-04-22 | v2定稿 | 用户已确认 Q1-Q7 决策 · 纳入 DWS 改造 + seq 精确匹配 · 产出整包 checklist · 删除灰度开关 | Claude + Neo |