| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- const fs = require('fs');
- const path = require('path');
- const DEFAULT_VOC_ISSUES = [
- {
- issue: '第一次了解怕踩雷',
- insight: '用户不是不想选,而是不知道怎么判断才稳。',
- title: '第一次了解我们,按这个顺序看不容易踩雷',
- hook: '第一次接触别急着下决定,先看这 3 点最稳。',
- script: '第一次了解我们,如果你怕选错、怕花冤枉钱,可以先按这个顺序判断。第一步先看核心产品或服务能不能解决你最关心的问题;第二步看真实用户的反馈和案例;第三步再对比价格和适配度。这样你不是盲选,而是在判断到底值不值。',
- shots: ['产品/服务全貌', '核心卖点特写', '真实案例展示', '用户使用场景'],
- commentPrompt: '第一次了解你最担心什么?评论区告诉我,我帮你判断。',
- metric: '评论里“怎么选/第一次/适不适合/怎么对比”的提问是否增加'
- },
- {
- issue: '觉得价格贵',
- insight: '用户真正想知道的是贵在哪里、值不值、怎么选更划算。',
- title: '为什么我们不是最便宜的,但很多老用户还会复购',
- hook: '如果你只看价格,会觉得我们不便宜,但这几个地方才是成本。',
- script: '很多用户第一次看价格会犹豫,所以今天直接讲清楚。我们贵的地方不是噱头,而是这几项投入、这套服务的稳定性,还有长期使用的保障。第一次不建议盲选,可以先从入门方案开始,既能体验核心价值,也不会一下子超预算。',
- shots: ['成本构成展示', '服务/工艺过程', '前后效果对比', '不同方案价格说明'],
- commentPrompt: '想看入门方案怎么选,还是进阶方案怎么搭配?',
- metric: '评论里“贵/值不值”下降,“方案/怎么选”上升'
- },
- {
- issue: '担心买了不适合自己',
- insight: '适配焦虑要用“适合谁、不适合谁”来降低。',
- title: '这个适合谁、不适合谁,我直接说清楚',
- hook: '先说不适合什么人,再说适合什么人。',
- script: '我们不想只讲好的一面,所以今天直接说清楚。我们适合这类需求、这种预算、这种使用场景的人;如果你的情况是另外几种,可能要先看看其他选择。讲清楚不适合谁,反而能减少你拿到手之后的落差。',
- shots: ['不同人群标签', '适配场景对比', '真实使用反馈', '正面说明'],
- commentPrompt: '你的需求和预算大概是什么?我帮你判断适不适合。',
- metric: '评论里“适不适合/像我这种/能不能用”的互动是否增加'
- },
- {
- issue: '不知道核心差异是什么',
- insight: '卖点要从“我们很好”讲到“为什么和别人不一样”。',
- title: '别人也在做,我们到底不一样在哪',
- hook: '不是所有卖点都重要,但这一个差异值得你了解。',
- script: '很多同行都说自己好,但用户真正想知道的是差异在哪。我们最核心的不是参数堆叠,而是这个做法、这套体验和这类长期结果。第一次了解,你可以不用全看完,但一定要看这一点,因为它最能代表我们想做的事。',
- shots: ['核心差异演示', '关键细节特写', '用户真实反应', '对比说明'],
- commentPrompt: '你选这类产品或服务时,最看重哪个点?',
- metric: '收藏、转发、评论里“想了解/值得/怎么联系”的信号是否增加'
- },
- {
- issue: '担心售后服务跟不上',
- insight: '服务焦虑要用流程透明和响应承诺来降低。',
- title: '买之前先知道:出问题找谁、多久响应',
- hook: '我们最怕的是你遇到问题不知道该找谁。',
- script: '售后和服务是很多人下单前最担心的,所以我们把流程提前说清楚。咨询、售后、退换、维护分别走哪个入口,多久能响应,都有明确说明。你不用猜流程,遇到问题直接按指引找对应的人,我们也会持续优化响应速度。',
- shots: ['服务流程图', '响应承诺说明', '售后入口', '真实处理记录'],
- commentPrompt: '你最在意响应速度、解决能力,还是后续保障?',
- metric: '评论里“没人管/服务慢”下降,“流程/保障”正向反馈增加'
- },
- {
- issue: '内容看着种草,实际怕落差',
- insight: '内容要降低夸张表达,主动讲适合谁、不适合谁。',
- title: '不想只拍好看的,今天把真实情况说清楚',
- hook: '先说缺点,再说适合什么人。',
- script: '我们不想只拍滤镜很好看的内容,所以今天直接说清楚。我们适合这类需求、愿意接受这些前提的人;如果你期待的是另外几种效果,可能要提前了解清楚。讲清楚真实情况,反而能减少你接触之后的落差。',
- shots: ['真实场景', '无滤镜实拍', '真实使用过程', '正面说明'],
- commentPrompt: '你接触之前最想先知道优点还是缺点?',
- metric: '评论里“真实/避坑/适合谁”的互动是否增加'
- },
- {
- issue: '不知道为什么值得专门选你',
- insight: '要把特色从“好”讲到“为什么值得选”。',
- title: '如果你只选一次,建议把这件事想明白',
- hook: '不是所有理由都重要,但这一个值得你了解一次。',
- script: '很多同行都说自己好,但用户真正想知道为什么值得专门选你。我们最核心的不是名头,而是这个方法、这份长期结果和这类真实场景。第一次了解,你可以不用全部记住,但一定要看这一点,因为它最能代表我们的价值。',
- shots: ['核心价值演示', '长期结果展示', '用户真实反馈', '场景氛围'],
- commentPrompt: '你会为了哪种结果专门选一家?',
- metric: '收藏、转发、评论里“想要/值得/怎么联系”的信号是否增加'
- }
- ];
- function splitList(value) {
- if (!value) return [];
- if (Array.isArray(value)) return value.flatMap(splitList);
- return String(value).split(/[;;\n\r]+/).map(item => item.trim()).filter(Boolean);
- }
- function readTextIfExists(filePath) {
- if (!filePath) return '';
- const absolute = path.resolve(filePath);
- if (!fs.existsSync(absolute)) return '';
- return fs.readFileSync(absolute, 'utf8').replace(/^\uFEFF/, '');
- }
- function collectIssues(input = {}) {
- const explicit = splitList(input.issues || input.problems || input.vocIssues || input.contentDirections || input.directions || input.evidence || input.comments);
- if (explicit.length) return explicit.slice(0, 12);
- const text = String(input.reportText || input.evidenceText || input.context || input.background || '').trim() || readTextIfExists(input.report || input.reportPath);
- const lines = splitList(text).filter(line => /价格|贵|踩雷|服务|售后|怎么选|适合|不适合|落差|案例|对比|值不值|效果|质量|体验|方案|差异|卖点|为什么|预算/.test(line));
- return lines.length ? lines.slice(0, 12) : DEFAULT_VOC_ISSUES.map(item => item.issue);
- }
- function inferTemplate(issue, index, input = {}) {
- const text = String(issue || '');
- const matched = DEFAULT_VOC_ISSUES.find(item =>
- text.includes(item.issue) ||
- item.issue.includes(text) ||
- (/踩雷|第一次|怎么选|不会选/.test(text) && item.issue.includes('踩雷')) ||
- (/贵|价格|性价比|值不值|预算/.test(text) && item.issue.includes('价格')) ||
- (/适合|不适合|适配/.test(text) && item.issue.includes('适合')) ||
- (/差异|卖点|特色|不一样|区别/.test(text) && item.issue.includes('差异')) ||
- (/服务|售后|没人管|响应|态度/.test(text) && item.issue.includes('服务')) ||
- (/落差|种草|滤镜|网红/.test(text) && item.issue.includes('落差')) ||
- (/为什么|值得|理由/.test(text) && item.issue.includes('值得'))
- );
- return matched || DEFAULT_VOC_ISSUES[index % DEFAULT_VOC_ISSUES.length];
- }
- function buildContentPlan(input = {}) {
- const brand = String(input.project || input.brand || input.store || '你的品牌').trim();
- const industry = String(input.industry || input.category || '通用行业').trim();
- const platform = String(input.platform || '抖音/小红书').trim();
- const days = Math.max(1, Math.min(Number(input.days || 7), 15));
- const issues = collectIssues(input);
- const plan = Array.from({ length: days }, (_, index) => {
- const sourceIssue = issues[index % issues.length];
- const template = inferTemplate(sourceIssue, index, input);
- return {
- day: index + 1,
- userIssue: sourceIssue || template.issue,
- insight: template.insight,
- title: template.title,
- hook: template.hook,
- script: template.script,
- shots: template.shots,
- commentPrompt: template.commentPrompt,
- validationMetric: template.metric,
- platform
- };
- });
- const assistantMessage = [
- '# 7 天 VOC 内容选题与口播脚本',
- '',
- '## 你这轮能拿到什么',
- `- 一份面向 ${brand} 的 7 天内容选题表。`,
- '- 每条内容都绑定一个真实用户问题,不做空泛爆款标题。',
- '- 每条都包含前 3 秒钩子、口播脚本、镜头建议、评论区引导和验证指标。',
- '',
- '## 业务口径',
- `- 品牌:${brand}`,
- `- 行业/品类:${industry}`,
- `- 发布平台:${platform}`,
- `- 内容周期:${days} 天`,
- '',
- '## 7 天内容表',
- ...plan.map(item => [
- '',
- `### Day ${item.day}:${item.title}`,
- `- 回应的用户问题:${item.userIssue}`,
- `- 洞察判断:${item.insight}`,
- `- 前 3 秒钩子:${item.hook}`,
- `- 口播脚本:${item.script}`,
- `- 镜头建议:${item.shots.join(' -> ')}`,
- `- 评论区引导:${item.commentPrompt}`,
- `- 验证指标:${item.validationMetric}`
- ].join('\n')),
- '',
- '## 拍摄提醒',
- '- 先拍真实场景、真实产品、真实服务流程,不要只拍滤镜氛围。',
- '- 每条内容只解决一个用户问题,避免一条视频讲太多。',
- '- 发完后把评论里的新问题继续放回 VOC 问题池,下一轮再优化。',
- '',
- '## 下一步建议',
- '1. 先选 2 条最容易拍的内容今天开拍。',
- '2. 发布 7 天后,把评论反馈告诉我,我会判断哪些选题有效、哪些要降权。',
- '3. 如果已经有趋势报告或问题池,可以把 Top 3 问题传进来,我会重排内容顺序。'
- ].join('\n');
- return {
- status: 'ok',
- assistantMessage,
- summary: {
- brand,
- industry,
- platform,
- days,
- topicCount: plan.length,
- sourceIssueCount: issues.length
- },
- data: {
- brand,
- industry,
- platform,
- issues,
- plan
- },
- files: [],
- nextActions: [
- '选择 2 条最容易拍的内容先执行',
- '发布后把评论反馈放回 VOC 问题池',
- '7 天后反馈有效/无效,我会更新内容方向'
- ],
- warnings: [],
- errors: []
- };
- }
- module.exports = {
- buildContentPlan
- };
|