refactor(workshop): 需求表单 8→6 行——叙事形态改名叙事结构,删核心情节/结尾落点 v18
- REQ_FIELDS:删 plot/end,form 标签 叙事形态→叙事结构(idea/pain/relate/blow/form/note 6 字段) - w 初值同步删 plot/end;情节与结尾由 AI 创作阶段按人设与选题自决,无需用户填写 - clarify 服务端模板同步 6 行 + 注释;提示补充情节/结尾/时长不由清单承载 - 空态/注释同步
This commit is contained in:
1 parent
0fbf25ce42
commit
f45eb4a692
2 files changed
+9
-8
No files matched your search
@@ -172,9 +172,10 @@ function bindBatchBar(container, opts) {
|
|||||||
// 09-02 v10:需求框定位=选题补充——只留本条视频选题侧字段 + 兜底「其他」;账号设定类(情绪基调/受众/结构性符号/广告植入)由人设卡提供,不在此重复收集
|
// 09-02 v10:需求框定位=选题补充——只留本条视频选题侧字段 + 兜底「其他」;账号设定类(情绪基调/受众/结构性符号/广告植入)由人设卡提供,不在此重复收集
|
||||||
// 09-02 v11:dur 时长目标 移出需求栏 → 需求栏下方「功能选项」下拉(30/60/120/240/自定义),值由 w.dur 单独承载
|
// 09-02 v11:dur 时长目标 移出需求栏 → 需求栏下方「功能选项」下拉(30/60/120/240/自定义),值由 w.dur 单独承载
|
||||||
// 09-02 v17:选题落点要素(痛点/爽点、共鸣/共情、爆点)加入需求框——创作流程需要但不是必填,空值由 AI 按账号设定与选题自决
|
// 09-02 v17:选题落点要素(痛点/爽点、共鸣/共情、爆点)加入需求框——创作流程需要但不是必填,空值由 AI 按账号设定与选题自决
|
||||||
|
// 09-02 v18:叙事形态→叙事结构;删核心情节/结尾落点(情节与结尾由 AI 创作阶段自决,无需用户填写)
|
||||||
const REQ_FIELDS = [
|
const REQ_FIELDS = [
|
||||||
['idea', '一句话创意'], ['pain', '痛点/爽点'], ['relate', '共鸣/共情'], ['blow', '爆点'],
|
['idea', '一句话创意'], ['pain', '痛点/爽点'], ['relate', '共鸣/共情'], ['blow', '爆点'],
|
||||||
['form', '叙事形态'], ['plot', '核心情节'], ['end', '结尾落点'], ['note', '其他约束'],
|
['form', '叙事结构'], ['note', '其他约束'],
|
||||||
];
|
];
|
||||||
// 需求 md 行 → 向导字段映射(parseReqSheet 用)
|
// 需求 md 行 → 向导字段映射(parseReqSheet 用)
|
||||||
const REQ_LABEL_TO_KEY = Object.fromEntries(REQ_FIELDS.map(([k, label]) => [label, k]));
|
const REQ_LABEL_TO_KEY = Object.fromEntries(REQ_FIELDS.map(([k, label]) => [label, k]));
|
||||||
@@ -214,11 +215,11 @@ function buildCreatePrompt(accountName, w, personaBlock, hasPersona, attachText)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* AI创作 弹窗(09-02 v2:需求框 + 输入框 对话式打磨)——
|
/* AI创作 弹窗(09-02 v2:需求框 + 输入框 对话式打磨)——
|
||||||
顶部=创作需求(8 行选题字段:创意+落点要素(痛点爽点/共鸣/爆点,可空)+形态/情节/结尾/其他,随对话逐轮被 AI 更新,空值标「待完善」)+ 下方时长选项条
|
顶部=创作需求(6 行选题字段:创意+落点要素(痛点爽点/共鸣/爆点,可空)+叙事结构/其他约束,随对话逐轮被 AI 更新,空值标「待完善」)+ 下方时长选项条
|
||||||
中部=对话流(用户输入 vs AI 整理/追问),底部=输入框 + 提交创作 */
|
中部=对话流(用户输入 vs AI 整理/追问),底部=输入框 + 提交创作 */
|
||||||
async function confirmAiScript(accountName, btn) {
|
async function confirmAiScript(accountName, btn) {
|
||||||
const accId = btn && btn.dataset.accId ? btn.dataset.accId : '';
|
const accId = btn && btn.dataset.accId ? btn.dataset.accId : '';
|
||||||
const w = { idea: '', pain: '', relate: '', blow: '', form: '', dur: '', plot: '', end: '', note: '' };
|
const w = { idea: '', pain: '', relate: '', blow: '', form: '', dur: '', note: '' };
|
||||||
const convo = []; // 对话流 { role: 'u' | 'ai', text }
|
const convo = []; // 对话流 { role: 'u' | 'ai', text }
|
||||||
let pHas = false, pContent = '';
|
let pHas = false, pContent = '';
|
||||||
let attach = { name: '', text: '' }; // 已上传需求附件(解析后的纯文本,打磨与创作共用)
|
let attach = { name: '', text: '' }; // 已上传需求附件(解析后的纯文本,打磨与创作共用)
|
||||||
@@ -281,7 +282,7 @@ async function confirmAiScript(accountName, btn) {
|
|||||||
mask.querySelector('[data-x]').addEventListener('click', close);
|
mask.querySelector('[data-x]').addEventListener('click', close);
|
||||||
const q = (s) => mask.querySelector(s);
|
const q = (s) => mask.querySelector(s);
|
||||||
|
|
||||||
/* —— 需求框渲染:8 个选题字段(含落点要素,可空),维度名作标题、内容在下方 —— */
|
/* —— 需求框渲染:6 个选题字段(含落点要素可空;情节/结尾由 AI 创作自决),维度名作标题、内容在下方 —— */
|
||||||
const renderSheet = () => {
|
const renderSheet = () => {
|
||||||
const rows = REQ_FIELDS.map(([k, label]) => {
|
const rows = REQ_FIELDS.map(([k, label]) => {
|
||||||
const v = String(w[k] || '').trim();
|
const v = String(w[k] || '').trim();
|
||||||
@@ -324,7 +325,7 @@ async function confirmAiScript(accountName, btn) {
|
|||||||
? convo.map((c) => (c.role === 'u'
|
? convo.map((c) => (c.role === 'u'
|
||||||
? `<div class="req-msg mine"><div class="req-bubble">${esc(c.text)}</div></div>`
|
? `<div class="req-msg mine"><div class="req-bubble">${esc(c.text)}</div></div>`
|
||||||
: `<div class="req-msg ai"><div class="req-bubble">${esc(c.text)}</div></div>`)).join('')
|
: `<div class="req-msg ai"><div class="req-bubble">${esc(c.text)}</div></div>`)).join('')
|
||||||
: `<div class="req-dialog-empty">在这里输入一个初步想法,AI 会引导你补齐创意 / 痛点爽点 / 共鸣 / 爆点 / 形态 / 情节 / 结尾等需求(落点要素可留空,由 AI 按账号设定与选题补齐);每条输入都会即时更新上方的创作需求框。支持上传附件。Enter 发送,Shift+Enter 换行。</div>`;
|
: `<div class="req-dialog-empty">在这里输入一个初步想法,AI 会引导你补齐创意 / 痛点爽点 / 共鸣 / 爆点 / 叙事结构等需求(落点要素可留空,由 AI 按账号设定与选题补齐,情节与结尾由 AI 创作);每条输入都会即时更新上方的创作需求框。支持上传附件。Enter 发送,Shift+Enter 换行。</div>`;
|
||||||
el.scrollTop = el.scrollHeight;
|
el.scrollTop = el.scrollHeight;
|
||||||
};
|
};
|
||||||
const setBusy = (b) => {
|
const setBusy = (b) => {
|
||||||
|
|||||||
@@ -652,7 +652,7 @@ const server = http.createServer((req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---------- AI 创作需求打磨(09-02 弹窗内对话:Dify chat-messages 同步、无状态多轮) ----------
|
// ---------- AI 创作需求打磨(09-02 弹窗内对话:Dify chat-messages 同步、无状态多轮) ----------
|
||||||
// body: { accountName, turn(本轮输入), reqMd(当前需求5行md), history(最近轮[{u,a}]) } → { answer }
|
// body: { accountName, turn(本轮输入), reqMd(当前需求6行md), history(最近轮[{u,a}]) } → { answer }
|
||||||
if (p === '/api/ai/clarify' && req.method === 'POST') {
|
if (p === '/api/ai/clarify' && req.method === 'POST') {
|
||||||
let body = '';
|
let body = '';
|
||||||
req.on('data', (c) => { body += c; if (body.length > 1e6) req.destroy(); });
|
req.on('data', (c) => { body += c; if (body.length > 1e6) req.destroy(); });
|
||||||
@@ -704,8 +704,8 @@ const server = http.createServer((req, res) => {
|
|||||||
+ '3. 若信息已基本齐备:用一两句话总结并提示用户可点击「提交创作」。\n'
|
+ '3. 若信息已基本齐备:用一两句话总结并提示用户可点击「提交创作」。\n'
|
||||||
+ '4. 输出必须严格分两段:第一段是对用户说的话(120 字以内,含必要时的问题);\n'
|
+ '4. 输出必须严格分两段:第一段是对用户说的话(120 字以内,含必要时的问题);\n'
|
||||||
+ '然后另起一行单独输出 ==REQ==;\n'
|
+ '然后另起一行单独输出 ==REQ==;\n'
|
||||||
+ '接着输出 8 行需求清单(仍未确定的项留空值,严禁编造),模板:\n'
|
+ '接着输出 6 行需求清单(仍未确定的项留空值,严禁编造;情节/结尾/时长不由本清单承载,由创作阶段按人设与选题自决),模板:\n'
|
||||||
+ '- 一句话创意:\n- 痛点/爽点:\n- 共鸣/共情:\n- 爆点:\n- 叙事形态:\n- 核心情节:\n- 结尾落点:\n- 其他约束:\n'
|
+ '- 一句话创意:\n- 痛点/爽点:\n- 共鸣/共情:\n- 爆点:\n- 叙事结构:\n- 其他约束:\n'
|
||||||
+ '5. 需求框只收集本条视频的选题相关信息;账号级设定(情绪基调 / 目标受众 / 结构性符号 / 广告植入等)已由账号设定自动带入创作,不要向达人重复询问这些维度——仅当达人主动提出与账号设定不同的要求时,将其记入「其他约束」。';
|
+ '5. 需求框只收集本条视频的选题相关信息;账号级设定(情绪基调 / 目标受众 / 结构性符号 / 广告植入等)已由账号设定自动带入创作,不要向达人重复询问这些维度——仅当达人主动提出与账号设定不同的要求时,将其记入「其他约束」。';
|
||||||
// 4) Dify blocking 调用
|
// 4) Dify blocking 调用
|
||||||
const difyUrl = String(process.env.DIFY_URL || 'https://mydify.youmanvideo.com/v1').replace(/\/+$/, '');
|
const difyUrl = String(process.env.DIFY_URL || 'https://mydify.youmanvideo.com/v1').replace(/\/+$/, '');
|
||||||
|
|||||||
Reference in new issue
Block a user