@starreel/mcp 0.1.144 → 0.1.149
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.
- package/SKILL.md +84 -13
- package/dist/i18n/instructions.en.md +3 -2
- package/dist/i18n/instructions.zh-CN.md +3 -2
- package/dist/i18n/tools.zh-CN.json +30 -27
- package/dist/tools/guide-data.js +12 -6
- package/dist/tools/produce.js +119 -36
- package/openapi.json +285 -26
- package/package.json +1 -1
- package/server.json +2 -2
package/dist/tools/guide-data.js
CHANGED
|
@@ -282,13 +282,18 @@ export const PIPELINE = [
|
|
|
282
282
|
];
|
|
283
283
|
export const REVIEW_GATES = [
|
|
284
284
|
{ after: '改写稿产出', run: 'review_script', before: ['extract_assets', 'generate_storyboards'] },
|
|
285
|
-
{ after: '分镜产出', run: 'review_storyboards', before: ['generate_frames'] },
|
|
286
|
-
{ after: '
|
|
285
|
+
{ after: '分镜产出', run: 'review_storyboards', before: ['generate_frames', 'generate_shot_frame'] },
|
|
286
|
+
{ after: '定妆图/设定图/场景图/道具图产出', run: 'review_assets', before: ['generate_frames', 'generate_shot_frame'] },
|
|
287
|
+
{ after: '镜头图产出', run: 'review_frames', before: ['generate_videos', 'regenerate_shot_video'] },
|
|
288
|
+
{ after: '视频/配音/配乐产出', run: 'review_videos', before: ['compose_episode'] },
|
|
287
289
|
];
|
|
288
|
-
export const REVIEW_GATE_RULE = '
|
|
290
|
+
export const REVIEW_GATE_RULE = '五道闸全部免费、服务端强制(跳过 → 400;资产层与视频层两道按剧灰度)。每次审查返回 review_token,把它随下游收费工具一起传' +
|
|
291
|
+
'(资产层那张作为 asset_review_token,与分镜层 review_token 一起传给出图工具);' +
|
|
289
292
|
'findings 逐条原样告诉客户(code=问题类型 · shots=命中镜号 · action=该调哪个工具修),按 action 修完复审再走。' +
|
|
290
293
|
'审查后又改了内容 → token 自动失效,复审一次即可。有 error 时默认拦截;只有客户知情并坚持才带 acknowledge_review:true——不要替客户做这个决定。' +
|
|
291
|
-
'`review_all` 是整集体检、不发 token。'
|
|
294
|
+
'`review_all` 是整集体检、不发 token。' +
|
|
295
|
+
'★每一层审查之后你(agent)还要**亲自质检**:按返回的 agent_qc 清单逐件看素材(图下载看、视频抽帧看、文本通读),' +
|
|
296
|
+
'调 submit_qc_report 提交报告;灰度剧没交报告 token 不能用,报告有问题与 error 同样默认拦。平台判有没有,你判像不像、对不对。';
|
|
292
297
|
export const QA_TOOLS = [
|
|
293
298
|
{ symptom: '话没说完就切 / 台词跑到别的镜上', run: 'scan_dialogue_coverage', then: ['repair_episode_dialogue(首选:换音频不重生,便宜)', 'regenerate_shot_video(特写镜或画面也错时)', 'compose_episode'] },
|
|
294
299
|
{ symptom: '切太快 / 一个镜头里画面跳来跳去', run: 'scan_intra_shot_cuts', then: ['update_project_settings(video_engine 改 seedance-2.5 或 hailuo-3)', 'regenerate_shot_video'] },
|
|
@@ -737,8 +742,9 @@ export function buildInstructions() {
|
|
|
737
742
|
'★第一步永远是判「客户手上有什么材料」——它决定入口,选错入口的返工都是真扣费(全表与要点:get_capabilities_guide):',
|
|
738
743
|
entry,
|
|
739
744
|
'',
|
|
740
|
-
'产线顺序(不跳步):create_drama(建剧即设好 setting_brief/画幅/video_engine/image_model/一致性锚,全免费) → set_script → rewrite_script → ★review_script → extract_assets → quote/generate_storyboards → ★review_storyboards → 定妆图+设定图 /
|
|
741
|
-
'
|
|
745
|
+
'产线顺序(不跳步):create_drama(建剧即设好 setting_brief/画幅/video_engine/image_model/一致性锚,全免费) → set_script → rewrite_script → ★review_script → extract_assets → quote/generate_storyboards → ★review_storyboards → 定妆图+设定图 / 场景图 / 道具图 / 色彩脚本 → ★review_assets → run_precheck → quote/generate_frames(默认只出首帧) → tail_frame_plan(免费·哪几镜要独立尾帧) → quote/generate_frames(frame_type=last_frame) → ★review_frames → quote/generate_videos → 音频 → ★review_videos → compose_episode → get_final_cut → review_final_cut。用 get_pipeline_status 查进度。',
|
|
746
|
+
'五道免费硬闸(跳过 → 400):review_script(extract_assets/分镜前)· review_storyboards(出图前,含单镜 generate_shot_frame)· review_assets(出图前,token 作 asset_review_token)· review_frames(出视频前,含单镜 regenerate_shot_video)· review_videos(compose_episode 前);review_token 随下游收费工具传(批量与单镜工具都要),findings 逐条原样告诉客户。',
|
|
747
|
+
'★每层审查之后你还要亲自质检:按返回的 agent_qc 逐件看素材,submit_qc_report 提交报告;灰度剧没交报告进不了下一步。',
|
|
742
748
|
'计费纪律:预付费不透支;大额步 quote_* → 把 estimated_points 原样告诉客户 → 客户同意后 generate_*(quote_id),绝不擅自确认;' +
|
|
743
749
|
'出图类报价给 estimated_points(上界,按它准备余额)与 typical_points(通常花费),两个都说;' +
|
|
744
750
|
'文本步按 token 后付;402 就停下让客户充值,别重试。',
|
package/dist/tools/produce.js
CHANGED
|
@@ -63,7 +63,30 @@ const REVIEW_GATE_HINT = (tool, layer) => `\n★【${layer}审查硬闸·免费
|
|
|
63
63
|
`(每条带 code=问题类型、shots=命中镜号、action=该调哪个工具修),再把 review_token 传进本工具。` +
|
|
64
64
|
`未审查会被 400 拒。审查后又改了内容 → token 自动失效,复审一次即可(仍免费)。` +
|
|
65
65
|
`有 error 时默认拦截;客户知情并坚持照现状继续,才带 acknowledge_review:true——` +
|
|
66
|
-
|
|
66
|
+
`带病推进大概率产出废片且照常扣费,不要替客户做这个决定。` +
|
|
67
|
+
`★灰度剧还要你(agent)亲自质检:按 ${tool} 返回的 agent_qc 逐件看素材、逐条答清单,调 submit_qc_report 提交后这张 token 才能用` +
|
|
68
|
+
`(没交 → 400 agent_qc_required;报告有问题 → 与 error 同样默认拦)。`;
|
|
69
|
+
/** 五个审查工具共用:平台审查之外,外部 agent 亲自看素材的质检(2026-10-14 用户拍板)。 */
|
|
70
|
+
const AGENT_QC_HINT = '\n★【agent 亲自质检·免费】返回的 agent_qc 里有本层清单(checklist)和本集要看的素材(materials,每件一个 ref,图/视频给链接)。' +
|
|
71
|
+
'平台只判「有没有、在不在、被没被拒」,「像不像、对不对、好不好」要你自己看:图片下载打开看、视频至少抽开头/中间/结尾帧、文本用 tool 通读。' +
|
|
72
|
+
'逐条回答清单(ok / issue / na,issue 写明哪件什么问题),看过的每个 ref 都列进 inspected,调 submit_qc_report 提交;质检结论原样讲给客户。' +
|
|
73
|
+
'agent_qc.required=true 时没提交报告这张 review_token 进不了下一步(400 agent_qc_required),报告有问题与平台 error 同样默认拦。';
|
|
74
|
+
/** 资产层 token:出镜头图同时要分镜层(review_token)与资产层两张,资产层这张单独一个参数。 */
|
|
75
|
+
const ASSET_REVIEW_ARG = {
|
|
76
|
+
asset_review_token: z.string().optional().describe('★来自 review_assets 的凭据(资产层硬闸:角色形象图/场景图/道具图;灰度剧必填,缺了 400 会点名 review_assets)。与分镜层的 review_token 一起传'),
|
|
77
|
+
};
|
|
78
|
+
/** 出镜头图的第二道闸提示(与分镜层并存)。 */
|
|
79
|
+
const ASSET_GATE_HINT = '\n★【资产层硬闸·免费】还要先跑 review_assets(角色形象图/场景图/道具图),把它的 review_token 作为 asset_review_token 传进来' +
|
|
80
|
+
'(与分镜层的 review_token 并存,两张都要;灰度剧缺了会 400 并点名 review_assets)。出完帧不会让资产 token 失效。';
|
|
81
|
+
/** 资产生成工具的「完成后质检」引导(每一步完成后都要质检,2026-10-14 用户拍板)。 */
|
|
82
|
+
const ASSET_QC_HINT = '\n★【完成后质检·免费】出完等图落库(轮询对应 get_* 看状态,pending 别重复调),再调 review_assets 审资产层' +
|
|
83
|
+
'(缺图 / 生成中 / 失败 / 被一致性闸拒收 / 道具时代冲突 / 色彩脚本),findings 原样告诉客户、按 action 修完复审;' +
|
|
84
|
+
'再按它返回的 agent_qc 亲自逐张看图(像不像档案、是不是同一个人、时代对不对),submit_qc_report 提交质检报告;' +
|
|
85
|
+
'出镜头图时把它的 review_token 作为 asset_review_token 传给 generate_frames / generate_shot_frame。';
|
|
86
|
+
/** 视频与配音生成工具的「完成后质检」引导。 */
|
|
87
|
+
const VIDEO_QC_HINT = '\n★【完成后质检·免费】出完等它落库(get_storyboards 看 video_status / get_pipeline_status),再调 review_videos 审视频与配音层' +
|
|
88
|
+
'(缺视频 / 旧帧出的视频 / 片尾硬切 / 意外人声 / 配音缺失 / 音色未分配 / 原声声线过期),修完复审;' +
|
|
89
|
+
'再按 agent_qc 亲自逐镜看视频、听配音(动作到位、不变脸、台词念全、嗓音对),submit_qc_report 提交;compose_episode 要带它的 review_token。';
|
|
67
90
|
/** 三个被闸的收费工具共用的参数(zod 形状,展开进各自 schema)。 */
|
|
68
91
|
const REVIEW_ARGS = {
|
|
69
92
|
review_token: z.string().optional().describe('★来自本层 review_* 工具的凭据(受闸项目必填;缺了会 400 并告诉你该调哪个审查工具)'),
|
|
@@ -72,12 +95,12 @@ const REVIEW_ARGS = {
|
|
|
72
95
|
// 完整工作流顺序(照 get_pipeline_status 的 10 步真相走,不要跳步):
|
|
73
96
|
// create_drama → set_script(原始) → rewrite_script(AI改写) → [get_script/edit_rewritten_script 审改]
|
|
74
97
|
// → extract_assets(角色/场景/道具) → quote/generate_storyboards(先分镜·纯文本拆镜)
|
|
75
|
-
// → generate_portraits_and_sheets(
|
|
98
|
+
// → generate_portraits_and_sheets(定妆图+设定图·一致性关键·分镜后建:先 delete_character 删**全剧**都不出场的出镜角色再出图——角色是剧级的,删了别的集也没了,拿不准就留着;它对所有未删的出镜角色出图、不看分镜绑定;画外音 voiceover 角色不出图也别删,配音还靠它;设定图不能省)
|
|
76
99
|
// → generate_color_script(剧目级资产·分镜后出图前·文本步无报价)
|
|
77
100
|
// → quote/generate_frames(默认只出首帧) → tail_frame_plan(免费) → generate_frames(frame_type=last_frame) → quote/generate_videos
|
|
78
101
|
// → compose_episode → get_final_cut / get_export
|
|
79
102
|
// 项目设定随时可 update_project_settings;色彩脚本保留主干,动作模板仅可选实验。
|
|
80
|
-
const WORKFLOW_HINT = '★三档执行策略(别把三档混着问客户):' +
|
|
103
|
+
export const WORKFLOW_HINT = '★三档执行策略(别把三档混着问客户):' +
|
|
81
104
|
'①【基础项目设定·免费·必做地基·建剧即设好,别建空壳】project_type/setting_brief(世界观·ERA LOCK)/' +
|
|
82
105
|
'ethnicity(族裔)/画幅分辨率,以及一致性锚 cinematography_prompt(摄影DNA)·art_bible(美术圣经)·visual_lock(视觉锁定)——' +
|
|
83
106
|
'全免费,是驱动全链一致性的地基;不设好,后续所有生成都跑偏、返工重花钱。用 create_drama/update_project_settings 直接设。' +
|
|
@@ -92,7 +115,7 @@ const WORKFLOW_HINT = '★三档执行策略(别把三档混着问客户):' +
|
|
|
92
115
|
'非人化形一并判成「不合时代」,把画面往写实古代拽。某一场要破例(如奇幻世界里一场现代回忆)' +
|
|
93
116
|
'用 update_scene 的 era_contract 单独覆写,场级压过集级。' +
|
|
94
117
|
'★★【逐环节审查协议·全部免费·这是防废片的主线,不是可选项】每个环节产出后先审查、把结论原样告诉客户,再进下一步。' +
|
|
95
|
-
'
|
|
118
|
+
'**五道硬闸(不过会被 400 拒;④⑤与 agent 质检见下文)**:①改写稿产出后 → review_script(在 extract_assets / generate_storyboards 之前);' +
|
|
96
119
|
'②分镜产出后 → review_storyboards(在 generate_frames 之前);③镜头图片产出后 → review_frames(在 generate_videos 之前)。' +
|
|
97
120
|
'每次审查返回 review_token,把它随下游收费工具一起传;findings 逐条讲给客户(code=问题类型·shots=命中镜号·action=该调哪个工具修),' +
|
|
98
121
|
'按 action 修完后**复审**再走。审查后又改了内容 → token 自动失效,复审一次即可(免费)。' +
|
|
@@ -116,7 +139,8 @@ const WORKFLOW_HINT = '★三档执行策略(别把三档混着问客户):' +
|
|
|
116
139
|
'且新版不保证保留旧版已改好的地方(三版实测会来回摆)。要修就 edit_rewritten_script 点改' +
|
|
117
140
|
'(get_script 取全文 → 只改那几场、其余逐字照抄 → 提交整篇),免费秒级、结果确定;' +
|
|
118
141
|
'误重跑用 get_script(include_previous=1) 回捞上一版。' +
|
|
119
|
-
'generate_portraits_and_sheets(
|
|
142
|
+
'generate_portraits_and_sheets(定妆图+设定图·分镜后建:先用 delete_character 删掉**全剧任何一集都不出场、后续集也不打算用**的出镜角色再出图——角色是剧级的,删了每一集的出帧装配都不再带它,只看本集分镜判不出来、拿不准就留着(一张定妆图的钱远小于后面某集丢身份锚);定妆图步对**所有未删的出镜角色**出图、不看分镜绑定,留着的都照常扣费;' +
|
|
143
|
+
'画外音角色(char_type=voiceover 的旁白/电话音)本就不出图、也没有分镜绑定,**别删**,配音还靠它)→' +
|
|
120
144
|
'★generate_color_script(色彩脚本·统一调色)→assign_voices(分配音色;旁白/角色库里没合适的→design_voice 按描述造一个,定样后 set_character_voice)→' +
|
|
121
145
|
'★quote_scene_images+generate_scene_images(空景基板·出帧前必做)→frames(默认只出首帧)→★tail_frame_plan(免费·哪几镜要独立尾帧)→frames(frame_type=last_frame)→★review_frames→videos→generate_tts→compose;' +
|
|
122
146
|
'★★【尾帧别跳·出帧是两趟】generate_frames 默认只出首帧。约三成的镜**末态≠首态**(大运镜/物体脱手/状态改变),这些镜需要一张独立尾帧,而判据在平台侧、你从分镜文本猜不出来——' +
|
|
@@ -139,10 +163,20 @@ const WORKFLOW_HINT = '★三档执行策略(别把三档混着问客户):' +
|
|
|
139
163
|
'用 generate_portraits_and_sheets 一键两步(每调一次推进一步,定妆图齐了再调一次才出设定图),或定妆图后接 generate_character_sheets。' +
|
|
140
164
|
'进度自检看 get_pipeline_status 的 generate_character_sheets 步(completed/total,portrait_only=只有定妆图的角色数)。' +
|
|
141
165
|
'★客户自带图:定妆图须单人、单张单角度、无文字;场景图须空景无人、无叠加文字——set_character_portrait/upload_scene_image 会自动检查、不合规 422 拒收。' +
|
|
142
|
-
'
|
|
166
|
+
'★别先建角色形象/道具设定图/动作模板再分镜——分镜是纯文本步、不依赖任何图;分镜后阵容才定,先删不出场角色再出图才省(动作模板本就必须分镜后)。收费步照现有 quote 报价确认流程。' +
|
|
143
167
|
'广告另需 add_product+generate_product_sheet;MV 走 set_mv_lyrics→generate_mv_story→generate_mv_script(没有现成歌曲:set_mv_lyrics 后先 suggest_mv_song_styles→quote_mv_song→generate_mv_song→get_mv_song→select_mv_song)。' +
|
|
168
|
+
'★★【④⑤两道新硬闸 + agent 亲自质检】④资产层:定妆图/设定图/场景图/道具图出完 → review_assets' +
|
|
169
|
+
'(在 generate_frames / generate_shot_frame 之前,它的 token 作为 asset_review_token、与分镜层 review_token 一起传);' +
|
|
170
|
+
'⑤视频与配音层:视频/配音/配乐出完 → review_videos(在 compose_episode 之前);成片出来后跑 review_final_cut。' +
|
|
171
|
+
'★每一层审查之后你还要**亲自质检**:按返回的 agent_qc 逐件看素材(图下载看、视频抽帧看、文本通读),逐条答清单,' +
|
|
172
|
+
'submit_qc_report 提交;灰度剧没交报告那张 review_token 进不了下一步,报告有问题与 error 同样默认拦。平台判有没有,你判像不像、对不对。' +
|
|
173
|
+
'★★【单镜路径同样受审查闸】:generate_shot_frame 同样要带分镜层 review_token,regenerate_shot_video 同样要带镜头图片层 review_token——' +
|
|
174
|
+
'别指望用逐镜工具绕开审查(实测有整集 25 镜全靠单镜出帧、一道闸都没碰到的做法,成片返工全是真扣费)。' +
|
|
175
|
+
'出完帧**不会**让分镜层 token 失效(指纹只看分镜内容);只有改了分镜内容——update_shot / 导入 / 增删镜 / 改角色绑定——才要复审一次(免费、秒级)。' +
|
|
176
|
+
'出完一镜的帧会让**镜头图片层** token 失效,下一镜 regenerate_shot_video 前 review_frames 一次即可。' +
|
|
144
177
|
'★★【原声剧先锁声线,再批量出视频】use_clip_audio=true(默认)时每镜嗓音由视频厂商各自采样,不锁必漂,事后统一要逐镜重出。' +
|
|
145
|
-
'有客户授权音源→clone_voice+set_character_voice,在 generate_videos
|
|
178
|
+
'有客户授权音源→clone_voice+set_character_voice,在 generate_videos 之前;没有→每个说话角色先用单镜工具 quote_regenerate_shot_video→regenerate_shot_video 出一镜台词较长的' +
|
|
179
|
+
'(generate_videos 没有按镜参数、只批量补缺视频的镜;单镜重生对还没有视频的镜同样可用),' +
|
|
146
180
|
'用 set_voice_anchor_from_shot 从那一镜选定声线,再出其余镜。get_pipeline_status 的 native_voice_anchor 会列出还没锁的' +
|
|
147
181
|
' unanchored_speakers 和锁之前出的、需要重出的 stale_shots。' +
|
|
148
182
|
'★世界观概念图=默认必做(提升整剧一致性、很多第三方平台漏做这步):分镜后默认调 generate_world_concept,' +
|
|
@@ -495,7 +529,9 @@ export function registerProduceTools(server, client) {
|
|
|
495
529
|
server.tool('extract_assets', '从可拍稿提取角色/场景/道具(一次写三表,是下游一致性的地基)。后台异步,文本步后付不欠费。' +
|
|
496
530
|
'前置:已 rewrite_script 产出改写稿(新项目强制;人物档案从改写稿提取才与剧本、分镜自洽)。' +
|
|
497
531
|
'★分钟级后台任务;用 get_run_status 判断是否还在跑,别拿 60 秒当失败判据。' +
|
|
498
|
-
REVIEW_GATE_HINT('review_script', '改写稿') + WORKFLOW_HINT
|
|
532
|
+
REVIEW_GATE_HINT('review_script', '改写稿') + WORKFLOW_HINT +
|
|
533
|
+
'\n★【完成后质检】提取完用 get_characters / get_scenes / get_props 核对档案(名字、外貌、时代)与客户剧本一致;' +
|
|
534
|
+
'定妆图/设定图/场景图/道具图出完后由 review_assets 统一审覆盖与质量。', { episode_id: z.number().int().positive(), ...REVIEW_ARGS }, async ({ episode_id, review_token, acknowledge_review }) => jsonResult(await client.producePost(`/episodes/${episode_id}/extract`, { review_token, acknowledge_review })));
|
|
499
535
|
// ---------- 完整工作流进度 ----------
|
|
500
536
|
server.tool('get_pipeline_status', '查某一集完整工作流的进度(script_rewrite/提取/分镜/语音/出图/出视频/合成/配乐/终拼…各步 ' +
|
|
501
537
|
'done/partial/pending/not_required)。照它按序推进、不跳步。免费。' +
|
|
@@ -510,16 +546,16 @@ export function registerProduceTools(server, client) {
|
|
|
510
546
|
'只出定妆图不出设定图,镜头人物换角度/换光/服装会漂——报价时把这一步一起告诉客户。' + QUOTE_RANGE_HINT, { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/portraits/quote`)));
|
|
511
547
|
server.tool('generate_character_portraits', '确认后批量出角色定妆图(★仅定妆图):后台异步。定妆图只是单张身份锚——镜头一致性(尤其服装)还需**设定图**,' +
|
|
512
548
|
'出完定妆图强烈建议 generate_character_sheets;或直接用 generate_portraits_and_sheets 一步到位。' +
|
|
513
|
-
'用 get_pipeline_status/get_storyboards 查进度。' + CONFIRM_HINT, { episode_id: z.number().int().positive(), quote_id: z.string().describe('来自 quote_character_portraits') }, async ({ episode_id, quote_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/portraits/generate`, { quote_id })));
|
|
549
|
+
'用 get_pipeline_status/get_storyboards 查进度。' + CONFIRM_HINT + ASSET_QC_HINT, { episode_id: z.number().int().positive(), quote_id: z.string().describe('来自 quote_character_portraits') }, async ({ episode_id, quote_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/portraits/generate`, { quote_id })));
|
|
514
550
|
// ---------- 设定图(镜头一致性根锚·批量 / 一键) ----------
|
|
515
551
|
server.tool('generate_character_sheets', '★批量给全剧角色出设定图(多视角 turnaround)。设定图是镜头帧/视频引用的**一致性根锚**——只出定妆图不出设定图,' +
|
|
516
552
|
'镜头人物换角度/换光/服装会漂移(2.5 更直接用设定图切片做骨相锚)。只对缺设定图的角色出(不重复扣费)。' +
|
|
517
553
|
'前置:角色须已有定妆图(缺则拦并提示先出定妆图)。图片步,按用量后付不欠费。轮询 get_characters 看 sheet_status:' +
|
|
518
554
|
'pending=生成/审计中(设定图约 2~4 分钟,别重复点);ready=入库;**rejected=图已出但被一致性闸拒收**,' +
|
|
519
|
-
'读 sheet_fail_hint 拒收判词——可重掷一次,连拒理由相同则是锚字段与人物档案冲突,先 run_precheck 再重掷。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/character-sheets`)));
|
|
555
|
+
'读 sheet_fail_hint 拒收判词——可重掷一次,连拒理由相同则是锚字段与人物档案冲突,先 run_precheck 再重掷。' + ASSET_QC_HINT, { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/character-sheets`)));
|
|
520
556
|
server.tool('generate_portraits_and_sheets', '★一键:定妆图 + 设定图(推荐,产线标配)。设定图强依赖定妆图,故智能两阶段——有角色缺定妆图就先派定妆图并提示,' +
|
|
521
557
|
'定妆图齐了再调一次即批量出设定图。避免只出定妆图导致镜头漂移。图片步,按用量后付不欠费。' +
|
|
522
|
-
'轮询 get_characters 看 image/sheet_url 就绪;每调一次推进一步。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/portraits-and-sheets`)));
|
|
558
|
+
'轮询 get_characters 看 image/sheet_url 就绪;每调一次推进一步。' + ASSET_QC_HINT, { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/portraits-and-sheets`)));
|
|
523
559
|
// ---------- 拆镜(storyboards) ----------
|
|
524
560
|
server.tool('quote_storyboards', '报价:把某一集的剧本拆成分镜(storyboards)要多少点。零扣费。返回 quote_id 与一个**区间**:' +
|
|
525
561
|
'estimated_points=预算上沿(按它备余额,拆到一半不会 402)、typical_points=通常花费——两个都告诉用户,' +
|
|
@@ -686,21 +722,56 @@ export function registerProduceTools(server, client) {
|
|
|
686
722
|
server.tool('review_script', '【第①道硬闸·免费】审查 AI 改写稿:对白行长度、情绪转折完整性、开场钩子、末场悬念。' +
|
|
687
723
|
'★extract_assets 与 generate_storyboards 之前必须先跑本工具——人物档案和分镜都从改写稿派生,' +
|
|
688
724
|
'稿子里的问题会被逐层放大到定妆图/出图/出视频,等成片才发现就是整集重来。' +
|
|
689
|
-
'把 findings 逐条告诉客户、按 action 修完(改稿走 edit_rewritten_script)后复审,再拿 review_token 往下走。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/review/script`)));
|
|
725
|
+
'把 findings 逐条告诉客户、按 action 修完(改稿走 edit_rewritten_script)后复审,再拿 review_token 往下走。' + AGENT_QC_HINT, { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/review/script`)));
|
|
690
726
|
server.tool('review_storyboards', '【第②道硬闸·免费】审查分镜:禁区词(会被厂商审核拒、白扣费)、镜头时长分布、相邻构图重复、' +
|
|
691
727
|
'同场景角色站位漂移、情绪曲线峰谷、关键镜标记。★generate_frames 之前必须先跑本工具——' +
|
|
692
728
|
'分镜里的问题一旦整集出图就变成整集废图,单镜修不回来。按 findings.action 用 update_shot/split_shot 修完再复审。' +
|
|
693
729
|
'★code=forbidden_term 的 finding 带 `terms` 字段(命中的具体词,如「背景音乐」「强光」)与 `shots`(镜号),' +
|
|
694
730
|
'照着这两个去 update_shot 改掉即可,不用逐字猜。' +
|
|
695
731
|
'★禁区词判**否定语义**:写「无背景音乐」「不要配乐」不算违规(那是在遵守约束),不必为此改稿。' +
|
|
696
|
-
'★warnings 里的「动作过载」(action_overload,advisory 不拦):一镜塞了超过时长承载量的独立动作,出视频前用 split_shot 拆开或删动作——成片后靠慢放/叠化补救不了。多人各做一件事或本身连贯的一串可忽略。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/review/storyboards`)));
|
|
732
|
+
'★warnings 里的「动作过载」(action_overload,advisory 不拦):一镜塞了超过时长承载量的独立动作,出视频前用 split_shot 拆开或删动作——成片后靠慢放/叠化补救不了。多人各做一件事或本身连贯的一串可忽略。' + AGENT_QC_HINT, { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/review/storyboards`)));
|
|
697
733
|
server.tool('review_frames', '【第③道硬闸·免费】审查镜头图片层:角色身份锚覆盖(缺定妆图的角色在镜头里必漂)、出图失败率、' +
|
|
698
734
|
'孤儿角色变体、场景图被人物污染。★generate_videos 之前必须先跑本工具——出视频是全链最贵的一步,' +
|
|
699
735
|
'拿着漂移的首帧整集出视频是最典型的废片形态。按 findings.action 修完(多为 generate_character_portraits / ' +
|
|
700
736
|
'generate_shot_frame 单镜重生)再复审。' +
|
|
701
737
|
'★还会报**镜间接缝**(warning,不拦):code=seam_unchained(标了连续却从没做过首尾帧衔接,或发起的衔接重生没落地 / 衔接后首帧又被重生)/' +
|
|
702
738
|
'seam_broken(接过但没接上,或上一镜尾帧在衔接之后又重生过),shots 是「与上一镜的缝有问题」的那一镜。出视频前修最便宜——视频按首帧生成,' +
|
|
703
|
-
'首帧与上镜尾帧对不上,成片就是一次跳切。修法看 action(整集 chain_frames 先 dry_run;单道 chain_shot_from_prev / chain_shot_from)。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/review/frames`)));
|
|
739
|
+
'首帧与上镜尾帧对不上,成片就是一次跳切。修法看 action(整集 chain_frames 先 dry_run;单道 chain_shot_from_prev / chain_shot_from)。' + AGENT_QC_HINT, { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/review/frames`)));
|
|
740
|
+
server.tool('review_assets', '【资产层硬闸·免费】审查本集**出镜**用到的角色形象图、场景图、道具图——覆盖与质量:' +
|
|
741
|
+
'出镜角色缺定妆图 / 缺设定图 / 还在生成 / 生成失败 / 设定图被一致性闸拒收(hint 带拒收理由);' +
|
|
742
|
+
'场景缺空景基板;分镜引用的道具缺道具图 / 被拒;道具图与本镜时代形制冲突;缺色彩脚本(warning)。' +
|
|
743
|
+
'★出完定妆图、设定图、场景图、道具图后都调一次;generate_frames / generate_shot_frame 之前必须审过——' +
|
|
744
|
+
'把返回的 review_token 作为 **asset_review_token**,连同分镜层的 review_token 一起传(两张都要)。' +
|
|
745
|
+
'只看资产本身:出完镜头图不会让它失效;换了定妆图/场景图/道具图会失效,复审即可(免费、秒级)。' +
|
|
746
|
+
'findings 逐条讲给客户(code=问题类型、names=哪个角色/场景/道具、shots=影响的镜号、action=该调哪个工具修)。' + AGENT_QC_HINT, { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/review/assets`)));
|
|
747
|
+
server.tool('review_videos', '【视频与配音层硬闸·免费】审查本集视频与配音——覆盖与质量:缺视频 / 还在生成 / 生成失败 / 被厂商拒;' +
|
|
748
|
+
'首帧换过而视频还是旧帧出的(video_stale);疑似片尾硬切、无台词镜意外人声(warning);' +
|
|
749
|
+
'配音剧:台词镜缺配音、会开口的角色没音色、缺旁白角色;原声剧:出片时没挂上当前声线的镜、没锁声线的角色;缺配乐(warning)。' +
|
|
750
|
+
'★出完视频、配音(generate_tts)、配乐后都调一次;compose_episode 之前必须审过,带返回的 review_token。' +
|
|
751
|
+
'更细的检查(要解码画面/音轨)另有 scan_intra_shot_cuts / scan_dialogue_coverage / audit_silent_shot_audio;拼完成片再跑 review_final_cut。' + AGENT_QC_HINT, { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/review/videos`)));
|
|
752
|
+
server.tool('submit_qc_report', '提交你(agent)亲自看素材做的质检报告。免费。先调对应层的 review_*(script / storyboards / assets / frames / videos),' +
|
|
753
|
+
'按返回的 agent_qc 逐件打开 materials 看、逐条回答 checklist,再调本工具。' +
|
|
754
|
+
'★平台只校验报告**是否完整**,不评判你的结论:checklist 每个 id 都要答(ok / issue / na,issue 必须写 note 说清哪件什么问题);' +
|
|
755
|
+
'materials 里的每个 ref 都要列进 inspected(漏一件就拒收并点名漏了哪些);有问题就 verdict=issues 并在 issues 里逐条写,有问题却报 pass 会被拒。' +
|
|
756
|
+
'★别走过场:没打开看就列进 inspected,等于替客户签了字——出了废片照常扣费。' +
|
|
757
|
+
'★verdict=issues 时这张 token 默认进不了下一步:把问题讲给客户,修完重新 review + 质检;客户明确知情并坚持照现状继续,下一步才带 acknowledge_review:true。' +
|
|
758
|
+
'审查之后内容又变了,报告会被拒,重新 review 后按新的 agent_qc 再质检。', {
|
|
759
|
+
episode_id: z.number().int().positive(),
|
|
760
|
+
review_token: z.string().describe('对应层 review_* 返回的 review_token(资产层也是这个值,出图时再作为 asset_review_token 传)'),
|
|
761
|
+
verdict: z.enum(['pass', 'issues']).describe("'pass'=本层没问题;'issues'=发现了问题"),
|
|
762
|
+
checklist: z.array(z.object({
|
|
763
|
+
id: z.string().describe('agent_qc.checklist[].id'),
|
|
764
|
+
result: z.enum(['ok', 'issue', 'na']),
|
|
765
|
+
note: z.string().optional().describe('result=issue 时必填:哪一件(ref)、看到了什么问题'),
|
|
766
|
+
})).describe('agent_qc.checklist 里每一项都要回答'),
|
|
767
|
+
inspected: z.array(z.string()).describe('亲自打开看过的素材 ref(agent_qc.materials[].ref),必须一件不漏'),
|
|
768
|
+
issues: z.array(z.object({
|
|
769
|
+
ref: z.string().optional().describe('哪一件(materials 的 ref)'),
|
|
770
|
+
problem: z.string().describe('看到的具体问题'),
|
|
771
|
+
action: z.string().optional().describe('建议怎么修(用哪个工具)'),
|
|
772
|
+
})).optional().describe("verdict='issues' 时至少一条"),
|
|
773
|
+
summary: z.string().optional().describe('给客户看的一段质检小结'),
|
|
774
|
+
}, async ({ episode_id, ...report }) => jsonResult(await client.producePost(`/episodes/${episode_id}/review/qc-report`, report)));
|
|
704
775
|
server.tool('review_all', '(推荐·免费)三层一次跑完的整体体检:剧本+分镜+镜头图片。开工前摸底、交付前复查用。' +
|
|
705
776
|
'★它**不发 review_token** —— 进收费步前仍需对应层的 review_script / review_storyboards / review_frames 各跑一次' +
|
|
706
777
|
'(闸认的是「针对当前产出物刚审过」,不是「审过一次」)。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/review/all`)));
|
|
@@ -752,18 +823,19 @@ export function registerProduceTools(server, client) {
|
|
|
752
823
|
'若 diagnosis_required,先按 fail_hint 诊断已有候选;审计错误先重审,证据不足先复核,不要原样重抽或擅自改剧本。' +
|
|
753
824
|
'然后**跑 run_precheck**(事后也能跑,会告诉你是哪种矛盾),按它的提示用 update_shot 改景别/描述/绑定角色再重生。' +
|
|
754
825
|
'别继续 generate_frames 空转,也别把客户支去外部工具做图——外部图绕开身份锚与帧审计,人脸服装画风必漂。' +
|
|
755
|
-
REVIEW_GATE_HINT('review_storyboards', '分镜') + CONFIRM_HINT, {
|
|
826
|
+
REVIEW_GATE_HINT('review_storyboards', '分镜') + ASSET_GATE_HINT + CONFIRM_HINT, {
|
|
756
827
|
episode_id: z.number().int().positive(),
|
|
757
828
|
quote_id: z.string().describe('来自 quote_frames'),
|
|
758
829
|
...REVIEW_ARGS,
|
|
830
|
+
...ASSET_REVIEW_ARG,
|
|
759
831
|
frame_type: FRAME_TYPE_ARG.optional().describe('默认 first_frame,须与报价时一致'),
|
|
760
832
|
image_model: z.string().optional().describe('临时覆盖本次出图模型(★必须与 quote_frames 传的那个一致,否则 400;不传=用 drama 级设定,默认 ChatGPT Image 2.5 Flare)。可选:' +
|
|
761
833
|
'gpt-image-2.5-flare(默认·基础11点+每张参考图18点)/gpt-image-2.5-sunburst(同价·中文字形与细节更准)/' +
|
|
762
834
|
'gemini-3.1-flash-image(Nano Banana 2·71点)/gemini-3-pro-image(Nano Banana Pro·更精细·175点)/' +
|
|
763
835
|
'gemini-3.1-flash-lite-image(Nano Banana 2 Lite·便宜·31点)/doubao-seedream-5-0-260128(Seedream 5.0)'),
|
|
764
836
|
allow_era_ref_conflict: z.boolean().optional().describe('★默认不传。时代审核判定客户上传的定妆图(或由它派生的身体/发型/设定图)与时代设定冲突时,出图会被拦下(400 ERA_USER_REF_CONFLICT,未扣费)。**先把冲突原因告诉客户、由客户确认造型无误**后才带 true:这些图保留不剔、照常出图。造型真不对就改定妆图,时代设定错了就 set_era_contract'),
|
|
765
|
-
}, async ({ episode_id, quote_id, frame_type, image_model, review_token, acknowledge_review, allow_era_ref_conflict }) => jsonResult(await client.producePost(`/episodes/${episode_id}/frames/generate`, {
|
|
766
|
-
quote_id, frame_type, image_model, review_token, acknowledge_review, allow_era_ref_conflict,
|
|
837
|
+
}, async ({ episode_id, quote_id, frame_type, image_model, review_token, asset_review_token, acknowledge_review, allow_era_ref_conflict }) => jsonResult(await client.producePost(`/episodes/${episode_id}/frames/generate`, {
|
|
838
|
+
quote_id, frame_type, image_model, review_token, asset_review_token, acknowledge_review, allow_era_ref_conflict,
|
|
767
839
|
})));
|
|
768
840
|
// ---------- 单镜出帧/重生(改某一镜的画面走这里,别去外部平台出图再传回来) ----------
|
|
769
841
|
server.tool('quote_shot_frame', '报价:重画/补出**某一镜的某一帧**要多少点(一帧=一张图)。返回 estimated_points、quote_id。零扣费。' +
|
|
@@ -787,16 +859,18 @@ export function registerProduceTools(server, client) {
|
|
|
787
859
|
'★★出尾帧若被拦下并提示「此镜标为状态改变,但没有任何地方说明结束时是什么样子」:' +
|
|
788
860
|
'正解是用 update_shot 把结束状态写进 last_frame_prompt 再出,**别直接加 allow_missing_terminal 硬上**——' +
|
|
789
861
|
'这类镜照现状出尾帧的历史成功率只有 9.4%(有终态描述的 40.2%),硬上多半是白扣一次钱。' +
|
|
790
|
-
'只有确认这一镜就是要「几乎不变」的画面时才带那个参数。' + CONFIRM_HINT, {
|
|
862
|
+
'只有确认这一镜就是要「几乎不变」的画面时才带那个参数。' + REVIEW_GATE_HINT('review_storyboards', '分镜') + ASSET_GATE_HINT + CONFIRM_HINT, {
|
|
791
863
|
storyboard_id: z.number().int().positive(),
|
|
792
864
|
quote_id: z.string().describe('来自 quote_shot_frame'),
|
|
865
|
+
...REVIEW_ARGS,
|
|
866
|
+
...ASSET_REVIEW_ARG,
|
|
793
867
|
frame_type: FRAME_TYPE_ARG.optional().describe('默认 first_frame,须与报价时一致;传了 reopen_pair_id 时会被忽略'),
|
|
794
868
|
replace_user_frame: z.boolean().optional().describe('默认 true(显式重生允许覆盖已上传帧);传 false 则保护已上传帧'),
|
|
795
869
|
reopen_pair_id: z.string().optional().describe('来自 get_storyboards 该镜的同名字段;只重掷有问题的那一侧,不必再传 frame_type'),
|
|
796
870
|
image_model: z.string().optional().describe('临时覆盖本次重画的图片模型(★必须与 quote_shot_frame 传的那个一致,否则 400 IMAGE_MODEL_MISMATCH;不传=用 drama 级设定,默认 ChatGPT Image 2.5 Flare);取值同 generate_frames'),
|
|
797
871
|
allow_missing_terminal: z.boolean().optional().describe('★逃生门,默认不传。本镜标为「状态改变」却没写终态时,出尾帧会被前置闸拦下;带 true 表示「我知道,照现状出」。正解是先 update_shot 补 last_frame_prompt——那类镜照现状出的成功率 9.4%,这个参数只用于确认本镜就该「几乎不变」的场合'),
|
|
798
872
|
allow_era_ref_conflict: z.boolean().optional().describe('★默认不传。时代审核判定客户上传的定妆图(或由它派生的身体/发型/设定图)与时代设定冲突时,出图会被拦下(400 ERA_USER_REF_CONFLICT,未扣费)。**先把冲突原因告诉客户、由客户确认造型无误**后才带 true:这些图保留不剔、照常出图。造型真不对就改定妆图,时代设定错了就 set_era_contract'),
|
|
799
|
-
}, async ({ storyboard_id, quote_id, frame_type, replace_user_frame, reopen_pair_id, image_model, allow_missing_terminal, allow_era_ref_conflict }) => jsonResult(await client.producePost(`/storyboards/${storyboard_id}/frame/generate`, { quote_id, frame_type, replace_user_frame, reopen_pair_id, image_model, allow_missing_terminal, allow_era_ref_conflict })));
|
|
873
|
+
}, async ({ storyboard_id, quote_id, review_token, asset_review_token, acknowledge_review, frame_type, replace_user_frame, reopen_pair_id, image_model, allow_missing_terminal, allow_era_ref_conflict }) => jsonResult(await client.producePost(`/storyboards/${storyboard_id}/frame/generate`, { quote_id, review_token, asset_review_token, acknowledge_review, frame_type, replace_user_frame, reopen_pair_id, image_model, allow_missing_terminal, allow_era_ref_conflict })));
|
|
800
874
|
// ---------- 出视频(videos,大额) ----------
|
|
801
875
|
server.tool('quote_videos', '报价:给某一集批量出视频要多少点(与实际扣费同函数,较准)。返回 estimated_points、quote_id。零扣费。' +
|
|
802
876
|
'⚠️ 视频是大额花费,务必把点数清楚告诉用户并等其确认。' +
|
|
@@ -819,7 +893,7 @@ export function registerProduceTools(server, client) {
|
|
|
819
893
|
}, async ({ episode_id, shot_engines }) => jsonResult(await client.producePost(`/episodes/${episode_id}/videos/quote`, shot_engines ? { shot_engines } : {})));
|
|
820
894
|
server.tool('generate_videos', '确认后批量出视频:一条后台链跑完整集,余额不足会自动中止整链(防重复扣)。' +
|
|
821
895
|
'要求本集已出首帧(未出会被拒)。用 get_storyboards 轮询 video_url 逐镜填充即完成。' +
|
|
822
|
-
REVIEW_GATE_HINT('review_frames', '镜头图片') + CONFIRM_HINT, {
|
|
896
|
+
REVIEW_GATE_HINT('review_frames', '镜头图片') + CONFIRM_HINT + VIDEO_QC_HINT, {
|
|
823
897
|
episode_id: z.number().int().positive(),
|
|
824
898
|
quote_id: z.string().describe('来自 quote_videos'),
|
|
825
899
|
...REVIEW_ARGS,
|
|
@@ -828,10 +902,16 @@ export function registerProduceTools(server, client) {
|
|
|
828
902
|
server.tool('compose_episode', '把某一集所有镜头视频拼成一条成片(终拼)。**免费**(纯拼接,无需报价确认),后台异步。' +
|
|
829
903
|
'发起前有终拼预检:缺视频的镜或仍在生成中的视频任务会 400 硬阻断并列出问题镜(blockers),' +
|
|
830
904
|
'补齐后重试;确要拼部分成片传 force=true(缺视频镜被跳过,成片缺镜,留痕)。' +
|
|
831
|
-
'响应里的 advisories(时长偏差/孤儿任务/缺TTS)只提醒不阻断,建议逐条处理后再拼。发起后用 get_final_cut 轮询。'
|
|
905
|
+
'响应里的 advisories(时长偏差/孤儿任务/缺TTS)只提醒不阻断,建议逐条处理后再拼。发起后用 get_final_cut 轮询。' +
|
|
906
|
+
REVIEW_GATE_HINT('review_videos', '视频与配音') +
|
|
907
|
+
'\n★【完成后质检·免费】成片出来后跑 review_final_cut(缺镜/字幕放不下/身份锚/跳轴/错别字,proofread=false 则不扣费),' +
|
|
908
|
+
'结论讲给客户;客户确认后才 confirm_delivery_acceptance。force 只越过终拼预检,不越过审查闸。', {
|
|
832
909
|
episode_id: z.number().int().positive(),
|
|
833
910
|
force: z.boolean().optional().describe('true=预检有硬阻断也强制照拼(默认拦截)。缺视频的镜会被静默跳过,成片为部分'),
|
|
834
|
-
|
|
911
|
+
...REVIEW_ARGS,
|
|
912
|
+
}, async ({ episode_id, force, review_token, acknowledge_review }) => jsonResult(await client.producePost(`/episodes/${episode_id}/compose`, {
|
|
913
|
+
review_token, acknowledge_review, ...(force ? { force: true } : {}),
|
|
914
|
+
})));
|
|
835
915
|
server.tool('get_final_cut', '查某一集成片状态与下载链接。status=completed 时返回 download_url(我方 COS 直链,可直接下载)。免费。' +
|
|
836
916
|
'★bgm_stale=true 表示配乐在成片之后生成/改动、尚未进成片:重新 compose_episode(免费)即可,别用 re-render。' +
|
|
837
917
|
'★若设过当前交付版(register_external_delivery 登记的外部版,或 set_current_delivery 指定的平台版),另返回 delivery——那才是客户实际收到的一版;download_url 仍是平台最新一次拼的成片。' +
|
|
@@ -1096,7 +1176,7 @@ export function registerProduceTools(server, client) {
|
|
|
1096
1176
|
'★出得不对不要重调本工具(它只补缺图的场景、已有图的一律跳过):' +
|
|
1097
1177
|
'`get_scene_prompt` 读正文 → `update_scene` 改 image_prompt → `regenerate_scene_image` 单场重出。' +
|
|
1098
1178
|
'★非现代题材先设 `set_era_contract`,否则基板会画成别的时代,下游整场的镜头帧跟着错。' +
|
|
1099
|
-
'★进度看 `get_pipeline_status` 的 generate_scene_images 步。' + CONFIRM_HINT, { drama_id: z.number().int().positive(), quote_id: z.string().describe('来自 quote_scene_images') }, async ({ drama_id, quote_id }) => jsonResult(await client.producePost(`/dramas/${drama_id}/scene-images/generate`, { quote_id })));
|
|
1179
|
+
'★进度看 `get_pipeline_status` 的 generate_scene_images 步。' + CONFIRM_HINT + ASSET_QC_HINT, { drama_id: z.number().int().positive(), quote_id: z.string().describe('来自 quote_scene_images') }, async ({ drama_id, quote_id }) => jsonResult(await client.producePost(`/dramas/${drama_id}/scene-images/generate`, { quote_id })));
|
|
1100
1180
|
// ========== A · 客户上传自己的图片 ==========
|
|
1101
1181
|
server.tool('upload_image', '把本地图片上传到我方 COS,返回 image_url(字节直传 COS、不过业务服务器)。' +
|
|
1102
1182
|
'用于给视频编辑当参考图,或先拿 URL 再登记。免费。', {
|
|
@@ -1120,7 +1200,7 @@ export function registerProduceTools(server, client) {
|
|
|
1120
1200
|
'★换图只是**解除引用**,旧定妆图/设定图的文件不会被删除。响应里的 old_assets 逐项列出被解除的旧资产:' +
|
|
1121
1201
|
'unlinked=[{kind,url,in_history}](in_history=false 表示平台没留记录——典型是客户上传的旧定妆图,要留就把 url 存下来),' +
|
|
1122
1202
|
'physically_deleted 恒为空。客户说「删掉旧图」时,如实告诉他只是不再使用、文件仍在,别说成已删除。' +
|
|
1123
|
-
'传本地文件(file_path,自动上传 COS)或已托管的图片 URL(image_url),二选一。登记免费;内容检查是一次小额视觉审核,按用量后付。', {
|
|
1203
|
+
'传本地文件(file_path,自动上传 COS)或已托管的图片 URL(image_url),二选一。登记免费;内容检查是一次小额视觉审核,按用量后付。' + ASSET_QC_HINT, {
|
|
1124
1204
|
character_id: z.number().int().positive(),
|
|
1125
1205
|
file_path: z.string().optional().describe('本地定妆图路径(与 image_url 二选一;自动上传 COS)'),
|
|
1126
1206
|
image_url: z.string().optional().describe('已上传的定妆图 URL(与 file_path 二选一)'),
|
|
@@ -1175,7 +1255,7 @@ export function registerProduceTools(server, client) {
|
|
|
1175
1255
|
'(或 generate_videos 的参考视频通道)。本工具的语义是「这段视频**就是**成片本身,不再生成」。' +
|
|
1176
1256
|
'两者传的是同一个文件,结果天差地别:走本工具会让该镜从此被出视频守卫拒(含 edit_video_shot),' +
|
|
1177
1257
|
'得先 clear_shot_footage 或显式 replace_user_footage 才能继续。' +
|
|
1178
|
-
'要换回 AI 生成请先 clear_shot_footage。', {
|
|
1258
|
+
'要换回 AI 生成请先 clear_shot_footage。' + VIDEO_QC_HINT, {
|
|
1179
1259
|
storyboard_id: z.number().int().positive(),
|
|
1180
1260
|
file_path: z.string().describe('本地视频路径(mp4/mov/webm/m4v,≤300MB)'),
|
|
1181
1261
|
}, async ({ storyboard_id, file_path }) => {
|
|
@@ -1220,7 +1300,7 @@ export function registerProduceTools(server, client) {
|
|
|
1220
1300
|
'登记免费;内容检查是一次小额视觉审核,按用量后付。' +
|
|
1221
1301
|
'★换了场景图后旧的打光参考图作废,平台**不会**自动补(那要另扣一张图的点数):' +
|
|
1222
1302
|
'回执 lighting_ref.needed=true 时把 estimated_points 告诉客户,客户同意再调 quote_scene_lighting_ref → generate_scene_lighting_ref。' +
|
|
1223
|
-
'上传时该场景还没有分镜(needed=false 且分镜还没建)的,等分镜建好后再调 quote_scene_lighting_ref 看要不要补——平台不会自动补上传图的打光参考图。', {
|
|
1303
|
+
'上传时该场景还没有分镜(needed=false 且分镜还没建)的,等分镜建好后再调 quote_scene_lighting_ref 看要不要补——平台不会自动补上传图的打光参考图。' + ASSET_QC_HINT, {
|
|
1224
1304
|
scene_id: z.number().int().positive(),
|
|
1225
1305
|
file_path: z.string().describe('本地场景图路径'),
|
|
1226
1306
|
allow_issues: z.boolean().optional().describe('内容检查报了问题、客户看过后仍明确坚持用这张图时才传 true(默认拦截)'),
|
|
@@ -1228,7 +1308,7 @@ export function registerProduceTools(server, client) {
|
|
|
1228
1308
|
const image_url = await client.uploadLocalFile(file_path, 'image');
|
|
1229
1309
|
return jsonResult(await client.producePost(`/scenes/${scene_id}/image`, { image_url, ...(allow_issues ? { allow_issues: true } : {}) }));
|
|
1230
1310
|
});
|
|
1231
|
-
server.tool('upload_prop_sheet', '用客户自有图片作为某道具的设定图。自动上传+登记。免费。', { prop_id: z.number().int().positive(), file_path: z.string().describe('本地道具图路径') }, async ({ prop_id, file_path }) => {
|
|
1311
|
+
server.tool('upload_prop_sheet', '用客户自有图片作为某道具的设定图。自动上传+登记。免费。' + ASSET_QC_HINT, { prop_id: z.number().int().positive(), file_path: z.string().describe('本地道具图路径') }, async ({ prop_id, file_path }) => {
|
|
1232
1312
|
const image_url = await client.uploadLocalFile(file_path, 'image');
|
|
1233
1313
|
return jsonResult(await client.producePost(`/props/${prop_id}/sheet`, { image_url }));
|
|
1234
1314
|
});
|
|
@@ -1250,7 +1330,7 @@ export function registerProduceTools(server, client) {
|
|
|
1250
1330
|
'再重新 quote 并编辑;**别连发**多条编辑想让它们叠加,每一条都只基于当时的源。' +
|
|
1251
1331
|
'若在途那条正是你刚提交的同一次编辑(例如调用超时后重发),直接等它的结果,不必再发。' +
|
|
1252
1332
|
'同一次编辑原样重发(指令、参考素材、区间、引擎都一样)不会 409,会挂到在途那条上、不重复扣费。' +
|
|
1253
|
-
CONFIRM_HINT, {
|
|
1333
|
+
CONFIRM_HINT + VIDEO_QC_HINT, {
|
|
1254
1334
|
storyboard_id: z.number().int().positive(),
|
|
1255
1335
|
quote_id: z.string().describe('来自 quote_edit_video_shot'),
|
|
1256
1336
|
instruction: z.string().describe('编辑指令(如"把背景换成夜晚")'),
|
|
@@ -1341,13 +1421,16 @@ export function registerProduceTools(server, client) {
|
|
|
1341
1421
|
'先等在途那条完成、看过结果,再重新 quote 并重生——否则拿到的视频是旧内容(旧提示词或旧首尾帧)出的,会误以为改动没效果。' +
|
|
1342
1422
|
'\n★成功但返回里带 **`reused_in_flight: true`** = 同样的内容已经在生成(重发/双击/批量出视频已派发),' +
|
|
1343
1423
|
'这次没有新建、没有新扣费,返回的就是在途那一条,看它的进度即可,不必再发。' +
|
|
1344
|
-
CONFIRM_HINT, {
|
|
1424
|
+
REVIEW_GATE_HINT('review_frames', '镜头图片') + CONFIRM_HINT + VIDEO_QC_HINT, {
|
|
1345
1425
|
storyboard_id: z.number().int().positive(),
|
|
1346
1426
|
quote_id: z.string().describe('来自 quote_regenerate_shot_video'),
|
|
1427
|
+
...REVIEW_ARGS,
|
|
1347
1428
|
prompt: z.string().optional().describe('可选:覆盖该镜视频 prompt'),
|
|
1348
1429
|
replace_user_footage: z.boolean().optional().describe('本镜是实拍素材镜时,显式允许用 AI 重生的视频覆盖它(不传则被守卫拒 409)。'),
|
|
1349
|
-
}, async ({ storyboard_id, quote_id, prompt, replace_user_footage }) => jsonResult(await client.producePost(`/storyboards/${storyboard_id}/regen/generate`, {
|
|
1430
|
+
}, async ({ storyboard_id, quote_id, review_token, acknowledge_review, prompt, replace_user_footage }) => jsonResult(await client.producePost(`/storyboards/${storyboard_id}/regen/generate`, {
|
|
1350
1431
|
quote_id,
|
|
1432
|
+
review_token,
|
|
1433
|
+
acknowledge_review,
|
|
1351
1434
|
...(prompt ? { prompt } : {}),
|
|
1352
1435
|
...(replace_user_footage !== undefined ? { replace_user_footage } : {}),
|
|
1353
1436
|
})));
|
|
@@ -1560,7 +1643,7 @@ export function registerProduceTools(server, client) {
|
|
|
1560
1643
|
'客户说「要慢一点、小调、用古筝」这类**明确参数**用 director;说「整体更压抑」这类**方向**用 prompt;两者可同时用。\n' +
|
|
1561
1644
|
'★candidates 可选=候选整版对比:每幕出 2~3 首**完整**候选让客户挑(挑中的直接用,听到什么成片里就是什么)。' +
|
|
1562
1645
|
'每首单独按首计费 ⇒ 费用是单首的 N 倍,**必须先 quote_bgm_candidates 报价、把点数告诉客户、客户同意后**带 quote_id 调本工具。' +
|
|
1563
|
-
'不传或传 1=原行为(每幕一首,无需报价)。生成后 get_bgm_status 的 candidate_groups 给每首的试听地址,select_bgm_candidate 挑选(免费),不挑默认第 1 首。', {
|
|
1646
|
+
'不传或传 1=原行为(每幕一首,无需报价)。生成后 get_bgm_status 的 candidate_groups 给每首的试听地址,select_bgm_candidate 挑选(免费),不挑默认第 1 首。' + VIDEO_QC_HINT, {
|
|
1564
1647
|
episode_id: z.number().int().positive(),
|
|
1565
1648
|
prompt: z.string().optional().describe('配乐方向,整集一条,≤800 字。只写音乐维度,别写剧情。传空字符串=清除已存的提示词。'
|
|
1566
1649
|
+ '不传=沿用该集上次填的(没填过就是全自动)。'),
|
|
@@ -1719,7 +1802,7 @@ export function registerProduceTools(server, client) {
|
|
|
1719
1802
|
'voiceStyle 不是提取时自动填的——不分配,generate_tts 就没音色。文本步(LLM)后付。' +
|
|
1720
1803
|
'已用 set_character_voice 绑了客户自己授权音色的角色会被锁定、不会被改成别的音色。' +
|
|
1721
1804
|
'后台异步,轮询 get_pipeline_status:assign_voices=done 即完成,再 generate_tts。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/assign-voices`)));
|
|
1722
|
-
server.tool('generate_tts', '给整集所有对白批量配音(TTS)。成片前的音频步——不配音成片会缺对白。前置:先 assign_voices 给角色分配音色。后台异步,按用量后付不欠费。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/tts`)));
|
|
1805
|
+
server.tool('generate_tts', '给整集所有对白批量配音(TTS)。成片前的音频步——不配音成片会缺对白。前置:先 assign_voices 给角色分配音色。后台异步,按用量后付不欠费。' + VIDEO_QC_HINT, { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/tts`)));
|
|
1723
1806
|
// ========== P1 · MV 子流程(project_type=mv 专用)==========
|
|
1724
1807
|
server.tool('set_mv_lyrics', 'MV 项目:设置歌词(整曲音频另在建剧时传)。免费。之后 generate_mv_story → generate_mv_script。', { drama_id: z.number().int().positive(), lyrics: z.string().min(1).describe('歌词全文') }, async ({ drama_id, lyrics }) => jsonResult(await client.producePost(`/dramas/${drama_id}/mv/lyrics`, { lyrics })));
|
|
1725
1808
|
server.tool('generate_mv_story', 'MV 项目:据歌词 AI 编一个 MV 故事线。后台/同步,按用量后付。', { drama_id: z.number().int().positive() }, async ({ drama_id }) => jsonResult(await client.producePost(`/dramas/${drama_id}/mv/story`)));
|
|
@@ -1838,7 +1921,7 @@ export function registerProduceTools(server, client) {
|
|
|
1838
1921
|
'这个是单场景强制重出。客户自己上传过的场景图也会被覆盖,先确认是不是要保留。' +
|
|
1839
1922
|
'★episode_id 通常不必传(平台按场景归属、再按引用它的分镜自动推导)。' +
|
|
1840
1923
|
'只有回执报「本场景被多集分镜共用」时才需要点名:那说明同一地点跨集复用,' +
|
|
1841
|
-
'平台不猜该按哪一集的时代/世界观出图——猜错就是画面年代静默错掉。', {
|
|
1924
|
+
'平台不猜该按哪一集的时代/世界观出图——猜错就是画面年代静默错掉。' + ASSET_QC_HINT, {
|
|
1842
1925
|
scene_id: z.number().int().positive(),
|
|
1843
1926
|
quote_id: z.string().optional().describe('来自 quote_regenerate_scene_image(建议必带)'),
|
|
1844
1927
|
episode_id: z.number().int().positive().optional()
|
|
@@ -1846,8 +1929,8 @@ export function registerProduceTools(server, client) {
|
|
|
1846
1929
|
}, async ({ scene_id, quote_id, episode_id }) => jsonResult(await client.producePost(`/scenes/${scene_id}/image/regenerate`, { ...(quote_id ? { quote_id } : {}), ...(episode_id ? { episode_id } : {}) })));
|
|
1847
1930
|
server.tool('delete_scene', '删除一个场景。免费。', { scene_id: z.number().int().positive() }, async ({ scene_id }) => jsonResult(await client.produceDelete(`/scenes/${scene_id}`)));
|
|
1848
1931
|
server.tool('generate_character_sheet', '给**单个**角色出三视图设定图(镜头一致性根锚,所有镜头帧都会引用;比定妆图更完整)。整集批量用 generate_character_sheets。' +
|
|
1849
|
-
'前置:该角色已有定妆图。图片步,按用量后付不欠费。', { character_id: z.number().int().positive(), episode_id: z.number().int().positive().optional() }, async ({ character_id, episode_id }) => jsonResult(await client.producePost(`/characters/${character_id}/sheet`, episode_id ? { episode_id } : {})));
|
|
1850
|
-
server.tool('generate_prop_sheet', '给道具 AI 出设定图。图片步,按用量后付不欠费。', { prop_id: z.number().int().positive() }, async ({ prop_id }) => jsonResult(await client.producePost(`/props/${prop_id}/generate-sheet`)));
|
|
1932
|
+
'前置:该角色已有定妆图。图片步,按用量后付不欠费。' + ASSET_QC_HINT, { character_id: z.number().int().positive(), episode_id: z.number().int().positive().optional() }, async ({ character_id, episode_id }) => jsonResult(await client.producePost(`/characters/${character_id}/sheet`, episode_id ? { episode_id } : {})));
|
|
1933
|
+
server.tool('generate_prop_sheet', '给道具 AI 出设定图。图片步,按用量后付不欠费。' + ASSET_QC_HINT, { prop_id: z.number().int().positive() }, async ({ prop_id }) => jsonResult(await client.producePost(`/props/${prop_id}/generate-sheet`)));
|
|
1851
1934
|
// ========== 剧目级资产:道具库 CRUD + 色彩脚本/动作模板读取 ==========
|
|
1852
1935
|
server.tool('get_props', '列剧目道具库(每个道具名称/类型/描述/设定图)。★道具库≠广告商品库(add_product/list_products 是带货商品);道具库是剧目道具。免费。' +
|
|
1853
1936
|
'★每个道具带 era_audit(道具图的时代/形制审核结论,图换过则为 null):form_consistent=false 表示道具图与它的 era_lock 不符,' +
|
|
@@ -1991,7 +2074,7 @@ export function registerProduceTools(server, client) {
|
|
|
1991
2074
|
const LIPSYNC_UNAVAILABLE_HINT = '\n★收到 **503 code=lipsync.unavailable** = 平台当前没有可用的口型服务,本次未提交、未扣费,重试无用;' +
|
|
1992
2075
|
'按返回的 alternatives 改走别的路(重生该镜视频 / 定向编辑 / 只换音频——最后一个不改嘴型)。';
|
|
1993
2076
|
server.tool('lipsync_shot', '给单镜做口型同步(对白与人物嘴型对齐)。按用量后付。' + LIPSYNC_UNAVAILABLE_HINT, { storyboard_id: z.number().int().positive() }, async ({ storyboard_id }) => jsonResult(await client.producePost(`/storyboards/${storyboard_id}/lipsync`)));
|
|
1994
|
-
server.tool('lipsync_episode', '给整集批量口型同步。后台异步,按用量后付。用 get_lipsync_status 查进度。' + LIPSYNC_UNAVAILABLE_HINT, { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/lipsync-batch`)));
|
|
2077
|
+
server.tool('lipsync_episode', '给整集批量口型同步。后台异步,按用量后付。用 get_lipsync_status 查进度。' + LIPSYNC_UNAVAILABLE_HINT + VIDEO_QC_HINT, { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/lipsync-batch`)));
|
|
1995
2078
|
server.tool('get_lipsync_status', '查整集口型同步进度。免费。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.produceGet(`/episodes/${episode_id}/lipsync-status`)));
|
|
1996
2079
|
server.tool('generate_episode_poster', '给某一集生成海报图。图片步,按用量后付。', { episode_id: z.number().int().positive() }, async ({ episode_id }) => jsonResult(await client.producePost(`/episodes/${episode_id}/poster`)));
|
|
1997
2080
|
server.tool('generate_drama_poster', '给整部剧生成海报图(KV)。图片步,按用量后付。', { drama_id: z.number().int().positive() }, async ({ drama_id }) => jsonResult(await client.producePost(`/dramas/${drama_id}/poster`)));
|