| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- #!/usr/bin/env node
- const fs = require('fs');
- const path = require('path');
- const HISTORY_HEADER = [
- 'brief编号',
- '项目名称',
- '类目',
- '客户原始Brief',
- '历史人工补号量基线',
- '参考账号或视频',
- '平台',
- '博主名称',
- '主页链接',
- '人工复核标签',
- '客户选择',
- '拒绝原因'
- ];
- const REVIEW_HEADER = [
- 'brief编号',
- '策略',
- '排名',
- '平台',
- '博主名称',
- '综合分',
- 'brief匹配分',
- '参考风格分',
- '主页证据分',
- '视觉质感分',
- '调性一致分',
- '证据加分',
- '证据风险扣分',
- '推荐理由',
- '风险提示',
- '主页链接',
- '人工复核标签',
- '客户选择',
- '归因类型',
- '反馈原因',
- '本轮人工补号量'
- ];
- function main() {
- const args = parseArgs(process.argv.slice(2));
- const output = path.resolve(args.output || path.join(__dirname, '..', 'outputs', `data-intake-pack-${Date.now()}`));
- fs.mkdirSync(output, { recursive: true });
- const historyCsv = path.join(output, 'history-data-template.csv');
- const reviewCsv = path.join(output, 'manual-review-template.csv');
- const readme = path.join(output, 'README.md');
- writeCsv(historyCsv, [
- HISTORY_HEADER,
- [
- 'brief-001',
- '示例项目',
- '母婴',
- '填写客户原始 Brief,不要写 token、报价明细或密钥。',
- '6',
- 'https://example.com/reference-video',
- '小红书',
- '示例博主',
- 'https://example.com/profile',
- '可直接发客户',
- '客户选中',
- ''
- ]
- ]);
- writeCsv(reviewCsv, [
- REVIEW_HEADER,
- [
- 'brief-001',
- 'result-first-broad',
- '1',
- '小红书',
- '示例博主',
- '86',
- '82',
- '78',
- '80',
- '79',
- '81',
- '4',
- '0',
- '命中 Brief 人群和参考风格,可给商务复核。',
- '需确认报价有效期、档期和近 30 天竞品合作。',
- 'https://example.com/profile',
- '商务复核',
- '待客户反馈',
- '',
- '',
- '3'
- ]
- ]);
- fs.writeFileSync(readme, withBom(renderReadme()), 'utf8');
- console.log(JSON.stringify({ output, files: { historyCsv, reviewCsv, readme } }, null, 2));
- }
- function renderReadme() {
- return [
- '# 提号真实数据收集包',
- '',
- '用途:把商务历史 Brief、人工名单、客户选择和本轮复核结果整理成可审计数据。',
- '',
- '## 文件',
- '',
- '- `history-data-template.csv`:整理 5-10 个真实历史 Brief,用于 `history:from-csv` 和 `history:audit`。',
- '- `manual-review-template.csv`:整理本轮 AI 产出的候选名单和商务/客户复核,用于 `review:metrics` 和 `customer-effect:audit`。',
- '',
- '## 填写规则',
- '',
- '- 不要写 Parse sessionToken、模型 token、鉴权头、npm token 或客户敏感报价明细。',
- '- `历史人工补号量基线` 必须填写,才能证明人工补号量减少 50%。',
- '- `客户选择` 建议填写 `客户选中`、`客户拒绝` 或 `待客户反馈`。',
- '- 负样本必须填写 `归因类型`,例如 `需求解析错`、`隐性规则漏`、`召回关键词错`、`主页证据不足`、`视频证据误判`、`排序权重错`、`输出解释错`。',
- '',
- '## 字段填写验收表',
- '',
- '### 历史数据表',
- '',
- '| 字段 | 必填规则 | 常见退回原因 |',
- '| --- | --- | --- |',
- '| brief编号 | 同一项目保持稳定编号。 | 空值、多个项目混用同一编号。 |',
- '| 客户原始Brief | 填客户原始需求,至少能看出类目、人群、平台、预算或内容方向。 | 只写“见附件”“种草合作”或少于 20 字。 |',
- '| 历史人工补号量基线 | 填数字,表示历史同类需求人工额外补号数量。 | 空值、写“不知道”、写区间。 |',
- '| 参考账号或视频 | 填真实主页链接或视频链接。 | example.com、截图占位、只写账号昵称无链接。 |',
- '| 博主名称/主页链接 | 填人工最终名单里的真实博主和主页。 | 只有博主名、主页缺失、候选占位。 |',
- '| 客户选择 | 填 `客户选中` 或 `客户拒绝`。 | 一直保留 `待客户反馈` 时不能进入客户效果审计。 |',
- '| 拒绝原因 | 客户拒绝样本必须填写。 | 拒绝样本为空,无法归因优化。 |',
- '',
- '### 人工复核表',
- '',
- '| 字段 | 必填规则 | 常见退回原因 |',
- '| --- | --- | --- |',
- '| 人工复核标签 | 每个候选都要填,例如 `可直接发客户`、`商务复核`、`跑偏`、`调性不符`。 | 空值或只写“看情况”。 |',
- '| 归因类型 | 负样本必须填固定归因类型。 | 跑偏、调性不符、硬性违约但没有归因。 |',
- '| 反馈原因 | 写具体原因,能让下一轮优化规则或权重。 | 只写“不合适”。 |',
- '| 客户选择 | 已有客户反馈时填 `客户选中` 或 `客户拒绝`。 | 全部待反馈时只能做复核指标,不能做客户效果。 |',
- '| 本轮人工补号量 | 填数字,表示本轮 AI 后仍需人工补号数量。 | 空值或非数字,无法计算人工补号减少率。 |',
- '',
- '## 先跑预审',
- '',
- '商务/投放填完后先跑:',
- '',
- '```powershell',
- 'npm run intake:readiness -- --data-pack outputs\\data-intake-pack-latest --video-pack outputs\\video-intake-pack-latest --output outputs\\intake-readiness-latest',
- '```',
- '',
- '预审 `failureCount=0` 后,再进入 history、review 和 customer-effect 审计。',
- '',
- '## 审计命令',
- '',
- '```powershell',
- 'npm run history:from-csv -- --input <history-data-template.csv> --output <history-dataset目录>',
- 'npm run history:audit -- --input <history-dataset目录> --output <历史审计输出目录> --strict',
- 'npm run review:metrics -- --input <manual-review-template.csv> --output <复核指标输出目录> --strict',
- 'npm run customer-effect:audit -- --review-csv <manual-review-template.csv> --history-audit <historical-dataset-audit.json> --output <客户效果输出目录> --strict',
- 'npm run evidence:index -- --output outputs\\evidence-index-latest',
- '```',
- '',
- '## 边界',
- '',
- '- 模板生成不代表业务效果达标。',
- '- 只有真实客户选择、真实历史补号基线和本轮补号量都通过审计,才能宣称客户效果证明完成。'
- ].join('\n');
- }
- function writeCsv(file, rows) {
- fs.writeFileSync(file, withBom(rows.map(row => row.map(csvCell).join(',')).join('\n')), 'utf8');
- }
- function csvCell(value) {
- const text = String(value ?? '');
- return /[",\n]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
- }
- function parseArgs(argv) {
- const args = {};
- for (let index = 0; index < argv.length; index += 1) {
- const raw = argv[index];
- if (!raw.startsWith('--')) continue;
- const key = raw.slice(2).replace(/-([a-z])/g, (_, char) => char.toUpperCase());
- const next = argv[index + 1];
- if (!next || next.startsWith('--')) args[key] = true;
- else {
- args[key] = next;
- index += 1;
- }
- }
- return args;
- }
- function withBom(text) {
- return `\uFEFF${text}`;
- }
- if (require.main === module) main();
- module.exports = {
- HISTORY_HEADER,
- REVIEW_HEADER,
- renderReadme
- };
|