铁律: - ④重写为白名单/黑名单机制 (今日4次违规) - ⑤新增 BREAKDOWN回验协议 - ⑥新增 管线不容跳步 核心经验: - 图片+视频混合管线路线验证 - ENV底板法 (i2i→i2v) - 提示词工程: 工业术语+禁人称词+负面约束 - CHAR真人检测绕过 (blur公式) - FFmpeg xfade拼接方法 - Seedance 2.0 参数 (9:16, 4-15s) 新增文件: - eererdan/experience/EED-EXPER-001~010 - protocols/BREAKDOWN-VERIFY.hdlp - deep-sea-voyage/STORYBOARD-EP01.hdlp (Pro版26→10镜) - deep-sea-voyage/PROMPTS-EP01.hdlp - deep-sea-voyage/DIRECTOR-ENCODING-EP01.hdlp - tools/jimeng3-gen.js, run_storyboard.js, run_prompts.js, run_adapt.js, run_gen.js, crop_watermark.py - lib/seedream.py (支持 Seedream 3/4/5) - outputs/ENV-*-v5, PROP-* (6张定稿资产) - outputs/videos/M01-v2.mp4, M02.mp4 下次继续: M03~M10 (9:16竖屏)
125 lines
4.3 KiB
Python
125 lines
4.3 KiB
Python
# LIB · 豆包对话模型适配器(剧本拆解/分镜生成)
|
||
# D180 · 2026-07-10
|
||
"""火山方舟 ARK Chat API → doubao-seed 系列模型"""
|
||
import json
|
||
import os
|
||
import subprocess
|
||
import tempfile
|
||
|
||
from lib.secrets import get_ark_key
|
||
ARK_KEY = get_ark_key()
|
||
ARK_CHAT_URL = "https://ark.cn-beijing.volces.com/api/v3/chat/completions"
|
||
|
||
# 可用模型
|
||
MODELS = {
|
||
"pro": "doubao-seed-2-1-pro-260628", # 最强·适合复杂剧本
|
||
"lite": "doubao-seed-2-0-lite-260215", # 轻量·适合批量
|
||
"deepseek": "deepseek-v4-pro-260425", # 深度思<E5BAA6><E6809D><EFBFBD>·适合拆解
|
||
}
|
||
|
||
def _curl_api(payload):
|
||
"""通过 subprocess curl 调用 API(绕过 Windows urllib 超时问题)"""
|
||
import subprocess, tempfile
|
||
tf = tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False, encoding='utf-8')
|
||
json.dump(payload, tf, ensure_ascii=False)
|
||
tf.close()
|
||
try:
|
||
result = subprocess.run([
|
||
"/mingw64/bin/curl", "-s", "--max-time", "180", ARK_CHAT_URL,
|
||
"-H", f"Authorization: Bearer {ARK_KEY}",
|
||
"-H", "Content-Type: application/json",
|
||
"-d", f"@{tf.name}"
|
||
], capture_output=True, text=True, timeout=180)
|
||
os.unlink(tf.name)
|
||
return json.loads(result.stdout) if result.stdout else {"error": result.stderr}
|
||
except subprocess.TimeoutExpired:
|
||
os.unlink(tf.name)
|
||
return {"error": "timeout"}
|
||
|
||
def chat(prompt, system="你是一个专业的短剧剧本分析专家", model="pro", temperature=0.3, max_tokens=4096):
|
||
"""调用豆包对话模型"""
|
||
payload = {
|
||
"model": MODELS.get(model, model),
|
||
"messages": [
|
||
{"role": "system", "content": system},
|
||
{"role": "user", "content": prompt}
|
||
],
|
||
"temperature": temperature,
|
||
"max_tokens": max_tokens
|
||
}
|
||
result = _curl_api(payload)
|
||
if "error" in result:
|
||
return result
|
||
choice = result.get("choices", [{}])[0]
|
||
return {
|
||
"content": choice.get("message", {}).get("content", ""),
|
||
"model": result.get("model", ""),
|
||
"tokens": result.get("usage", {}),
|
||
"finish": choice.get("finish_reason", "")
|
||
}
|
||
|
||
def breakdown_script(script_text, episode_num=1):
|
||
"""拆解一集剧本为结构化分镜"""
|
||
prompt = f"""请将以下短剧剧本第{episode_num}集拆解为结构化分镜。
|
||
|
||
要求:
|
||
1. 按镜头拆分,每个镜头包含:镜头编号、景别(特写/近景/中景/全景/POV)、时长(秒)
|
||
2. 提取每个镜头中出现的角色、场景、道具
|
||
3. 写出每个镜头的画面描述(50字以内)
|
||
4. 标注镜头类型:establishing/action/reaction/closeup/transition
|
||
|
||
输出JSON格式:
|
||
{{
|
||
"episode": {episode_num},
|
||
"shots": [
|
||
{{
|
||
"shot_number": "S01",
|
||
"description": "画面描述",
|
||
"camera": "POV|特写|近景|中景|全景",
|
||
"duration": 6,
|
||
"type": "establishing|action|reaction|closeup|transition",
|
||
"characters": ["角色名"],
|
||
"scenes": ["场景名"],
|
||
"props": ["道具名"],
|
||
"dialogue": null
|
||
}}
|
||
]
|
||
}}
|
||
|
||
剧本内容:
|
||
{script_text}"""
|
||
return chat(prompt, system="你是专业的短剧剧本拆解专家,输出纯JSON,不添加任何解释。")
|
||
|
||
def generate_keyframes(shot_description, character_refs, scene_refs):
|
||
"""为单个镜头生成关键帧 prompt"""
|
||
prompt = f"""基于以下镜头描述,生成即梦4.0图像生成提示词。
|
||
|
||
镜头:{shot_description}
|
||
可用角色:{json.dumps(character_refs, ensure_ascii=False)}
|
||
可用场景:{json.dumps(scene_refs, ensure_ascii=False)}
|
||
|
||
要求:
|
||
1. 提示词用英文
|
||
2. 包含景别、角色位置、场景细节、光照、风格
|
||
3. 不超过150词
|
||
4. 输出纯提示词,不加任何解释"""
|
||
return chat(prompt, model="pro", temperature=0.5, max_tokens=300)
|
||
|
||
# ====== CLI ======
|
||
if __name__ == "__main__":
|
||
import sys
|
||
if len(sys.argv) < 2:
|
||
print("Usage: python doubao_chat.py <command> [args]")
|
||
print(" chat <prompt> 直接对话")
|
||
print(" breakdown <file> 拆解剧本文件")
|
||
sys.exit(1)
|
||
cmd = sys.argv[1]
|
||
if cmd == "chat":
|
||
r = chat(sys.argv[2])
|
||
print(r["content"] if "content" in r else r)
|
||
elif cmd == "breakdown":
|
||
with open(sys.argv[2], "r", encoding="utf-8") as f:
|
||
script = f.read()
|
||
r = breakdown_script(script)
|
||
print(r["content"] if "content" in r else r)
|