fix: 修复工作流执行记录创建与静音模式约束

- 执行记录仅在合法 execId 时写入,查询失败时返回错误
- 字幕构建改为词级精确对齐并增加比例兜底,避免整句被吞
- 静音模式下从转写与段级 prompt 双重杜绝口播/字幕/口型
- 清理静音事件描述中的说话类动词
This commit is contained in:
2026-08-21 19:38:25 +08:00
parent a06766db6d
commit 5b93a7a507
7 changed files with 202 additions and 71 deletions
@@ -108,6 +108,10 @@ func ScriptTranscribeLambda(ctx context.Context, input any) (any, error) {
} else {
systemPrompt += shotDurationConstraintPrompt(maxSeg)
}
// 静音模式硬约束:从转写源头杜绝对白/旁白/开口说话,后续清洗只做兜底
if noSpeech {
systemPrompt += noSpeechSystemPromptConstraint()
}
info, err := gateway.GetModelInfoById(ctx, &gateway.GetModelInfoByIdReq{ModelId: nodeInput.Config.ModelConfig.ModelId})
if err != nil {
@@ -148,6 +152,8 @@ func ScriptTranscribeLambda(ctx context.Context, input any) (any, error) {
for i := range shots {
shots[i].Dialogue = ""
shots[i].Narration = ""
// event 里的说话动词仍会经 事件:%s 块写进分段 prompt,导致视频模型生成口型/字幕,需确定性清洗
shots[i].Event = cleanSpeechVerbs(shots[i].Event)
}
}
@@ -164,6 +170,7 @@ func ScriptTranscribeLambda(ctx context.Context, input any) (any, error) {
FlatRefs: refsItem,
Seed: nodeInput.Global.ExecutionId % 1000000,
NegativePrompt: nodeInput.Config.NegativePrompt,
NoSpeech: noSpeech,
}
data, err := processor.Call(ctx, "split_shots_pipeline", gconv.Map(args))
if err != nil {
@@ -320,6 +327,32 @@ func splitDialogueNarration(s string) (dialogue, narration string) {
}
}
// cleanSpeechVerbs 静音模式下清洗 event 中的说话动词:把常见说话/喊叫/对白表达替换为空串,
// 避免"事件:%s"块里残留的说话动词让视频模型生成口型/字幕。仅做机械兜底,硬约束在转写提示词。
// NewReplacer 按最长匹配替换,故先列含"说/喊"的非开口语义词做保护(no-op,如"说明""呐喊"),
// 再列开口表达;"叫"语义多变(呼叫/叫停/叫住),不做裸清洗以免误伤。
func cleanSpeechVerbs(s string) string {
if s == "" {
return ""
}
repl := strings.NewReplacer(
"说明", "说明", "解说", "解说", "据说", "据说", "传说", "传说", "小说", "小说",
"学说", "学说", "说法", "说法", "说服", "说服", "呐喊", "呐喊",
"开口说话", "", "开口说", "", "开口", "",
"说道:", "", "说道:", "", "说道", "",
"说着", "", "说话", "", "讲话", "", "台词", "", "对白", "",
"喊道:", "", "喊道:", "", "喊道", "", "大喊", "", "喊叫", "",
"叫道:", "", "叫道:", "", "叫道", "", "叫到", "", "叫喊", "",
"回答", "", "答道", "", "回应", "", "回话", "",
"问道:", "", "问道:", "", "问道", "",
"念叨", "", "嘟囔", "", "嘀咕", "", "自言自语", "",
"呼唤", "", "呼叫", "", "叫唤", "", "惊叫", "", "惨叫", "",
"说:", "", "说:", "", "说", "",
"喊:", "", "喊:", "", "喊", "",
)
return strings.TrimSpace(repl.Replace(s))
}
// splitList 按常见分隔符拆分人名/道具列表(兼容中英文顿号、逗号、分号、"和""及"等)。
func splitList(s string) []string {
repl := strings.NewReplacer("、", "|", "", "|", ",", "|", "", "|", ";", "|", "和", "|", "及", "|", "&", "|", "/", "|", " ", "|")
@@ -339,3 +372,13 @@ func shotDurationConstraintPrompt(maxSeg int) string {
}
return fmt.Sprintf("\n\n单个镜头时长不超过 %d 秒:每镜的 startTime 与 endTime 之差必须 ≤ %d 秒。", maxSeg, maxSeg)
}
// noSpeechSystemPromptConstraint 静音模式的转写硬约束:要求模型从源头就不产出对白/旁白/说话动词,
// 后续 noSpeech 清洗(清空台词旁白 + cleanSpeechVerbs)只做机械兜底。
func noSpeechSystemPromptConstraint() string {
return "\n\n本片为静音模式,镜头里禁止任何声音类内容:\n" +
"- 所有镜头禁止出现台词、旁白、画外音,narration 与 dialogue 一律留空、不要输出;\n" +
"- 禁止角色开口说话,事件描述只能写无声的动作、表情、神态、场景变化,不要出现“说”“喊”“叫”“对白”“讲话”“开口”“问”“回答”“念叨”等说话类动词;\n" +
"- characters 只是出镜角色名,不代表开口说话;\n" +
"- 视频不包含口型动作与字幕,据此调整分镜描写。"
}