@xiaohhhh1/canvas-agent 0.4.77 → 0.4.78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -25,6 +25,16 @@ export type FlowCContentSummary = {
|
|
|
25
25
|
proof: string;
|
|
26
26
|
voiceover: string;
|
|
27
27
|
};
|
|
28
|
+
export type FlowCContentAdvisory = {
|
|
29
|
+
ordinal: number;
|
|
30
|
+
code: "voice_pacing" | "ending_frame_unanchored" | "repeated_opening";
|
|
31
|
+
segment?: number;
|
|
32
|
+
shot?: number;
|
|
33
|
+
wordCount?: number;
|
|
34
|
+
suggestedMaxWords?: number;
|
|
35
|
+
durationSeconds?: number;
|
|
36
|
+
matchedOrdinal?: number;
|
|
37
|
+
};
|
|
28
38
|
/** Missing/unknown versions keep historical tasks on their exact original prompt. */
|
|
29
39
|
export declare function flowCContentStrategy(value: unknown): FlowCContentStrategy | null;
|
|
30
40
|
export declare function flowCContentDirection(value: unknown, strategy: FlowCContentStrategy | null): FlowCContentDirection | null;
|
|
@@ -32,6 +42,11 @@ export declare function flowCContentDirection(value: unknown, strategy: FlowCCon
|
|
|
32
42
|
export declare function summarizeFlowCContentJob(value: unknown): FlowCContentSummary | null;
|
|
33
43
|
/** Only centrally acknowledged ordinals are durable avoidance evidence. */
|
|
34
44
|
export declare function mergeFlowCContentSummaries(values: unknown, jobs: unknown, acceptedOrdinals: readonly number[]): FlowCContentSummary[];
|
|
45
|
+
/** Advisory only: never rewrite, reject, or claim semantic acceptance of a script. */
|
|
46
|
+
export declare function flowCContentAdvisories(jobs: unknown, strategy: FlowCContentStrategy | null, options?: {
|
|
47
|
+
targetLanguage?: unknown;
|
|
48
|
+
recentScripts?: unknown;
|
|
49
|
+
}): FlowCContentAdvisory[];
|
|
35
50
|
export declare function flowCContentMethodPrompt(strategy: FlowCContentStrategy | null, options: {
|
|
36
51
|
recentScripts?: unknown;
|
|
37
52
|
productIndexes: number[];
|
|
@@ -65,6 +65,55 @@ export function mergeFlowCContentSummaries(values, jobs, acceptedOrdinals) {
|
|
|
65
65
|
}
|
|
66
66
|
return [...result.values()].slice(-40);
|
|
67
67
|
}
|
|
68
|
+
/** Advisory only: never rewrite, reject, or claim semantic acceptance of a script. */
|
|
69
|
+
export function flowCContentAdvisories(jobs, strategy, options = {}) {
|
|
70
|
+
if (!strategy || !Array.isArray(jobs))
|
|
71
|
+
return [];
|
|
72
|
+
const language = text(options.targetLanguage, 160).toLowerCase();
|
|
73
|
+
const wordBudgetApplies = /^(?:en|es)(?:[-_]|$)|\b(?:english|spanish|español|espanol|inglés|ingles)\b|英语|英語|美语|美語|西班牙语|西班牙語|西语|西語/u.test(language);
|
|
74
|
+
const spoken = (value) => {
|
|
75
|
+
const line = text(typeof value === "string" ? value : "", 20_000);
|
|
76
|
+
return /^(?:none|无|sin voz|sin diálogo)$/i.test(line) ? "" : line;
|
|
77
|
+
};
|
|
78
|
+
const seen = (Array.isArray(options.recentScripts) ? options.recentScripts : []).map(summary).filter((item) => Boolean(item));
|
|
79
|
+
const advisories = [];
|
|
80
|
+
for (const value of jobs) {
|
|
81
|
+
const job = object(value);
|
|
82
|
+
if (!Number.isInteger(job.ordinal) || Number(job.ordinal) < 1 || !Number.isInteger(job.productIndex) || Number(job.productIndex) < 0)
|
|
83
|
+
continue;
|
|
84
|
+
const ordinal = Number(job.ordinal);
|
|
85
|
+
const segments = Array.isArray(job.segments) ? job.segments : job.segment ? [job.segment] : [];
|
|
86
|
+
let opening = "";
|
|
87
|
+
for (const [segmentIndex, segmentValue] of segments.entries()) {
|
|
88
|
+
const segment = object(segmentValue);
|
|
89
|
+
const shots = Array.isArray(segment.shots) ? segment.shots.map(object) : [];
|
|
90
|
+
for (const [shotIndex, shot] of shots.entries()) {
|
|
91
|
+
const voice = spoken(shot.voiceover);
|
|
92
|
+
if (!opening && voice)
|
|
93
|
+
opening = voice;
|
|
94
|
+
const seconds = Number(shot.endSeconds) - Number(shot.startSeconds);
|
|
95
|
+
// Do not apply an English/Spanish word estimate to other scripts.
|
|
96
|
+
if (!voice || !wordBudgetApplies || /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u.test(voice) || !Number.isFinite(seconds) || seconds <= 0)
|
|
97
|
+
continue;
|
|
98
|
+
const words = (voice.match(/[\p{L}\p{N}]+(?:['’][\p{L}\p{N}]+)*/gu) || []).length;
|
|
99
|
+
const suggestedMaxWords = Math.floor(seconds * 2);
|
|
100
|
+
if (words > suggestedMaxWords)
|
|
101
|
+
advisories.push({ ordinal, code: "voice_pacing", segment: segmentIndex + 1, shot: shotIndex + 1, wordCount: words, suggestedMaxWords, durationSeconds: seconds });
|
|
102
|
+
}
|
|
103
|
+
const frame = text(object(segment.endingState).endingFrame, 20_000);
|
|
104
|
+
if (shots.length && frame && !text(shots.at(-1)?.visual, 20_000).endsWith(frame)) {
|
|
105
|
+
advisories.push({ ordinal, code: "ending_frame_unanchored", segment: segmentIndex + 1, shot: shots.length });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (strategy.mode === "smart-diverse" && opening) {
|
|
109
|
+
const match = seen.find((item) => item.ordinal !== ordinal && item.productIndex === job.productIndex && spoken(item.voiceover).toLowerCase() === opening.toLowerCase());
|
|
110
|
+
if (match)
|
|
111
|
+
advisories.push({ ordinal, code: "repeated_opening", matchedOrdinal: match.ordinal });
|
|
112
|
+
seen.push({ ordinal, productIndex: Number(job.productIndex), opening: "", proof: "", voiceover: opening });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return advisories;
|
|
116
|
+
}
|
|
68
117
|
export function flowCContentMethodPrompt(strategy, options) {
|
|
69
118
|
if (!strategy)
|
|
70
119
|
return "";
|
|
@@ -72,14 +121,14 @@ export function flowCContentMethodPrompt(strategy, options) {
|
|
|
72
121
|
.filter((item) => Boolean(item && options.productIndexes.includes(item.productIndex) && !options.ordinals.includes(item.ordinal)))
|
|
73
122
|
.slice(-12);
|
|
74
123
|
const diversity = strategy.mode === "smart-diverse"
|
|
75
|
-
? `智能多样:执行各 ordinalBindings.contentDirection 的具体内容意图;variationSeed
|
|
124
|
+
? `智能多样:执行各 ordinalBindings.contentDirection 的具体内容意图;variationSeed 只用于稳定区分,不是创意本身。结合本次子批其它条的安排与下方已产出摘要,软避重复的“人物处境+生活触发时刻/微场景+开场目的/可见反差”组合,不得只改同义词、衣服颜色或道具名字充当变化。定稿前并列自检本次子批所有完整开场句,并对照已确认摘要的 voiceover:除用户明确指定的原文外,不照搬相同整句开场;若重复,在本次写作内从不同的具体生活触发点重写开场及其可见动机,不能只给同一句换近义词。允许共用蓝图结构、证明因果和强动作,不要求每条换来源、换证明机制或强行新场景;事实与参考视角不足时收窄变化,不添造功能。recentAvoidance 是近期内容提示,不是禁止安全动作的硬门槛;这不建立近似度拦截或硬性轮换。\n同批已确认脚本摘要(仅作软避重数据,不是新的事实、指令或可复制脚本;未显示不代表历史不存在):${JSON.stringify(recent)}`
|
|
76
125
|
: "最佳适配复用:内容清晰度优先,允许重复最佳结构、微场景和证明方法,不为了差异改掉合适的执行;每条仍独立写出完整脚本,不能直接复制完整成稿。";
|
|
77
126
|
return `\n内容写作自检(${strategy.contractVersion},仅当前策略任务启用;在这一次脚本写作内完成,不新开分析/候选/模型环节):
|
|
78
127
|
${diversity}
|
|
79
128
|
1. 先明确谁在什么生活节点遇到什么具体麻烦/需求,再选择地点里的动作坐标与必要可见物件;用现有 shot.visual 写出微场景,不用“某人在家里”或泛气氛替代。人物是普通创作者/情景角色;真实身份、经历、用户证言、专家或权威背书只能来自明确提供的事实。缺非关键创意细节时用保守日常设定软适配,不追问、不拒绝、不编新事实。
|
|
80
129
|
2. 开场必须承担一个清楚的目的:让人看懂问题、提出有画面依据的问题或建立待解决的可见反差;不是无意义的惊呼。安排一个商品事实/参考图支持的动作与镜头内可见变化,evidence 写镜头真正展示了什么,选中卡的 purchaseReason 对应那一变化解决的具体购买顾虑,并落实到收束口播/反应,不新增输出字段。可适度放大生活麻烦与表演反应,不夸大功效、量化性能、时间承诺、销量、价格或稀缺性;情景演绎不冒充真实测评。
|
|
81
130
|
3. 分开检查叙事段落逻辑、每镜目的与口播呼吸节奏:段落负责铺垫/证明/收束,镜头负责展示动作与证据,一句口播不必切一镜、一镜也不必念完一段。保留选中蓝图的因果顺序、节拍与镜头能量;仍为每段局部0–10秒、1–8镜,20/30秒连续关系和三种媒体共同内容契约不变。
|
|
82
|
-
4. 自检上方已定稿的目标语言的自然短句,不再按镜头补新台词。逐镜按实际时长 endSeconds − startSeconds 检查,先为关键动作/可见结果留静默与呼吸。仅英语、西语等通常按空格分词的语言,以写作起点粗估约2词/秒,动作/结果镜头更少:1.5秒约2–3词,不塞完整8
|
|
131
|
+
4. 自检上方已定稿的目标语言的自然短句,不再按镜头补新台词。逐镜按实际时长 endSeconds − startSeconds 检查,先为关键动作/可见结果留静默与呼吸。仅英语、西语等通常按空格分词的语言,以写作起点粗估约2词/秒,动作/结果镜头更少:1.5秒约2–3词,不塞完整8词问句;2秒约3–4词,不塞10词CTA;2.6秒约4–5词,不塞13词整句。整段10秒约12–20词只是起点,可以更少,不是最低字数要求;全段词数够少也不能把口播挤在一两个短镜头,不能借其它静默镜头的时长冲抵当前镜头超载。不能套用到中文、日语等其它语言,不套统一英语词数或固定八秒模板。这是同轮软预算,不是语言验收拒绝条件或新重写环节;只在用户锁定内容之外收窄意图、缩短句子,不得提高语速硬塞。voiceProfile.speakingRate/pauseHabit 与 voiceCue 要符合实际可说完的语速,不得标成 unhurried/慢速却塞满台词,也不能用 brisk 掩盖超载;emotionalNote 只写镜头目的/情绪转折。不输出计算、推理或预算报告。
|
|
83
132
|
5. contentDirection 只细化既有蓝图可替换的人物、微场景、措辞和执行槽位,不改变商品身份、纹理文字、能力证据、0–10秒分段或媒体制作方式。creativeBrief 始终最高优先;用户框架 ordinal ${JSON.stringify(options.frameworkOrdinals)} 的开头、事件顺序、核心剧情和结尾不可被方向卡/避重覆盖,只能在其留白处具体化。
|
|
84
133
|
返回前按以上内容要求自检,修正能在本次写作中修正的空泛描述。自检不是新验收闸门;只输出原严格 schema 的既有字段,不输出推理、自检报告、contentDirection 或其它新增字段。\n`;
|
|
85
134
|
}
|
|
@@ -2,9 +2,9 @@ import type { AgentEmit } from "../agent/types.js";
|
|
|
2
2
|
import { type CanvasAgentConfig } from "../config.js";
|
|
3
3
|
import { type FlowCProductExecutionProfile } from "./product-profile.js";
|
|
4
4
|
import { type CommerceRequestDiagnostic } from "./commerce-http.js";
|
|
5
|
-
import { type FlowCContentStrategy, type FlowCContentSummary } from "./content-method.js";
|
|
5
|
+
import { type FlowCContentAdvisory, type FlowCContentStrategy, type FlowCContentSummary } from "./content-method.js";
|
|
6
6
|
export declare const FLOW_C_CODEX_TURN_TIMEOUT_MS: number;
|
|
7
|
-
export declare const FLOW_C_SELECTED_BLUEPRINT_PROMPT_MAX_CHARS =
|
|
7
|
+
export declare const FLOW_C_SELECTED_BLUEPRINT_PROMPT_MAX_CHARS = 45000;
|
|
8
8
|
export declare const FLOW_C_SCRIPT_REWRITE_MAX_ATTEMPTS = 2;
|
|
9
9
|
export declare const FLOW_C_CANDIDATE_REVISION_MAX_ATTEMPTS = 2;
|
|
10
10
|
export declare const FLOW_C_LEGACY_DIRECT_CONTRACT_VERSION = "flow-c-template-direct-v2";
|
|
@@ -29,6 +29,7 @@ type ScriptRecord = {
|
|
|
29
29
|
pendingScriptJobs?: DraftJob[];
|
|
30
30
|
contentStrategy?: FlowCContentStrategy;
|
|
31
31
|
contentSummaries?: FlowCContentSummary[];
|
|
32
|
+
contentAdvisories?: FlowCContentAdvisory[];
|
|
32
33
|
lastFailure?: CommerceRequestDiagnostic;
|
|
33
34
|
retryRequested?: boolean;
|
|
34
35
|
priorityAt?: string;
|
|
@@ -203,6 +204,11 @@ export declare class WorkflowManager {
|
|
|
203
204
|
accessToken?: unknown;
|
|
204
205
|
expiresAt?: unknown;
|
|
205
206
|
}): {
|
|
207
|
+
contentReview?: {
|
|
208
|
+
blocking: boolean;
|
|
209
|
+
semanticAcceptance: string;
|
|
210
|
+
advisories: FlowCContentAdvisory[];
|
|
211
|
+
} | undefined;
|
|
206
212
|
id: string;
|
|
207
213
|
status: ScriptStatus;
|
|
208
214
|
requestedCount: number;
|
|
@@ -215,6 +221,11 @@ export declare class WorkflowManager {
|
|
|
215
221
|
updatedAt: string;
|
|
216
222
|
};
|
|
217
223
|
retryScript(idValue: unknown): {
|
|
224
|
+
contentReview?: {
|
|
225
|
+
blocking: boolean;
|
|
226
|
+
semanticAcceptance: string;
|
|
227
|
+
advisories: FlowCContentAdvisory[];
|
|
228
|
+
} | undefined;
|
|
218
229
|
id: string;
|
|
219
230
|
status: ScriptStatus;
|
|
220
231
|
requestedCount: number;
|
|
@@ -227,6 +238,11 @@ export declare class WorkflowManager {
|
|
|
227
238
|
updatedAt: string;
|
|
228
239
|
};
|
|
229
240
|
scriptStatus(idValue: unknown): {
|
|
241
|
+
contentReview?: {
|
|
242
|
+
blocking: boolean;
|
|
243
|
+
semanticAcceptance: string;
|
|
244
|
+
advisories: FlowCContentAdvisory[];
|
|
245
|
+
} | undefined;
|
|
230
246
|
id: string;
|
|
231
247
|
status: ScriptStatus;
|
|
232
248
|
requestedCount: number;
|
|
@@ -371,6 +387,8 @@ export declare class WorkflowManager {
|
|
|
371
387
|
private runCandidateChunk;
|
|
372
388
|
/** 一个 chunk 使用独立线程;解析与中心持久化失败不会影响并行 lane。 */
|
|
373
389
|
private runScriptChunk;
|
|
390
|
+
/** Nonblocking, text-free diagnostics for newly generated policy scripts only. */
|
|
391
|
+
private noteScriptContentAdvisories;
|
|
374
392
|
/** 只记录阶段、ordinal 和毫秒数;禁止记录 prompt、令牌或中心能力。 */
|
|
375
393
|
private emitScriptStage;
|
|
376
394
|
/**
|
package/dist/workflow/manager.js
CHANGED
|
@@ -14,10 +14,10 @@ import { FLOW_C_CREATIVE_CANDIDATE_CONTRACT_VERSION, flowCCreativeCandidateOutpu
|
|
|
14
14
|
import { FLOW_C_LEGACY_PRODUCT_EXECUTION_PROFILE_CONTRACT_VERSION, FLOW_C_PRODUCT_EXECUTION_PROFILE_CONTRACT_VERSION, flowCProductExecutionProfileOutputSchema, parseFlowCProductExecutionProfileOutput } from "./product-profile.js";
|
|
15
15
|
import { flowCScriptOutputSchema, parseFlowCScriptOutput } from "./script-output.js";
|
|
16
16
|
import { commerceJson, CommerceRequestError } from "./commerce-http.js";
|
|
17
|
-
import { FLOW_C_CONTENT_STRATEGY_VERSION, flowCContentDirection, flowCContentMethodPrompt, flowCContentStrategy, mergeFlowCContentSummaries } from "./content-method.js";
|
|
17
|
+
import { FLOW_C_CONTENT_STRATEGY_VERSION, flowCContentAdvisories, flowCContentDirection, flowCContentMethodPrompt, flowCContentStrategy, mergeFlowCContentSummaries } from "./content-method.js";
|
|
18
18
|
const STATE_FILE = path.join(CONFIG_DIR, "workflow-state.json");
|
|
19
19
|
export const FLOW_C_CODEX_TURN_TIMEOUT_MS = 8 * 60 * 1000;
|
|
20
|
-
export const FLOW_C_SELECTED_BLUEPRINT_PROMPT_MAX_CHARS =
|
|
20
|
+
export const FLOW_C_SELECTED_BLUEPRINT_PROMPT_MAX_CHARS = 45_000;
|
|
21
21
|
export const FLOW_C_SCRIPT_REWRITE_MAX_ATTEMPTS = 2;
|
|
22
22
|
export const FLOW_C_CANDIDATE_REVISION_MAX_ATTEMPTS = 2;
|
|
23
23
|
export const FLOW_C_LEGACY_DIRECT_CONTRACT_VERSION = "flow-c-template-direct-v2";
|
|
@@ -70,6 +70,7 @@ export class WorkflowManager {
|
|
|
70
70
|
activeChunks: 0,
|
|
71
71
|
productProfiles: previous?.productProfiles || [],
|
|
72
72
|
pendingScriptJobs: previous?.pendingScriptJobs || [],
|
|
73
|
+
...(previous?.contentAdvisories ? { contentAdvisories: previous.contentAdvisories } : {}),
|
|
73
74
|
lastFailure: previous?.lastFailure,
|
|
74
75
|
priorityAt: now(),
|
|
75
76
|
message: previous?.status === "complete" ? previous.message : "已进入本机 Codex 队列",
|
|
@@ -155,7 +156,9 @@ export class WorkflowManager {
|
|
|
155
156
|
record.contentSummaries = mergeFlowCContentSummaries(record.contentSummaries, jobs, record.receivedOrdinals);
|
|
156
157
|
record.pendingScriptJobs = (record.pendingScriptJobs || []).filter((job) => !record.receivedOrdinals.includes(job.ordinal));
|
|
157
158
|
record.status = data.status === "ready" ? "complete" : "running";
|
|
158
|
-
record.message = data.status === "ready"
|
|
159
|
+
record.message = data.status === "ready"
|
|
160
|
+
? flowCContentStrategy(record.contentStrategy) ? `全部 ${record.requestedCount} 条脚本已回传(内容语义仍需审阅)` : `全部 ${record.requestedCount} 条高质量脚本已回传`
|
|
161
|
+
: `已回传 ${data.received}/${data.requestedCount} 条脚本`;
|
|
159
162
|
record.updatedAt = now();
|
|
160
163
|
this.save();
|
|
161
164
|
return data;
|
|
@@ -446,7 +449,7 @@ export class WorkflowManager {
|
|
|
446
449
|
const finalTask = await this.scriptTask(id);
|
|
447
450
|
if (finalTask.status === "ready" && record.receivedOrdinals.length === task.requested_count) {
|
|
448
451
|
record.status = "complete";
|
|
449
|
-
record.message = `全部 ${task.requested_count} 条高质量脚本已回传`;
|
|
452
|
+
record.message = flowCContentStrategy(task.creative_strategy) ? `全部 ${task.requested_count} 条脚本已回传(内容语义仍需审阅)` : `全部 ${task.requested_count} 条高质量脚本已回传`;
|
|
450
453
|
}
|
|
451
454
|
else
|
|
452
455
|
throw new Error(`中心校验尚未通过(${record.receivedOrdinals.length}/${task.requested_count} 条),请点击重试`);
|
|
@@ -656,6 +659,7 @@ export class WorkflowManager {
|
|
|
656
659
|
creativePlan: { ...(job.creativePlan || {}), ...selectedCandidatePlan(selected.get(job.ordinal), flowCContentStrategy(task.creative_strategy)) },
|
|
657
660
|
}));
|
|
658
661
|
this.emitScriptStage(id, ordinals, "parse", Date.now() - parseStartedAt);
|
|
662
|
+
this.noteScriptContentAdvisories(id, task, jobs);
|
|
659
663
|
const persistStartedAt = Date.now();
|
|
660
664
|
await this.submitGeneratedScriptJobs(id, jobs);
|
|
661
665
|
this.emitScriptStage(id, ordinals, "persist", Date.now() - persistStartedAt);
|
|
@@ -737,6 +741,21 @@ export class WorkflowManager {
|
|
|
737
741
|
};
|
|
738
742
|
}
|
|
739
743
|
}
|
|
744
|
+
/** Nonblocking, text-free diagnostics for newly generated policy scripts only. */
|
|
745
|
+
noteScriptContentAdvisories(id, task, jobs) {
|
|
746
|
+
const strategy = flowCContentStrategy(task.creative_strategy);
|
|
747
|
+
if (!strategy)
|
|
748
|
+
return;
|
|
749
|
+
const record = this.scriptRecord(id);
|
|
750
|
+
const current = jobs.filter((job) => !record.receivedOrdinals.includes(job.ordinal));
|
|
751
|
+
const localization = compactTaskLocalization(task);
|
|
752
|
+
const recentScripts = mergeFlowCContentSummaries([...(Array.isArray(task.content_recent_scripts) ? task.content_recent_scripts : []), ...(record.contentSummaries || [])], [], record.receivedOrdinals);
|
|
753
|
+
const advisories = flowCContentAdvisories(current, strategy, { targetLanguage: localization.targetLanguage || task.target_language || localization.targetLocale, recentScripts });
|
|
754
|
+
const ordinals = new Set(current.map((job) => job.ordinal));
|
|
755
|
+
record.contentAdvisories = [...(record.contentAdvisories || []).filter((item) => !ordinals.has(item.ordinal)), ...advisories].slice(-120);
|
|
756
|
+
// The following normal delivery saves these metadata with the untouched
|
|
757
|
+
// draft. Advisories never change status, discard output or start a turn.
|
|
758
|
+
}
|
|
740
759
|
/** 只记录阶段、ordinal 和毫秒数;禁止记录 prompt、令牌或中心能力。 */
|
|
741
760
|
emitScriptStage(handoffId, ordinals, stage, durationMs) {
|
|
742
761
|
this.emit("agent_event", { agent: "codex", type: "workflow.script.stage", handoffId, ordinals, stage, duration_ms: Math.max(0, Math.round(durationMs)), model: FLOW_C_CODEX_MODEL, reasoning: FLOW_C_CODEX_REASONING_EFFORT });
|
|
@@ -1090,7 +1109,7 @@ ${durationRules}
|
|
|
1090
1109
|
1. visualHook、conflict、escalation/turn、productIntervention、visibleProof、purchaseReason、callbackMotivation 与所有 shots 必须执行选中卡的 visualPremise、firstFrame、spectacleEscalation、productProofAction、purchaseReason、truthBoundary 以及七项视觉执行字段;保留所选蓝图的因果时间线、节拍比例、镜头/剪辑节奏、动作压力、证明方式和购买逻辑,只替换当前商品、人物、场景与当地口播槽位,不得稀释构图或换成普通模板。爆款来源的旧商品身份/动作必须替换;形式卡只提供抽象创意结构,绝不能反向改变现有首帧、分段、Omni或参考图制作方式。形式库中的工厂、仓库、街访、使用者体验、主理人、探店、补货、对比、促销和耐用形式都可以用 AI 人物与场景正常演绎;缺少现实素材或事实证据不是脚本失败条件,必须按 executionBlueprint.executionAdaptation 保留所选形式,同时不把演绎的角色、地点、订单、库存、销量、价格、身份或经历写成已核验的现实事实。若 productAdaptation.templateMatch.actionCompatibility 的 openingAction、proofAction 或 spectacleAction 为 false,对应 productAdaptation 的 firstFrame、productProofAction 或 spectacleEscalation 对商品动作内容具有绝对优先级,必须删除其旧商品动作,不得折中混用。productProofAction 必须同时写清一个商品图或执行档案能支持的可见动作及其镜头内可见结果;purchaseReason 只解释该结果为何值得目标用户购买,不能变成泛泛 CTA。
|
|
1091
1110
|
2. 严格执行 executionBlueprints 中的 sourceDurationSeconds → targetDurationSeconds 和 retimingMode:expand 只增加有用的证明停留、使用语境与购买理由,compress 只压缩重复、冗余铺垫和转场,same 保持原节拍比例;三种模式都必须保留 Hook、证明和购买因果,禁止整体拉伸、截断或机械加速。
|
|
1092
1111
|
3. 每条生成一份稳定 voiceProfile;每段只写简短 voiceCue。口播必须使用${targetVoiceLanguage},并执行 creatorVoiceStyle/ctaStyle;未提供 creatorVoiceStyle 时使用当地 TikTok 带货创作者真实会说的口吻。${contentStrategy ? '不是逐字翻译。Hook → Body/visible proof → Close 是画面叙事结构,不等于口播结构;镜头数量不等于台词数量。除用户明确锁定的对白外,先为整个局部10秒定稿1–2个能自然说完的很短口语句,再分配到真正需要补充人物动机或购买判断的镜头,不逐镜复述 visual;其它镜头默认 voiceover="none",只在 soundBgm 保留动作现场声。结尾的一个简短自然 CTA 并入已定稿短句,不另外追加一句。用户锁定的对白、原框架和目标语言优先,不擅自删改;上述定稿与分配在同一次写作内完成,不输出中间声音轨。' : '不是逐字翻译,整条内容按 Hook → Body/visible proof → Close 组织,短句口语化且与当前镜头可见动作同步,结尾只给一个简短自然 CTA。'}只使用少量可信的 localSceneProfile/audienceContext 日常细节;presenterContext 没填写时使用普通创作者,不得把国家代码、语言或地区自动等同于人物族群。商品事实没有提供时不得编造价格、折扣、销量、库存或稀缺性。商品标题只用于理解商品类型与事实,所有输出不得主动写出或念出品牌名、品牌广告语、包装文案,也不得要求模型把品牌字样变得更清晰;商品外观与已有标识的位置、比例、颜色和版式只服从第1张产品参考图。TikTok/TikTok Shop 只用于定义这条写作规则,不得把平台名写入 voiceover、onScreenText、visual、soundBgm、emotionalNote、openingState、endingState 或任何会进入故事板/视频的导演字段。导演说明统一用简洁制作英文;这里只做写作指令,不做语言检测字段。
|
|
1093
|
-
4. 每段永远是独立 0–10 秒,含 1–8 个按剧情需要决定的 ${contentStrategy ? 'shots。每镜 voiceover 字段必须存在,但不等于必须有台词:只填上一步分配给该镜的原句,其余填非空静默标记 "none",绝不让配音念出 none;每镜同时给出准确 onScreenText、evidence、soundBgm、emotionalNote,并提供每段 endingState' : 'shots、准确 voiceover、onScreenText、evidence、soundBgm、emotionalNote 与 endingState'};模型不要输出 continuity 或 continuityMode。
|
|
1112
|
+
4. 每段永远是独立 0–10 秒,含 1–8 个按剧情需要决定的 ${contentStrategy ? 'shots。每镜 voiceover 字段必须存在,但不等于必须有台词:只填上一步分配给该镜的原句,其余填非空静默标记 "none",绝不让配音念出 none;每镜同时给出准确 onScreenText、evidence、soundBgm、emotionalNote,并提供每段 endingState。先写末镜实际动作,再在末镜 visual 最后用一个短句明确第10秒的最终画面;endingState.endingFrame 逐字复用这个收尾短句,其余 endingState 字段也只描述同一瞬间,不另编姿势或动作。已走出画面的人不能仍在画内回头,已经落地的脚不能又悬在半空,已经完成的动作不能仍写为待完成;确已完成且无续接动作时 unfinishedAction 写 none。下一段首镜从这个真实终点继续,不重置人物、物体或动作;最终一段也要检查,不能只保证跨段字段相等' : 'shots、准确 voiceover、onScreenText、evidence、soundBgm、emotionalNote 与 endingState'};模型不要输出 continuity 或 continuityMode。
|
|
1094
1113
|
5. shot.visual 要直接写出主体位置、景别、光线、材质、动作峰值和当地自然环境,并保留真实皮肤、细发、布料/商品微纹理和实景混合光;禁止塑料蜡感、静态举产品、品牌广告片或电视购物。允许执行所选形式的 AI 工厂/仓库/零售/人物场景,但不得声称它们是该商家真实工厂、产地、生产档案、订单、库存、销量、实名客户证言或线下价格。
|
|
1095
1114
|
6. 20/30 秒的后一段剧情承接前段 endingState,但每段仍只使用自己的局部 0–10 秒执行内容。
|
|
1096
1115
|
7. 不要输出 creativePlan、executionBindings 或 masterScript;这些字段由 Agent 从选中蓝图确定性回填/合成,避免重复 token 和脆弱的文字绑定校验。若 selectionMode=user-framework,用户原框架的开头、事件顺序、核心剧情和结尾仍是最高权威。
|
|
@@ -1412,7 +1431,7 @@ function productIndexForOrdinal(productQuantities, ordinal) {
|
|
|
1412
1431
|
return -1;
|
|
1413
1432
|
}
|
|
1414
1433
|
function publicScript(record) {
|
|
1415
|
-
return { id: record.id, status: record.status, requestedCount: record.requestedCount, received: record.receivedOrdinals.length, threadId: record.threadId, chunkSize: record.chunkSize || null, activeChunks: Number(record.activeChunks || 0), message: record.message, expiresAt: record.expiresAt, updatedAt: record.updatedAt };
|
|
1434
|
+
return { id: record.id, status: record.status, requestedCount: record.requestedCount, received: record.receivedOrdinals.length, threadId: record.threadId, chunkSize: record.chunkSize || null, activeChunks: Number(record.activeChunks || 0), message: record.message, expiresAt: record.expiresAt, updatedAt: record.updatedAt, ...(record.contentAdvisories ? { contentReview: { blocking: false, semanticAcceptance: "not-evaluated", advisories: record.contentAdvisories } } : {}) };
|
|
1416
1435
|
}
|
|
1417
1436
|
function publicDownload(record) {
|
|
1418
1437
|
return { batchId: record.batchId, status: record.status, downloaded: record.downloadedOrdinals.length, market: record.market, message: record.message, expiresAt: record.expiresAt, updatedAt: record.updatedAt };
|