# NS2：AI Prompt 细化 — ai-prompt-refinement

> 优先级：高（NS1 完成后立即执行，Prompt 细化是 AI 功能闭环的关键）
> 预估工作量：中等
> 前置条件：P5-A 已完成（AI 骨架就绪）、NS1 已完成（后端 API 数据结构确定）
> 参考基准：`docs/prd/specs/P5-miniapp-ai-integration.md`（P5-B 阶段定义）
> SPEC 状态：✅ 三件套已完成（requirements.md → design.md → tasks.md）
> SPEC 路径：`.kiro/specs/ai-prompt-refinement/`
> 实施状态：⏳ 未开始（代码实施待启动）

---

## 一、背景与目标

P5-A 阶段已交付 AI 集成管道：百炼封装、缓存 API、SSE 框架、应用 2/8 完整实现、应用 1/3/4/5/6/7 的触发机制和调用骨架。但应用 1/3/4/5/6/7 的 `build_prompt()` 函数仍为占位骨架，核心数据字段（`consumption_records`、`service_history`、`objective_data` 等）标记为 TODO。

本 SPEC 目标：将 6 个应用的 Prompt 拼接函数从骨架升级为完整实现，使 AI 应用能基于真实数据生成有价值的分析结果。

### 当前骨架状态

| 应用 | 文件 | 骨架状态 | 待细化字段 |
|------|------|---------|-----------|
| 应用 1 | `app1_chat.py` | 页面上下文文本化工具留接口 | `page_context` + `screen_content` 合并为 `build_page_text()` 输出（详见 3.7 设计决策） |
| 应用 3 | `app3_clue.py` | `build_prompt()` 占位 | `consumption_records`（DWD+DWS 订单明细全维度） |
| 应用 4 | `app4_analysis.py` | `build_prompt()` 占位 | `service_history`、`assistant_info` |
| 应用 5 | `app5_tactics.py` | `build_prompt()` 占位 | `service_history`、`assistant_info`（同应用 4） |
| 应用 6 | `app6_note.py` | `build_prompt()` 占位 | `consumption_data`（同应用 3 的 main_data） |
| 应用 7 | `app7_customer.py` | `build_prompt()` 占位 | `objective_data`（同应用 3 的 main_data） |

---

## 二、技术架构

### 2.1 数据获取层（新建共享模块）

Prompt 细化的核心是从数据库获取真实数据并格式化为 AI 可读的 JSON。多个应用共享相同的数据获取逻辑，应抽取为共享模块：

```
apps/backend/app/ai/
├── data_fetchers/              🆕 新建
│   ├── __init__.py
│   ├── member_data.py          # 客户数据获取（消费记录、会员卡、余额等）
│   ├── assistant_data.py       # 助教数据获取（基本信息、服务历史）
│   ├── service_history.py      # 服务记录获取（助教-客户维度）
│   └── page_context.py         # 页面上下文文本化（应用 1 专用）
├── apps/
│   ├── app1_chat.py            🔧 补充页面文本化调用
│   ├── app3_clue.py            🔧 补充 consumption_records 拼接
│   ├── app4_analysis.py        🔧 补充 service_history + assistant_info
│   ├── app5_tactics.py         🔧 补充 service_history + assistant_info
│   ├── app6_note.py            🔧 补充 consumption_data
│   └── app7_customer.py        🔧 补充 objective_data
```

### 2.2 数据获取函数设计

#### `member_data.py` — 客户数据获取（应用 3/6/7 共用）

```python
async def fetch_member_consumption_data(
    site_id: int, member_id: int, months: int = 3
) -> dict:
    """获取客户近 N 个月消费数据（DWD+DWS 全维度）。

    返回结构对应 P5 spec 中 main_data：
    - consumption_records: 台桌结账 + 商城订单明细列表
    - member_cards: 会员卡明细
    - card_balance_total: 储值卡余额合计
    - stored_value_balance_total: 储值余额合计
    - expected_visit_date: 预计到店日期
    - days_since_last_visit: 距上次到店天数
    """
```

数据源（全部通过 FDW）：
- `fdw_etl.v_dwd_settlement_head` + `fdw_etl.v_dwd_table_fee_log`（台桌结账）
- `fdw_etl.v_dwd_store_goods_sale`（商城订单）
- `fdw_etl.v_dwd_recharge_order`（充值记录）
- `fdw_etl.v_dim_member_card_account`（会员卡明细）
- `fdw_etl.v_dws_member_consumption_summary`（消费汇总 + 余额）
- `fdw_etl.v_dws_member_visit_detail`（到店明细 → 计算预计到店日期）

> ⚠️ 金额口径：使用 `items_sum`（= table_charge_money + goods_money + assistant_pd_money + assistant_cx_money + electricity_money），禁止 `consume_money`
> ⚠️ 会员字段断档（DQ-6/DQ-7）：member_phone/member_name 通过 member_id JOIN dim_member

#### `assistant_data.py` — 助教数据获取（应用 4/5 共用）

```python
async def fetch_assistant_info(
    site_id: int, assistant_id: int
) -> dict:
    """获取助教基本信息（花名、级别、工龄等）。"""

async def fetch_service_history(
    site_id: int, assistant_id: int, member_id: int, months: int = 3
) -> list[dict]:
    """获取助教服务该客户的历史记录。"""
```

数据源：
- `fdw_etl.v_dim_assistant`（基本信息）
- `fdw_etl.v_dwd_assistant_service_log`（服务记录，按助教+客户筛选）
- `fdw_etl.v_dws_member_assistant_relation_index`（关系指数）
- `fdw_etl.v_dws_member_assistant_intimacy`（亲密度）

#### `page_context.py` — 页面上下文文本化（应用 1 专用）

```python
async def build_page_text(
    source_page: str, context_data: dict, site_id: int
) -> str:
    """将页面数据转换为 AI 可读的结构化文本。

    根据 source_page 类型，从数据库获取对应数据并格式化。
    """
```

支持的页面类型及数据获取：
| source_page | 数据获取 | 文本化内容 |
|-------------|---------|-----------|
| `task-detail` | coach_tasks + member + notes + ai_cache | 任务信息 + 客户信息 + 备注 + AI 分析 |
| `customer-detail` | member + consumption + clues | 客户全信息 + 消费记录 + 维客线索 |
| `coach-detail` | assistant + tasks + notes | 助教信息 + 任务统计 + 备注 |
| `board-finance` | finance DWS 汇总 | 财务数据摘要 |
| `board-customer` | 当前筛选维度 top 列表 | 客户排名摘要 |
| `board-coach` | 当前筛选维度排名 | 助教排名摘要 |
| `performance` | salary_calc + daily_detail | 绩效数据摘要 |
| `my-profile` | user info + assistant binding | 个人信息摘要 |

### 2.3 数据库连接模式

- 所有 FDW 查询通过 `get_etl_readonly_connection(site_id)` 或 `fdw_etl.*` 视图
- 业务库查询通过 `get_connection()`
- 查询前必须 `SET LOCAL app.current_site_id`（RLS 隔离）

---

## 三、各应用 Prompt 细化详细设计

### 3.1 应用 3：客户数据维客线索分析

`build_prompt()` 需要拼接的完整 JSON 结构（参考 P5 spec）：

```json
{
  "current_time": "2026-03-08 14:30:25",
  "member_nickname": "客户昵称",
  "main_data": {
    "consumption_records": [
      {
        "settle_date": "2026-03-05",
        "settle_type": 1,
        "items_sum": 280.00,
        "table_charge_money": 180.00,
        "assistant_pd_money": 80.00,
        "assistant_cx_money": 0,
        "goods_money": 20.00,
        "room_name": "VIP-3",
        "duration_minutes": 120,
        "assistant_names": ["张助教"]
      }
    ],
    "member_cards": [
      {"card_type": "金卡", "balance": 1500.00, "gift_balance": 200.00}
    ],
    "card_balance_total": 1700.00,
    "stored_value_balance_total": 1700.00,
    "expected_visit_date": "2026-03-10",
    "days_since_last_visit": 15
  },
  "reference": {
    "app6_clues": [...],
    "app8_history": [
      {"generated_at": "2026-03-01", "clues": [...]},
      {"generated_at": "2026-02-15", "clues": [...]}
    ]
  }
}
```

实现要点：
- `consumption_records` 从 `v_dwd_settlement_head` + `v_dwd_table_fee_log` 获取，settle_type IN (1,3)
- 金额字段逐项拆分（table_charge_money / assistant_pd_money / assistant_cx_money / goods_money），不使用 consume_money
- `expected_visit_date` 从 `v_dws_member_visit_detail` 的到店间隔推算
- reference 从 `ai_cache` 获取 app6 + app8 最近 2 套历史

### 3.2 应用 4：关系分析/任务建议

`build_prompt()` 需要拼接的完整 JSON 结构：

```json
{
  "current_time": "2026-03-08 14:30:25",
  "assistant_info": {
    "nickname": "张助教",
    "level": "高级",
    "hire_date": "2024-06-01",
    "tenure_months": 21,
    "monthly_customers": 45,
    "performance_tier": "A档"
  },
  "service_history": [
    {
      "service_date": "2026-03-01",
      "duration_minutes": 90,
      "items_sum": 250.00,
      "room_name": "VIP-3",
      "is_pd": true
    }
  ],
  "task_assignment_basis": "优先召回",
  "customer_data": {
    "system_data": { /* 同应用 3 的 main_data */ },
    "notes": [
      {"recorded_by": "张助教", "content": "客户喜欢打斯诺克", "created_at": "2026-02-28"}
    ]
  },
  "reference": {
    "app8_current": {...},
    "app8_history": [...]
  }
}
```

实现要点：
- `assistant_info` 从 `v_dim_assistant` + `v_dws_assistant_salary_calc` 获取
- `service_history` 从 `v_dwd_assistant_service_log` 按助教+客户筛选
- `customer_data.system_data` 复用 `member_data.fetch_member_consumption_data()`
- `customer_data.notes` 从 `biz.notes` 获取该客户的全部备注
- reference 从 `ai_cache` 获取 app8 最新 + 最近 2 套历史

### 3.3 应用 5：话术参考

结构与应用 4 基本一致，额外增加 `task_suggestion` 字段（应用 4 的返回结果）。

实现要点：
- 复用应用 4 的 `assistant_info` + `service_history` + `customer_data` 获取逻辑
- `task_suggestion` 从应用 4 的 `run()` 返回值传入（调用链：应用 4 → 应用 5）

### 3.4 应用 6：备注分析

```json
{
  "current_time": "2026-03-08 14:30:25",
  "current_note": {
    "content": "客户说下周要带朋友来打球",
    "recorded_by": "张助教",
    "created_at": "2026-03-08 14:25:00"
  },
  "reference": {
    "member_nickname": "王先生",
    "consumption_data": { /* 同应用 3 的 main_data */ },
    "all_notes": [...],
    "app3_clues": [...],
    "app8_history": [...]
  }
}
```

实现要点：
- `current_note` 从触发上下文获取（备注提交事件传入）
- `consumption_data` 复用 `member_data.fetch_member_consumption_data()`
- `all_notes` 从 `biz.notes` 获取所有助教对该客户的全部备注
- reference 从 `ai_cache` 获取 app3 + app8 历史

### 3.5 应用 7：客户分析

```json
{
  "current_time": "2026-03-08 14:30:25",
  "member_id": 12345,
  "member_nickname": "王先生",
  "objective_data": { /* 同应用 3 的 main_data */ },
  "subjective_data": {
    "notes": [
      {"recorded_by": "张助教", "content": "...", "created_at": "..."}
    ]
  },
  "reference": {
    "app8_current": {...},
    "app8_history": [...]
  }
}
```

实现要点：
- `objective_data` 复用 `member_data.fetch_member_consumption_data()`
- `subjective_data.notes` 从 `biz.notes` 获取全部备注，标注创建者
- 主观信息需在 Prompt 中标注【来源：XXX，请甄别信息真实性】
- reference 从 `ai_cache` 获取 app8 最新 + 最近 2 套历史

### 3.6 应用 1：biz_params 权限隔离（已实现，需确认）

> ✅ RNS1.4 已在 `app1_chat.py` 的 `_build_system_prompt()` 中实现 biz_params 拼接。

应用 1 通过 `biz_params.user_prompt_params` 向百炼 AI 传入用户身份信息，实现数据查询隔离：

```python
system_content = {
    "task": "你是台球门店的 AI 助手...",
    "biz_params": {
        "user_prompt_params": {
            "User_ID": str(user_id),
            "Role": role,        # "助教" | "管理者"
            "Nickname": nickname,
        },
    },
}
```

权限控制规则（百炼平台侧执行）：
- 助教身份：仅允许查询与该助教相关的数据，禁止查询其他助教业绩/工资/客户关系等敏感数据，禁止查询门店级财务数据
- 管理者身份：可查询该门店下所有数据
- 所有查询必须包含 `site_id` 过滤

NS2 需确认事项：
- 前端调用 `POST /api/chat/conversations/:id/messages` 时，是否正确传入 `user_id`/`role`/`nickname`
- 后端从 JWT token 中提取用户信息并传入 `_build_system_prompt()`，确保不可伪造

### 3.7 应用 1：页面上下文传递与文本化

#### P5 原始设计 vs NS2 实际方案

P5 PRD 定义应用 1 首条 Prompt 包含三个字段：

| 字段 | P5 定义 | 含义 |
|------|---------|------|
| `source_page` | 来源页面标识 | 如 `task-detail`、`board-finance` |
| `page_context` | 页面上下文摘要 | 结构化数据，后端可从 DB 获取 |
| `screen_content` | 用户当前屏幕可见内容的文本化描述 | 需前端传入当前视口内容 |

AI需求2.md 原文对应：
- `page_context` → "用户正在查看页面内容：页面上下文（来源页面+内容摘要）"
- `screen_content` → "用户页面视野内容：当前屏幕内容"

#### `screen_content` 的设计决策

> ⚠️ **设计决策（NS2 与 P5 的差异点）**：NS2 不要求前端传入 `screen_content`，改由后端根据 `contextType` + `contextId` + 筛选参数自动查询数据库，生成等效的页面上下文文本。

**决策理由：**
1. 微信小程序前端难以将当前屏幕 DOM 序列化为结构化文本（无 `innerText` 等 Web API）
2. RNS1.4 已实现 `contextType`/`contextId` 参数机制，后端可据此精确获取页面数据
3. 后端自动查询可获取比前端视口更完整的上下文（如关联的 AI 分析、历史备注等）
4. 看板类页面通过额外传入筛选参数（`timeDimension`、`dimension` 等），后端可还原用户当前视图的数据范围

**等效覆盖说明：**
- P5 的 `page_context`（结构化数据摘要）→ NS2 通过 `build_page_text()` 从 DB 获取，完全覆盖
- P5 的 `screen_content`（屏幕可见内容）→ NS2 通过 `contextType` + 筛选参数推断用户当前视图，近似覆盖
- 对于详情类页面（task-detail、customer-detail 等），后端获取的数据与用户屏幕内容高度一致
- 对于看板类页面，通过筛选参数还原当前视图的数据维度和范围

**局限性：**
- 无法获取用户滚动位置（如长列表中用户正在看第几条）
- 无法获取用户输入中但未提交的内容
- 看板页面若前端未传筛选参数，使用默认值可能与用户实际视图不一致

#### 实际方案：后端根据 contextType 自动获取页面上下文

与 RNS1.4 已有的 `contextType`/`contextId` 参数兼容：

```
前端传入 contextType="task-detail" + contextId="12345"（+ 看板类页面的筛选参数）
→ 后端 build_page_text("task-detail", 12345, site_id, filters?)
→ 后端从数据库获取任务详情、客户信息、备注、AI 分析等
→ 格式化为结构化中文文本（同时覆盖 page_context 和 screen_content 的信息需求）
→ 拼接为首条 user message 的 page_context 字段
```

#### contextType 映射表

| 前端 contextType | 后端数据获取 | 文本化内容 |
|-----------------|-------------|-----------|
| `task-detail` | coach_tasks + member + notes + ai_cache | 任务信息 + 客户信息 + 备注 + AI 分析 |
| `customer-detail` | member + consumption + clues | 客户全信息 + 消费记录 + 维客线索 |
| `coach-detail` | assistant + tasks + notes | 助教信息 + 任务统计 + 备注 |
| `board-finance` | finance DWS 汇总（按当前筛选条件） | 财务数据摘要 |
| `board-customer` | 当前筛选维度 top 列表 | 客户排名摘要 |
| `board-coach` | 当前筛选维度排名 | 助教排名摘要 |
| `performance` | salary_calc + daily_detail | 绩效数据摘要 |
| `my-profile` | user info + assistant binding | 个人信息摘要 |
| `task-list` | 长按的任务摘要 + 客户-助教关系 | 任务概要 |
| `customer-service-records` | 服务记录列表 | 服务记录摘要 |

#### 前端需要补充的参数

当前前端仅传 `contextType` + `contextId`，对于看板类页面需要额外传入筛选条件：

| contextType | 必传参数 | 可选参数 |
|-------------|---------|---------|
| `task-detail` | contextId（taskId） | — |
| `customer-detail` | contextId（memberId） | — |
| `coach-detail` | contextId（assistantId） | — |
| `board-finance` | — | `timeDimension`（时间维度）、`areaFilter`（区域筛选） |
| `board-customer` | — | `dimension`（排序维度）、`typeFilter`（类型筛选） |
| `board-coach` | — | `dimension`、`projectFilter`、`timeDimension` |
| `performance` | — | `timeDimension` |
| `task-list` | contextId（taskId） | — |
| `my-profile` | — | — |

#### 实现要点

- 每个页面类型对应一个文本化函数，在 `data_fetchers/page_context.py` 中实现
- 文本化输出为结构化中文描述（非 JSON），便于 AI 理解上下文
- 数据量控制：每个页面上下文不超过 2000 字符，避免 token 浪费
- 敏感信息脱敏：不传入 member_phone 等断档字段
- 看板类页面：若前端未传筛选条件，使用默认值（如 board-finance 默认"本月"）
- 错误降级：数据获取失败时，page_context 传入"页面上下文获取失败"，不阻断对话

---

## 四、数据库审查

### 4.1 数据获取涉及的表

| 数据获取函数 | 涉及表 | 连接方式 |
|-------------|--------|---------|
| `fetch_member_consumption_data` | v_dwd_settlement_head, v_dwd_table_fee_log, v_dwd_store_goods_sale, v_dwd_recharge_order, v_dim_member_card_account, v_dws_member_consumption_summary, v_dws_member_visit_detail | FDW |
| `fetch_assistant_info` | v_dim_assistant, v_dws_assistant_salary_calc | FDW |
| `fetch_service_history` | v_dwd_assistant_service_log, v_dws_member_assistant_relation_index, v_dws_member_assistant_intimacy | FDW |
| `build_page_text` | 以上全部 + biz.coach_tasks, biz.notes, biz.ai_cache, public.member_retention_clue | FDW + 业务库 |

### 4.2 无需新建表

本 SPEC 不需要新建数据库表。所有数据获取基于 NS1 已建立的 FDW 映射和 P4/P5-A 已建立的业务表。

### 4.3 性能考虑

- `fetch_member_consumption_data` 涉及 7 张 FDW 表查询，建议：
  - 消费记录限制最近 3 个月（WHERE settle_date >= NOW() - INTERVAL '3 months'）
  - 单次查询最多返回 100 条记录
  - 考虑使用 NS1 中建议的 `biz.task_detail_cache` 缓存聚合结果
- 页面文本化（应用 1）需要实时获取数据，建议设置 5 秒查询超时

---

## 五、调用链与数据流

### 5.1 消费事件链

```
新结算单到达
  → 应用 3.build_prompt(member_data) → 百炼 API → ai_cache
    → 应用 8.run() → member_retention_clue
      → 应用 7.build_prompt(member_data + notes) → 百炼 API → ai_cache
      → 应用 4.build_prompt(assistant_data + member_data) → 百炼 API → ai_cache
        → 应用 5.build_prompt(app4_result) → 百炼 API → ai_cache
```

### 5.2 备注提交事件链

```
备注提交
  → 应用 6.build_prompt(note + member_data) → 百炼 API → ai_cache
    → 应用 8.run() → member_retention_clue
```

### 5.3 应用 1 对话流

```
用户点击 AI 入口
  → 前端传入 contextType + contextId（+ 看板类页面的筛选参数）
  → 后端 build_page_text(contextType, contextId, site_id, filters?)
  → 从数据库获取对应页面数据，格式化为结构化中文文本
  → 文本同时覆盖 P5 定义的 page_context（数据摘要）和 screen_content（屏幕内容）
  → 拼接为首条 user message 的 page_context 字段
  → 注入 biz_params（User_ID/Role/Nickname）到 system prompt
  → SSE 流式调用百炼 API
```

> ⚠️ 与 P5 PRD 原始设计的差异（详见 3.7 设计决策）：
> P5 设计为前端传入 `source_page` + `page_context` + `screen_content` 三个独立字段，
> NS2 采用后端自动查询方案，前端仅传入 `contextType` + `contextId` + 筛选参数，
> 后端通过 `build_page_text()` 生成合并的页面上下文文本，等效覆盖 `page_context` 和 `screen_content` 的信息需求。

---

## 六、参考文档

| 文档 | 路径 | 用途 |
|------|------|------|
| P5 AI 集成 spec | `docs/prd/specs/P5-miniapp-ai-integration.md` | Prompt JSON 结构定义、调用链时序 |
| AI 应用 Prompt | `docs/prd/ai-app-prompts.md` | 8 个应用的 System Prompt、biz_params 定义 |
| AI 需求文档 | `docs/prd/AI需求2.md` | 8 个应用的详细需求表、传参格式 |
| AI 应用→页面映射 | `docs/reports/2026-03-20__ai_app_page_mapping.md` | 各应用输出内容在哪个页面的什么位置展示 |
| API 契约 | `docs/miniprogram-dev/API-contract.md` | 接口响应格式（决定数据结构） |
| DWD-DOC 标杆 | `docs/reports/DWD-DOC/` | 金额口径、字段语义权威参考 |
| AI 应用骨架代码 | `apps/backend/app/ai/apps/app*.py` | 当前骨架实现（含 biz_params 已实现） |
| 百炼客户端 | `apps/backend/app/ai/bailian_client.py` | API 调用封装 |
| 缓存服务 | `apps/backend/app/ai/cache_service.py` | ai_cache 读写 |
| NS1 接口设计 | `docs/prd/Neo_Specs/NS1-xcx-backend-api.md` | 后端数据结构参考 |
| RNS1.4 任务计划 | `.kiro/specs/rns1-chat-integration/tasks.md` | 当前 chat 模块实施进度 |

---

## 七、预审查清单（已确认）

> 以下问题已在 SPEC 细化阶段（requirements.md / design.md）中逐一确认。

### 7.1 数据结构

1. **消费记录字段范围**：每条记录包含 `settle_date`、`settle_type`、`items_sum`、`table_charge_money`、`assistant_pd_money`、`assistant_cx_money`、`goods_money`、`room_name`、`duration_minutes`、`assistant_names` 共 10 个字段。不包含折扣明细和支付方式明细（token 预算有限，这些字段对 AI 分析价值低）。
2. **服务记录字段范围**：每条记录包含 `service_date`、`duration_minutes`、`items_sum`、`room_name`、`is_pd`（是否陪打）。不包含台桌类型和客户评价。
3. **备注内容截断**：单条备注最大 500 字符，超出截断并附加"…（已截断）"标记。备注总数最多 50 条（按 `created_at DESC`）。
4. **会员卡明细粒度**：仅包含 `card_type`（卡类型）、`balance`（余额）、`gift_balance`（赠送余额），不含卡号、开卡日期、有效期。

### 7.2 Prompt 优化

5. **Token 预算**：应用 3/4/5/6/7 的 system message content ≤ 8000 字符（约 4000 token）；应用 1 的 system prompt（含页面上下文）≤ 4000 字符（约 2000 token）。
6. **数据时间窗口**：默认近 3 个月，通过 `months` 参数可配置。各应用统一使用 3 个月窗口。
7. **空数据处理**：使用明确的空状态提示词（如"该客户暂无消费记录，请基于已有信息分析"），不传入空数据不做说明。`reference` 无历史数据时设为空对象并标注"暂无历史线索"。

### 7.3 页面文本化（应用 1）

8. **文本化格式**：输出为结构化中文描述文本（分段标题 + 缩进），非 JSON。中文描述更便于 AI 理解上下文语义。
9. **数据量控制**：每个页面上下文统一 ≤ 2000 字符，不按页面类型动态调整。超出截断并标注。
10. **实时性要求**：实时获取最新数据（不使用缓存），设置 5 秒 FDW 查询超时。数据获取失败时返回降级文本，不阻断对话。

### 7.4 性能与安全

11. **FDW 查询并发**：支持。使用 `asyncio.gather(return_exceptions=True)` 并发执行多个数据获取函数，部分失败不阻断。同一连接上的多个 FDW 查询串行执行（共享 RLS 设置）。
12. **数据脱敏**：`member_phone` 已断档不传。会员信息通过 `member_id JOIN v_dim_member (scd2_is_current=1)` 获取昵称，不传入手机号等敏感字段。`build_page_text()` 输出中不包含 `member_phone`。
13. **错误降级**：部分失败继续。失败部分使用默认空值（空数组/空对象），在 Prompt 中标注"该部分数据获取失败"，继续生成 Prompt 并调用百炼 API。确保 Prompt JSON 不含 `null`。

---

## 八、任务清单（已细化，详见 SPEC tasks.md）

> SPEC 三件套已完成：`requirements.md`（14 条需求）→ `design.md`（17 个正确性属性）→ `tasks.md`（17 个顶层任务）。
> 以下为 PRD 级任务概览，详细实施步骤见 `.kiro/specs/ai-prompt-refinement/tasks.md`。

### Batch A：共享数据获取层（tasks 1-4）
- [ ] T1：创建 `data_fetchers/` 模块骨架（`__init__.py` + 3 个子模块）
- [ ] T2：实现 `member_data.py`（`fetch_member_consumption_data` + `fetch_member_notes`）
- [ ] T3：实现 `assistant_data.py`（`fetch_assistant_info` + `fetch_service_history`）
- [ ] T4：检查点 — 数据获取层完成

### Batch B：Prompt 拼接实现（tasks 5-11）
- [ ] T5：完善 `app3_clue.py` 的 `build_prompt()`（async + 真实数据）
- [ ] T6：完善 `app4_analysis.py` 的 `build_prompt()`（asyncio.gather 并发获取）
- [ ] T7：完善 `app5_tactics.py` 的 `build_prompt()`（复用 App4 + task_suggestion）
- [ ] T8：完善 `app6_note.py` 的 `build_prompt()`（消费数据 + 全部备注）
- [ ] T9：完善 `app7_customer.py` 的 `build_prompt()`（客观+主观数据，标注来源）
- [ ] T10：检查点 — 应用 3-7 完成
- [ ] T11：实现错误降级与 Token 预算控制

### Batch C：应用 1 页面文本化 + 前端配合（tasks 12-15）
- [ ] T12：实现 `page_context.py`（10 种页面类型文本化）
- [ ] T13：集成 `app1_chat.py` 的 `_build_page_context()` 调用 `build_page_text()`
- [ ] T14：检查点 — 页面上下文完成
- [ ] T15：前端看板筛选参数传递（小程序 ai-float-button + chat 页面参数解析）

### Batch D：集成联调（tasks 16-17）
- [ ] T16：集成连线（dispatcher await 正确性 + 端到端验证）
- [ ] T17：最终检查点

### 属性测试（可选，标记 * 的子任务）
- 17 个正确性属性（P1-P17）对应 Hypothesis 属性测试
- 测试文件：`tests/test_data_fetchers/`、`tests/test_ai_apps/`
