# 全局需求热力图实施计划

> **执行方式：** 当前会话内逐项实现和验证，不修改 `visualization/` 之外的文件。

**目标：** 将 00 页面改造成读取真实全局树数据的分层冰柱热力图，并批量输出具有外部搜索意义的需求、0～1 热度 Score、可信度、粒度和可追溯 Reason。

**架构：** 保留 `全局分类树.html` 作为唯一数据源。浏览器端解析其 `export-data`，分别完成数据标准化、子树布局、热度计算、需求识别、Reason 生成和交互渲染。主图使用 Canvas 绘制全量冰柱色块，右侧使用 HTML 输出需求结果。

**技术栈：** 原生 HTML、CSS、JavaScript、Canvas 2D、本地 Python 静态服务。

## 全局约束

- 所有项目文件操作仅限 `visualization/`。
- 不修改原始 `visualization/全局分类树.html`。
- 后验指标保持“真实 ROV / 真实 VOV”名称。
- 不展示 P/H 或百分位术语。
- 热度 Score 和可信度统一为 `0～1`。
- 数据完全缺失与 Score 0 必须区分。
- 宽泛、主题级、具体、长尾只描述需求粒度，不评价好坏。
- “战争”等具有外部搜索意义的宽泛词是需求。
- “表象”等只承担分类结构作用、无法独立表达搜索意图的词不是需求。
- Reason 必须区分“数据事实”和“模型解释”。

---

### 任务 1：重构页面信息架构

**文件：**

- 修改：`visualization/pages/全局分类树-需求热度与关系分析.html`
- 修改：`visualization/pages/global-tree-analysis.css`

**产出：**

- 顶部全局概览和单一热度图例。
- 左侧全量冰柱热力图。
- 右侧批量需求输出区。
- 下方选中节点的路径、数据事实、模型 Reason 和多个挂靠点。

**验证：**

- 页面不出现“综合观察”“先验热点”“P80”“H80”等旧术语。
- 首屏主视觉与 05 页面保持蓝色结构、橙色需求、紫色证据的视觉语言。

### 任务 2：实现真实数据的 0～1 Score

**文件：**

- 修改：`visualization/pages/global-tree-analysis.js`

**接口：**

- `buildMetricModel(nodes)`：生成六项指标的稳健归一化参数。
- `computeNodeScores(node, metricModel)`：返回 `{ heatScore, confidence, dataState, priorScore, posteriorScore }`。
- `computeSubtreeScore(node)`：基于自身和子树信号生成全局展示 Score。

**规则：**

- 六项原始指标先使用 `log1p` 抑制极端值，再按全局 95% 分位截断并归一化到 `0～1`。
- 四项先验保留独立贡献；真实 ROV / VOV 作为后验校正。
- 缺少真实 ROV / VOV 时降低可信度，但不把需求判为 0。
- 六项指标全部缺失或为 0 时标记 `dataState: "missing"`。

**验证：**

- 所有非缺失 Score 位于 `[0, 1]`。
- “真实 ROV / VOV”有样本的节点比同等先验但无样本的节点具有更高可信度。

### 任务 3：实现需求识别和粒度判断

**文件：**

- 修改：`visualization/pages/global-tree-analysis.js`

**接口：**

- `classifySearchIntent(candidate)`：返回 `{ isDemand, granularity, reasonCode }`。
- `extractDemandCandidates(nodes, elements)`：从分类节点和元素生成候选需求。
- `mergeDemandCandidates(candidates)`：合并同名需求并保留多个挂靠点。

**规则：**

- 优先采用上游元素名称，其次采用能够独立表达搜索意义的分类节点名称。
- 分类根词、结构词和孤立平台动作词进入排除词表。
- “战争”保留为宽泛需求。
- 同名需求合并为一个需求实体，并保留所有分类路径。
- 粒度通过词语范围、路径深度和对象/事件限定程度判断为宽泛、主题级、具体或长尾。

**验证：**

- “战争”输出为宽泛需求。
- “家庭清洁”输出为主题级或具体需求。
- “驱蚊技巧”即使位于冷区也输出为探索需求。
- “表象”不进入需求结果。

### 任务 4：实现数据驱动的 Reason

**文件：**

- 修改：`visualization/pages/global-tree-analysis.js`

**接口：**

- `buildFactReason(demand)`：只描述上游可验证事实。
- `buildModelReason(demand)`：描述搜索意义、粒度、数据缺口和判断结论。

**规则：**

- 数据事实列出挂靠路径、主要热度来源、真实 ROV / VOV 覆盖、元素原始 reason。
- 模型解释不得声称上游没有提供的事实。
- 数据缺失必须表述为“待验证”或“数据覆盖不足”，不能表述为负反馈。

**验证：**

- 每条需求同时具有 `factReason` 和 `modelReason`。
- 页面对两类 Reason 使用不同标题和颜色。

### 任务 5：实现全量冰柱布局与交互

**文件：**

- 修改：`visualization/pages/global-tree-analysis.js`
- 修改：`visualization/pages/global-tree-analysis.css`

**接口：**

- `layoutIcicle(roots)`：按子树叶节点数量分配纵向面积，按深度分配横向列。
- `drawIcicle()`：一次绘制全部节点色块。
- `focusBranch(node)`：点击节点后放大该分支。
- `resetGlobalView()`：回到完整全局视图。

**交互：**

- 全局态始终保留冷区。
- 悬停显示名称、Score、可信度和数据状态。
- 点击放大分支并同步筛选右侧需求。
- 搜索词可以定位分类节点或需求。

**验证：**

- 2205 个节点全部拥有布局坐标。
- 全局截图中同时可见红橙热点和蓝白冷区。
- 不绘制全量父子曲线。

### 任务 6：接入服务并完成浏览器验证

**文件：**

- 修改：`visualization/README.md`
- 保持：`visualization/service/serve.py` 中 00 页面入口。

**验证命令：**

```bash
python3 -m py_compile visualization/service/serve.py
node --check visualization/pages/global-tree-analysis.js
curl -fsS http://localhost:8765/pages/global-tree-analysis.js >/dev/null
```

使用本地 Chrome 无头渲染桌面截图，并检查：

- 2205 个分类节点和 3008 条元素成功加载。
- 页面无 P/H、百分位和旧模式按钮。
- 页面出现热度 Score、可信度、宽泛需求、数据事实和模型 Reason。
- “战争”进入需求结果，“表象”只保留为分类节点。
- 全局态热区与冷区同时清晰可见。
