| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677 |
- #!/usr/bin/env node
- const {
- parseCasesFromSheets,
- buildFewshotCases,
- buildInsights,
- buildHistoryCsv,
- buildRuleSuggestions,
- extractSignalTags
- } = require('./case-template-import');
- // 模拟 readXlsxRows 返回的 sheets 结构(含标题行、说明行、表头行、数据行)
- const SHEETS = [
- {
- name: '案例喂养',
- rows: [
- ['AI 选号 · 案例喂养模板(每单执行后回填,用于训练选号模型)'],
- ['说明:结果标签用于 few-shot/反向学习;关键原因请写实,越具体模型越准。'],
- ['案例ID', '品类', '投放目标', '目标人群', '品牌调性', '平台', '选中账号特征', '结果标签', '关键原因', '客户反馈', '执行数据'],
- ['如 CASE-2026-001', '美妆护肤/3C/母婴...', '种草转化/品牌曝光...', '如 20-28岁油痘肌女性', '硬核测评/生活化种草...', 'douyin/xiaohongshu...', '粉丝量/互动率/调性/报价 摘要', '爆文 / 达标 / 一般 / 翻车', '为什么成/败?', '确认/修改/否决及理由', '互动量/ROI/搜索提升等'],
- ['CASE-2026-001', '美妆护肤', '种草转化', '20-28岁油痘肌女性', '硬核测评', 'xiaohongshu', '18w粉/互动率3.5%/成分党/报价8k', '爆文', '赞藏比高+垂直度92%,与油皮人群高度契合', '客户确认续投', '单篇赞评藏1200,搜索占位TOP3'],
- ['CASE-2026-002', '美妆护肤', '种草转化', '20-28岁油痘肌女性', '硬核测评', 'douyin', '500w粉/互动率0.8%/搞笑/报价20w', '翻车', '粉丝画像匹配仅35%,互动率虚胖,内容疲劳', '客户否决', '无转化,评论区负面'],
- ['CASE-2026-003', '3C', '品牌曝光', '25-35岁男性', '硬核测评', 'douyin', '200w粉/互动率1.2%/科技测评/报价15w', '一般', '数据达标但人群略泛', '待二次确认', '阅读稳定']
- ]
- }
- ];
- function main() {
- const cases = parseCasesFromSheets(SHEETS);
- assert(cases.length === 3, '应解析出 3 条案例,跳过说明行');
- const positive = cases.filter(item => item.outcome === 'positive');
- const neutral = cases.filter(item => item.outcome === 'neutral');
- const negative = cases.filter(item => item.outcome === 'negative');
- assert(positive.length === 1, '爆文应为正样本');
- assert(neutral.length === 1, '一般应为中性样本');
- assert(negative.length === 1, '翻车应为负样本');
- assert(cases[0].platform === 'xiaohongshu', '平台应归一化为内部代码');
- assert(cases[0].platformLabel === '小红书', '平台中文标签应正确');
- assert(cases[1].platform === 'douyin', '抖音平台应归一化');
- assert(extractSignalTags('互动率虚胖,粉丝画像匹配仅35%').includes('数据虚胖'), '应识别数据虚胖信号');
- assert(extractSignalTags('垂直度92%,人群高度契合').includes('人群契合'), '应识别人群契合信号');
- const fewshot = buildFewshotCases(cases, 'case_template.xlsx');
- assert(fewshot.version === 1, 'few-shot 库应带版本号');
- assert(fewshot.cases.length === 3, 'few-shot 库应包含全部案例');
- assert(fewshot.cases[0].outcome === 'positive', 'few-shot 库应保留 outcome 方向');
- const insights = buildInsights(cases);
- assert(insights.includes('正样本共性'), '洞察应包含正样本段落');
- assert(insights.includes('负样本共性'), '洞察应包含负样本段落');
- assert(insights.includes('CASE-2026-002'), '洞察应包含翻车案例');
- const historyCsv = buildHistoryCsv(cases);
- assert(historyCsv.startsWith('brief编号'), 'history CSV 应以 HISTORY_HEADER 开头');
- assert(historyCsv.includes('CASE-2026-002') && historyCsv.includes('客户拒绝'), '负样本应映射为客户拒绝');
- assert(historyCsv.includes('粉丝画像匹配仅35%'), '负样本拒绝原因应写入 CSV');
- // 规则建议:两条负样本同属「互动率」信号才会触发建议,当前只有一条负样本,应返回空
- assert(buildRuleSuggestions(cases).length === 0, '样本不足时不应生成高频规则建议');
- console.log(JSON.stringify({
- ok: true,
- caseCount: cases.length,
- positive: positive.length,
- neutral: neutral.length,
- negative: negative.length,
- fewshotCases: fewshot.cases.length
- }, null, 2));
- }
- function assert(condition, message) {
- if (!condition) throw new Error(message);
- }
- if (require.main === module) main();
|