From d8e83ad3688e2102d6ba783e3a4cb8f12ef25d9a Mon Sep 17 00:00:00 2001 From: maogeigei Date: Mon, 7 Sep 2026 15:03:21 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9C=AF=E8=AF=AD=E7=BB=9F=E4=B8=80=EF=BC=9A?= =?UTF-8?q?=E8=A7=86=E8=A7=89=E8=84=9A=E6=9C=AC=E2=86=92=E8=A7=86=E8=A7=89?= =?UTF-8?q?=E7=9F=AD=E7=89=87=E3=80=81=E9=A2=84=E8=AE=BE=E9=92=A9=E5=AD=90?= =?UTF-8?q?=E2=86=92=E5=BC=80=E5=9C=BA=E9=92=A9=E5=AD=90=EF=BC=88=E6=8A=80?= =?UTF-8?q?=E8=83=BD=E5=B1=82=E5=85=A8=E9=87=8F=EF=BC=89+=20clarify=20v30?= =?UTF-8?q?=20=E7=BB=B4=E5=BA=A6=E5=8F=A3=E5=BE=84=E8=A1=94=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - P0: D·视觉脚本→D·视觉短片 20 处/5 文件(SKILL.md S9、6_框架映射表、9_脚本、创作流程规范、07_格式指南);F07 纯视觉短片保留 - P2a: 预设钩子→开场钩子 10 处/3 文件(5_选题、7_大纲、04_爆款开场口径说明) - P1a/P1b: clarify.md 新增第 7 条维度口径衔接(内容形式4选→S6细化、故事框架倾向词→S6定型、开场钩子同口径) CHANGELOG v30 - SKILL.md frontmatter updated_at=2026-09-07; references-add/变更日志.md 归档 09-07 条目 - 需求框 v29(内容形式置首/Vlog去人物采访/叙事结构→故事框架) 已于上轮提交范围 --- project/短视频脚本创作/V1.0/SKILL.md | 6 +- .../docs/AI会话任务输入输出对照.md | 10 +- .../docs/对话式选题完善_用户场景案例.md | 51 ++--- .../V1.0/mcn-work-shop/prompts/clarify.md | 35 ++-- .../V1.0/mcn-work-shop/public/data-pages.js | 182 ++++++++++++++---- .../V1.0/mcn-work-shop/public/style.css | 35 +++- .../V1.0/mcn-work-shop/server.js | 164 ++++++++++------ .../V1.0/references-add/变更日志.md | 7 + .../references/创作流程/5_生成短视频选题.md | 12 +- .../references/创作流程/6_生成短视频框架.md | 4 +- .../references/创作流程/7_生成短视频大纲.md | 6 +- .../references/创作流程/9_生成短视频脚本.md | 12 +- .../V1.0/references/创作流程规范.md | 6 +- .../知识库/03_框架节奏/07_脚本格式选择指南.md | 16 +- .../04_爆款开场/00_开场方式全量覆盖分析.md | 2 +- 15 files changed, 376 insertions(+), 172 deletions(-) diff --git a/project/短视频脚本创作/V1.0/SKILL.md b/project/短视频脚本创作/V1.0/SKILL.md index abb4ebd..9fbfe1e 100644 --- a/project/短视频脚本创作/V1.0/SKILL.md +++ b/project/短视频脚本创作/V1.0/SKILL.md @@ -8,8 +8,8 @@ description: > 触发场景:写脚本、创作短视频、生成脚本、拆解对标视频、分析爆款视频、 诊断脚本质量、设计账号人设、选题策划、生成分镜、广告植入设计、生成创作回顾。 version: "1.0" -updated_at: "2026-09-04" -last_change: "09-04 技能树体量优化(R73/R74):frontmatter 历史明细迁 references-add/变更日志.md、流程总览压缩为行式 Gate 链、新增阅读分层说明;本技能近期实质变更(09-04 子技能索引登记+外部依赖路径修正、09-03 更名短视频工作台、09-02 AI任务入口契约+执行铁律)完整历史见 references-add/变更日志.md;browser-harness 2 个 14B 占位 SKILL.md 删除(防宿主误注册,外部依赖段补维护提示)" +updated_at: "2026-09-07" +last_change: "09-07 术语统一:D·视觉脚本→D·视觉短片、预设钩子→开场钩子(S5/S7 字段全量对齐,明细见 references-add/变更日志.md)" deployment: "source" agent_created: true --- @@ -273,7 +273,7 @@ S1 意图识别 → S2 需求完善 → [G0 分析确认] ### 步骤 9:生成短视频脚本(情绪演绎层) -**目的:** 基于 S6 情绪规划(场次结构表+情绪设计卡)和 S7 结构骨架(模板+分段),按叙事形态选择对应脚本格式(A·场次叙事/B·Vlog双轨/C·口播要点/D·视觉脚本,参见知识库 `03_框架节奏/07_脚本格式选择指南.md`),具象化为完整脚本——台词、动作、多维度情绪表达,不做任何情绪维度的重新规划。 +**目的:** 基于 S6 情绪规划(场次结构表+情绪设计卡)和 S7 结构骨架(模板+分段),按叙事形态选择对应脚本格式(A·场次叙事/B·Vlog双轨/C·口播要点/D·视觉短片,参见知识库 `03_框架节奏/07_脚本格式选择指南.md`),具象化为完整脚本——台词、动作、多维度情绪表达,不做任何情绪维度的重新规划。 **输出:** 脚本概览 + 对应格式脚本(逐场故事演绎+情绪表达+口播台词)+ 广告植入对齐检查 + 自检清单。 diff --git a/project/短视频脚本创作/V1.0/mcn-work-shop/docs/AI会话任务输入输出对照.md b/project/短视频脚本创作/V1.0/mcn-work-shop/docs/AI会话任务输入输出对照.md index b9f6b40..940063f 100644 --- a/project/短视频脚本创作/V1.0/mcn-work-shop/docs/AI会话任务输入输出对照.md +++ b/project/短视频脚本创作/V1.0/mcn-work-shop/docs/AI会话任务输入输出对照.md @@ -101,7 +101,7 @@ 请帮我把抖音账号 {账号名称「x」, 账号地址「y」} 添加:通过抖音网页获取账号信息与视频列表,写入账号列表并保存到本地 ``` -### 9 需求打磨 clarify(09-02 弹窗内对话式 · 09-07 模板文件化) +### 9 需求打磨 clarify(09-02 弹窗内对话式 · 09-07 模板文件化 · v29 八维编导名) > 账号列表「AI创作」弹窗底部的对话打磨,非 AI 会话任务(不走 /api/run),直调 Dify chat-messages。 @@ -113,14 +113,14 @@ |----|------|------|---------| | 正文 | 无 | 对用户说的话(150 字内);追问时问题**独占一段**;收尾时提示「可直接点击右下角 ✓ 提交创作」 | 渲染为 AI 气泡 | | 候选 | `==OPTS==` | 每行一个候选,格式「骨架词——具体展开」(≤45 字、无编号);结合本条创意/人设落地,禁干巴术语 | 解析为气泡下内嵌按钮行,点选=以整行文本发一轮 | -| 需求 | `==REQ==` | 5 行清单:一句话创意/创新·爆点/痛点·爽点/叙事结构/其他约束(空项留空) | 解析回填需求框字段 | +| 需求 | `==REQ==` | 8 行清单:内容形式/一句话故事/开场钩子/故事爆点/故事爽点/故事框架/人物关系/特别要求(空项留空) | 解析回填需求框字段 | **System Prompt 已抽为文件**:`mcn-work-shop/prompts/clarify.md`(==PROMPT== 与 ==CHANGELOG== 之间为正文,头尾说明不发模型)。**迭代 prompt 只改该文件**,server.js 启动按 mtime 缓存读取、占位符注入(`{{accountName}}/{{personaText}}/{{contentText}}/{{attachText}}/{{reqMdText}}/{{histText}}/{{turnText}}`),不再改 server.js 字符串。 **关键规则(clarify.md 内固化)**: -- 候选骨架受控词表:叙事结构(钩子前置/悬念反转/递进铺垫/先抑后扬)、内容形式(剧情/Vlog/口播/**视觉短片**)、剧情人物关系(一次一层);每项必须结合本条创意给「具体形态」 -- 收尾门槛:创意/爆点/爽点/叙事结构 **至少 3 项明确**才可提示提交,否则继续补缺口 -- 前端草稿缓存:`localStorage aicreate_draft_{账号}`(w/convo/attach,每轮回复后存、提交后删);点遮罩外不关闭,仅 X/「重置」可关 +- 候选骨架受控词表:内容形式(剧情/Vlog/口播/**视觉短片**)、故事框架(钩子前置/悬念反转/递进铺垫/先抑后扬)、开场钩子(悬念提问/反差画面/直击痛点/身份共鸣/利益承诺)、剧情人物关系(一次一层);每项必须结合本条创意给「具体形态」 +- 收尾门槛(09-07 v29):内容形式/一句话故事/开场钩子/故事爆点/故事爽点/故事框架 **核心 6 项至少 4 项明确**才可提示提交;且内容形式=剧情时「人物关系」也须明确。未达门槛继续补最关键缺口 +- 前端草稿缓存:`localStorage aicreate_draft_{账号}`(w/convo/attach,每轮回复后存、提交后删);点遮罩外不关闭,仅 X/「重置」可关;旧草稿缺 v28 新字段(hook/ftype/cast)自动为空,兼容不报错 ## 关键细节 diff --git a/project/短视频脚本创作/V1.0/mcn-work-shop/docs/对话式选题完善_用户场景案例.md b/project/短视频脚本创作/V1.0/mcn-work-shop/docs/对话式选题完善_用户场景案例.md index 0f1dbcd..3540505 100644 --- a/project/短视频脚本创作/V1.0/mcn-work-shop/docs/对话式选题完善_用户场景案例.md +++ b/project/短视频脚本创作/V1.0/mcn-work-shop/docs/对话式选题完善_用户场景案例.md @@ -1,6 +1,6 @@ # 「对话式选题完善」用户模拟使用场景案例 -> 版本:09-07 OPTS 候选 chips 版(视觉短片/口播等编导词定稿)|入口:账号列表 → 任一行「AI创作」→ 弹窗底部输入框 +> 版本:09-07 v29 八维编导名版(内容形式/一句话故事/开场钩子/故事爆点/故事爽点/故事框架/人物关系/特别要求)|入口:账号列表 → 任一行「AI创作」→ 弹窗底部输入框 > 一句话:用户在弹窗里像聊天一样逐步说出想法,AI 每轮把缺口整理成**可点选候选**,点一下即回填,补齐后一键提交创作。 --- @@ -9,14 +9,17 @@ | # | 场景 | 模拟目标 | 关键验证点 | |---|------|---------|-----------| -| 1 | 从零想法 → 提交创作 | 主流程闭环 | 5 字段逐步点亮、叙事结构 chips 点选、提交按钮 | -| 2 | 想拍"视觉短片" | 内容形式四选项分支 | D·视觉短片词条出现、点击后画面驱动进需求 | +| 1 | 从零想法 → 提交创作 | 主流程闭环 | 8 字段逐步点亮、故事框架/开场钩子 chips 点选、提交按钮 | +| 2 | 想拍"视觉短片" | 内容形式四选项分支 | 视觉短片词条出现、点击后画面驱动进内容形式行 | | 3 | 想做剧情演绎但人物没想好 | 人物/关系分支追问 | AI 追问人物关系、chips 提供关系候选 | | 4 | 想法已经很完整 | 快速收尾 | AI 直接总结、提示提交、不再弹 chips | | 5 | 上传需求文档起步 | 附件 + 对话结合 | 附件被通读、诉求进清单 | -> 术语对照(文档内统一用编导词): -> 内容形式 = 剧情(场次叙事)/ Vlog 纪实·人物采访 / 口播(干货·教程·盘点) / **视觉短片**(画面+音乐驱动,测评种草或纯氛围) +> 术语对照(文档内统一用 v29 编导词): +> 内容形式 = 剧情(场次叙事)/ Vlog / 口播(干货·教程·盘点) / **视觉短片**(画面+音乐驱动,测评种草或纯氛围) → 独立字段「内容形式」,**置需求框首位** +> 故事框架 = 钩子前置 / 悬念反转 / 递进铺垫 / 先抑后扬 → 独立字段「故事框架」 +> 开场钩子 = 悬念提问 / 反差画面 / 直击痛点 / 身份共鸣 / 利益承诺 → 独立字段「开场钩子」(前 3 秒留人) +> 收尾门槛 = 核心 6 项(内容形式/一句话故事/开场钩子/故事爆点/故事爽点/故事框架)取 4;内容形式=剧情时「人物关系」也须明确 --- @@ -27,29 +30,29 @@ **① 用户输入:** > 想拍一条亲子互动的搞笑短剧 -**AI 行为:** 吸收"亲子 + 搞笑 + 短剧"→ 填入一句话创意;判断内容形式可定为剧情(场次叙事);发现叙事结构缺口 → 追问并给 4 个候选 chips。 +**AI 行为:** 吸收"亲子 + 搞笑 + 短剧"→ 内容形式可定为剧情(场次叙事)写入内容形式行(首位) → 判断一句话故事可推断"天使宝宝 × 打工人妈妈 反差互动"填入 → 发现故事框架缺口 → 追问并给 4 个候选 chips。 **AI 气泡(实测同款):** -> 没问题!"天使宝宝 × 打工人妈妈"的日常反差互动,精准戳中宝妈笑点。既然定了剧情短剧,这条视频您更倾向哪种叙事结构? +> 没问题!"天使宝宝 × 打工人妈妈"的日常反差互动,精准戳中宝妈笑点。既然定了剧情短剧,这条视频您更倾向哪种故事框架? > > 〔钩子前置〕〔悬念反转〕〔递进铺垫〕〔先抑后扬〕 -**需求框变化:** `2/5 已填`(一句话创意 ✅、叙事结构=剧情·场次叙事 ✅) +**需求框变化:** `2/8 已填`(内容形式=剧情·场次叙事 ✅、一句话故事 ✅) **② 用户点击:** 〔钩子前置〕 **用户侧效果:** 点击文本以"钩子前置"作为用户输入发一轮,chips 即时收起(防重复点)。 -**AI 行为(下一轮):** 吸收选择 → 叙事结构行更新为"钩子前置(开场即抛钩子)" → 发现爆点/痛点仍有缺口 → 继续追问并给候选。 +**AI 行为(下一轮):** 吸收选择 → 故事框架行更新为"钩子前置(开场即抛钩子)" → 发现开场钩子缺口(v28 新增追问层) → 追问开场钩子并给候选(悬念提问/反差画面/直击痛点/身份共鸣/利益承诺 × 亲子落点)。 -**需求框变化:** `3/5 已填` +**需求框变化:** `3/8 已填` -**③ 用户继续:** (示例)点击爆点候选或直接输入 "反转的点是爸爸藏私房钱被妈妈抓包"。 +**③ 用户点击:** 〔反差画面——妈妈躲门后偷看,爸爸独自带娃鸡飞狗跳〕 -**AI 行为:** 吸收 → 补爆点 → 若痛点/爽点仍空 → 依账号设定(妈妈带娃痛点)给爽点候选。 +**AI 行为:** 吸收 → 开场钩子行写入"反差画面:妈妈躲门后偷看、爸爸带娃鸡飞狗跳(前 3 秒)" → 故事爆点/故事爽点若仍空 → 依账号设定(妈妈带娃痛点)给爽点候选。 -**④ 直到需求框 ≥4/5,AI 回复:** -> 需求已齐:剧情短剧 × 钩子前置,笑点结构完整。可以直接点右下角 ✓ 提交创作了! +**④ 直到核心 6 项 ≥4 项明确(内容形式/一句话故事/故事框架/开场钩子已满 4),AI 回复:** +> 需求已齐:剧情短剧 × 钩子前置 × 反差开场,笑点结构完整。可以直接点右下角 ✓ 提交创作了! **⑤ 用户点击** ✓ 提交创作 → 确认弹窗展示需求清单 → 确认 → 创建 S1-S11 全流程创作任务。 @@ -73,17 +76,17 @@ **AI 气泡(预期):** > 明白了,画面优先、台词极少——这属于**视觉短片**。确认一下内容形式: > -> 〔剧情(多角色对白)〕〔Vlog 纪实·人物采访〕〔口播(干货·教程·盘点)〕〔视觉短片(画面+音乐驱动)〕 +> 〔剧情(多角色对白)〕〔Vlog〕〔口播(干货·教程·盘点)〕〔视觉短片(画面+音乐驱动)〕 **② 用户点击:** 〔视觉短片(画面+音乐驱动)〕 -**AI 行为(下一轮):** 叙事结构行写入"视觉短片(画面驱动、配乐承载情绪)" → 追问音乐氛围倾向/时长 → 给候选(如〔治愈氛围〕〔氛围卡点〕)。 +**AI 行为(下一轮):** 内容形式行写入"视觉短片(画面驱动、配乐承载情绪)" → 追问故事框架/开场钩子(氛围类常用递进铺垫或氛围卡点开场) → 给候选(如〔治愈氛围〕〔氛围卡点〕)。 -**需求框变化:** 叙事结构字段呈现 **视觉短片** 字样 ✅(验证本轮术语定稿生效) +**需求框变化:** 内容形式字段呈现 **视觉短片** 字样 ✅(验证 v28 内容形式独立字段生效) **验收清单:** - [ ] 选项文案是否为「视觉短片」而非旧词「视觉脚本」 -- [ ] 点击后叙事结构行是否注明"画面驱动" +- [ ] 点击后**内容形式**行是否写入"视觉短片"并注明"画面驱动"(勿再落入结构类字段) --- @@ -103,14 +106,14 @@ **② 用户点击:** 〔闺蜜反目(多年挚友)〕 -**AI 行为(下一轮):** 把人物关系吸收进创意行(如"十年闺蜜因…反目")→ 继续追问决裂导火索或爆点 → 给候选。 +**AI 行为(下一轮):** 把人物关系吸收进**人物关系行**(如"闺蜜反目(多年挚友):十年闺蜜因…反目")→ 继续追问决裂导火索或故事爆点 → 给候选。 -**需求框变化:** 一句话创意行包含人物关系信息 ✅ +**需求框变化:** 人物关系行包含"闺蜜反目(多年挚友)"信息 ✅(v28 独立字段,不再挤在创意行) **验收清单:** -- [ ] 剧情向输入是否触发"人物/关系"追问(此前 flova 缺口) +- [ ] 剧情向输入是否触发"人物关系"追问(此前 flova 缺口) - [ ] 追问是否一次一层、不堆问题 -- [ ] 点击关系候选后是否写入创意行 +- [ ] 点击关系候选后是否写入**人物关系行**(而非一句话故事行) --- @@ -126,7 +129,7 @@ **AI 气泡(预期):** > 需求已经很完整:美妆测评、30 秒、妆前妆后反差开场、口播节奏快、结尾挂优惠。直接点 ✓ 提交创作即可。 -**需求框变化:** `5/5 已填` +**需求框变化:** 核心 6 项已明确 4 项(内容形式=口播、一句话故事=美妆测评种草、开场钩子=妆前妆后反差、故事框架=反差直给)→ AI 不再追问 ✅ **验收清单:** - [ ] 信息齐备时 AI 是否**不**输出候选(chips 不出现 = 正常) @@ -149,7 +152,7 @@ > > 〔营养早餐的一天〕〔挑食宝宝秒喝光〕〔妈妈偷喝被发现〕 -**需求框变化:** 其他约束行出现"儿童牛奶 · 早餐场景 · 禁功效词" ✅ +**需求框变化:** 特别要求行出现"儿童牛奶 · 早餐场景 · 禁功效词" ✅ **验收清单:** - [ ] 附件诉求是否进入需求框对应字段 diff --git a/project/短视频脚本创作/V1.0/mcn-work-shop/prompts/clarify.md b/project/短视频脚本创作/V1.0/mcn-work-shop/prompts/clarify.md index 4c434eb..51e368c 100644 --- a/project/短视频脚本创作/V1.0/mcn-work-shop/prompts/clarify.md +++ b/project/短视频脚本创作/V1.0/mcn-work-shop/prompts/clarify.md @@ -27,24 +27,35 @@ 1. 理解本轮输入:可能是新想法、对上一问的回答、或想修改某一维度。将信息吸收进需求清单,可改写得更专业准确,但保留用户原意与账号结构性符号。 2. 若关键维度仍有缺口且本轮输入不足以补齐:只追问 1 个问题,不要一次抛出多个,也不要重复已明确的维度。问题必须单独另起一段(前后空行),便于界面突出显示;且必须给出可点选的候选(==OPTS==),供达人一键选用,严禁让达人自己空想选项。 3. 候选生成规则:选项骨架须受控(来自以下词表),但每项必须结合本条视频的创意/人设给出「落到这条视频里的具体形态」——即选项 = 骨架词 + 一句话怎么拍/什么内容,让达人一眼知道选了会得到什么,而不是干巴巴的术语。 - - 叙事结构未定 → 骨架:钩子前置 / 悬念反转 / 递进铺垫 / 先抑后扬,每项后缀具体展开(如:钩子前置——开场 3 秒就用「男保姆被娃折腾」制造笑点;悬念反转——结尾突然揭晓妈妈一直在偷看)。 - - 内容形式(叙事形态)未定 → 骨架:剧情(场次叙事,多角色对白)/ Vlog 纪实·人物采访(第一视角)/ 口播(干货·教程·盘点)/ 视觉短片(画面+音乐驱动,测评种草或纯氛围,台词极少),后缀结合本条内容给画面特征。 - - 达人倾向剧情演绎但人物与关系未明 → 骨架:主要人物身份 / 人物间关系(如:父子 / 夫妻 / 同事 / 陌生冲突),每项后缀结合已有创意给关系落点,一次只问一个层面。 -4. 收尾门槛:仅当以下 4 项中至少 3 项已明确时才可总结收尾——一句话创意、创新/爆点、痛点/爽点、叙事结构(含内容形式)。未达门槛前,即使达人本轮回答已吸收,也须继续补齐最关键的缺口(把缺口本身作为问题+候选追问);已明确项可从达人输入或账号设定合理推断,但推断项须在回复中点明「我按账号设定帮你定了XX,如不对可点重置改」。 + - 内容形式未定(最先确认,放第一位)→ 骨架:剧情(场次叙事,多角色对白)/ Vlog(第一视角记录)/ 口播(干货·教程·盘点)/ 视觉短片(画面+音乐驱动,测评种草或纯氛围,台词极少),后缀结合本条内容给画面特征 → 写入「内容形式」行。 + - 故事框架未定(内容形式已明确时追问)→ 骨架:钩子前置 / 悬念反转 / 递进铺垫 / 先抑后扬,每项后缀具体展开(如:钩子前置——开场 3 秒就用「男保姆被娃折腾」制造笑点;悬念反转——结尾突然揭晓妈妈一直在偷看)→ 写入「故事框架」行。 + - 开场钩子未定(内容形式与框架已有时追问)→ 骨架:悬念提问 / 反差画面 / 直击痛点 / 身份共鸣 / 利益承诺(五选一),后缀结合本条内容给前 3 秒的具体画面或台词 → 写入「开场钩子」行。 + - 达人倾向剧情演绎但人物与关系未明 → 骨架:主要人物身份 / 人物间关系(如:父子 / 夫妻 / 同事 / 陌生冲突),每项后缀结合已有创意给关系落点,一次只问一个层面 → 写入「人物关系」行。 +4. 收尾门槛:仅当以下 6 项中至少 4 项已明确时才可总结收尾——内容形式、一句话故事、开场钩子、故事爆点、故事爽点、故事框架。且若内容形式=剧情(场次叙事),「人物关系」也须明确才可提示提交。未达门槛前,即使达人本轮回答已吸收,也须继续补齐最关键的缺口(把缺口本身作为问题+候选追问);已明确项可从达人输入或账号设定合理推断,但推断项须在回复中点明「我按账号设定帮你定了XX,如不对可点重置改」。 5. 输出三段结构,顺序固定: 第一段:对用户说的话(150 字以内)。需追问时:先一句确认本轮吸收的内容,空一行后单独输出问题(问题独占一段,不要与解释挤在一行);无需追问时:总结已明确项并提示「可直接点击右下角 ✓ 提交创作」; 若本轮提供了候选:另起一行单独输出 ==OPTS==,每行一个候选,格式为「骨架词——具体展开」(≤45 字、不含编号前缀),达人点选后整行即作为其回复;若未提供候选则不输出该段; - 最后另起一行单独输出 ==REQ==,接着输出 5 行需求清单(仍未确定的项留空值,严禁编造;情节/结尾/时长不由本清单承载,由创作阶段按人设与选题自决;共鸣/共情亦不收集,创作时由 AI 依痛点 / 爽点自然发挥),模板: -- 一句话创意: -- 创新 / 爆点: -- 痛点 / 爽点: -- 叙事结构: -- 其他约束: - 达人点击候选后,把该选择吸收进对应字段(如内容形式选「视觉短片」→ 写入叙事结构行并注明画面驱动;人物选择 → 写入一句话创意行的事件或人物信息)。 -6. 需求框只收集本条视频的选题相关信息;账号级设定(情绪基调 / 目标受众 / 结构性符号 / 广告植入等)已由账号设定自动带入创作,不要向达人重复询问这些维度——仅当达人主动提出与账号设定不同的要求时,将其记入「其他约束」。 + 最后另起一行单独输出 ==REQ==,接着输出 8 行需求清单(仍未确定的项留空值,严禁编造;情节/结尾/时长不由本清单承载,由创作阶段按人设与选题自决),模板(顺序固定,内容形式在第一行): +- 内容形式: +- 一句话故事: +- 开场钩子: +- 故事爆点: +- 故事爽点: +- 故事框架: +- 人物关系: +- 特别要求: + 达人点击候选后,把该选择吸收进对应字段(如内容形式选「视觉短片」→ 写入内容形式行并注明画面驱动;故事框架选「钩子前置」→ 写入故事框架行并给开场画面;人物选择 → 写入人物关系行;钩子候选 → 写入开场钩子行)。 +6. 需求框只收集本条视频的选题相关信息;账号级设定(情绪基调 / 目标受众 / 结构性符号 / 广告植入等)已由账号设定自动带入创作,不要向达人重复询问这些维度——仅当达人主动提出与账号设定不同的要求时,将其记入「特别要求」。 +7. 维度口径衔接(需求框词表是达人口径,落到创作由技能流程定型,本环节只记不细问): + - 内容形式:达人只需粗选 剧情 / Vlog / 口播 / 视觉短片;落到创作时由 S6 按映射细化(口播→教程 / 知识分享 / 盘点,视觉短片→测评种草 / 沉浸式),不要在本环节向达人追问更细品类——达人主动给出的品类细节照常记入该行。 + - 故事框架:候选词(钩子前置 / 悬念反转 / 递进铺垫 / 先抑后扬)是达人倾向表达,仅记录倾向本身;具体框架由 S6 用叙事形态(F01-F08)+ 结构模板(三段式/五段式)落地定型,本环节不展开。 + - 开场钩子:与 S5 选题方案的开场钩子同口径(10 种开场方式,抓眼元素前 3 秒内出现、开场段总时长 3-7 秒铁律)。 ==CHANGELOG== ## 变更记录(append-only,最新在上) +- 09-07 v30:新增行为要求第 7 条「维度口径衔接」——内容形式 4 项为粗选(落创作由 S6 细化:口播→教程/知识分享/盘点,视觉短片→测评种草/沉浸式);故事框架四词=达人倾向词(由 S6 叙事形态 F01-F08 + 结构模板定型);开场钩子与 S5 选题方案同口径(10 种开场方式 + 3-7s 铁律)。随技能层术语统一(视觉短片/开场钩子改名)落地。 +- 09-07 v29:内容形式置首位(最先确认的维度);Vlog 项去除「人物采访」(Vlog 就是 Vlog);叙事结构 → 故事框架(==REQ== 模板第 6 行、门槛枚举、候选规则同步更名;key 与草稿兼容不变)。 +- 09-07 v28:需求框升级 8 维编导名(与前端 REQ_FIELDS 同步)——一句话故事/开场钩子/故事爆点/故事爽点/内容形式/叙事结构/人物关系/特别要求;内容形式与叙事结构拆为两行独立收集;开场钩子新增追问层(悬念提问/反差画面/直击痛点/身份共鸣/利益承诺 五选一);收尾门槛由「4 项取 3」升级为「核心 6 项取 4」(一句话故事/开场钩子/故事爆点/故事爽点/内容形式/叙事结构),且内容形式=剧情时人物关系也须明确才可提示提交;==REQ== 模板 5 行 → 8 行。 - 09-07 v26:从 server.js 内嵌字符串抽取为独立模板(prompts/clarify.md)。规则内容与 09-07 四项修复(收尾门槛 4 项至少 3 项、候选具体展开「骨架——展开」、追问问题独占一段、必须出 ==OPTS==)一致,无行为变更。 diff --git a/project/短视频脚本创作/V1.0/mcn-work-shop/public/data-pages.js b/project/短视频脚本创作/V1.0/mcn-work-shop/public/data-pages.js index 3d08ba6..0d96dcd 100644 --- a/project/短视频脚本创作/V1.0/mcn-work-shop/public/data-pages.js +++ b/project/短视频脚本创作/V1.0/mcn-work-shop/public/data-pages.js @@ -200,14 +200,34 @@ function bindBatchBar(container, opts) { // 需求单字段顺序:弹窗需求框展示、AI 输出模板、创作 prompt 共用(值空 = 待 AI 按账号设定与选题自决) // 09-02 v10:需求框定位=选题补充——只留本条视频选题侧字段 + 兜底「其他」;账号设定类(情绪基调/受众/结构性符号/广告植入)由人设卡提供,不在此重复收集 // 09-02 v11:dur 时长目标 移出需求栏 → 需求栏下方「功能选项」下拉(30/60/120/240/自定义),值由 w.dur 单独承载 -// 09-02 v17:选题落点要素(痛点/爽点、共鸣/共情、爆点)加入需求框——创作流程需要但不是必填,空值由 AI 按账号设定与选题自决 +// 09-02 v17:选题落点要素加入需求框——创作流程需要但不是必填,空值由 AI 按账号设定与选题自决 // 09-02 v18:叙事形态→叙事结构;删核心情节/结尾落点(情节与结尾由 AI 创作阶段自决,无需用户填写) -// 09-02 v19:爆点→创新/爆点,字段序调整至「一句话创意」之后 -// 09-02 v20:删共鸣/共情(由 AI 按痛点/爽点自然发挥,无需用户单独填写) +// 09-02 v20:删共鸣/共情(由 AI 按爽点自然发挥,无需用户单独填写) +// 09-07 v28:8 维编导名定稿(用户确认)——idea 一句话故事 / hook 开场钩子 / blow 故事爆点 / pain 故事爽点 / +// ftype 内容形式 / form 叙事结构(不再含内容形式)/ cast 人物关系(剧情向)/ note 特别要求; +// 收尾门槛=核心 6 项取 4(clarify.md 同步);旧草稿缓存缺新字段自动为空,不报错 +// 09-07 v29:内容形式(ftype)置首位;Vlog 项去掉「人物采访」(Vlog 就是 Vlog);叙事结构 → 故事框架(key form 不变,草稿兼容) const REQ_FIELDS = [ - ['idea', '一句话创意'], ['blow', '创新 / 爆点'], ['pain', '痛点 / 爽点'], - ['form', '叙事结构'], ['note', '其他约束'], + ['ftype', '内容形式'], + ['idea', '一句话故事'], + ['hook', '开场钩子'], + ['blow', '故事爆点'], + ['pain', '故事爽点'], + ['form', '故事框架'], + ['cast', '人物关系'], + ['note', '特别要求'], ]; +// 字段 → 悬停提示(renderSheet 用;用编导语言解释落到本条视频要定什么,hover 标题可见) +const REQ_FIELD_HINTS = { + ftype: '内容形式:剧情 / Vlog / 口播 / 视觉短片(画面+音乐驱动)', + idea: '主角 + 事件 + 看点,一句话说清这条视频讲什么', + hook: '开场前 3 秒靠什么留人:悬念提问 / 反差画面 / 直击痛点 / 身份共鸣 / 利益承诺(S5 开场钩子场,3-7s 铁律)', + blow: '全片最炸的那一下 / 记忆点 / 反转高潮,观众看完记住什么', + pain: '观众为什么代入、看了爽在哪 / 被治愈在哪(目标体感:爽感 / 治愈感 / 获得感)', + form: '故事框架走向:钩子前置 / 悬念反转 / 递进铺垫 / 先抑后扬', + cast: '剧情向必补:主要人物是谁、彼此什么关系(父子 / 夫妻 / 同事 / 陌生冲突)', + note: '禁忌、广告植入、参考资料、与账号设定的差异要求等', +}; // 需求 md 行 → 向导字段映射(parseReqSheet 用) const REQ_LABEL_TO_KEY = Object.fromEntries(REQ_FIELDS.map(([k, label]) => [label, k])); // 需求单 markdown 解析 → 状态对象(仅解析已知字段,空值/待填写忽略) @@ -246,7 +266,8 @@ function buildCreatePrompt(accountName, w, personaBlock, hasPersona, attachText) } /* AI创作 弹窗(09-02 v2:需求框 + 输入框 对话式打磨)—— - 顶部=创作需求(5 行选题字段:创意/创新爆点/痛点爽点/叙事结构/其他约束,共鸣共情由 AI 按痛点爽点发挥,随对话逐轮被 AI 更新,空值标「待完善」)+ 下方时长选项条 + 顶部=创作需求(09-07 v29:8 个选题字段——内容形式/一句话故事/开场钩子/故事爆点/故事爽点/故事框架/人物关系/特别要求; + 内容形式置首位;Vlog 不含人物采访;人物关系剧情向补;空值标「待完善」)+ 下方时长选项条 中部=对话流(用户输入 vs AI 整理/追问),底部=输入框 + 提交创作 */ async function confirmAiScript(accountName, btn) { const accId = btn && btn.dataset.accId ? btn.dataset.accId : ''; @@ -259,7 +280,7 @@ async function confirmAiScript(accountName, btn) { } catch (e) { return null; } }; const cache = readCache(); - const w = { idea: '', pain: '', blow: '', form: '', dur: '', note: '', ...(cache && cache.w ? cache.w : {}) }; + const w = { idea: '', hook: '', blow: '', pain: '', ftype: '', form: '', cast: '', note: '', dur: '', ...(cache && cache.w ? cache.w : {}) }; const convo = (cache && Array.isArray(cache.convo)) ? cache.convo.filter((c) => c && c.role && typeof c.text === 'string').map((c) => ({ role: c.role === 'ai' ? 'ai' : 'u', text: String(c.text), opts: Array.isArray(c.opts) ? c.opts.filter((o) => typeof o === 'string') : [] })) : []; // 对话流 { role: 'u' | 'ai', text, opts } let pHas = false, pContent = ''; let attach = (cache && cache.attach && cache.attach.name) @@ -343,11 +364,12 @@ async function confirmAiScript(accountName, btn) { showToast('已重置,本次对话已清空'); }; - /* —— 需求框渲染:5 个选题字段(落点要素可空;共鸣/情节/结尾由 AI 按痛点爽点自决),维度名作标题、内容在下方 —— */ + /* —— 需求框渲染:8 个选题字段(内容形式置首位;开场钩子/人物关系 v28 新增、v29 故事框架命名),维度名作标题、内容在下方 —— */ const renderSheet = () => { const rows = REQ_FIELDS.map(([k, label]) => { const v = String(w[k] || '').trim(); - return `
${label}
${v ? esc(v) : '待完善'}
`; + const tip = REQ_FIELD_HINTS[k] ? ` title="${esc(REQ_FIELD_HINTS[k])}"` : ''; + return `
${label}
${v ? esc(v) : '待完善'}
`; }).join(''); q('#reqSheet').innerHTML = rows; const filled = REQ_FIELDS.filter(([k]) => String(w[k] || '').trim()).length; @@ -380,18 +402,20 @@ async function confirmAiScript(accountName, btn) { const n = Math.round(Number(durNum.value)); w.dur = (Number.isFinite(n) && n > 0) ? n + '秒' : ''; }); - /* —— 对话流渲染(09-07:AI 消息可携带 ==OPTS== 候选,渲染为可点选 chips;点击即以该候选作为用户输入发送) —— */ + /* —— 对话流渲染(09-07 v27:GPT 风格——用户主色实心圆角、AI 无背景 markdown;候选可点;支持流式追加与思考占位) —— */ const renderDialog = () => { const el = q('#reqDialog'); el.innerHTML = convo.length ? convo.map((c, ci) => { - if (c.role === 'u') return `
${esc(c.text)}
`; + if (c.role === 'u') return `
${esc(c.text)}
`; + const thinking = c.thinking ? `
正在思考
` : ''; + const body = c.streaming ? '
' : (c.text ? `
${esc(c.text)}
` : ''); const opts = (Array.isArray(c.opts) && c.opts.length) ? `
${c.opts.map((o) => ``).join('')}
` : ''; - return `
${esc(c.text)}
${opts}
`; + return `
${thinking}${body}${opts}
`; }).join('') - : `
在这里输入一个初步想法,AI 会引导你补齐创意 / 创新·爆点 / 痛点爽点 / 叙事结构等需求(落点要素可留空,由 AI 按账号设定与选题补齐,共鸣与情节结尾由 AI 依痛点爽点发挥);每条输入都会即时更新上方的创作需求框,AI 追问时会给出可点选的候选。支持上传附件。Enter 发送,Shift+Enter 换行。
`; + : `
像聊天一样说出想法,AI 帮你把这条视频的需求聊到可开拍。
`; el.querySelectorAll('.req-opt-chip').forEach((btn) => { btn.addEventListener('click', () => { if (busy) return; @@ -458,7 +482,7 @@ async function confirmAiScript(accountName, btn) { parsing = false; paperBtn.disabled = false; paperBtn.classList.remove('loading'); }); paperBtn.addEventListener('click', () => { if (!parsing) attachInput.click(); }); - /* —— 发送一轮:调 /api/ai/clarify(同步打磨,返回 reply + ==REQ== 段) —— */ + /* —— 发送一轮(09-07 v27:流式输出——首字节前显示"正在思考"占位,逐 chunk 追加到 AI 气泡;流结束后再解析 ==OPTS==/==REQ==) —— */ const sendTurn = async (raw) => { let text = String(raw || '').trim(); if (busy) return; @@ -469,46 +493,91 @@ async function confirmAiScript(accountName, btn) { convo.push({ role: 'u', text }); q('#reqInput').value = ''; autoGrow(); + // 先放占位 AI 气泡(thinking=true),再渲染:用户可见"正在思考"动画 + const aiIdx = convo.length; + convo.push({ role: 'ai', text: '', opts: [], thinking: true }); renderDialog(); setBusy(true); try { // 带最近 2 轮问答做上下文(无状态:需求本身在 reqMd 中已完整携带) const history = []; - for (let i = convo.length - 2; i >= 0 && history.length < 2; i--) { - if (convo[i].role === 'ai') { + for (let i = convo.length - 3; i >= 0 && history.length < 2; i--) { + if (convo[i].role === 'ai' && !convo[i].thinking) { const u = convo[i - 1] && convo[i - 1].role === 'u' ? convo[i - 1].text : ''; history.unshift({ u, a: convo[i].text }); i--; } } - const r = await api('/api/ai/clarify', { + const resp = await fetch('/api/ai/clarify', { method: 'POST', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ accountName, turn: text, reqMd: reqMdOf(w), history, attach: attach.text }), + body: JSON.stringify({ accountName, turn: text, reqMd: reqMdOf(w), history, attach: attach.text, stream: 1 }), }); - if (r.error) throw new Error(r.error); - const answer = String(r.answer || '').trim(); - const ri = answer.lastIndexOf('==REQ=='); - const head = (ri >= 0 ? answer.slice(0, ri) : answer).trim(); - // 09-07:==OPTS== 候选段(位于正文与 ==REQ== 之间,每行一个可点选短句)→ 渲染为 chips - const oi = head.lastIndexOf('==OPTS=='); - const reply = (oi >= 0 ? head.slice(0, oi) : head).trim(); - const opts = oi >= 0 - ? head.slice(oi + '==OPTS=='.length).split('\n').map((s) => s.trim()).filter((s) => s && !s.startsWith('==')).slice(0, 6) - : []; - const fields = ri >= 0 ? parseReqSheet(answer.slice(ri + '==REQ=='.length)) : {}; - let updated = 0; - for (const k of Object.keys(fields)) { - const v = String(fields[k] || '').trim(); - if (v && w[k] !== v) { w[k] = v; updated++; } + if (!resp.ok) { + const t = await resp.text().catch(() => ''); + throw new Error('HTTP ' + resp.status + (t ? ': ' + t.slice(0, 120) : '')); } - if (reply || opts.length) convo.push({ role: 'ai', text: reply || '(已记录本轮信息,继续补充或点选上方候选)', opts }); - saveCache(); // 09-07 v26:每轮成功后即时存草稿,防刷新/误关丢失 - renderSheet(); - renderDialog(); - showToast(updated > 0 ? `✅ 需求已更新 ${updated} 项` : (ri >= 0 ? '✅ 已记录(本次无新变更)' : '⚠️ 本次未返回结构化需求,可换种说法再试')); + const reader = resp.body && resp.body.getReader ? resp.body.getReader() : null; + const ctype = resp.headers.get('content-type') || ''; + // 若后端未真正流式(仍走 blocking),兼容降级:读全部 → 一次性写入 + if (!reader || !ctype.includes('text/event-stream')) { + const t = await resp.text(); + const buf = String(t || ''); + // 兼容 data:{answer:"..."} JSON 包裹 + let ans = buf; + try { const j = JSON.parse(buf); if (j && typeof j.answer === 'string') ans = j.answer; } catch (e) {} + convo[aiIdx] = { role: 'ai', text: ans, opts: [], thinking: false }; + finishAi(aiIdx, ans); + saveCache(); + renderDialog(); + showToast('✅ 已记录'); + return; + } + const dec = new TextDecoder('utf-8'); + let buf2 = ''; + let firstByte = false; + // 流式:首字节到达即关闭 thinking 占位,进入"边推边渲染" + while (true) { + const { value, done } = await reader.read(); + if (done) break; + buf2 += dec.decode(value, { stream: true }); + // 按行切分(SSE 事件以空行分隔,每行以 data: 开头) + const lines = buf2.split('\n'); + buf2 = lines.pop() || ''; + for (const line of lines) { + if (!line.startsWith('data:')) continue; + const payload = line.slice(5).trim(); + if (payload === '[DONE]') continue; + let chunk = ''; + try { + const j = JSON.parse(payload); + if (typeof j.answer === 'string') chunk = j.answer; + else if (typeof j.text === 'string') chunk = j.text; + else if (typeof j.delta === 'string') chunk = j.delta; + } catch (e) { continue; } + if (!chunk) continue; + if (!firstByte) { + firstByte = true; + convo[aiIdx].thinking = false; + convo[aiIdx].text = ''; + convo[aiIdx].streaming = true; + renderDialog(); + appendStreamChunk(aiIdx, chunk); + } else { + appendStreamChunk(aiIdx, chunk); + } + } + } + // 流结束 + convo[aiIdx].streaming = false; + const finalText = convo[aiIdx].text || ''; + finishAi(aiIdx, finalText); + saveCache(); + showToast('✅ 已记录'); } catch (e) { - convo.pop(); // 移除失败的用户消息,恢复输入 + // 移除失败的两条占位(用户消息 + 思考占位) + convo.splice(aiIdx, 1); + convo.pop(); q('#reqInput').value = text; autoGrow(); renderDialog(); @@ -516,6 +585,41 @@ async function confirmAiScript(accountName, btn) { } setBusy(false); }; + // 流式过程中:把新片段追加到 convo[aiIdx].text,就地更新对应 .req-md-stream 容器(不重建整个对话流) + function appendStreamChunk(aiIdx, chunk) { + convo[aiIdx].text = (convo[aiIdx].text || '') + chunk; + const el = q('#reqDialog'); + if (!el) return; + const node = el.querySelector('.req-md-stream[data-stream="' + aiIdx + '"]'); + if (node) { + node.textContent = convo[aiIdx].text; // 纯文本直写,不做 HTML(防注入) + el.scrollTop = el.scrollHeight; + } + } + // 流结束后:解析 ==OPTS== / ==REQ==,回填需求框与候选 + function finishAi(aiIdx, fullText) { + const ri = fullText.lastIndexOf('==REQ=='); + const head = (ri >= 0 ? fullText.slice(0, ri) : fullText).trim(); + const oi = head.lastIndexOf('==OPTS=='); + const reply = (oi >= 0 ? head.slice(0, oi) : head).trim(); + const opts = oi >= 0 + ? head.slice(oi + '==OPTS=='.length).split('\n').map((s) => s.trim()).filter((s) => s && !s.startsWith('==')).slice(0, 6) + : []; + const fields = ri >= 0 ? parseReqSheet(fullText.slice(ri + '==REQ=='.length)) : {}; + let updated = 0; + for (const k of Object.keys(fields)) { + const v = String(fields[k] || '').trim(); + if (v && w[k] !== v) { w[k] = v; updated++; } + } + convo[aiIdx].text = reply || '(已记录本轮信息,继续补充或点选上方候选)'; + convo[aiIdx].opts = opts; + convo[aiIdx].thinking = false; + convo[aiIdx].streaming = false; + renderSheet(); + renderDialog(); + if (updated > 0) showToast(`✅ 需求已更新 ${updated} 项`); + else if (ri < 0) showToast('⚠️ 本次未返回结构化需求,可换种说法再试'); + } /* —— 事件 —— */ const inputEl = q('#reqInput'); /* 输入自动增高:textarea scrollHeight 动态写入 style.height,上限 100px(与 CSS max-height 一致) */ diff --git a/project/短视频脚本创作/V1.0/mcn-work-shop/public/style.css b/project/短视频脚本创作/V1.0/mcn-work-shop/public/style.css index 3e48fca..d14d8da 100644 --- a/project/短视频脚本创作/V1.0/mcn-work-shop/public/style.css +++ b/project/短视频脚本创作/V1.0/mcn-work-shop/public/style.css @@ -260,13 +260,38 @@ main { height: calc(100vh - 54px); } .req-msg { display: flex; } .req-msg.mine { justify-content: flex-end; } .req-msg.ai { justify-content: flex-start; } -.req-bubble { - max-width: 88%; padding: 7px 11px; border-radius: 10px; +/* 09-07 v27:GPT 风格气泡——用户主色实心圆角右对齐,AI 无背景直接 markdown 铺排左对齐 */ +.req-bubble-mine { + max-width: 80%; + background: var(--primary); color: #fff; + padding: 10px 14px; border-radius: 14px; font-size: 14px; line-height: 1.55; white-space: pre-wrap; word-break: break-word; } -/* 09-07 v26:AI 消息 column 布局下气泡宽度自适应文本(最长行限制在消息行宽内) */ -.req-msg.ai { align-items: flex-start; } -.req-msg.ai .req-bubble { max-width: 100%; width: fit-content; min-width: 0; } +.req-md { + max-width: 100%; + color: var(--text); font-size: 14.5px; line-height: 1.7; + white-space: pre-wrap; word-break: break-word; +} +/* 流式占位容器(流结束时会被替换为最终 markdown 节点) */ +.req-md-stream { min-height: 1.6em; } +/* 思考动画:三个点交替跳动 + "正在思考"提示(不展示模型思考内容) */ +.req-thinking { + display: inline-flex; align-items: center; gap: 6px; + color: var(--dim); font-size: 13px; + padding: 4px 2px; +} +.req-thinking .dot { + width: 6px; height: 6px; border-radius: 50%; + background: var(--primary); display: inline-block; + animation: req-dot 1.2s ease-in-out infinite; +} +.req-thinking .dot:nth-child(2) { animation-delay: .15s; } +.req-thinking .dot:nth-child(3) { animation-delay: .30s; } +.req-thinking-label { margin-left: 2px; } +@keyframes req-dot { + 0%, 80%, 100% { opacity: .25; transform: translateY(0); } + 40% { opacity: 1; transform: translateY(-3px); } +} .req-msg.mine .req-bubble { background: var(--primary); color: #fff; border-bottom-right-radius: 3px; } .req-msg.ai .req-bubble { background: #f0f2f5; color: var(--text); border-bottom-left-radius: 3px; } /* 09-07:AI 消息内嵌候选按钮行(flova 式:气泡下方紧跟可点选项,无额外提示语/虚线框) */ diff --git a/project/短视频脚本创作/V1.0/mcn-work-shop/server.js b/project/短视频脚本创作/V1.0/mcn-work-shop/server.js index 6ae04fe..382baeb 100644 --- a/project/短视频脚本创作/V1.0/mcn-work-shop/server.js +++ b/project/短视频脚本创作/V1.0/mcn-work-shop/server.js @@ -859,76 +859,130 @@ const server = http.createServer((req, res) => { return; } - // ---------- AI 创作需求打磨(09-02 弹窗内对话:Dify chat-messages 同步、无状态多轮) ---------- - // body: { accountName, turn(本轮输入), reqMd(当前需求5行md), history(最近轮[{u,a}]) } → { answer } + // ---------- AI 创作需求打磨(09-02 弹窗内对话:阻塞/流式 两种模式;09-07 v27 新增 SSE 流式输出,前端可逐 chunk 渲染 + 思考占位) ---------- if (p === '/api/ai/clarify' && req.method === 'POST') { let body = ''; req.on('data', (c) => { body += c; if (body.length > 1e6) req.destroy(); }); req.on('end', async () => { + let payload; + try { payload = JSON.parse(body || '{}'); } catch (e) { return sendErr(400, 'JSON 解析失败'); } + const { accountName, turn, reqMd, history, attach, stream } = payload; + if (!accountName || typeof accountName !== 'string') return sendErr(400, '缺少账号名'); + if (!turn || typeof turn !== 'string' || !turn.trim()) return sendErr(400, '缺少用户输入'); + // 1) 账号设定摘要(工作台库,与选题接口同源) + let personaText = '', contentText = ''; try { - const { accountName, turn, reqMd, history, attach } = JSON.parse(body || '{}'); - if (!accountName || typeof accountName !== 'string') return sendErr(400, '缺少账号名'); - if (!turn || typeof turn !== 'string' || !turn.trim()) return sendErr(400, '缺少用户输入'); - // 1) 账号设定摘要(工作台库,与选题接口同源) - let personaText = '', contentText = ''; - try { - const { DatabaseSync } = require('node:sqlite'); - const db = new DatabaseSync(path.join(ROOT, 'mcn-plugin.db'), { readOnly: true }); - const acc = db.prepare('SELECT * FROM hot_accounts WHERE account_name=? LIMIT 1').get(accountName); - if (acc) { - contentText = String(acc.content || '').slice(0, 300); - const p = db.prepare('SELECT content_json, summary FROM account_persona WHERE account_id=? ORDER BY id DESC LIMIT 1').get(acc.id); - if (p) personaText = String(p.summary || p.content_json || '').slice(0, 900); - } - db.close(); - } catch (e) { /* 库不可用不阻塞 */ } - // 2) Dify key:环境变量优先,其次技能脚本内置默认 - let apiKey = process.env.DIFY_MCN_CYLG_KEY || ''; - if (!apiKey) { - const scriptPath = path.join(ROOT, '..', 'scripts', 'MCN_CYLG_API.py'); - try { - const src = fs.readFileSync(scriptPath, 'utf8'); - const m = src.match(/DEFAULT_CYLG_KEY\s*=\s*"([^"]+)"/); - if (m) apiKey = m[1]; - } catch (e) { /* 脚本不存在则走 env */ } + const { DatabaseSync } = require('node:sqlite'); + const db = new DatabaseSync(path.join(ROOT, 'mcn-plugin.db'), { readOnly: true }); + const acc = db.prepare('SELECT * FROM hot_accounts WHERE account_name=? LIMIT 1').get(accountName); + if (acc) { + contentText = String(acc.content || '').slice(0, 300); + const p = db.prepare('SELECT content_json, summary FROM account_persona WHERE account_id=? ORDER BY id DESC LIMIT 1').get(acc.id); + if (p) personaText = String(p.summary || p.content_json || '').slice(0, 900); } - if (!apiKey) return sendErr(500, '未配置 DIFY_MCN_CYLG_KEY(环境变量或技能脚本内置 KEY)'); - // 3) 构造打磨 query(无状态:携带当前需求 + 最近对话,让 AI 输出 reply + ==OPTS==/==REQ== 结构化段) - // 09-07:prompt 规则已抽为 prompts/clarify.md(文件化迭代);此处只负责动态上下文注入 - const his = Array.isArray(history) ? history.filter((h) => h && h.u).slice(-2) : []; - const histText = his.map((h) => '你:' + String(h.a || '') + '\n达人:' + String(h.u || '')).join('\n'); - const reqMdText = String(reqMd || '').trim(); - const attachText = String(attach || '').trim().slice(0, 20000); - const tpl = loadPrompt('clarify'); - if (!tpl) return sendErr(500, '缺少 prompts/clarify.md 需求打磨模板'); - const query = tpl - .replace('{{accountName}}', accountName) - .replace('{{personaText}}', personaText || '(暂无可用账号设定,按通用编导经验澄清)') - .replace('{{contentText}}', contentText ? '【账号定位】\n' + contentText : '') - .replace('{{attachText}}', attachText ? '【附件素材】(达人上传的需求文档/资料,打磨与最终创作都须充分参考;把其中的关键诉求/约束吸收进需求清单)\n' + attachText : '') - .replace('{{reqMdText}}', reqMdText || '(空:等待用户先给出初步想法)') - .replace('{{histText}}', histText ? '【对话历史】\n' + histText : '') - .replace('{{turnText}}', turn.trim()); - // 4) Dify blocking 调用 - const difyUrl = String(process.env.DIFY_URL || 'https://mydify.youmanvideo.com/v1').replace(/\/+$/, ''); + db.close(); + } catch (e) { /* 库不可用不阻塞 */ } + // 2) Dify key:环境变量优先,其次技能脚本内置默认 + let apiKey = process.env.DIFY_MCN_CYLG_KEY || ''; + if (!apiKey) { + const scriptPath = path.join(ROOT, '..', 'scripts', 'MCN_CYLG_API.py'); + try { + const src = fs.readFileSync(scriptPath, 'utf8'); + const m = src.match(/DEFAULT_CYLG_KEY\s*=\s*"([^"]+)"/); + if (m) apiKey = m[1]; + } catch (e) { /* 脚本不存在则走 env */ } + } + if (!apiKey) return sendErr(500, '未配置 DIFY_MCN_CYLG_KEY(环境变量或技能脚本内置 KEY)'); + // 3) 构造打磨 query(无状态:携带当前需求 + 最近对话;prompt 模板读自 prompts/clarify.md) + const his = Array.isArray(history) ? history.filter((h) => h && h.u).slice(-2) : []; + const histText = his.map((h) => '你:' + String(h.a || '') + '\n达人:' + String(h.u || '')).join('\n'); + const reqMdText = String(reqMd || '').trim(); + const attachText = String(attach || '').trim().slice(0, 20000); + const tpl = loadPrompt('clarify'); + if (!tpl) return sendErr(500, '缺少 prompts/clarify.md 需求打磨模板'); + const query = tpl + .replace('{{accountName}}', accountName) + .replace('{{personaText}}', personaText || '(暂无可用账号设定,按通用编导经验澄清)') + .replace('{{contentText}}', contentText ? '【账号定位】\n' + contentText : '') + .replace('{{attachText}}', attachText ? '【附件素材】(达人上传的需求文档/资料,打磨与最终创作都须充分参考;把其中的关键诉求/约束吸收进需求清单)\n' + attachText : '') + .replace('{{reqMdText}}', reqMdText || '(空:等待用户先给出初步想法)') + .replace('{{histText}}', histText ? '【对话历史】\n' + histText : '') + .replace('{{turnText}}', turn.trim()); + const difyUrl = String(process.env.DIFY_URL || 'https://mydify.youmanvideo.com/v1').replace(/\/+$/, ''); + // 4) 流式 / 阻塞 分支 + if (stream) { + res.writeHead(200, { + 'Content-Type': 'text/event-stream; charset=utf-8', + 'Cache-Control': 'no-cache, no-transform', + 'Connection': 'keep-alive', + 'X-Accel-Buffering': 'no', + }); + const write = (obj) => { try { res.write('data: ' + JSON.stringify(obj) + '\n\n'); } catch (e) {} }; const ctrl = new AbortController(); const timer = setTimeout(() => ctrl.abort(), 100000); - let resp; try { - resp = await fetch(difyUrl + '/chat-messages', { + const difyResp = await fetch(difyUrl + '/chat-messages', { method: 'POST', headers: { 'Authorization': 'Bearer ' + apiKey, 'Content-Type': 'application/json' }, - body: JSON.stringify({ inputs: { is_think: '0', is_online: '0', model: process.env.DIFY_MCN_MODEL || 'gemini' }, query, response_mode: 'blocking', user: 'mcn-work-shop' }), + body: JSON.stringify({ inputs: { is_think: '0', is_online: '0', model: process.env.DIFY_MCN_MODEL || 'gemini' }, query, response_mode: 'streaming', user: 'mcn-work-shop' }), signal: ctrl.signal, }); + if (!difyResp.ok || !difyResp.body) { + write({ error: 'AI 接口错误: HTTP ' + difyResp.status }); + res.write('data: [DONE]\n\n'); + return res.end(); + } + const reader = difyResp.body.getReader(); + const dec = new TextDecoder('utf-8'); + let buf = ''; + let lastAnswer = ''; + while (true) { + const { value, done } = await reader.read(); + if (done) break; + buf += dec.decode(value, { stream: true }); + const events = buf.split('\n\n'); + buf = events.pop() || ''; + for (const ev of events) { + const lines = ev.split('\n'); + for (const line of lines) { + if (!line.startsWith('data:')) continue; + const pl = line.slice(5).trim(); + if (!pl || pl === '[DONE]') continue; + let json = null; + try { json = JSON.parse(pl); } catch (e) { continue; } + if (json.event === 'message' && typeof json.answer === 'string') { + const delta = json.answer.slice(lastAnswer.length); + lastAnswer = json.answer; + if (delta) write({ delta }); + } + // message_end / workflow_finished 等终态事件:暂不额外处理,依赖流关闭 + } + } + } + log('AI 需求打磨(stream): ' + accountName); + res.write('data: [DONE]\n\n'); + res.end(); + } catch (e) { + try { write({ error: e.message }); res.write('data: [DONE]\n\n'); res.end(); } catch (e) {} } finally { clearTimeout(timer); } - const data = await resp.json().catch(() => ({})); - if (!resp.ok) return sendErr(502, 'AI 接口错误: ' + (data.message || resp.status)); - log('AI 需求打磨: ' + accountName); - return dshOk(200, { answer: data.answer || '' }); - } catch (e) { - return sendErr(500, '需求打磨失败: ' + e.message); + return; } + // 阻塞模式(兼容) + const ctrl = new AbortController(); + const timer = setTimeout(() => ctrl.abort(), 100000); + let difyResp; + try { + difyResp = await fetch(difyUrl + '/chat-messages', { + method: 'POST', + headers: { 'Authorization': 'Bearer ' + apiKey, 'Content-Type': 'application/json' }, + body: JSON.stringify({ inputs: { is_think: '0', is_online: '0', model: process.env.DIFY_MCN_MODEL || 'gemini' }, query, response_mode: 'blocking', user: 'mcn-work-shop' }), + signal: ctrl.signal, + }); + } finally { clearTimeout(timer); } + const data = await difyResp.json().catch(() => ({})); + if (!difyResp.ok) return sendErr(502, 'AI 接口错误: ' + (data.message || difyResp.status)); + log('AI 需求打磨: ' + accountName); + return dshOk(200, { answer: data.answer || '' }); }); return; } diff --git a/project/短视频脚本创作/V1.0/references-add/变更日志.md b/project/短视频脚本创作/V1.0/references-add/变更日志.md index 5d14f02..3603ef3 100644 --- a/project/短视频脚本创作/V1.0/references-add/变更日志.md +++ b/project/短视频脚本创作/V1.0/references-add/变更日志.md @@ -6,6 +6,13 @@ --- + +## 2026-09-07(术语统一:视觉短片 / 开场钩子 / 需求框-技能衔接) + +- **D·视觉脚本 → D·视觉短片(全量)**:S9 格式列举(SKILL.md)、S6 内容形式映射表(F04 测评种草/F07 纯视觉短片)、S9 正文 6 处、创作流程规范 3 处、03_框架节奏/07_脚本格式选择指南 8 处——与工作台需求框/用户既定术语对齐,消除「纯视觉短片→D·视觉脚本」混用。 +- **预设钩子 → 开场钩子(全量)**:S5 选题方案字段(含 10 种开场方式引用)+ S7 大纲回扣语义 + 04_爆款开场/00_口径说明——对外统称「开场钩子」(=S6 场1 段名、工作台需求框 hook 字段同名同义),S5 阶段语义由「预设」改为直接以开场钩子表达。 +- **需求框-技能衔接注明(mcn-work-shop/prompts/clarify.md)**:内容形式 4 项为粗选方向,落创作时细化为 S6 口径(口播→教程/知识分享/盘点,视觉短片→测评/沉浸式);故事框架四词(钩子前置/悬念反转/递进铺垫/先抑后扬)=达人倾向词,由 S6 用叙事形态 F01-F08 + 结构模板(三段式/五段式)定型。 + ## 2026-09-04(R73/R74 技能树体量优化) 主 SKILL.md 压缩维护:frontmatter `last_change` 明细迁移至本文件(历史归档);11 步流程 ASCII 总览图压缩为行式 Gate 链(S1→[G0]→…→[G6]);文件底部新增「阅读分层」说明(🔑 导航行/🚫 红线/无标记正文三类)。 diff --git a/project/短视频脚本创作/V1.0/references/创作流程/5_生成短视频选题.md b/project/短视频脚本创作/V1.0/references/创作流程/5_生成短视频选题.md index 2a7d392..8856fe4 100644 --- a/project/短视频脚本创作/V1.0/references/创作流程/5_生成短视频选题.md +++ b/project/短视频脚本创作/V1.0/references/创作流程/5_生成短视频选题.md @@ -256,7 +256,7 @@ 选题创作方法论:参照处理规则「二、选题创作方法论」中定义的 5 大方法及其推荐流程。方法论来源及更详细的创作技巧见知识库文件: - `知识库/02_故事选题/` 全部文件(元素叠加/跨界重组/新鲜度维度/落地路径/**选题三环模型**) -- `知识库/04_爆款开场/` 全部文件(10种开场方式,用于选题的"预设钩子"环节) +- `知识库/04_爆款开场/` 全部文件(10种开场方式,用于选题的"开场钩子"环节) - `知识库/07_情绪共鸣/` 分块4(网感坐标:赛道×叙事形态→目标体感映射表 + 体感兑现闭环)— 用于选题的"目标体感"确定 选题验证标准:参照处理规则「三、选题验证标准」中的硬性验证、三环校验和钩子预设要求。 @@ -314,7 +314,7 @@ | **人设匹配度** | {与达人身份/性格/价值的一致性说明} | | **目标体感** | {从网感坐标选取的目标体感 + 情绪标签 + 避免体感} | | **共鸣切入点** | {选题的「为什么」+ 切入点类型(缺失补偿/困境投射/身份反转/愿望实现)+ 一句共鸣测试语} | -| **预设钩子** | {钩子类型 + 开场方式(抓眼元素前3秒内出现;开场段总时长另守 3-7 秒铁律)} | +| **开场钩子** | {钩子类型 + 开场方式(抓眼元素前3秒内出现;开场段总时长另守 3-7 秒铁律)} | | **故事前提** | 剧情/悬念/反转类必填:核心设定 / 核心秘密 / 秘密曝光节点 / 信息边界 / 来源标注([U]/[AI]/[对标]);非剧情类标"不适用" | --- @@ -330,7 +330,7 @@ | **人设匹配度** | {与达人身份/性格/价值的一致性说明} | | **目标体感** | {从网感坐标选取的目标体感 + 情绪标签 + 避免体感} | | **共鸣切入点** | {选题的「为什么」+ 切入点类型 + 一句共鸣测试语} | -| **预设钩子** | {钩子类型 + 开场方式(抓眼元素前3秒内出现;开场段总时长另守 3-7 秒铁律)} | +| **开场钩子** | {钩子类型 + 开场方式(抓眼元素前3秒内出现;开场段总时长另守 3-7 秒铁律)} | | **故事前提** | 剧情/悬念/反转类必填:核心设定 / 核心秘密 / 秘密曝光节点 / 信息边界 / 来源标注([U]/[AI]/[对标]);非剧情类标"不适用" | --- @@ -346,7 +346,7 @@ | **人设匹配度** | {与达人身份/性格/价值的一致性说明} | | **目标体感** | {从网感坐标选取的目标体感 + 情绪标签 + 避免体感} | | **共鸣切入点** | {选题的「为什么」+ 切入点类型 + 一句共鸣测试语} | -| **预设钩子** | {钩子类型 + 开场方式(抓眼元素前3秒内出现;开场段总时长另守 3-7 秒铁律)} | +| **开场钩子** | {钩子类型 + 开场方式(抓眼元素前3秒内出现;开场段总时长另守 3-7 秒铁律)} | | **故事前提** | 剧情/悬念/反转类必填:核心设定 / 核心秘密 / 秘密曝光节点 / 信息边界 / 来源标注([U]/[AI]/[对标]);非剧情类标"不适用" | --- @@ -362,7 +362,7 @@ | **人设匹配度** | {与达人身份/性格/价值的一致性说明} | | **目标体感** | {从网感坐标选取的目标体感 + 情绪标签 + 避免体感} | | **共鸣切入点** | {选题的「为什么」+ 切入点类型 + 一句共鸣测试语} | -| **预设钩子** | {钩子类型 + 开场方式(抓眼元素前3秒内出现;开场段总时长另守 3-7 秒铁律)} | +| **开场钩子** | {钩子类型 + 开场方式(抓眼元素前3秒内出现;开场段总时长另守 3-7 秒铁律)} | | **故事前提** | 剧情/悬念/反转类必填:核心设定 / 核心秘密 / 秘密曝光节点 / 信息边界 / 来源标注([U]/[AI]/[对标]);非剧情类标"不适用" | --- @@ -378,7 +378,7 @@ | **人设匹配度** | {与达人身份/性格/价值的一致性说明} | | **目标体感** | {从网感坐标选取的目标体感 + 情绪标签 + 避免体感} | | **共鸣切入点** | {选题的「为什么」+ 切入点类型 + 一句共鸣测试语} | -| **预设钩子** | {钩子类型 + 开场方式(抓眼元素前3秒内出现;开场段总时长另守 3-7 秒铁律)} | +| **开场钩子** | {钩子类型 + 开场方式(抓眼元素前3秒内出现;开场段总时长另守 3-7 秒铁律)} | | **故事前提** | 剧情/悬念/反转类必填:核心设定 / 核心秘密 / 秘密曝光节点 / 信息边界 / 来源标注([U]/[AI]/[对标]);非剧情类标"不适用" | --- diff --git a/project/短视频脚本创作/V1.0/references/创作流程/6_生成短视频框架.md b/project/短视频脚本创作/V1.0/references/创作流程/6_生成短视频框架.md index d540545..01298c9 100644 --- a/project/短视频脚本创作/V1.0/references/创作流程/6_生成短视频框架.md +++ b/project/短视频脚本创作/V1.0/references/创作流程/6_生成短视频框架.md @@ -35,10 +35,10 @@ |---------|---------|------------| | 剧情 | F01 剧情演绎 | A·场次叙事 | | Vlog | F02 Vlog纪实 | B·Vlog双轨 | -| 测评 | F04 测评种草 | D·视觉脚本 | +| 测评 | F04 测评种草 | D·视觉短片 | | 教程 | F05 教程教学 | C·口播要点 | | 挑战 | F01 剧情演绎 | A·场次叙事 | -| 沉浸式 | F07 纯视觉短片 | D·视觉脚本 | +| 沉浸式 | F07 纯视觉短片 | D·视觉短片 | | 知识分享 | F03 干货口播 | C·口播要点 | > 补充叙事形态(无直接内容形式映射,需在框架阶段显式选择): diff --git a/project/短视频脚本创作/V1.0/references/创作流程/7_生成短视频大纲.md b/project/短视频脚本创作/V1.0/references/创作流程/7_生成短视频大纲.md index 957a3c4..6d93696 100644 --- a/project/短视频脚本创作/V1.0/references/创作流程/7_生成短视频大纲.md +++ b/project/短视频脚本创作/V1.0/references/创作流程/7_生成短视频大纲.md @@ -5,7 +5,7 @@ --- ## 输入 -- 步骤5输出:选题方案(主题/切入角度/爆款逻辑/预设钩子/差异化特征/目标体感/共鸣切入点) +- 步骤5输出:选题方案(主题/切入角度/爆款逻辑/开场钩子/差异化特征/目标体感/共鸣切入点) - 步骤6输出:用户选定的框架方案(核心框架+内容形式+时长+主线) - 步骤6输出:场次结构展开表(场次划分/时长区间/叙事节奏/情绪) - 步骤6输出:情绪设计卡(弧线类型/情绪标注/情绪起伏/缺口自检)— 只引用不重做 @@ -206,7 +206,7 @@ S6 场次结构表「事件」列是规划概述(如「准备场景 → 核心 - **场景预演**:在展开每场事件序列之前,先在内部完成该场关键场景的文字预演(空间/时间/灯光/人物位置/最显眼的 3 个细节),再基于预演画面写出事件序列。预演内容不写入最终大纲 - **素材使用**:按知识库/05_极致事件/确定的极致类型调取对应素材内容填充大纲,而非从零推断。极致类型(感官/戏剧)映射到具体素材类型,参见素材库/分块定义/。所有具名内容必须有品牌/材质/型号等具体信息,禁止泛称 - 结构模板选择规则:知识分享/技能展示/15s内→三段式,Vlog/剧情/测评/30s+→五段式 -- 钩子类型须回扣 S5 选题方案中的「预设钩子」,不另起炉灶 +- 钩子类型须回扣 S5 选题方案中的「开场钩子」,不另起炉灶 - 叙事模型必须明确选择一种,须与框架方案的爆款逻辑一致 - 时长分配须参照 S6 场次结构展开表,不得自行改动 - 口播须符合达人人设的语言风格和口头禅 @@ -222,7 +222,7 @@ S6 场次结构表「事件」列是规划概述(如「准备场景 → 核心 - 不得用概要式对话替代完整台词(如"她表达了感谢""他说他很贴心"),必须展开为「角色名(语气):具体台词」,一组对话至少 2 句有来有回 - 不得用概括替代操作步骤链(如"认真做饭""收拾房间"),必须拆解为 ≥3 步连贯动作(做什么 → 怎么做 → 细节) - 不得跳过密度自检直接提交 -- 不得在已有选题预设钩子的情况下重新设计钩子方向 +- 不得在已有选题开场钩子的情况下重新设计钩子方向 - 不得偏离 S6 场次结构展开表中的时长区间和情绪弧线 - 不得使用模糊描述(如"做一个有趣的钩子"),必须具体到台词和画面 - 不得跳过合规自检 diff --git a/project/短视频脚本创作/V1.0/references/创作流程/9_生成短视频脚本.md b/project/短视频脚本创作/V1.0/references/创作流程/9_生成短视频脚本.md index 221d48e..15cf07b 100644 --- a/project/短视频脚本创作/V1.0/references/创作流程/9_生成短视频脚本.md +++ b/project/短视频脚本创作/V1.0/references/创作流程/9_生成短视频脚本.md @@ -21,7 +21,7 @@ | F01 剧情演绎 | A·场次叙事 | 场1/场2… | 多角色+对白驱动+场景转换 | | F02/F08 | B·Vlog双轨 | 场1/场2… | 画面描述+旁白双轨,第一视角 | | F03/F05/F06 | C·口播要点 | 场1/场2… | 口播主体+[B-roll]标注 | -| F04/F07 | D·视觉脚本 | 场1/场2… | 画面驱动,少台词 | +| F04/F07 | D·视觉短片 | 场1/场2… | 画面驱动,少台词 | **格式间禁止混用**:F01 剧情类严禁旁白;F02/F08 不得套用剧情对白格式;F03/F05/F06 不得堆砌动作叙述;F07 不得写大段台词。 @@ -74,7 +74,7 @@ [B-roll:{画面描述}] ``` -**_D·视觉脚本(F04/F07):_** +**_D·视觉短片(F04/F07):_** ``` **场1:画面主题(Xs-Xs)** @@ -197,7 +197,7 @@ - F01 剧情演绎 → **A·场次叙事**(场1/场2…) - F02 Vlog纪实 / F08 人物采访 → **B·Vlog双轨**(场1/场2…) - F03 干货口播 / F05 教程教学 / F06 盘点解说 → **C·口播要点**(场1/场2…) - - F04 测评种草 / F07 纯视觉短片 → **D·视觉脚本**(场1/场2…) + - F04 测评种草 / F07 纯视觉短片 → **D·视觉短片**(场1/场2…) 3. 三大段结构(开场钩子/中间主体/结尾收口)+ 每段下设场次 4. 台词口语化、可直接朗读/配音,标注说话人和语气 5. 广告植入严格对齐广告植入大纲 @@ -228,7 +228,7 @@ - **B-roll 标注**:`[B-roll:画面描述]` 放在对应口播段落之后 - 结尾场以实用信息收尾,不强行升华 -**_D·视觉脚本(F04/F07):_** +**_D·视觉短片(F04/F07):_** - **场次标题**:**加粗**,`场N:画面主题(Xs-Xs)` - **画面驱动**:每场核心是画面描述(构图/光影/色彩/运镜节奏) - **配乐/音效标注**:`(配乐/音效:描述)` @@ -326,7 +326,7 @@ → F01 剧情演绎 → **A·场次叙事** 格式(场1/场2…) → F02/F08 → **B·Vlog双轨** 格式(场1/场2…) → F03/F05/F06 → **C·口播要点** 格式(场1/场2…) -→ F04/F07 → **D·视觉脚本** 格式(场1/场2…) +→ F04/F07 → **D·视觉短片** 格式(场1/场2…) 完整格式定义参见知识库 `03_框架节奏/07_脚本格式选择指南.md`。 @@ -419,7 +419,7 @@ **{角色名}**(口播·{语气}):{口播台词} ``` -## D·视觉脚本(F04/F07) +## D·视觉短片(F04/F07) ``` **场1:{画面主题}({X}-{X}秒)** diff --git a/project/短视频脚本创作/V1.0/references/创作流程规范.md b/project/短视频脚本创作/V1.0/references/创作流程规范.md index 1a8aa87..7676852 100644 --- a/project/短视频脚本创作/V1.0/references/创作流程规范.md +++ b/project/短视频脚本创作/V1.0/references/创作流程规范.md @@ -199,7 +199,7 @@ | F01 剧情演绎 | **A·场次叙事** | 场1/场2… | 多角色+对白驱动+场景转换 | | F02 Vlog纪实 / F08 人物采访 | **B·Vlog双轨** | 场1/场2… | 画面+旁白双轨,第一视角 | | F03 干货口播 / F05 教程教学 / F06 盘点解说 | **C·口播要点** | 场1/场2… | 口播主体 + B-roll标注 | -| F04 测评种草 / F07 纯视觉短片 | **D·视觉脚本** | 场1/场2… | 画面驱动,少台词 | +| F04 测评种草 / F07 纯视觉短片 | **D·视觉短片** | 场1/场2… | 画面驱动,少台词 | ### 格式间禁止混用 @@ -310,12 +310,12 @@ F05 教程教学类脚本必须包含**完整步骤链**,缺任一段 = 格式 --- -## D·视觉脚本格式(F04 测评种草 / F07 纯视觉短片) +## D·视觉短片格式(F04 测评种草 / F07 纯视觉短片) ``` **场1:画面主题(Xs-Xs)** -(画面描述——构图、光影、色彩、运镜节奏。视觉脚本的核心是画面本身。) +(画面描述——构图、光影、色彩、运镜节奏。视觉短片的核心是画面本身。) (配乐/音效:氛围音乐或关键音效,可选。) diff --git a/project/短视频脚本创作/V1.0/references/知识库/03_框架节奏/07_脚本格式选择指南.md b/project/短视频脚本创作/V1.0/references/知识库/03_框架节奏/07_脚本格式选择指南.md index a246e27..90ce118 100644 --- a/project/短视频脚本创作/V1.0/references/知识库/03_框架节奏/07_脚本格式选择指南.md +++ b/project/短视频脚本创作/V1.0/references/知识库/03_框架节奏/07_脚本格式选择指南.md @@ -23,7 +23,7 @@ | **A·场次叙事** | 场1/场2… | F01 剧情演绎 | 多角色+对白驱动+场景转换,完整场次叙事 | | **B·Vlog双轨** | 场1/场2… | F02 Vlog纪实 / F08 人物采访 | 画面+旁白/口播双层,第一视角沉浸 | | **C·口播要点** | 场1/场2… | F03 干货口播 / F05 教程教学 / F06 盘点解说 | 口播主体 + B-roll辅助,信息密度优先 | -| **D·视觉脚本** | 场1/场2… | F04 测评种草 / F07 纯视觉短片 | 画面驱动为主,少量台词/字幕说明 | +| **D·视觉短片** | 场1/场2… | F04 测评种草 / F07 纯视觉短片 | 画面驱动为主,少量台词/字幕说明 | --- @@ -167,7 +167,7 @@ --- -### D·视觉脚本(测评/纯视觉专用) +### D·视觉短片(测评/纯视觉专用) > 适用:F04 测评种草 / F07 纯视觉短片 | 编号:场1、场2、场3… > 特征:画面优先、氛围驱动、少台词或无台词 @@ -179,16 +179,16 @@ **内容格式:** ``` -(画面描述——自然段落,镜头中看到什么、镜头怎么运动、光影氛围。视觉脚本的核心就是画面本身。) +(画面描述——自然段落,镜头中看到什么、镜头怎么运动、光影氛围。视觉短片的核心就是画面本身。) (配乐/音效标注——该场次的氛围音乐或关键音效,可选。) -(台词/字幕:如有口播或字幕说明,简短标注。视觉脚本以画面为主,台词极少。) +(台词/字幕:如有口播或字幕说明,简短标注。视觉短片以画面为主,台词极少。) ``` **核心规则:** - 编号用阿拉伯数字:场1、场2、场3… -- 场标题:画面主题 + 时长,不标注地点·时间(视觉脚本的"地点"在画面描述中呈现) +- 场标题:画面主题 + 时长,不标注地点·时间(视觉短片的"地点"在画面描述中呈现) - **画面驱动**:每个场次的核心是画面描述——构图、光影、色彩、运镜节奏 - **配乐/音效敏感**:因为台词少,音乐和音效承担情绪引导功能,必须标注 - 台词极少(或完全没有),字幕仅为必要信息说明 @@ -229,7 +229,7 @@ │ → 内容:口播主体 + [B-roll:画面描述] 标注 │ └─ 叙事形态 = F04 测评种草 或 F07 纯视觉短片 - → 使用 D·视觉脚本格式 + → 使用 D·视觉短片格式 → 编号:场1、场2… → 内容:画面描述为主 + 配乐/音效 + 少量台词/字幕 ``` @@ -243,7 +243,7 @@ | F01 剧情类使用旁白 | 剧情必须靠角色行为和对话演绎,严禁旁白驱动 | | F02/F08 Vlog类使用角色对白格式 | Vlog的画面+旁白双轨≠剧情类的多角色对白 | | F03/F05/F06 口播类堆砌动作叙述 | 口播主体是讲述,动作描述仅标注关键手势/走位 | -| F07 纯视觉类写大段台词 | 视觉脚本画面驱动,台词极少或无 | +| F07 纯视觉类写大段台词 | 视觉短片画面驱动,台词极少或无 | | F04 测评类套用剧情格式 | 测评是画面+信息驱动,不需要角色扮演和冲突 | --- @@ -255,4 +255,4 @@ | 有剧本、有多个人物对话、有冲突反转 | **A·场次叙事(场1/场2…)** | | 第一视角边走边拍、画外音讲述感受 | **B·Vlog双轨(场1/场2…)** | | 一个人面对镜头讲干货、步骤清晰 | **C·口播要点(场1/场2…)** | -| 靠画面说话、音乐很重要、台词很少 | **D·视觉脚本(场1/场2…)** | +| 靠画面说话、音乐很重要、台词很少 | **D·视觉短片(场1/场2…)** | diff --git a/project/短视频脚本创作/V1.0/references/知识库/04_爆款开场/00_开场方式全量覆盖分析.md b/project/短视频脚本创作/V1.0/references/知识库/04_爆款开场/00_开场方式全量覆盖分析.md index 91cf9a3..a4b0cf2 100644 --- a/project/短视频脚本创作/V1.0/references/知识库/04_爆款开场/00_开场方式全量覆盖分析.md +++ b/project/短视频脚本创作/V1.0/references/知识库/04_爆款开场/00_开场方式全量覆盖分析.md @@ -4,7 +4,7 @@ > 目标:将零散的爆款开场知识系统化重组为10个标准分类文件 > **开场钩子时长铁律:3-7秒,硬上限10秒**(绝对时长口径,不看总时长占比)——任何开场方式(下表10种)都必须在此时间内完成核心钩子动作。 -> > 注意区分两个口径:本铁律管**开场段总时长**;流程文件中的「前3秒 / 3秒抓眼」(S5 预设钩子、S6 场1、S7 钩子有效性)指**抓眼元素呈现窗口**(刷到后3秒内必须出现),两者兼容不冲突。 +> > 注意区分两个口径:本铁律管**开场段总时长**;流程文件中的「前3秒 / 3秒抓眼」(S5 开场钩子、S6 场1、S7 钩子有效性)指**抓眼元素呈现窗口**(刷到后3秒内必须出现),两者兼容不冲突。 ---