用途:分析层代码落地 · 基于
_merged.json搭建可供章节模块调用的查询 API。 阶段:Phase 5 · Analysis(前半段,渲染前) 预计耗时:30-60 分钟(拷贝 + 定制) 输入:_merged.json+2.VOC 深度思路.md的 H1-H8 输出:
scripts/tools/<品类>-analyze.js(核心 API 层)- 一次
node <品类>-analyze.jsself-test 通过
你是一位数据建模 + 查询层工程师。核心信念:
getEvidence(items, opts) 足以覆盖 90% 取证需求templates/analyze.template.js 复制到 scripts/tools/<品类>-analyze.jsHYPOTHESES 字典(按 H1-H8 填)isSubstantive()(根据品类调整最小字符数)inferTags()(品类特征标签推断,可选)loadMerged() 读得到文件getEvidence() 能按 H1-H8 分别取到数据loadMerged()读 docs/<品类>/raw/_merged.json。返回 { meta, items }。
getEvidence(items, opts)参数:
{
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<Item>(已去重、已筛实质、已按 likes 排序、已按 seed 乱序)
filterByPlatform / filterByHypothesis / filterByProduct / filterByKeyword / filterBySentimentfilterByContentMatch(items, regex)isSubstantive(content, minChars) · 剥表情后判断字符数shuffleWithSeed(arr, seed) · 可重复种子乱序statsByPlatform / Hypothesis / Product / Sentiment · 聚合统计在 <品类>-analyze.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,期待输出:
HYPOTHESES 字典按你在 2.VOC 深度思路.md 里定的假设,直接抄过来:
const HYPOTHESES = {
H1: '妈咪爱冷链 + 处方门槛',
H2: '亿活金双岐高价依赖',
H3: '喂药依从性',
H4: '乳酸菌素片 OTC 信任',
H5: '家长社交焦虑',
H6: '分龄 × 分场景无人地带',
H7: '入园新人群',
H8: '卡通 IP × 咀嚼片',
};
isSubstantive默认 minChars=10,但:
电商评论:存在「未收货 / 还没拆 / 等我反馈」这类半评论 → 加一个黑名单
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));
}
inferTags(可选)只有你计划在章节里用 tags 字段聚合时才需要。
// 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 无错HYPOTHESES 字典和 2.VOC 深度思路.md 完全一致node -c 通过)require('./ <品类>-analyze.js')isSubstantive 阈值没调:婴幼儿品类用默认 10 字符 → 过滤掉 60% 真实短评seed 写死:所有章节都用 seed: 1 → 全报告引用前 5 条 VOC,剩下的都被浪费getEvidence 没做去重:同一条高赞 VOC 出现在 5 个子章 → 看起来数据匮乏../05-phase5-analysis-rendering.md../templates/analyze.template.jse:\workspace\openclaw-voc-skill\scripts\tools\lactic-analyze.js