# P4 · Analysis · 数据分析层 Prompt > **用途**:分析层代码落地 · 基于 `_merged.json` 搭建可供章节模块调用的查询 API。 > **阶段**:Phase 5 · Analysis(前半段,渲染前) > **预计耗时**:30-60 分钟(拷贝 + 定制) > **输入**:`_merged.json` + `2.VOC 深度思路.md` 的 H1-H8 > **输出**: > - `scripts/tools/<品类>-analyze.js`(核心 API 层) > - 一次 `node <品类>-analyze.js` self-test 通过 --- ## 🎬 角色设定 你是一位**数据建模 + 查询层工程师**。核心信念: - **一个 API 搞定所有章节**:`getEvidence(items, opts)` 足以覆盖 90% 取证需求 - **筛选链式、参数化**:平台 / 关键词 / 假设 / 情绪 / 内容正则 / 最小字数 / 最小点赞 / seed 乱序 - **内容实质性判断**:过滤纯表情、水字、广告水军 - **种子乱序**:避免同一条证据被多个章节反复引用 --- ## 🎯 本阶段目标 1. 从 `templates/analyze.template.js` 复制到 `scripts/tools/<品类>-analyze.js` 2. 定制以下 3 项: - `HYPOTHESES` 字典(按 H1-H8 填) - `isSubstantive()`(根据品类调整最小字符数) - `inferTags()`(品类特征标签推断,可选) 3. 写 1 段 self-test 验证: - `loadMerged()` 读得到文件 - `getEvidence()` 能按 H1-H8 分别取到数据 - 每个 H 假设 top 3 都是内容 ≥ 25 字的实质性 VOC --- ## 📦 核心 API 清单 ### `loadMerged()` 读 `docs/<品类>/raw/_merged.json`。返回 `{ meta, items }`。 ### `getEvidence(items, opts)` **参数**: ```ts { keyword?: string; // 单 kw 筛选 product?: string; // 按本品 / 竞品 SKU platform?: 'xhs'|'douyin'|...; // 平台 contentMatch?: RegExp|string; // 内容正则 hypotheses?: string[]; // 必含 H 标签(AND 关系) sentiment?: 'positive'|'negative'|'neutral'; minChars?: number; // 默认 10 minLikes?: number; // 默认 0 top?: number; // 默认 5 seed?: number; // 乱序种子 } ``` **返回**:`Array`(已去重、已筛实质、已按 likes 排序、已按 seed 乱序) ### 辅助工具 - `filterByPlatform` / `filterByHypothesis` / `filterByProduct` / `filterByKeyword` / `filterBySentiment` - `filterByContentMatch(items, regex)` - `isSubstantive(content, minChars)` · 剥表情后判断字符数 - `shuffleWithSeed(arr, seed)` · 可重复种子乱序 - `statsByPlatform / Hypothesis / Product / Sentiment` · 聚合统计 --- ## 🔬 self-test 代码(必写) 在 `<品类>-analyze.js` 底部加一段: ```js if (require.main === module) { const data = loadMerged(); console.log(`\n=== <品类> analyze · self-test ===`); console.log(`总量: ${data.items.length} | 平台: ${Object.keys(data.meta?.platforms||{}).join('+')}`); // 1. 每个 H 假设 top 3 for (let i = 1; i <= 8; i++) { const h = 'H' + i; const hits = getEvidence(data.items, { hypotheses: [h], top: 3, seed: i }); console.log(`\n▶ ${h} · ${HYPOTHESES[h]} (${hits.length} hits)`); hits.forEach((it, k) => { console.log(` [${k+1}] ${it.platform} · ${it.nickname} · ♥${it.likes}`); console.log(` ${String(it.content||'').slice(0,100)}`); }); } // 2. 平台 × 情绪矩阵 console.log(`\n▶ 平台 × 情绪分布`); for (const p of ['xhs','douyin']) { const pItems = filterByPlatform(data.items, p); const st = statsBySentiment(pItems); console.log(` ${p}: ${JSON.stringify(st)}`); } } ``` 跑 `node scripts/tools/<品类>-analyze.js`,期待输出: - 每个 H 至少 3 条 hit,且每条内容 ≥ 25 字 - 每个平台都能看到 positive / negative / neutral 分布 --- ## 🧪 定制点 1 · `HYPOTHESES` 字典 按你在 `2.VOC 深度思路.md` 里定的假设,直接抄过来: ```js const HYPOTHESES = { H1: '妈咪爱冷链 + 处方门槛', H2: '亿活金双岐高价依赖', H3: '喂药依从性', H4: '乳酸菌素片 OTC 信任', H5: '家长社交焦虑', H6: '分龄 × 分场景无人地带', H7: '入园新人群', H8: '卡通 IP × 咀嚼片', }; ``` --- ## 🧪 定制点 2 · `isSubstantive` 默认 `minChars=10`,但: - **婴幼儿品类**:很多评论都是短「有用」「没用」 → 放宽到 6-8 - **送礼品类**:很多评论只是 emoji 和贴图 → 拔高到 15 - **电商评论**:存在「未收货 / 还没拆 / 等我反馈」这类半评论 → 加一个黑名单 ```js function isSubstantive(content, minChars = 10) { const s = String(content || '').trim(); if (s.length < minChars) return false; // 黑名单(可选) if (/^(还没|未收|等我|马上|物流|快递|包装|发货)/.test(s)) return false; const stripped = s .replace(/\[[^\]]+\]/g, '') .replace(/[\s\p{P}\p{Emoji_Presentation}\p{Extended_Pictographic}]/gu, ''); return stripped.length >= Math.max(4, Math.floor(minChars / 2)); } ``` --- ## 🧪 定制点 3 · `inferTags`(可选) 只有你计划在章节里用 `tags` 字段聚合时才需要。 --- ## 📐 章节取证常见 Pattern(chapter.template.js 里会用到) ```js // 1. 基础:单假设 top 4 const voc = LA.getEvidence(items, { hypotheses: ['H1'], minChars: 25, top: 4, seed: 7, }); // 2. 竞品 SKU 精准取证 const voc = LA.getEvidence(items, { keyword: '妈咪爱', contentMatch: /冷链|处方|医院/, top: 4, seed: 11, }); // 3. 负面情绪筛选 const negVoc = LA.getEvidence(items, { hypotheses: ['H3'], sentiment: 'negative', top: 3, seed: 13, }); // 4. 双假设交叉 const voc = LA.getEvidence(items, { hypotheses: ['H4', 'H5'], // AND 关系 top: 3, seed: 17, }); ``` **seed 取值建议**:每个子章用不同素数(7, 11, 13, 17, 19, 23, 29, ...)避免重复抽到同几条。 --- ## ✅ 退出门控 - [ ] `scripts/tools/<品类>-analyze.js` 存在且 `require` 无错 - [ ] self-test 通过:H1-H8 每条都能抽到 ≥ 3 条实质 VOC - [ ] `HYPOTHESES` 字典和 `2.VOC 深度思路.md` 完全一致 - [ ] 无 JS 语法错误(`node -c` 通过) - [ ] 可正常 `require('./ <品类>-analyze.js')` --- ## ⚠️ 常见失误 1. **忘了写 self-test**:写完代码不跑,一直到章节模块报错才回头调 → 浪费 2 小时 2. **HYPOTHESES 只写 key 不写 value**:后续 audit / 章节 hero 都缺标题 3. **`isSubstantive` 阈值没调**:婴幼儿品类用默认 10 字符 → 过滤掉 60% 真实短评 4. **`seed` 写死**:所有章节都用 `seed: 1` → 全报告引用前 5 条 VOC,剩下的都被浪费 5. **`getEvidence` 没做去重**:同一条高赞 VOC 出现在 5 个子章 → 看起来数据匮乏 --- ## 📌 引用参考 - Phase 5 详细文档:`../05-phase5-analysis-rendering.md` - 代码骨架:`../templates/analyze.template.js` - 案例参考:`e:\workspace\openclaw-voc-skill\scripts\tools\lactic-analyze.js`