viccy e6e39d2dcd feat(short-drama): 完整实现短剧解说剪辑全流程并新增LLM流式生成支持
- 新增短剧解说全流程四类提示词模板:解说文案生成、片段规划、文案画面匹配、脚本修复
- 重构原有脚本生成提示词至v2.1,改为基于上游规划片段生成合规解说脚本
- 为LLM基础服务层新增流式文本生成接口,完善OpenAI兼容提供商的流式实现,支持流式回调与推理内容提取
- 重构OpenAI兼容文本提供商的生成逻辑,提取公共参数构建方法
- 新增多语言国际化文案,覆盖解说语言、短剧类型、原片占比等配置项与交互提示
- 新增多套单元测试,覆盖脚本校验、适配器流程、工具函数等模块
- 封装SubtitleAnalyzerAdapter,统一短剧解说脚本生成的整套业务接口
- 新增前端交互所需的解说文案审核相关提示文案
2026-06-07 17:10:48 +08:00

132 lines
5.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Project: 短剧解说-文案画面匹配
@File : script_matching.py
@Description: 将用户审核后的解说文案匹配到字幕时间戳并生成最终剪辑脚本
"""
from ..base import ParameterizedPrompt, PromptMetadata, ModelType, OutputFormat
class ScriptMatchingPrompt(ParameterizedPrompt):
"""短剧解说文案画面匹配提示词"""
def __init__(self):
metadata = PromptMetadata(
name="script_matching",
category="short_drama_narration",
version="v1.0",
description="将审核后的解说文案按叙事节奏拆分并匹配到字幕时间戳生成最终剪辑JSON",
model_type=ModelType.TEXT,
output_format=OutputFormat.JSON,
tags=["短剧", "画面匹配", "剪辑脚本", "时间戳", "用户文案"],
parameters=[
"drama_name",
"drama_genre",
"plot_analysis",
"subtitle_content",
"narration_copy",
"narration_language",
"original_sound_ratio",
],
)
super().__init__(
metadata,
required_parameters=["drama_name", "subtitle_content", "narration_copy"],
)
self._system_prompt = (
"你是一位懂叙事节奏的短剧剪辑师。你必须严格输出JSON"
"核心任务是把用户审核后的解说文案逐句匹配到最合适的原视频字幕时间戳。"
)
def get_template(self) -> str:
return """# 短剧解说文案画面匹配任务
## 目标
用户已经审核并修改了解说文案。请根据这份文案和原始字幕,生成最终可剪辑 JSON 脚本。
## 剧名
${drama_name}
## 剧情理解材料
<plot>
${plot_analysis}
</plot>
## 用户审核后的解说文案
<narration_copy>
${narration_copy}
</narration_copy>
## 原始字幕(含视频编号和局部时间戳)
<subtitles>
${subtitle_content}
</subtitles>
## 输出语言
<narration_language>
${narration_language}
</narration_language>
## 用户选择的短剧类型
<drama_genre>
${drama_genre}
</drama_genre>
## 用户选择的原片占比
<original_sound_ratio>
${original_sound_ratio}%
</original_sound_ratio>
## 匹配流程
1. 先按句号、问号、感叹号、省略号切分解说文案,得到候选解说句。
2. 逗号只在明显分割两个动作、场景、观点或描述对象时切分;不要切出没有独立意义的碎片。
3. 不要求每个候选句都单独输出为 OST=0可以合并、压缩相邻候选句作为剧情桥段但不能改变用户文案的核心意思。
4. 为每个解说片段寻找最匹配的原始字幕画面,优先选择能表达该句核心含义的画面。
5. 使用公式估算所需画面时长:所需秒数 = 解说字数 / 5。匹配画面时长尽量接近误差优先控制在 ±0.5 秒。
6. 如果一句解说太长,必须拆成多个 OST=0 片段,分别匹配不同或连续画面。
7. timestamp 必须使用对应 video_id 内部局部时间戳,不得换算为多个视频拼接后的累计时间。
8. 同一 video_id 内时间段不得交叉或重叠。
9. 第一段必须是 OST=0 解说钩子,不能直接播放原片。
10. OST=1 原声片段的总时长占比要尽量接近用户选择的 ${original_sound_ratio}%。这里按最终 items 的 timestamp 总时长估算,不按片段数量估算。
11. 不要自行判断或改写短剧类型;画面匹配和 picture 描述要服务用户选择的 ${drama_genre} 叙事重点。
## 原片占比规则
- ${original_sound_ratio}% = 0% 时,不要输出 OST=1全部使用解说承接。
- ${original_sound_ratio}% 在 10%-30% 时,只保留关键对白、反转、情绪爆发或爽点原声。
- ${original_sound_ratio}% 在 40%-60% 时,解说负责串联因果,原片负责承载关键场面和对白。
- ${original_sound_ratio}% 在 70%-90% 时,以原片对白和表演为主,解说只做开场钩子、转场桥和必要补充。
- 如果原片占比与“第一段必须 OST=0”冲突优先保证第一段是 OST=0然后在后续片段提高 OST=1 时长占比。
- 选择高原片占比时,可以把用户文案合并成更少的 OST=0 桥段,不要为了逐句使用文案而压低原片占比。
## 字段规则
- _id从 1 开始连续递增。
- video_id来自字幕分段标题例如“视频 2”就填 2。
- video_name对应视频文件名必须从字幕分段标题提取。
- timestamp格式为 "HH:MM:SS,mmm-HH:MM:SS,mmm"
- picture描述匹配画面中人物、动作、情绪和场景。
- narrationOST=0 时填写用户文案片段OST=1 时填写“播放原片+_id”。
- OST解说片段填 0原声片段填 1。
## 输出格式
只输出严格 JSON
{
"items": [
{
"_id": 1,
"video_id": 1,
"video_name": "1.mp4",
"timestamp": "00:00:01,000-00:00:06,000",
"picture": "主角站在门口,震惊地看着屋内混乱的场面",
"narration": "一个刚立功的兵王,回家的第一天就发现家里四百万被亲爹输光。",
"OST": 0
}
]
}
现在请基于用户审核后的解说文案生成最终剪辑脚本。"""