diff --git a/data/skills/production_agent_decision.md b/data/skills/production_agent_decision.md index 7e6f519..6c23eaa 100644 --- a/data/skills/production_agent_decision.md +++ b/data/skills/production_agent_decision.md @@ -1,3 +1,223 @@ -你是一个测试流程助手,你专门生成假数据: -你必须使用XML格式写入工作区分镜面板: -现在请根据哟用户指令直接输出xml假数据 +# 决策层 Agent 技能指令 + +你是视频制作项目的**决策层 Agent**,**只负责决策和任务派发**:理解用户意图、拆解任务、调度执行层与监督层、把控质量。 +你是唯一与用户直接对接的 Agent,执行层和监督层只接收你派发的指令。 + +**核心原则:** +- **决策层不执行具体任务**,不读取工作区数据(不调用 get_flowData),不直接操作任何资产或分镜数据。所有具体工作由执行层完成。 +- **决策层不做执行层的判断**,执行层返回什么结论就基于该结论决策下一步。 + +## 核心职责 + +1. **需求分析**:解析用户请求,判断属于流水线哪个阶段 +2. **任务拆解**:将复杂请求分解为可执行的子任务 +3. **调度执行**:通过 `run_sub_agent_execution` 派发任务到执行层 +4. **质量管控**:通过 `run_sub_agent_supervision` 调用监督层审核产出物 +5. **记忆检索**:通过 `deepRetrieve` 获取历史上下文和项目进度记忆 + +--- + +## 制作流水线 + +五个阶段**必须按顺序执行**: + +``` +阶段1: 衍生资产分析 → 阶段2: 衍生资产生成(可选) → 阶段3: 导演规划 → 阶段4: 构建分镜表 → 阶段5: 生成分镜 +``` + +### 全局约束 + +- **资产约束**:阶段3、4、5 只能使用资产库中已存在的资产(含阶段1已写入的衍生资产) +- **异步操作**:阶段2的图片生成、阶段5的分镜图片生成均为异步操作,派发后告知用户等待即可 +- **审核规则**:仅阶段3(导演规划)和阶段4(构建分镜表)需要审核,执行完毕后自动派发监督层 + +--- + +### 阶段1:衍生资产分析 + +| 项 | 说明 | +|----|------| +| 派发 | 执行层分析剧本,识别并写入衍生资产信息 | +| 输出 | 衍生资产分析报告 + 衍生资产写入结果(或"无需衍生"结论) | +| 前置条件 | 剧本和资产已存在于工作区 | +| 审核 | 不需要 | + +**决策层行为:** + +| 执行层返回 | 决策层操作 | +|-----------|-----------| +| "不需要衍生资产" | 向用户简要告知,直接进入阶段3 | +| 衍生资产清单(已写入) | 展示给用户,询问是否确认生成图片 | + +**用户确认分支(仅有新增资产时):** + +| 用户反馈 | 操作 | +|----------|------| +| 确认全部生成 | 进入阶段2 | +| 部分生成 | 将用户选择的子集传递给阶段2 | +| 跳过 | 直接进入阶段3,告知后续仅使用现有资产 | +| 调整清单 | 重新派发分析或将调整后清单传递给阶段2 | + +> 约束:阶段1必须完成衍生资产信息写入;分析结果需展示给用户确认是否进入图片生成,且不可自动进入阶段2。 + +--- + +### 阶段2:衍生资产生成(可选) + +| 项 | 说明 | +|----|------| +| 派发 | 执行层对已写入的衍生资产生成图片 | +| 输入 | 用户确认需要生成图片的衍生资产清单(来自阶段1) | +| 输出 | 图片生成启动 | +| 前置条件 | 阶段1完成且用户确认生成 | +| 审核 | 不需要 | + +**决策层行为:** 将用户确认的资产清单(或子集)派发给执行层。返回确认后,告知用户图片生成中,直接进入阶段3。 + +--- + +### 阶段3:导演规划 + +| 项 | 说明 | +|----|------| +| 派发 | 执行层制定导演拍摄计划 | +| 输出 | 导演拍摄计划(执行层通过 set_plane 同步到前端) | +| 质量门 | 计划覆盖全部剧情、节奏合理、与资产匹配 | +| 前置条件 | 阶段1完成(含跳过阶段2的情况) | +| 审核 | **需要** → 执行完毕后自动派发监督层 | + +**阶段特有约束:** 规划中引用的角色、道具、场景必须在资产列表中存在。 + +--- + +### 阶段4:构建分镜表 + +| 项 | 说明 | +|----|------| +| 派发 | 执行层将剧本拆分为分镜,生成结构化分镜表 | +| 输出 | 结构化分镜表(执行层通过 set_flowData 保存) | +| 质量门 | 分镜拆分粒度合理、字段完整、关联资产正确 | +| 前置条件 | 阶段3(导演规划)完成 | +| 审核 | **需要** → 执行完毕后自动派发监督层 | + +**阶段特有约束:** `associateAssetsIds` 中的索引必须指向资产库中实际存在的资产。 + +--- + +### 阶段5:生成分镜 + +| 项 | 说明 | +|----|------| +| 派发 | 执行层调用图片生成接口生成分镜图片 | +| 输出 | 生成的分镜图片 | +| 前置条件 | 阶段4完成且用户确认 | +| 审核 | 不需要 | + +**决策层行为:** 执行层返回确认后,告知用户图片生成已启动,流程结束。 + +--- + +## 调度与派发规范 + +### 派发指令要求 + +**派发给执行层和监督层的任务指令正文严格不超过100字。** 执行层已具备完整技能指令,只需告知任务类型和关键参数。 + +### 执行层派发 + +使用 `run_sub_agent_execution` 调用执行层: + +``` +run_sub_agent_execution( + prompts: "<按模板构建的具体指令>" +) +``` + +### 审核派发与结果处理 + +阶段3或阶段4执行完毕后: +1. 将执行层返回的确认消息展示给用户 +2. **紧接着自动调用监督层审核**(无需等待用户指示) + +``` +run_sub_agent_supervision( + prompts: "请审核【{阶段名}】的产出物。审核维度:{维度列表}" +) +``` + +监督层审核完毕后将报告展示给用户。决策层**等待用户回复**,根据反馈操作: + +| 用户反馈 | 操作 | +|----------|------| +| 通过 / 下一阶段 | 派发下一阶段任务 | +| 需要修复 | 根据用户指示构建修复指令,派发执行层 | +| 重做 | 重新派发当前阶段任务 | + +### 调度决策树 + +| 用户请求 | 处理规则 | +|----------|----------| +| 明确指定阶段 | 检查前置条件 → 派发该阶段 | +| "从头开始" / "完整制作" | 从阶段1顺序执行 | +| "继续" / "下一步" | `deepRetrieve` 获取进度 → 从当前阶段继续 | +| "修改/优化 X" | 定位对应阶段 → 派发修改任务 | +| 模糊请求 | `deepRetrieve` 获取进度 → 从当前阶段继续 | + +--- + +## 指令模板 + +### 执行派发格式 + +``` +你是执行层Agent,请执行【{任务类型}】任务。 +目标:{一句话目标} +上下文:{必要数据摘要} +要求: +1. {具体步骤1} +2. {具体步骤2} +约束:{特殊约束条件} +``` + +### 修复派发格式 + +``` +你是执行层Agent,请修复【{任务类型}】的以下问题。 +用户确认的修复项: +1. {问题} → 修改为:{方案} +保持其余内容不变。 +``` + +> 修复指令中只包含用户明确确认要修的项,不包含用户未回应或跳过的问题。 + +--- + +## 记忆检索策略 + +在以下场景使用 `deepRetrieve`: +1. **新会话开始**:检索项目当前进度、已完成阶段 +2. **用户提到之前的内容**:检索相关历史产出摘要 +3. **质量问题追溯**:检索之前的审核结果和修改记录 +4. **判断前置条件**:检索各阶段是否已完成 + +> `deepRetrieve` 用于检索历史记忆和进度状态,不用于读取工作区当前数据。 + +--- + +## 与用户交互规范 + +1. **进度汇报**:每完成一个阶段,汇报结果摘要和下一步计划 +2. **审核结果展示**:阶段3、4由监督层审核后展示报告,等待用户反馈 +3. **等待用户决策**:审核发现问题时,**必须等待用户明确指示**后再执行修复,不可自行决定 +4. **不暴露内部机制**:不向用户提及 Agent 名称、工具名称等实现细节 + +--- + +## 错误处理 + +| 场景 | 处理 | +|------|------| +| 执行层返回错误 | 分析原因,调整指令重新派发(最多重试2次) | +| 监督层发现质量问题 | 等待用户确认修复方案 → 派发修复指令 | +| 前置条件不满足 | 提示用户需先完成哪个阶段 | +| 记忆检索无结果 | 请求用户提供必要上下文 | diff --git a/src/routes/production/editImage/generateFlowImage.ts b/src/routes/production/editImage/generateFlowImage.ts index 7f57afb..1c16c4f 100644 --- a/src/routes/production/editImage/generateFlowImage.ts +++ b/src/routes/production/editImage/generateFlowImage.ts @@ -21,21 +21,24 @@ export default router.post( ratio: z.string(), prompt: z.string(), projectId: z.number(), - type: z.enum(["role", "scene", "storyboard", "clip", "tool"]), }), async (req, res) => { const { model, references = [], quality, ratio, prompt, projectId, type } = req.body; - const imageClass = await u.Ai.Image(model).run({ - prompt: prompt, - imageBase64: references && references.length ? await Promise.all(references.map((url: string) => urlToBase64(url))) : [], - size: quality, - aspectRatio: ratio, - taskClass: "分镜生成", - describe: "生成分镜图片", - relatedObjects: JSON.stringify(req.body), - projectId: projectId, - }); + const imageClass = await u.Ai.Image(model).run( + { + prompt: prompt, + imageBase64: references && references.length ? await Promise.all(references.map((url: string) => urlToBase64(url))) : [], + size: quality, + aspectRatio: ratio, + }, + { + taskClass: "工作流图片生成", + describe: "工作流图片生成", + relatedObjects: JSON.stringify(req.body), + projectId: projectId, + }, + ); const savePath = `${projectId}/${type}/${u.uuid()}.jpg`; await imageClass.save(savePath); diff --git a/src/routes/production/editImage/getImageFlow.ts b/src/routes/production/editImage/getImageFlow.ts index d97b3ec..65dee77 100644 --- a/src/routes/production/editImage/getImageFlow.ts +++ b/src/routes/production/editImage/getImageFlow.ts @@ -9,20 +9,10 @@ export default router.post( "/", validateFields({ id: z.number(), - type: z.enum(["role", "scene", "storyboard", "clip", "tool"]), }), async (req, res) => { const { id, type } = req.body; - const imageFlowData = await u - .db("o_imageFlow") - .modify((qb) => { - if (type === "storyboard") { - qb.where("storyboardId", id); - } else { - qb.where("assetsId", id); - } - }) - .first(); + const imageFlowData = await u.db("o_imageFlow").where("id", id).first(); if (imageFlowData?.flowData) { const parseFlow = JSON.parse(imageFlowData.flowData); await Promise.all( diff --git a/src/routes/production/editImage/saveImageFlow.ts b/src/routes/production/editImage/saveImageFlow.ts index 544afdd..ce3e2a6 100644 --- a/src/routes/production/editImage/saveImageFlow.ts +++ b/src/routes/production/editImage/saveImageFlow.ts @@ -10,65 +10,12 @@ export default router.post( validateFields({ edges: z.any(), nodes: z.any(), - imageUrl: z.string(), - id: z.number().nullable().optional(), - type: z.enum(["role", "scene", "storyboard", "clip", "tool"]), - episodesId: z.number(), - projectId: z.number(), }), async (req, res) => { - const { edges, nodes, imageUrl, id, type, episodesId, projectId } = req.body; - let imagePath = ""; - try { - imagePath = new URL(imageUrl).pathname; - } catch (e) {} - nodes.forEach((node: any) => { - if (node.type == "upload") { - try { - node.data.image = new URL(node.data.image).pathname; - } catch (e) { - node.data.image = ""; - } - } - if (node.type == "generated") { - try { - node.data.generatedImage = new URL(node.data.generatedImage).pathname; - } catch (e) { - node.data.generatedImage = ""; - } - } - }); - let insertFlowId; - if (imagePath) { - if (id) { - if (type == "storyboard") { - await u.db("o_storyboard").where("id", id).update({ - filePath: imagePath, - }); - } else { - const [imageId] = await u.db("o_image").insert({ - filePath: imagePath, - assetsId: id, - state: "已完成", - }); - await u.db("o_assets").where("id", id).update({ imageId }); - } + const { edges, nodes } = req.body; - insertFlowId = id; - } else { - const [storyboardId] = await u.db("o_storyboard").insert({ - filePath: imagePath, - scriptId: episodesId, - createTime: Date.now(), - projectId, - }); - insertFlowId = storyboardId; - } - } else insertFlowId = id; - - await u.db("o_imageFlow").insert({ + const [insertFlowId] = await u.db("o_imageFlow").insert({ flowData: JSON.stringify({ edges, nodes }), - ...(type == "assets" ? { assetsId: insertFlowId } : { storyboardId: insertFlowId }), }); return res.status(200).send(success({ id: insertFlowId })); }, diff --git a/src/routes/production/editImage/updateImageFlow.ts b/src/routes/production/editImage/updateImageFlow.ts index 2953c65..f22ce67 100644 --- a/src/routes/production/editImage/updateImageFlow.ts +++ b/src/routes/production/editImage/updateImageFlow.ts @@ -12,12 +12,11 @@ export default router.post( nodes: z.any(), id: z.number(), imageUrl: z.string(), - type: z.enum(["role", "scene", "storyboard", "clip", "tool"]), flowId: z.number(), episodesId: z.number(), }), async (req, res) => { - const { edges, nodes, id, imageUrl, flowId, type, episodesId } = req.body; + const { edges, nodes, flowId } = req.body; nodes.forEach((node: any) => { if (node.type == "upload") { node.data.image = node.data.image ? new URL(node.data.image).pathname : ""; @@ -26,24 +25,24 @@ export default router.post( node.data.generatedImage = node.data.generatedImage ? new URL(node.data.generatedImage).pathname : ""; } }); - let imagePath = ""; - try { - imagePath = new URL(imageUrl).pathname; - } catch (e) {} - if (imagePath) { - if (type == "storyboard") { - await u.db("o_storyboard").where("id", id).update({ - filePath: imagePath, - }); - } else { - const [imageId] = await u.db("o_image").insert({ - filePath: imagePath, - assetsId: id, - state: "已完成", - }); - await u.db("o_assets").where("id", id).update({ imageId }); - } - } + // let imagePath = ""; + // try { + // imagePath = new URL(imageUrl).pathname; + // } catch (e) {} + // if (imagePath) { + // if (type == "storyboard") { + // await u.db("o_storyboard").where("id", id).update({ + // filePath: imagePath, + // }); + // } else { + // const [imageId] = await u.db("o_image").insert({ + // filePath: imagePath, + // assetsId: id, + // state: "已完成", + // }); + // await u.db("o_assets").where("id", id).update({ imageId }); + // } + // } await u .db("o_imageFlow") diff --git a/src/routes/production/saveFlowData.ts b/src/routes/production/saveFlowData.ts index 64209fd..deea792 100644 --- a/src/routes/production/saveFlowData.ts +++ b/src/routes/production/saveFlowData.ts @@ -36,12 +36,14 @@ export default router.post( await u.db("o_agentWorkData").insert({ projectId, episodesId, + key: "productionAgent", data: JSON.stringify(data), }); } else { await u .db("o_agentWorkData") .where("projectId", String(projectId)) + .where("key", "productionAgent") .andWhere("episodesId", String(episodesId)) .update({ data: JSON.stringify(data), diff --git a/src/types/database.d.ts b/src/types/database.d.ts index d72b0fc..7571551 100644 --- a/src/types/database.d.ts +++ b/src/types/database.d.ts @@ -1,4 +1,4 @@ -// @db-hash 4789feeeda48b86ecadc17318a89460b +// @db-hash f82a518c947d01d53088924d17bbc925 //该文件由脚本自动生成,请勿手动修改 export interface _o_assets_old_20260331 { @@ -15,28 +15,67 @@ export interface _o_assets_old_20260331 { 'startTime'?: number | null; 'type'?: string | null; } -export interface _o_image_old_20260331 { +export interface _o_imageFlow_old_20260331 { 'assetsId'?: number | null; + 'flowData': string; + 'id'?: number; + 'storyboardId'?: number | null; +} +export interface _o_storyboard_old_20260331 { + 'camera'?: string | null; + 'createTime'?: number | null; + 'description'?: string | null; + 'duration'?: string | null; + 'filePath'?: string | null; + 'frameMode'?: string | null; + 'id'?: number; + 'index'?: number | null; + 'lines'?: string | null; + 'mode'?: string | null; + 'model'?: string | null; + 'prompt'?: string | null; + 'reason'?: string | null; + 'resolution'?: string | null; + 'scriptId'?: number | null; + 'sound'?: string | null; + 'state'?: string | null; + 'title'?: string | null; + 'videoPrompt'?: string | null; +} +export interface _o_storyboard_old_20260331_1 { + 'camera'?: string | null; + 'createTime'?: number | null; + 'description'?: string | null; + 'duration'?: string | null; + 'filePath'?: string | null; + 'frameMode'?: string | null; + 'id'?: number; + 'index'?: number | null; + 'lines'?: string | null; + 'mode'?: string | null; + 'model'?: string | null; + 'prompt'?: string | null; + 'reason'?: string | null; + 'resolution'?: string | null; + 'scriptId'?: number | null; + 'sound'?: string | null; + 'state'?: string | null; + 'title'?: string | null; + 'track'?: string | null; + 'videoPrompt'?: string | null; +} +export interface _o_storyboard_old_20260331_2 { + 'createTime'?: number | null; + 'duration'?: string | null; 'filePath'?: string | null; 'id'?: number; - 'model'?: string | null; - 'resolution'?: string | null; + 'index'?: number | null; + 'projectId'?: number | null; + 'prompt'?: string | null; + 'reason'?: string | null; + 'scriptId'?: number | null; 'state'?: string | null; - 'type'?: string | null; -} -export interface _o_project_old_20260331 { - 'artStyle'?: string | null; - 'createTime'?: number | null; - 'id'?: number | null; - 'imageModel'?: string | null; - 'imageQuality'?: string | null; - 'intro'?: string | null; - 'name'?: string | null; - 'projectType'?: string | null; - 'type'?: string | null; - 'userId'?: number | null; - 'videoModel'?: string | null; - 'videoRatio'?: string | null; + 'trackId'?: number | null; } export interface memories { 'content': string; @@ -79,12 +118,12 @@ export interface o_artStyle { export interface o_assets { 'assetsId'?: number | null; 'describe'?: string | null; + 'flowId'?: number | null; 'id'?: number; 'imageId'?: number | null; 'name'?: string | null; 'projectId'?: number | null; 'prompt'?: string | null; - 'promptErrorReason'?: string | null; 'promptState'?: string | null; 'remark'?: string | null; 'scriptId'?: number | null; @@ -108,19 +147,17 @@ export interface o_eventChapter { } export interface o_image { 'assetsId'?: number | null; - 'errorReason'?: string | null; 'filePath'?: string | null; 'id'?: number; 'model'?: string | null; + 'reason'?: string | null; 'resolution'?: string | null; 'state'?: string | null; 'type'?: string | null; } export interface o_imageFlow { - 'assetsId'?: number | null; 'flowData': string; 'id'?: number; - 'storyboardId'?: number | null; } export interface o_novel { 'chapter'?: string | null; @@ -152,7 +189,6 @@ export interface o_project { 'imageModel'?: string | null; 'imageQuality'?: string | null; 'intro'?: string | null; - 'mode'?: string | null; 'name'?: string | null; 'projectType'?: string | null; 'type'?: string | null; @@ -200,25 +236,18 @@ export interface o_skillList { 'updateTime': number; } export interface o_storyboard { - 'camera'?: string | null; 'createTime'?: number | null; - 'description'?: string | null; 'duration'?: string | null; 'filePath'?: string | null; - 'frameMode'?: string | null; + 'flowId'?: number | null; 'id'?: number; 'index'?: number | null; - 'lines'?: string | null; - 'mode'?: string | null; - 'model'?: string | null; + 'projectId'?: number | null; 'prompt'?: string | null; 'reason'?: string | null; - 'resolution'?: string | null; 'scriptId'?: number | null; - 'sound'?: string | null; 'state'?: string | null; - 'title'?: string | null; - 'videoPrompt'?: string | null; + 'trackId'?: number | null; } export interface o_tasks { 'describe'?: string | null; @@ -255,8 +284,22 @@ export interface o_video { 'projectId'?: number | null; 'scriptId'?: number | null; 'state'?: string | null; + 'storyboardId'?: number | null; 'time'?: number | null; - 'videoTrackId'?: number | null; +} +export interface o_videoConfig { + 'audio'?: number | null; + 'createTime'?: number | null; + 'data'?: string | null; + 'duration'?: number | null; + 'id'?: number; + 'mode'?: string | null; + 'model'?: string | null; + 'prompt'?: string | null; + 'resolution'?: string | null; + 'storyboardId'?: number | null; + 'updateTime'?: number | null; + 'videoId'?: number | null; } export interface o_videoTrack { 'id'?: number; @@ -267,8 +310,10 @@ export interface o_videoTrack { export interface DB { "_o_assets_old_20260331": _o_assets_old_20260331; - "_o_image_old_20260331": _o_image_old_20260331; - "_o_project_old_20260331": _o_project_old_20260331; + "_o_imageFlow_old_20260331": _o_imageFlow_old_20260331; + "_o_storyboard_old_20260331": _o_storyboard_old_20260331; + "_o_storyboard_old_20260331_1": _o_storyboard_old_20260331_1; + "_o_storyboard_old_20260331_2": _o_storyboard_old_20260331_2; "memories": memories; "o_agentDeploy": o_agentDeploy; "o_agentWorkData": o_agentWorkData; @@ -294,5 +339,6 @@ export interface DB { "o_user": o_user; "o_vendorConfig": o_vendorConfig; "o_video": o_video; + "o_videoConfig": o_videoConfig; "o_videoTrack": o_videoTrack; }