优化视频创作
This commit is contained in:
@@ -27,7 +27,7 @@ from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.db import transaction
|
||||
|
||||
from .creation import append_message, pin_refs
|
||||
from .creation_presets import apply_image_preset_prompt, preset_guidance
|
||||
from .creation_presets import apply_image_preset_prompt, preset_guidance, preset_workflow_guidance
|
||||
from .mentions import TYPE_LABELS, infer_field_types, resolve_refs, search_mentions
|
||||
from .models import CreationConversation, CreationMessage, ModelConfig
|
||||
from .services import build_provider, get_default_model, get_seed_text_model, resolve_text_model
|
||||
@@ -41,12 +41,13 @@ MAX_TOOL_ROUNDS = 8
|
||||
MAX_BILLED_GENERATIONS = 1
|
||||
|
||||
# 视频闸门阶段(落在 conversation.memory.stage;resume 靠它)
|
||||
# clarify → strategy → plan → prompt → confirm → done
|
||||
# prompt 仅兼容历史会话。新视频链路在方案确认后直接转入出片确认,Prompt 始终由平台在后台维护。
|
||||
# clarify → strategy → plan → confirm → done
|
||||
VIDEO_GATE_STAGES = ("clarify", "strategy", "plan", "prompt", "confirm", "done")
|
||||
_STEP_CONFIRM_LABELS = {
|
||||
"strategy": "创作策略已写好。确认后继续写方案;要改就点「我想改」或直接说改哪里。",
|
||||
"plan": "视频方案已写好。确认后继续出 Prompt;要改就点「我想改」或直接说改哪里。",
|
||||
"prompt": "出片 Prompt 已写好。确认后选择参数并生成;要改就点「我想改」或直接说改哪里。",
|
||||
"plan": "视频方案已写好。确认后我会整理出片细节,并带你确认生成参数;要改就点「我想改」或直接说改哪里。",
|
||||
"prompt": "出片指令已整理好。确认后核对参数并生成;要改就点「我想改」或直接说改哪里。",
|
||||
}
|
||||
|
||||
|
||||
@@ -1552,7 +1553,7 @@ def build_system_prompt(context: AgentContext, *, allow_plan: bool = True, has_c
|
||||
"- 用户选择暂不提供某项素材时,把它当成明确授权:按已有信息和合理默认继续。除非任务客观上无法完成,否则不要再次追问同一素材。",
|
||||
"- 用户说「你来定」「你帮我选」「随便」「都行」时,就是授权你做专业判断;直接选合理方案继续,不要把选择题再抛回去。",
|
||||
"- 禁止问「要不要继续」「要不要生成」「是否开始创作」这类流程问题。缺信息用 ask_user;信息够了就写策略。"
|
||||
" 视频每写完一步(策略/方案/Prompt)平台会出确认卡,由用户点「按这个继续」或提出修改;不要口头问流程。",
|
||||
" 视频每写完策略或方案,平台会出确认卡;方案确认后平台会在后台整理出片指令,再让用户核对生成参数。不要口头问流程。",
|
||||
"- 用户打招呼或闲聊(hi / 你好 / 在吗 / 你在干什么 / 嗯 / 好的 / ok):"
|
||||
" **禁止**调用 write_strategy、write_plan、generate_image,不要「整理方案」或直接开写脚本。",
|
||||
"- 会话里**还没有**商品/方向时:自然地告诉用户可以直接丢一句想法,别硬推销,也别用客服式结束语。",
|
||||
@@ -1582,6 +1583,15 @@ def build_system_prompt(context: AgentContext, *, allow_plan: bool = True, has_c
|
||||
]
|
||||
if context.is_video:
|
||||
lines.extend(["", _OMNI_VIDEO_PROMPT_RULES.strip()])
|
||||
lines.extend([
|
||||
"",
|
||||
"【视频创作工作原则】",
|
||||
"- 先从文字和已锁定素材整理商品事实、人物/服装、场景、参考视频或音频、现成脚本,以及时长、比例和语言;已经给出的信息不重复问。",
|
||||
"- 用户已给脚本或分镜时,以它为基础补足,不强制从头重写。只改用户点名的镜头、人物、商品、台词或参数,其他内容保持。",
|
||||
"- 可以自己决定转场、灯光、普通镜头细节;商品功能、规格、价格、活动、功效和关键使用边界不能猜,缺失时一次只问一项。",
|
||||
"- 用户上传的人物、商品、服装和场景优先作为参考;如确实需要额外生成角色、场景或道具,先说明用途和预计积分,等用户确认。",
|
||||
"- 出片前检查:主卖点都有画面或台词证据、口播能在时长内说完、脚本中每位人物/商品/服装/场景都有对应素材、商品正常使用、全片一致、所有 SKU 都已安排。内容超出时长时建议删减、延长或拆分,而不是硬塞。",
|
||||
])
|
||||
# 按会话时长给出口播字数锚点(与专业创作 narration_limit 同口径)
|
||||
try:
|
||||
dur = video_duration(params)
|
||||
@@ -1591,14 +1601,13 @@ def build_system_prompt(context: AgentContext, *, allow_plan: bool = True, has_c
|
||||
hi = max(lo, min(85, int(dur * 5.7)))
|
||||
lines.append(f"- 当前按约 {dur} 秒出片,口播建议 {lo}–{hi} 字;write_plan 的 voice_chars 填这个区间。")
|
||||
lines.append(
|
||||
"- 视频 5 步闸门(不可同轮连跳):①缺信息 ask_user 停 → ②write_strategy 停等确认 → "
|
||||
"③用户确认后 write_plan 停等确认 → ④用户确认后 write_prompt(或平台代出 Prompt)停等确认 → "
|
||||
"⑤用户确认后才出现积分确认卡出片。"
|
||||
"- 视频 4 步闸门(不可同轮连跳):①缺信息 ask_user 停 → ②write_strategy 停等确认 → "
|
||||
"③用户确认后 write_plan 停等确认 → ④方案确认后平台在后台整理完整出片指令,并直接出现积分确认卡。"
|
||||
)
|
||||
lines.append(
|
||||
"- 只有用户明确要做片、出方案、改方案、换卖点/剧情时才调用 write_strategy / write_plan / write_prompt;"
|
||||
"- 只有用户明确要做片、出方案、改方案、换卖点/剧情时才调用 write_strategy / write_plan;"
|
||||
"闲聊与打招呼绝对不要。用户对某一步提出修改时,只重写那一步,不要跳到后面。"
|
||||
"video_prompt 按上面的秒级分镜规范写满,不要只给大纲,也禁止只回「好的,有需要再说」。"
|
||||
"write_plan 里的 video_prompt 要按上面的秒级分镜规范写满,供平台后台出片使用;不要只给大纲,也禁止只回「好的,有需要再说」。"
|
||||
)
|
||||
stage = get_video_gate_stage(conversation)
|
||||
memory = conversation.memory if isinstance(conversation.memory, dict) else {}
|
||||
@@ -1606,13 +1615,13 @@ def build_system_prompt(context: AgentContext, *, allow_plan: bool = True, has_c
|
||||
stage_hint = {
|
||||
"clarify": "当前阶段=澄清:缺关键信息就 ask_user;信息够了只调 write_strategy。",
|
||||
"strategy": (
|
||||
"当前阶段=策略已确认,请调用 write_plan 写方案;不要再写策略,不要 write_prompt。"
|
||||
"当前阶段=策略已确认,请调用 write_plan 写方案;不要再写策略。"
|
||||
if strategy_confirmed else
|
||||
"当前阶段=等策略确认:不要再 write_plan/write_prompt;用户确认后才会进入方案。若用户在改策略,只重调 write_strategy。"
|
||||
"当前阶段=等策略确认:不要再写方案或出片;用户确认后才会进入方案。若用户在改策略,只重调 write_strategy。"
|
||||
),
|
||||
"plan": "当前阶段=等方案确认:不要 write_prompt 或出片。若用户在改方案,只重调 write_plan。",
|
||||
"prompt": "当前阶段=等 Prompt 确认:不要出积分确认/出片。若用户在改 Prompt,只重调 write_prompt。",
|
||||
"confirm": "当前阶段=等出片确认:不要再写策略/方案/Prompt;用户会在确认卡上点开始生成。",
|
||||
"plan": "当前阶段=等方案确认:不要出片。若用户在改方案,只重调 write_plan。",
|
||||
"prompt": "当前阶段=兼容历史会话的出片指令确认:不要出片,按用户反馈重写内部出片指令。",
|
||||
"confirm": "当前阶段=等出片确认:不要再写策略/方案;用户会在确认卡上点开始生成。",
|
||||
"done": "当前阶段=已出片:等用户新的修改或新需求再行动。用户说重新来/重来/从头开始时,当作新一轮创作,从澄清或 write_strategy 重开,不要再弹旧模特/商品追问。",
|
||||
}.get(stage, "")
|
||||
if stage_hint:
|
||||
@@ -1651,6 +1660,9 @@ def build_system_prompt(context: AgentContext, *, allow_plan: bool = True, has_c
|
||||
if guidance:
|
||||
lines.append(guidance)
|
||||
lines.append("用户选了这个预设,就按它的拍法来;要偏离得先问过用户。")
|
||||
workflow_guidance = preset_workflow_guidance(conversation.preset) if context.is_video else ""
|
||||
if workflow_guidance:
|
||||
lines.append(f"【当前预设的工作重点】{workflow_guidance}")
|
||||
|
||||
resolved = resolve_refs(context.team, conversation.pinned_refs or [])
|
||||
if resolved.facts:
|
||||
@@ -2344,7 +2356,7 @@ _TOOL_LABELS = {
|
||||
"generate_image": "生成图片",
|
||||
"write_strategy": "梳理创作策略",
|
||||
"write_plan": "编排视频方案",
|
||||
"write_prompt": "编写出片 Prompt",
|
||||
"write_prompt": "整理出片指令",
|
||||
}
|
||||
|
||||
|
||||
@@ -2403,11 +2415,21 @@ def _elicit_payload_for_fields(
|
||||
else:
|
||||
gate_label = _GATE_LABELS.get(primary_type, _GATE_LABELS["asset"])
|
||||
|
||||
asset_label = {
|
||||
"product": "商品",
|
||||
"character": "角色",
|
||||
"model": "模特",
|
||||
"scene": "场景",
|
||||
}.get(primary_type, "素材")
|
||||
gate_field = {
|
||||
"key": "_asset_gate",
|
||||
"label": gate_label,
|
||||
"type": "text",
|
||||
"required": False,
|
||||
"options": [
|
||||
{"value": "send", "label": f"发{asset_label}列表"},
|
||||
{"value": "auto", "label": "你来推荐"},
|
||||
],
|
||||
}
|
||||
return {
|
||||
# 这是 Agent 的一句自然追问,不是让用户点选流程的卡片。
|
||||
@@ -2526,7 +2548,7 @@ def _dispatch_tool(
|
||||
kind=CreationMessage.Kind.PLAN, payload=plan_payload,
|
||||
)
|
||||
events.append({"type": "message", "message": _message_payload(plan)})
|
||||
# video_prompt 先缓存:用户确认方案后由平台展示 Prompt 卡,本步不出 Prompt/积分卡
|
||||
# video_prompt 只存后台。用户确认方案后直接进入积分确认,不展示内部 Prompt。
|
||||
set_video_gate_stage(
|
||||
context.conversation, "plan", pending_video_prompt=video_prompt
|
||||
)
|
||||
@@ -2545,12 +2567,16 @@ def _dispatch_tool(
|
||||
return {"payload": {"error": "video_prompt 不能为空,请把完整出片指令写进去"}}, False
|
||||
video_prompt = apply_product_voice_visual_guard(context.conversation, video_prompt)
|
||||
video_prompt = apply_product_reality_guard(video_prompt)
|
||||
emitted = emit_prompt_gate(context.conversation, video_prompt)
|
||||
if not emitted:
|
||||
return {"payload": {"error": "无法写出 Prompt 卡"}}, False
|
||||
events = [{"type": "message", "message": _message_payload(m)} for m in emitted]
|
||||
set_video_gate_stage(context.conversation, "prompt", pending_video_prompt=video_prompt)
|
||||
confirm = emit_final_confirm_gate(context.conversation, context=context)
|
||||
if confirm is None:
|
||||
return {"payload": {"error": "无法准备出片确认"}}, False
|
||||
events = [
|
||||
{"type": "message", "message": _message_payload(confirm)},
|
||||
{"type": "credits", "estimated": int((confirm.payload or {}).get("estimated_credits") or 0)},
|
||||
]
|
||||
return {
|
||||
"payload": {"awaiting_step": "prompt", "written": True},
|
||||
"payload": {"awaiting_confirmation": True, "prepared": True},
|
||||
"_events": events,
|
||||
}, True
|
||||
|
||||
|
||||
Reference in New Issue
Block a user