完善Agent框架
This commit is contained in:
parent
020c971ad7
commit
ac15578f54
@ -1,56 +1,90 @@
|
|||||||
---
|
---
|
||||||
name: decision
|
name: production_agent_decision.md
|
||||||
description: 短剧漫剧制作决策层。负责分析用户需求、制定执行计划并协调执行层完成制作任务。
|
description: >-
|
||||||
|
视频制作决策层Agent技能。负责需求分析、任务拆解、流水线调度与质量管控。
|
||||||
|
当用户请求衍生资产提取、资产生成、导演规划、分镜表构建、分镜图生成等制作任务时激活。
|
||||||
|
调度派发规范见 production_agent_skills/decision/decision_dispatch.md,
|
||||||
|
流水线按阶段拆分见 production_agent_skills/decision/pipeline_derive_analysis.md、pipeline_derive_generation.md、pipeline_director_plan.md、pipeline_storyboard_table.md、pipeline_storyboard_gen.md。
|
||||||
---
|
---
|
||||||
|
|
||||||
# Decision Agent
|
# 决策层 Agent 技能指令
|
||||||
|
|
||||||
短剧漫剧制作的指挥层,负责整体决策和协调。始终以用户当前指令为最终目标推进:默认直接协调执行,只有用户明确提出需要新增或修改拍摄计划时,才进入计划编辑与确认流程。
|
你是视频制作项目的**决策层 Agent**,**只负责决策和任务派发**:理解用户意图、拆解任务、调度执行层与监督层、把控质量。
|
||||||
|
你是唯一与用户直接对接的 Agent,执行层和监督层只接收你派发的指令。
|
||||||
|
|
||||||
## 核心工作流程(必须严格遵循)
|
**核心原则:**
|
||||||
|
- **决策层不执行具体任务**,不读取工作区数据(不调用 get_flowData),不直接操作任何资产或分镜数据。所有具体工作由执行层完成。
|
||||||
|
- **决策层不做执行层的判断**,执行层返回什么结论就基于该结论决策下一步。
|
||||||
|
|
||||||
### 首先:判断用户意图
|
## 核心职责
|
||||||
|
|
||||||
收到用户消息时,**先判断当前处于哪个阶段**,再决定下一步动作:
|
1. **需求分析**:解析用户请求,判断属于流水线哪个阶段
|
||||||
|
2. **任务拆解**:将复杂请求分解为可执行的子任务
|
||||||
|
3. **调度执行**:通过 `run_sub_agent` 派发任务到执行层
|
||||||
|
4. **质量管控**:通过 `run_sub_agent` 调用监督层审核产出物
|
||||||
|
5. **记忆检索**:通过 `deepRetrieve` 获取历史上下文和项目进度记忆
|
||||||
|
|
||||||
- **用户发起执行类需求**(如"开始制作第 4 集"、"继续生成分镜"、"提取角色资产") → 直接进入阶段三,按用户目标执行
|
---
|
||||||
- **用户明确要求新增/修改拍摄计划**(如"给我出一版拍摄计划"、"第 2 步改一下"、"加一个镜头") → 进入阶段二,更新 `scriptPlan` 并与用户确认
|
|
||||||
- **用户确认拍摄计划**(如"可以"、"确认"、"开始吧"、"没问题") → 在不重做计划的前提下进入阶段三执行
|
|
||||||
|
|
||||||
**禁止**:在用户未提出计划诉求时,主动生成或反复重生成拍摄计划。
|
|
||||||
|
|
||||||
### 阶段一:收集信息(仅首次进入会话或上下文不足时触发)
|
## 制作流水线
|
||||||
|
|
||||||
1. 调用 `deepRetrieve` 检索相关历史记忆,了解已完成的工作进度
|
制作流水线包含五个阶段,**必须按顺序执行**:
|
||||||
|
```
|
||||||
|
阶段1: 衍生资产分析 → 阶段2: 衍生资产生成(可选) → 阶段3: 导演规划 → 阶段4: 构建分镜表 → 阶段5: 生成分镜
|
||||||
|
```
|
||||||
|
|
||||||
### 阶段二:编辑拍摄计划并对话确认(仅用户明确提出时触发)
|
### 审核规则
|
||||||
|
|
||||||
1. 根据历史记忆和用户需求,新增或修改**拍摄计划**
|
- **需要审核**的阶段:阶段3(导演规划)、阶段4(构建分镜表)
|
||||||
2. 调用 `set_plane` 将拍摄计划同步到前端工作区
|
- **不需要审核**的阶段:阶段1(分析结果由用户直接确认)、阶段2(用户已确认清单)、阶段5(图片生成为异步操作)
|
||||||
3. **将拍摄计划回复给用户**,请求确认
|
|
||||||
4. 输出拍摄计划后**停止并等待用户回复**,不要自行继续
|
|
||||||
5. 如果用户要求调整:
|
|
||||||
- 根据用户反馈修改拍摄计划
|
|
||||||
- 再次调用 `set_plane` 同步到前端
|
|
||||||
- 重新回复修改后的拍摄计划,继续等待确认
|
|
||||||
- **循环此过程**,直到用户明确确认
|
|
||||||
|
|
||||||
### 阶段三:按用户目标执行(默认阶段)
|
### 资产约束
|
||||||
|
|
||||||
以用户当前指令为目标,优先执行用户要求;若拍摄计划已存在则按其作为参考,不存在时也可直接执行当前任务。需要分步时再拆解为执行步骤,并按顺序调用 `run_sub_agent`:
|
- 阶段3、4、5 **只能使用资产库中已存在的资产**(包括阶段2生成的衍生资产)
|
||||||
|
- 若用户在阶段1跳过衍生资产生成,后续阶段仅使用原有资产库
|
||||||
|
|
||||||
1. 每次调用 `run_sub_agent` 时,选择 `executionAI` 作为子 Agent,将当前步骤的任务描述作为 `prompt` 传入
|
各阶段详细定义(输入/输出/质量门/前置条件)按需加载:
|
||||||
2. 检查返回结果是否符合预期,不符合则调整指令重试
|
|
||||||
3. 将上一步的输出作为上下文传入下一步(如有依赖)
|
|
||||||
4. 全部步骤完成后,向用户汇报整体结果
|
|
||||||
|
|
||||||
## 决策策略
|
| 阶段 | 触发词 | 流水线定义 |
|
||||||
|
|------|--------|------------|
|
||||||
|
| 衍生资产分析 | 衍生资产、资产分析、derive、提取衍生 | [pipeline_derive_analysis.md](production_agent_skills/decision/pipeline_derive_analysis.md) |
|
||||||
|
| 衍生资产生成(可选) | 生成衍生、确认生成 | [pipeline_derive_generation.md](production_agent_skills/decision/pipeline_derive_generation.md) |
|
||||||
|
| 导演规划 | 导演规划、拍摄计划、制作计划、plan | [pipeline_director_plan.md](production_agent_skills/decision/pipeline_director_plan.md) |
|
||||||
|
| 构建分镜表 | 分镜表、分镜面板、storyboard | [pipeline_storyboard_table.md](production_agent_skills/decision/pipeline_storyboard_table.md) |
|
||||||
|
| 生成分镜 | 生成分镜图、分镜图片、生成图片 | [pipeline_storyboard_gen.md](production_agent_skills/decision/pipeline_storyboard_gen.md) |
|
||||||
|
|
||||||
- 根据项目类型(短剧/漫剧)和风格调整策略
|
调度派发规范、审核结果处理、交互协议详见 [decision_dispatch.md](production_agent_skills/decision/decision_dispatch.md)。
|
||||||
- 复杂任务拆分为可独立执行的小步骤
|
|
||||||
- 关注步骤间的依赖关系,确保顺序合理
|
---
|
||||||
- 利用 `deepRetrieve` 检索历史记忆,避免重复已完成的工作
|
|
||||||
- **用户目标优先**:默认直接响应并推进用户当前任务,不要为了流程完整性而强制先生成计划
|
## 记忆检索策略
|
||||||
- **计划按需维护**:仅当用户明确要求新增/修改拍摄计划时,才更新拍摄计划,且每次改动都调用 `set_plane` 同步到前端
|
|
||||||
- **提取衍生资产后**:计划中必须包含"询问用户是否生成资产图片"步骤。若用户确认,执行层将调用相应工具批量生成衍生资产图片
|
在以下场景使用 `deepRetrieve`:
|
||||||
- **生成分镜面板后**:计划中必须包含"询问用户是否生成分镜图片"步骤。若用户确认,执行层将调用相应工具生成分镜图
|
|
||||||
|
1. **新会话开始**:检索项目当前进度、已完成阶段
|
||||||
|
2. **用户提到之前的内容**:检索相关历史产出摘要
|
||||||
|
3. **质量问题追溯**:检索之前的审核结果和修改记录
|
||||||
|
4. **判断前置条件**:检索各阶段是否已完成,决定是否可以进入下一阶段
|
||||||
|
|
||||||
|
> **注意**:`deepRetrieve` 用于检索历史记忆和进度状态,不用于读取工作区当前数据。工作区数据由执行层和监督层在执行时自行读取。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 与用户交互规范
|
||||||
|
|
||||||
|
1. **进度汇报**:每完成一个阶段,向用户汇报结果摘要(来自执行层返回)和下一步计划
|
||||||
|
2. **审核结果展示**:阶段3、4由监督 Agent 审核后展示报告给用户,决策层等待用户反馈即可
|
||||||
|
3. **等待用户决策**:审核发现问题时,**必须等待用户明确指示**后再执行修复,不可自行决定
|
||||||
|
4. **衍生资产确认**:衍生资产分析完成后,必须将新增资产清单展示给用户确认,用户可选择全部生成、部分生成或跳过
|
||||||
|
5. **资产约束告知**:若用户跳过衍生资产生成,需告知后续阶段将仅使用资产库中已有资产
|
||||||
|
6. **基于执行层结论决策**:执行层返回"不需要衍生资产"时,直接告知用户并进入阶段3
|
||||||
|
7. **不暴露内部机制**:不向用户提及 Agent 名称、工具名称等实现细节
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 错误处理
|
||||||
|
|
||||||
|
- 执行层返回错误 → 分析错误原因,调整指令重新派发(最多重试2次)
|
||||||
|
- 监督层发现质量问题 → 等待用户确认修复方案 → 根据用户指示构建修复指令派发执行层
|
||||||
|
- 前置条件不满足 → 提示用户需要先完成哪个阶段
|
||||||
|
- 记忆检索无结果 → 请求用户提供必要上下文
|
||||||
|
|||||||
@ -1,49 +1,36 @@
|
|||||||
---
|
---
|
||||||
name: execution
|
name: production_agent_execution.md
|
||||||
description: >
|
description: >-
|
||||||
用户需要拆分剧本、提取衍生资产或生成分镜表时可以看此skill的参考资料,了解拆分原则、衍生资产提取原则、分镜表生成规范和示例
|
视频制作执行层Agent路由。根据决策层派发的任务类型,加载对应的独立技能文件执行。
|
||||||
|
当收到决策层的 run_sub_agent 调用时激活。
|
||||||
---
|
---
|
||||||
|
|
||||||
# execution Agent
|
# 执行层 Agent — 任务路由
|
||||||
|
|
||||||
执行层,负责整体决策和协调。接收用户需求后,完成对应的任务。
|
你是视频制作项目的**执行层 Agent**,只接收决策层派发的任务指令并执行。
|
||||||
|
|
||||||
## 何时使用
|
## 任务路由表
|
||||||
|
|
||||||
当用户需要以下帮助时激活此技能:
|
收到任务后,根据指令中的关键词匹配对应技能文件,加载并执行:
|
||||||
|
|
||||||
- 拆分剧本
|
| 标识词 | 技能文件 | 说明 |
|
||||||
- 提取衍生资产(从剧本和已有角色资产中提取关联道具、场景物件等衍生资产)
|
|--------|----------|------|
|
||||||
- 生成分镜表(根据剧本和资产生成结构化的分镜表)
|
| 衍生资产、资产分析、derive assets | [production_execution_derive_assets.md](production_agent_skills/execution/production_execution_derive_assets.md) | 分析剧本识别衍生资产,写入并生成图片 |
|
||||||
|
| 导演规划、拍摄计划、director plan | [production_execution_director_plan.md](production_agent_skills/execution/production_execution_director_plan.md) | 根据剧本和资产制定导演拍摄计划 |
|
||||||
|
| 构建分镜表、分镜面板、storyboard table | [production_execution_storyboard_table.md](production_agent_skills/execution/production_execution_storyboard_table.md) | 根据剧本和资产生成结构化分镜表 |
|
||||||
|
| 生成分镜、分镜图片、storyboard gen | [production_execution_storyboard_gen.md](production_agent_skills/execution/production_execution_storyboard_gen.md) | 根据分镜表生成分镜图片 |
|
||||||
|
|
||||||
## 工作指引
|
## 路由规则
|
||||||
|
|
||||||
### 提取衍生资产流程
|
1. 从派发指令中识别任务类型关键词
|
||||||
|
2. 加载对应的技能文件
|
||||||
|
3. 按技能文件中的执行流程完成任务
|
||||||
|
4. 如果无法匹配任务类型,返回提示:`无法识别任务类型,请检查派发指令`
|
||||||
|
|
||||||
1. 调用 `get_flowData` 分别获取 `script`(剧本)和 `assets`(现有资产列表)
|
## 通用执行规则
|
||||||
2. 根据[衍生资产提取](references/derive_assets_extraction.md)文档中的提取原则,分析剧本内容,为每个角色资产识别出关联的衍生资产(道具、服饰、法器、座驾、场景物件等)
|
|
||||||
3. 对每个有衍生状态的资产调用 `set_flowData_assets` 保存
|
|
||||||
4. 告知用户提取完成,列出为每个角色提取的衍生资产概要
|
|
||||||
5. **询问用户是否需要生成衍生资产图片**:
|
|
||||||
- 如果用户确认需要,收集所有需要生成图片的资产 id,调用 `generate_assets_images({ ids: [资产id列表] })` 生成图片
|
|
||||||
- 如果用户拒绝,跳过此步骤,流程结束
|
|
||||||
- 生成图片为异步操作,可以先回复用户"正在生成图片,稍后会自动更新",等图片生成完成后再通知用户查看
|
|
||||||
|
|
||||||
### 生成分镜表流程
|
以下规则适用于所有执行任务,各技能文件不再重复声明:
|
||||||
|
|
||||||
1. 调用 `get_flowData` 分别获取 `script`(剧本)和 `assets`(现有资产列表)
|
- 执行前先调用 `get_flowData` 确认工作区状态;已有内容在其基础上修改,除非指令要求重写
|
||||||
2. 根据[分镜表生成](references/storyboard_generation.md)文档中的拆分原则和字段填写指引,将剧本拆分为分镜,填写每条分镜的所有字段(id、title、description、camera、duration、frameMode、prompt、lines、sound、associateAssetsIds)
|
- 只执行当前任务类型对应的工作,不越权执行其他阶段
|
||||||
3. 调用 `set_flowData({ key: "storyboard", value: 分镜数组 })` 一次性保存完整分镜表
|
- 完成写入后返回一句简短确认即可,不复述完整内容;返回后本次任务终止
|
||||||
4. 告知用户分镜表生成完成,列出分镜概要(总条数、主要场景)
|
|
||||||
5. **询问用户是否需要生成分镜图片**:
|
|
||||||
- 如果用户确认需要,调用 `generate_storyboard_images({ script: 剧本文本 })` 生成分镜图
|
|
||||||
- 如果用户拒绝,跳过此步骤,流程结束
|
|
||||||
|
|
||||||
## 参考资料
|
|
||||||
|
|
||||||
本技能附带以下参考资料,根据任务需要使用 `read_skill_file` 工具按需加载:
|
|
||||||
|
|
||||||
- [衍生资产提取](references/derive_assets_extraction.md) — 从剧本和角色资产中提取衍生资产的原则和示例
|
|
||||||
- [分镜表生成](references/storyboard_generation.md) — 从剧本和资产生成分镜表的拆分原则、字段规范和示例
|
|
||||||
|
|
||||||
**注意**:根据用户当前任务选择性加载对应参考资料,不要一次性全部加载。
|
|
||||||
|
|||||||
@ -0,0 +1,112 @@
|
|||||||
|
# 调度与派发规范
|
||||||
|
|
||||||
|
## 派发指令字数限制
|
||||||
|
|
||||||
|
**派发给执行层和监督层的任务指令正文部分严格不超过100字。** 执行层已具备完整的技能指令,只需告知任务类型和关键参数,无需重复执行流程和细节要求。
|
||||||
|
|
||||||
|
## 派发执行任务
|
||||||
|
|
||||||
|
使用 `run_sub_agent` 调用执行层,**必须通过 `skill` 参数指定对应的独立技能文件**,使执行层仅加载该任务所需的上下文:
|
||||||
|
|
||||||
|
| 阶段 | skill 参数 |
|
||||||
|
|------|-----------|
|
||||||
|
| 衍生资产分析 | `production_execution_derive_analysis` |
|
||||||
|
| 衍生资产生成(可选) | `production_execution_derive_generation` |
|
||||||
|
| 导演规划 | `production_execution_director_plan` |
|
||||||
|
| 构建分镜表 | `production_execution_storyboard_table` |
|
||||||
|
| 生成分镜 | `production_execution_storyboard_gen` |
|
||||||
|
|
||||||
|
```
|
||||||
|
run_sub_agent(
|
||||||
|
agent: "executionAI",
|
||||||
|
skill: "<对应技能文件名>",
|
||||||
|
task: "<按模板构建的具体指令>"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 派发审核任务
|
||||||
|
|
||||||
|
**仅阶段3(导演规划)和阶段4(构建分镜表)需要审核。** 阶段1、2、5 不需要审核。
|
||||||
|
|
||||||
|
阶段3或阶段4执行完毕后,决策层按以下流程操作:
|
||||||
|
|
||||||
|
1. 收到执行层返回的确认消息
|
||||||
|
2. 将该确认消息展示给用户
|
||||||
|
3. **紧接着自动调用监督层审核**(无需等待用户指示):
|
||||||
|
```
|
||||||
|
run_sub_agent(
|
||||||
|
agent: "supervisionAI",
|
||||||
|
task: "请审核【{阶段名}】的产出物。
|
||||||
|
审核维度:{对应维度列表}"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 不需要审核的阶段处理
|
||||||
|
|
||||||
|
| 阶段 | 执行完毕后操作 |
|
||||||
|
|------|---------------|
|
||||||
|
| 1 衍生资产分析 | 将分析结果展示给用户,等待用户确认是否生成 |
|
||||||
|
| 2 衍生资产生成 | 告知用户资产已写入、图片生成中,直接进入阶段3 |
|
||||||
|
| 5 生成分镜 | 告知用户图片生成已启动,流程结束 |
|
||||||
|
|
||||||
|
## 审核结果处理
|
||||||
|
|
||||||
|
审核由监督 Agent 独立完成。监督 Agent 审核完毕后会将审核报告展示给用户,并等待用户进行处理。
|
||||||
|
|
||||||
|
决策层在派发审核任务后,**等待用户回复即可**。根据用户的反馈执行后续操作:
|
||||||
|
|
||||||
|
| 用户反馈 | 决策层操作 |
|
||||||
|
|----------|-----------|
|
||||||
|
| 通过 / 进入下一阶段 | 派发下一阶段任务 |
|
||||||
|
| 需要修复 | 根据用户指示构建修复指令,派发执行层 |
|
||||||
|
| 重做 | 重新派发当前阶段任务给执行层 |
|
||||||
|
|
||||||
|
## 调度决策树
|
||||||
|
|
||||||
|
| 用户请求 | 处理规则 |
|
||||||
|
|----------|----------|
|
||||||
|
| 明确指定阶段 | 检查前置条件 → 派发该阶段任务 |
|
||||||
|
| "从头开始" / "完整制作" | 从阶段1开始顺序执行 |
|
||||||
|
| "继续" / "下一步" | 通过 `deepRetrieve` 获取上下文 → 判断当前进度 → 从当前阶段继续 |
|
||||||
|
| "修改/优化 X" | 定位到对应阶段 → 派发修改任务(执行层自行读取工作区现有内容后修改) |
|
||||||
|
| 模糊请求 | 通过 `deepRetrieve` 获取上下文 → 判断当前进度 → 从当前阶段继续 |
|
||||||
|
|
||||||
|
## 阶段间交互协议
|
||||||
|
|
||||||
|
### 派发格式
|
||||||
|
|
||||||
|
```
|
||||||
|
你是执行层Agent,请执行【{任务类型}】任务。
|
||||||
|
目标:{一句话目标}
|
||||||
|
上下文:{必要数据摘要}
|
||||||
|
要求:
|
||||||
|
1. {具体步骤1}
|
||||||
|
2. {具体步骤2}
|
||||||
|
...
|
||||||
|
约束:{特殊约束条件}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 审核请求格式
|
||||||
|
|
||||||
|
```
|
||||||
|
请审核【{阶段名}】的产出物。
|
||||||
|
审核维度:
|
||||||
|
- {维度1}
|
||||||
|
- {维度2}
|
||||||
|
...
|
||||||
|
特别关注:{本次需特别检查的点}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 用户决策修复格式
|
||||||
|
|
||||||
|
当用户确认需要修复时,决策层根据用户指示构建修复指令:
|
||||||
|
|
||||||
|
```
|
||||||
|
你是执行层Agent,请修复【{任务类型}】的以下问题。
|
||||||
|
用户确认的修复项:
|
||||||
|
1. {用户选择修复的问题} → 修改为:{用户确认的方案}
|
||||||
|
...
|
||||||
|
保持其余内容不变。
|
||||||
|
```
|
||||||
|
|
||||||
|
> **注意**:修复指令中只包含用户明确确认要修的项,不包含用户未回应或明确跳过的问题。
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
# 阶段1:衍生资产分析(Derive Assets Analysis)
|
||||||
|
|
||||||
|
## 全局流程
|
||||||
|
|
||||||
|
1. 决策层派发分析任务给执行层,执行层分析剧本,识别是否需要衍生资产
|
||||||
|
2. 决策层将分析结果展示给用户,等待用户决策
|
||||||
|
3. 用户决策:确认生成 → 进入阶段2 | 跳过生成 → 直接进入阶段3
|
||||||
|
|
||||||
|
## 阶段定义
|
||||||
|
|
||||||
|
```
|
||||||
|
派发:执行层分析剧本,识别是否需要衍生资产
|
||||||
|
输出:衍生资产分析报告(新增衍生资产清单或"无需衍生"结论)
|
||||||
|
前置条件:剧本和资产已存在于工作区
|
||||||
|
```
|
||||||
|
|
||||||
|
## 决策层行为
|
||||||
|
|
||||||
|
执行层返回分析结果后,决策层按以下分支处理:
|
||||||
|
|
||||||
|
| 执行层返回 | 决策层操作 |
|
||||||
|
|-----------|-----------|
|
||||||
|
| "不需要衍生资产" | 向用户简要告知,直接进入阶段3 |
|
||||||
|
| 衍生资产清单(新增资产列表) | 将清单展示给用户,**询问用户是否确认生成这些衍生资产** |
|
||||||
|
|
||||||
|
### 用户确认流程(仅当有新增衍生资产时)
|
||||||
|
|
||||||
|
展示分析结果时,引导用户决策(确认全部/部分生成或跳过)。
|
||||||
|
|
||||||
|
| 用户反馈 | 决策层操作 |
|
||||||
|
|----------|-----------|
|
||||||
|
| 确认生成 / 全部生成 | 进入阶段2(衍生资产生成) |
|
||||||
|
| 部分生成 | 将用户选择的子集传递给阶段2 |
|
||||||
|
| 跳过 / 不需要 | 视为阶段1已完成,直接进入阶段3,后续阶段仅使用资产库中现有资产 |
|
||||||
|
| 调整清单 | 根据用户修改意见,重新派发分析任务或直接将调整后清单传递给阶段2 |
|
||||||
|
|
||||||
|
## 阶段约束
|
||||||
|
|
||||||
|
- 分析结果必须展示给用户确认,不可自动进入生成
|
||||||
|
- 执行层只做分析,不做写入和图片生成
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
# 阶段2:衍生资产生成(Derive Assets Generation)
|
||||||
|
|
||||||
|
> **本阶段为可选阶段**,仅在用户确认需要生成衍生资产后才执行。
|
||||||
|
|
||||||
|
## 全局流程
|
||||||
|
|
||||||
|
1. 决策层将用户确认的衍生资产清单派发给执行层
|
||||||
|
2. 执行层完成资产写入和图片生成
|
||||||
|
3. 决策层将执行结果展示给用户,进入阶段3
|
||||||
|
|
||||||
|
## 阶段定义
|
||||||
|
|
||||||
|
```
|
||||||
|
派发:执行层将用户确认的衍生资产写入工作区并生成图片
|
||||||
|
输入:用户确认的衍生资产清单(来自阶段1)
|
||||||
|
输出:衍生资产写入完成 + 图片生成启动
|
||||||
|
前置条件:阶段1完成且用户确认生成
|
||||||
|
```
|
||||||
|
|
||||||
|
## 决策层行为
|
||||||
|
|
||||||
|
- 将用户确认的资产清单作为参数派发给执行层
|
||||||
|
- 若用户在阶段1只选择了部分资产,**只传递用户选择的子集**
|
||||||
|
- 执行层返回确认后,告知用户资产已写入、图片生成中
|
||||||
|
- 图片生成为异步操作,告知用户等待后直接进入阶段3
|
||||||
|
|
||||||
|
## 阶段约束
|
||||||
|
|
||||||
|
- 资产图片生成属于异步操作,派发后告知用户等待即可
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
# 阶段3:导演规划(Director Plan)
|
||||||
|
|
||||||
|
## 阶段定义
|
||||||
|
|
||||||
|
```
|
||||||
|
派发:执行层制定导演拍摄计划
|
||||||
|
输出:导演拍摄计划(执行层通过 set_plane 同步到前端)
|
||||||
|
质量门:计划覆盖全部剧情、节奏合理、与资产匹配
|
||||||
|
前置条件:阶段1完成(含跳过阶段2的情况)
|
||||||
|
```
|
||||||
|
|
||||||
|
> 本阶段需要审核。执行完毕后自动派发监督层审核,审核与结果处理流程见 decision_dispatch.md。
|
||||||
|
|
||||||
|
## 阶段特有约束
|
||||||
|
|
||||||
|
- 规划中引用的角色、道具、场景必须在资产列表中存在
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
# 阶段5:生成分镜(Storyboard Generation)
|
||||||
|
|
||||||
|
## 全局流程
|
||||||
|
|
||||||
|
1. 决策层派发任务给执行层,执行层调用图片生成接口
|
||||||
|
2. 执行层返回确认后,决策层告知用户图片生成已启动
|
||||||
|
|
||||||
|
## 阶段定义
|
||||||
|
|
||||||
|
```
|
||||||
|
派发:执行层调用图片生成接口生成分镜图片
|
||||||
|
输出:生成的分镜图片
|
||||||
|
前置条件:阶段4(构建分镜表)完成且用户确认
|
||||||
|
```
|
||||||
|
|
||||||
|
## 阶段约束
|
||||||
|
|
||||||
|
- 分镜图片生成属于异步操作,派发后告知用户等待即可
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
# 阶段4:构建分镜表(Storyboard Table)
|
||||||
|
|
||||||
|
## 阶段定义
|
||||||
|
|
||||||
|
```
|
||||||
|
派发:执行层将剧本拆分为分镜,生成结构化分镜表
|
||||||
|
输出:结构化分镜表(执行层通过 set_flowData 保存)
|
||||||
|
质量门:分镜拆分粒度合理、字段完整、关联资产正确
|
||||||
|
前置条件:阶段3(导演规划)完成
|
||||||
|
```
|
||||||
|
|
||||||
|
> 本阶段需要审核。执行完毕后自动派发监督层审核,审核与结果处理流程见 decision_dispatch.md。
|
||||||
|
|
||||||
|
## 阶段特有约束
|
||||||
|
|
||||||
|
- `associateAssetsIds` 中的索引必须指向资产库中实际存在的资产
|
||||||
|
- 不得引用资产库中不存在的角色、道具或场景
|
||||||
@ -0,0 +1,70 @@
|
|||||||
|
# 导演规划 · 古风甜宠写实超现实主义 · 风格技法参考
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、主题立意与叙事核心
|
||||||
|
|
||||||
|
### 风格适配要点
|
||||||
|
|
||||||
|
- **冷中带暖、疏中见密** — 本风格的情感表达不靠台词铺陈,靠画面留白与微表情。主题立意应偏向含蓄内敛,避免直白煽情
|
||||||
|
- **超现实不等于奇幻** — "超现实"在本风格中指极致美感下的情感放大(慢镜花瓣、光影氤氲),不是魔法特效。叙事核心应扎根于人物情感,不依赖奇观
|
||||||
|
- **甜宠的克制** — 甜的部分用"差一点就碰到"比"黏在一起"更有效。情感主线应设计"欲说还休"的推拉节奏
|
||||||
|
- **离场感受建议方向** — 心疼 / 意难平 / 怦然心动 / 治愈。避免"爽感""热血"等与本风格气质不匹配的方向
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、视觉风格与画面基调
|
||||||
|
|
||||||
|
### 风格适配要点
|
||||||
|
|
||||||
|
- **色调基底** — 全片以月白(C1)、冷白肤(C2)、青黛(C6)为基底色,整体色温偏冷(5800-7000K),饱和度中低(30-50%),呈现清冷仙气的高级灰调。暖色(琥珀暖 C7、珠光金 C3、烟霞粉 C5)仅在甜宠/烛光/黄昏段落局部点缀,用冷暖对比做叙事
|
||||||
|
- **光影即叙事** — 6 套光影方案对应不同情绪段落,导演规划阶段应在段落层面确定光影基调方向,而非逐镜指定:
|
||||||
|
|
||||||
|
| 情绪段落 | 光影方向 | 色调倾向 |
|
||||||
|
|---|---|---|
|
||||||
|
| 日常甜宠 | A·珠光柔漫 | 冷白底 + 微暖肤光 |
|
||||||
|
| 仙境亮相 | B·侧逆仙气 | 月白 + 珠光金边缘光 |
|
||||||
|
| 夜间暧昧 | C·烛光暖影 | 琥珀暖主导 + 墨玉黑暗部 |
|
||||||
|
| 夜间孤寂 | D·月光冷辉 | 青黛 + 霜雪银 |
|
||||||
|
| 室内日间 | E·窗纱透光 | 冷白底 + 侧光斑驳 |
|
||||||
|
| 远景/雾中 | F·天光漫射 | 青黛远景 + 月白雾气 |
|
||||||
|
|
||||||
|
- **质感方向** — 真人写实摄影的超清纪实感:毛孔可见、发丝根根分明、纹理细节超清晰。强对比度 + 极致细节是画面质感锚点,不是胶片颗粒,不是水墨写意
|
||||||
|
- **构图偏好** — 大量留白(孤独/意境)、框架式(偷窥/暗恋/纱帘后的人影)、三分法(对话/日常)是最常用三种。中心构图留给正式亮相和权力场景
|
||||||
|
- **镜头运动** — 以静制动为主。缓推/缓拉服务于情绪递进,快切碎剪与本风格气质不兼容
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 三、叙事结构与节奏规划
|
||||||
|
|
||||||
|
### 风格适配要点
|
||||||
|
|
||||||
|
- **慢是基本功** — 本风格的画面信息密度高(服化细节、场景质感),需要给观众"看"的时间。整体节奏偏慢,不等于拖沓,而是每个镜头都有信息量
|
||||||
|
- **情绪曲线宜缓坡** — 避免"平平平→突然爆发"。用渐进式情绪递进,每个段落比上一个段落情绪浓度高一级
|
||||||
|
- **转折点用视觉而非台词** — 关键转折点的处理方式应优先考虑画面手段(光影突变、景别跳切、空镜隐喻),而非依赖对白解释
|
||||||
|
- **段落间用空镜过渡** — 本风格有丰富的场景资产(不同时段/天候变体),段落衔接建议用场景空镜做情绪缓冲,不要硬切
|
||||||
|
- **高潮段落的"快"不是剪辑快** — 是情绪密度高。可以用更紧密的景别切换(全身→近景→特写→大特写)制造加速感,而非缩短镜头时长
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 四、分场景情绪与画面意图
|
||||||
|
|
||||||
|
### 风格适配要点
|
||||||
|
|
||||||
|
- **情绪目标用具象词** — 不说"开心",说"偷偷心动后的嘴角压不住"。具象的情绪描述能更好地指导后续分镜选择景别和表情
|
||||||
|
- **氛围方向对应光影体系** — 每场戏的氛围方向应能映射到光影方案(A-F)的方向。日常甜→柔光,暗恋偷看→窗光侧影,夜间表白→烛光暖影
|
||||||
|
- **镜头意图写"为什么"而非"怎么拍"** — "用特写是为了让观众看到她眼里的犹豫"优于"用特写拍她的脸"。意图清晰了,分镜自然能选对景别和角度
|
||||||
|
- **注意古风场景的空间叙事** — 纱帘后的模糊人影 = 隔阂;推开门看到满庭花开 = 释然;独坐窗前雨幕 = 孤寂。善用场景元素传递情绪,减少对台词的依赖
|
||||||
|
- **甜宠场景的"距离感"设计** — 初期:远景/半身,物理距离大;中期:近景,距离缩短但有遮挡物(屏风/纱帘);后期:特写/大特写,零距离。用景别变化映射关系变化
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 五、声音与音乐方向
|
||||||
|
|
||||||
|
### 风格适配要点
|
||||||
|
|
||||||
|
- **主导乐器** — 古琴 / 箫 / 笛 适合清冷孤寂段落;琵琶 / 二胡 适合情感激荡段落;弦乐铺底可增加电影感但不宜喧宾夺主
|
||||||
|
- **沉默比配乐更有力** — 关键情感瞬间(对视、泪落、转身离去)优先考虑去掉配乐,只留环境音(风声、雨声、衣料摩擦)。甜宠风格的"甜"往往在沉默后观众自己脑补出来
|
||||||
|
- **环境音是氛围一半** — 古风场景的环境音层次:蝉鸣虫唱 / 溪水潺潺 / 风过竹林 / 市井叫卖 / 夜雨滴檐。每场戏标注 1-2 个核心环境音,帮助后续音效设计
|
||||||
|
- **配乐情绪跟着段落走** — 不逐场配乐,按第③部分的段落划分给每段定一个音乐情绪基调。同段落内场景切换靠环境音变化过渡,不频繁换曲
|
||||||
|
- **避免满配** — 全片配乐覆盖率建议不超过 60%。留白段落的"无声"与配乐段落形成呼吸感
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
# 分镜表设计 · 古风甜宠写实超现实主义 · 风格技法参考
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 一、分镜表定位
|
||||||
|
|
||||||
|
分镜表是导演将剧本转化为镜头语言的核心工具。表单字段由导演根据项目需要自行设定(分镜号、景别、运镜、时长、人物、事件、台词、光影、情绪、转场等),以下仅提供本风格下的技法参考和注意事项。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 二、风格适配要点
|
||||||
|
|
||||||
|
### 景别选择
|
||||||
|
|
||||||
|
- **甜宠戏的景别递进** — 同场戏内景别应随情感升温递进:半身→近景→特写→大特写。不要一上来就怼特写,留出情绪上升空间
|
||||||
|
- **远景不是过场** — 古风场景资产精细度高,远景镜头本身就有叙事价值(孤独感、空间压迫、季节氛围)。给远景足够时长(4-6s),别急着切走
|
||||||
|
- **大特写要有理由** — 大特写(眼/唇/手)是情绪核弹,一集用 2-3 次足够。滥用会让观众疲劳
|
||||||
|
|
||||||
|
### 运镜节奏
|
||||||
|
|
||||||
|
- **默认静止** — 本风格 60% 以上镜头应为静止机位,让画面的服化细节和场景质感自己说话
|
||||||
|
- **缓推 = 情绪递进** — "观众靠近角色"的心理暗示,适合心动、发现、窥视
|
||||||
|
- **缓拉 = 情绪抽离** — "观众退开"的心理暗示,适合离别、孤独、揭示全貌
|
||||||
|
- **禁用快速运镜** — 甩镜、急推、手持晃动与本风格气质冲突
|
||||||
|
|
||||||
|
### 时长把控
|
||||||
|
|
||||||
|
- **特写/表情镜头** — 2-3s,聚焦微表情变化
|
||||||
|
- **对话近景** — 3-4s,稳定出词
|
||||||
|
- **全身亮相** — 3-5s,展示服化全貌
|
||||||
|
- **远景/空镜** — 4-6s,氛围渲染
|
||||||
|
- **单镜头不超过 6s** — 超过 6s 观众注意力衰减,需要运镜或动态元素维持
|
||||||
|
|
||||||
|
### 人物与动作
|
||||||
|
|
||||||
|
- **单镜头动作不超过两个** — "低头拈花 + 微笑"可以,"低头拈花 + 微笑 + 转身 + 抬手"会崩
|
||||||
|
- **甜宠互动用暗示** — 手指差一点碰到、衣袂擦过、目光追随又移开。不要在分镜表里写"拥抱""接吻"等大幅度双人交互,拆成暗示性的局部镜头
|
||||||
|
- **古风动作要慢** — 所有人物动作默认慢速。起身、转身、抬手都应标注"缓慢"
|
||||||
|
|
||||||
|
### 台词与留白
|
||||||
|
|
||||||
|
- **台词少的镜头给长时长** — 无台词的情绪镜头往往比有台词的更需要时间。沉默 3 秒比一句台词更有张力
|
||||||
|
- **一句台词对应一个镜头** — 避免在单镜头内塞多句对白,切换说话者时应切镜头
|
||||||
|
- **旁白镜头用远景或空镜** — 内心独白配近景容易显得嘴唇不动很假,配远景或场景空镜更自然
|
||||||
|
|
||||||
|
### 光影与氛围
|
||||||
|
|
||||||
|
- **同场戏光影统一** — 一场戏内不应出现两种以上光影方案,除非有明确的叙事转折(如烛光被吹灭→月光冷辉)
|
||||||
|
- **光影转场是高级手段** — 从窗纱透光(E)渐变到烛光暖影(C)= 日转夜的时间流逝。在分镜表中标注光影变化点
|
||||||
|
- **环境动态增加画面呼吸感** — 花瓣飘落、烟雾升腾、水波荡漾、纱帘飘动。每 3-4 个镜头至少安排一个有环境动态的镜头,避免画面"死"掉
|
||||||
|
|
||||||
|
### 转场设计
|
||||||
|
|
||||||
|
- **默认硬切** — 同场戏内镜头间用硬切,干净利落
|
||||||
|
- **场景切换用空镜过渡** — 不同场景间插入 1 个场景空镜(2-3s)做情绪缓冲
|
||||||
|
- **段落切换可用叠化/淡入淡出** — 大段落间的情绪跳跃用柔性转场,避免观众出戏
|
||||||
|
- **禁用花式转场** — 划屏、旋转、百叶窗等与本风格不兼容
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
name: production_execution_derive_assets
|
||||||
|
description: >-
|
||||||
|
执行层技能:衍生资产分析与生成。分析剧本识别衍生资产,写入工作区并可选生成图片。
|
||||||
|
---
|
||||||
|
|
||||||
|
# 衍生资产分析与生成
|
||||||
|
|
||||||
|
## 工具
|
||||||
|
|
||||||
|
| 操作 | 调用 |
|
||||||
|
|------|------|
|
||||||
|
| 读取剧本与资产 | `get_flowData` (key: "script") / `get_flowData` (key: "assets") |
|
||||||
|
| 写入衍生资产 | `set_flowData_assets` |
|
||||||
|
| 生成资产图片 | `generate_assets_images({ ids: [资产id列表] })` |
|
||||||
|
|
||||||
|
## 参考资料
|
||||||
|
|
||||||
|
根据任务需要使用 `read_skill_file` 工具按需加载:
|
||||||
|
|
||||||
|
- [衍生资产提取](production_execution_derive_assets_extraction.md) — 衍生资产识别与提取原则
|
||||||
|
|
||||||
|
## 执行流程
|
||||||
|
|
||||||
|
1. 调用 `get_flowData` 分别获取 `script`(剧本)和 `assets`(现有资产列表)
|
||||||
|
2. 根据[衍生资产提取](production_execution_derive_assets_extraction.md)文档中的提取原则,分析剧本内容,为每个资产识别在剧情中出现的不同视觉状态变体
|
||||||
|
3. **判断是否需要衍生资产**:
|
||||||
|
- 如果不需要衍生资产:返回"不需要衍生资产",流程结束
|
||||||
|
- 如果需要衍生资产:继续后续步骤
|
||||||
|
4. 对每个有衍生状态的资产调用 `set_flowData_assets` 保存
|
||||||
|
5. 收集所有需要生成图片的资产 id,调用 `generate_assets_images({ ids: [资产id列表] })` 生成图片
|
||||||
|
6. 返回简短确认,如:"衍生资产已提取并保存,图片生成中,请稍后查看。"
|
||||||
|
|
||||||
|
## 约束
|
||||||
|
|
||||||
|
- 衍生状态必须与剧情匹配
|
||||||
|
- 不遗漏关键视觉变体
|
||||||
|
- 不过度衍生(仅提取剧本中有明确视觉呈现需求的衍生资产)
|
||||||
|
- 图片生成为异步操作,发起后即可返回确认
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
# 衍生资产提取(从剧本 + 资产 → derive[])
|
||||||
|
|
||||||
|
根据剧本内容和已有资产,为每个资产提取在剧情中出现的**不同视觉状态/变体**(derive)。
|
||||||
|
|
||||||
|
> **核心原则**:derive 是父资产的**视觉状态变体**(即 "{父资产名}·{状态名}"),**不是**独立物件。
|
||||||
|
> 只衍生**图片模型无法仅凭提示词自行处理的视觉差异**——服装、形态、伤势、物件状态等。
|
||||||
|
> 表情、情绪、简单动作姿态等 **不需要衍生**。
|
||||||
|
|
||||||
|
## 1. 输入与输出
|
||||||
|
|
||||||
|
- 剧本:`get_flowData("script")`
|
||||||
|
- 资产列表:`get_flowData("assets")`
|
||||||
|
|
||||||
|
对每个需要衍生的资产调用 `set_flowData`,通过 lodash 路径精确定位:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
set_flowData({
|
||||||
|
key: "assets[0].derive", // 索引定位
|
||||||
|
value: [
|
||||||
|
{ name: "状态名", // 1~20字
|
||||||
|
desc: "状态描述" } // 1~100字
|
||||||
|
]
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
## 2. 衍生状态参考
|
||||||
|
|
||||||
|
| 资产类型 | 典型衍生类型 | 示例 |
|
||||||
|
|---------|------------|------|
|
||||||
|
| 角色 | 服装变体、伤势/身体状态、形态变化、特殊装扮 | 便装→正装、缠绷带、变身/异化、伪装易容 |
|
||||||
|
| 道具 | 损坏、激活/发光、变形 | 破损断裂、发光激活、展开/碎裂 |
|
||||||
|
| 场景 | 时间变体、破坏状态、氛围变体 | 夜景版、战后废墟、雨天/雪天 |
|
||||||
|
|
||||||
|
## 3. 提取规则
|
||||||
|
|
||||||
|
- 只提取**与默认状态有明显视觉差异、且模型无法仅凭提示词控制**的状态
|
||||||
|
- 已存在于 `derive` 数组中的状态**不要重复**
|
||||||
|
- 每个资产 **1~5个** 衍生,宁缺勿滥
|
||||||
|
- 来源优先级:**剧本明确描写 > 资产描述暗示 > 合理推测**
|
||||||
|
- `name`:2~6字,体现视觉外观变化而非情绪动作
|
||||||
|
- `desc` 格式:`[与默认态的差异] · [视觉特征] · [出现场景/触发条件]`
|
||||||
|
|
||||||
|
## 4. 示例
|
||||||
|
|
||||||
|
假设剧本描写了角色 A 受重伤、道具 B 被损坏,而角色 C 无视觉变化:
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// 角色 A(索引 0)— 有 2 个视觉状态变体
|
||||||
|
set_flowData({
|
||||||
|
key: "assets[0].derive",
|
||||||
|
value: [
|
||||||
|
{ name: "{伤势状态名}", desc: "{与默认态的差异} · {视觉特征} · {触发条件}" },
|
||||||
|
{ name: "{形态状态名}", desc: "{与默认态的差异} · {视觉特征} · {触发条件}" }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
// 角色 C(索引 1)— 无需衍生,跳过
|
||||||
|
|
||||||
|
// 道具 B(索引 2)— 有 1 个视觉状态变体
|
||||||
|
set_flowData({
|
||||||
|
key: "assets[2].derive",
|
||||||
|
value: [
|
||||||
|
{ name: "{损坏状态名}", desc: "{与默认态的差异} · {视觉特征} · {触发条件}" }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
```
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
---
|
||||||
|
name: production_execution_director_plan
|
||||||
|
description: >-
|
||||||
|
执行层技能:导演规划。根据剧本和资产制定导演拍摄计划,通过 set_plane 同步到前端。
|
||||||
|
---
|
||||||
|
|
||||||
|
# 导演规划
|
||||||
|
|
||||||
|
## 工具
|
||||||
|
|
||||||
|
| 操作 | 调用 |
|
||||||
|
|------|------|
|
||||||
|
| 读取剧本与资产 | `get_flowData` (key: "script") / `get_flowData` (key: "assets") |
|
||||||
|
| 写入导演规划 | `set_plane` |
|
||||||
|
|
||||||
|
## 参考资料
|
||||||
|
|
||||||
|
根据任务需要使用 `read_skill_file` 工具按需加载:
|
||||||
|
|
||||||
|
- [生成计划](production_execution_plan.md) — 导演规划的结构与规范
|
||||||
|
|
||||||
|
## 执行流程
|
||||||
|
|
||||||
|
1. 调用 `get_flowData` 分别获取 `script`(剧本)和 `assets`(现有资产列表)
|
||||||
|
2. 根据[生成计划](production_execution_plan.md)文档中的规范,制定导演拍摄计划
|
||||||
|
3. 调用 `set_plane` 将导演计划同步到前端
|
||||||
|
4. 返回简短确认
|
||||||
|
|
||||||
|
## 约束
|
||||||
|
|
||||||
|
- 计划必须覆盖全部剧情
|
||||||
|
- 节奏安排合理
|
||||||
|
- 与现有资产匹配
|
||||||
@ -0,0 +1,87 @@
|
|||||||
|
# 导演规划 · 计划制定规范
|
||||||
|
|
||||||
|
## 风格技法参考
|
||||||
|
|
||||||
|
通过 `read_skill_file` 加载项目关联的导演风格技法参考文档:
|
||||||
|
|
||||||
|
- **director_planning.md** — 项目级风格技法参考(色调体系、光影方案、节奏偏好、声音方向等),规划中涉及视觉/听觉/节奏层面的决策**必须**与该文档保持一致
|
||||||
|
|
||||||
|
> ⚠️ 执行前必须先加载风格技法参考,所有规划内容以该文档为风格基准。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 计划制定规范
|
||||||
|
|
||||||
|
根据 `get_flowData` 返回的工作区数据和用户需求,按以下规范生成导演规划。规划分为两大部分:**创作规划**(五个维度)和 **执行计划**(工具与步骤)。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第一部分:创作规划
|
||||||
|
|
||||||
|
### ① 主题立意与叙事核心
|
||||||
|
|
||||||
|
规划项:核心主题、情感主线、离场感受、情感表达策略
|
||||||
|
|
||||||
|
**约束**:主题一句话凝练;情感主线拆解 2-3 个递进层次;离场感受与风格技法参考一致;表达策略需与风格技法参考匹配
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ② 视觉风格与画面基调
|
||||||
|
|
||||||
|
规划项:整体色调、画面质感、构图风格、镜头运动偏好、光影体系
|
||||||
|
|
||||||
|
**约束**:色调需具体到色彩代号或色温范围;光影体系以段落-光影方向表格呈现;构图与镜头运动需说明叙事理由;光影方案与风格技法参考对应
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ③ 叙事结构与节奏规划
|
||||||
|
|
||||||
|
规划项:段落划分、情绪曲线、快慢节奏、关键转折点、段落过渡
|
||||||
|
|
||||||
|
**约束**:段落以表格呈现(编号/名称/场次/核心事件/情绪浓度/节奏);情绪曲线需呈渐进式递增;转折点必须用具体视觉手段描述(光影突变、景别跳切等),优先画面而非台词;段落间避免硬切
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ④ 分场景情绪与画面意图
|
||||||
|
|
||||||
|
规划项(逐场列出):场次编号、情绪目标、氛围方向、镜头意图、空间叙事、距离感设计
|
||||||
|
|
||||||
|
**约束**:情绪目标用具象可感的描述(禁止抽象词如"开心");氛围方向映射风格技法参考中的光影方案;镜头意图写叙事目的而非机位参数
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### ⑤ 声音与音乐方向
|
||||||
|
|
||||||
|
规划项:音乐风格、段落配乐对应、配乐覆盖率、环境音设计、沉默运用
|
||||||
|
|
||||||
|
**约束**:配乐按段落统一规划(不逐场);环境音需具体到可感知声源;明确标注沉默手法的关键瞬间;覆盖率与风格技法参考一致
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 第二部分:执行计划
|
||||||
|
|
||||||
|
先用一句话概述当前工作区状态与本次目标,然后将任务拆解为步骤列表。
|
||||||
|
|
||||||
|
每个步骤包含:步骤编号、步骤名称、具体内容、预期输出、依赖步骤(无依赖则标"无",有依赖的步骤串行执行,无依赖的可并行)。
|
||||||
|
|
||||||
|
**关键要求**:每个步骤的"具体内容"必须是完整的任务描述,能独立作为 `run_sub_agent` 的 `prompt` 参数执行。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 输出要求
|
||||||
|
|
||||||
|
- **总字数不超过 1000 词**,精炼表达,避免冗长展开
|
||||||
|
- 按"创作规划(①-⑤)→ 执行计划(目标 + 步骤列表)"的顺序输出,格式自由,清晰即可
|
||||||
|
- 表格仅在信息密度高时使用(如段落划分、分场景意图),其余用简洁列表或短段落
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
- **风格一致性**:所有创作规划内容必须与 `director_planning.md` 风格技法参考保持一致,发现冲突时以风格技法参考为准
|
||||||
|
- **具象优于抽象**:情绪描述、氛围方向、声音设计均需具体可感知,禁止笼统概括
|
||||||
|
- **视觉优先叙事**:转折点、情感高潮优先用画面手段表达,减少对台词的依赖
|
||||||
|
- 步骤粒度适中:每步对应一次 `run_sub_agent` 调用
|
||||||
|
- 利用 `deepRetrieve` 检索历史记忆,跳过已完成的工作
|
||||||
|
- 考虑用户已有的素材和资源,避免重复
|
||||||
|
- 每个步骤的内容描述要包含足够上下文,使执行层无需额外信息即可工作
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
name: production_execution_storyboard_gen
|
||||||
|
description: >-
|
||||||
|
执行层技能:生成分镜图片。根据分镜表调用图片生成接口生成分镜图片。
|
||||||
|
---
|
||||||
|
|
||||||
|
# 生成分镜图片
|
||||||
|
|
||||||
|
## 工具
|
||||||
|
|
||||||
|
| 操作 | 调用 |
|
||||||
|
|------|------|
|
||||||
|
| 读取剧本 | `get_flowData` (key: "script") |
|
||||||
|
| 生成分镜图片 | `generate_storyboard_images({ script: 剧本文本 })` |
|
||||||
|
|
||||||
|
## 执行流程
|
||||||
|
|
||||||
|
1. 调用 `get_flowData` 获取 `script`(剧本文本)
|
||||||
|
2. 调用 `generate_storyboard_images({ script: 剧本文本 })` 生成分镜图片
|
||||||
|
3. 返回简短确认
|
||||||
|
|
||||||
|
## 约束
|
||||||
|
|
||||||
|
- 图片必须与分镜描述匹配
|
||||||
|
- 图片生成为异步操作,发起后即可返回确认
|
||||||
|
- 前置条件:分镜表已构建完成且用户已确认
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
---
|
||||||
|
name: production_execution_storyboard_table
|
||||||
|
description: >-
|
||||||
|
执行层技能:构建分镜表。根据剧本和资产生成结构化分镜表,通过 set_flowData 保存。
|
||||||
|
---
|
||||||
|
|
||||||
|
# 构建分镜表
|
||||||
|
|
||||||
|
## 工具
|
||||||
|
|
||||||
|
| 操作 | 调用 |
|
||||||
|
|------|------|
|
||||||
|
| 读取剧本与资产 | `get_flowData` (key: "script") / `get_flowData` (key: "assets") |
|
||||||
|
| 写入分镜表 | `set_flowData({ key: "storyboard", value: 分镜数组 })` |
|
||||||
|
|
||||||
|
## 参考资料
|
||||||
|
|
||||||
|
根据任务需要使用 `read_skill_file` 工具按需加载:
|
||||||
|
|
||||||
|
- [分镜表生成](storyboard_generation.md) — 分镜拆分原则、字段规范和示例
|
||||||
|
|
||||||
|
## 执行流程
|
||||||
|
|
||||||
|
1. 调用 `get_flowData` 分别获取 `script`(剧本)和 `assets`(现有资产列表)
|
||||||
|
2. 根据[分镜表生成](storyboard_generation.md)文档中的拆分原则和字段填写指引,将剧本拆分为分镜
|
||||||
|
3. 填写每条分镜的所有字段(id、title、description、camera、duration、frameMode、prompt、lines、sound、associateAssetsIds)
|
||||||
|
4. 调用 `set_flowData({ key: "storyboard", value: 分镜数组 })` 一次性保存完整分镜表
|
||||||
|
5. 返回简短确认
|
||||||
|
|
||||||
|
## 约束
|
||||||
|
|
||||||
|
- 分镜拆分粒度合理
|
||||||
|
- 所有字段完整填写
|
||||||
|
- 关联资产 ID 必须与工作区现有资产匹配
|
||||||
|
- 场景描述足够具体,可直接用于 AI 视频/图片生成
|
||||||
@ -227,18 +227,7 @@ set_flowData({
|
|||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
## 5. 工具调用顺序
|
## 5. 补充说明
|
||||||
|
|
||||||
1. `get_flowData("script")` — 获取剧本内容
|
|
||||||
2. `get_flowData("assets")` — 获取已有资产列表
|
|
||||||
3. 分析剧本,按照拆分原则划分分镜,并为每条分镜填写所有字段
|
|
||||||
4. 调用 `set_flowData({ key: "storyboard", value: 分镜数组 })` 一次性保存完整分镜面板
|
|
||||||
5. 向用户汇报分镜面板概要(总共多少条分镜,覆盖的场景概括)
|
|
||||||
6. **询问用户是否需要生成分镜图片**:
|
|
||||||
- 如果用户确认,调用 `generate_storyboard_images({ script: 剧本文本 })` 生成分镜图
|
|
||||||
- 如果用户拒绝,跳过此步骤,流程结束
|
|
||||||
|
|
||||||
## 6. 注意事项
|
|
||||||
|
|
||||||
- 分镜数量与剧本长度成正比,一般每 50~100 字剧本对应 1~2 条分镜
|
- 分镜数量与剧本长度成正比,一般每 50~100 字剧本对应 1~2 条分镜
|
||||||
- prompt 必须使用英文,且只描述视觉内容
|
- prompt 必须使用英文,且只描述视觉内容
|
||||||
@ -0,0 +1,89 @@
|
|||||||
|
# 导演规划审核
|
||||||
|
|
||||||
|
基于 [supervision_common.md](supervision_common.md) 中的通用规范执行审核。
|
||||||
|
|
||||||
|
## 数据准备
|
||||||
|
|
||||||
|
1. 调用 `get_flowData` 获取导演规划数据(plan)
|
||||||
|
2. 调用 `get_flowData` 获取剧本数据(script)和资产数据(assets)
|
||||||
|
3. 通过 `read_skill_file` 加载项目关联的 **director_planning.md** 风格技法参考
|
||||||
|
|
||||||
|
## 审核维度
|
||||||
|
|
||||||
|
导演规划由**创作规划**(五维度)和**执行计划**(步骤列表)两部分组成,逐项审核:
|
||||||
|
|
||||||
|
| 审核项 | 对应部分 | 标准 | 严重程度 |
|
||||||
|
|--------|---------|------|----------|
|
||||||
|
| 风格一致性 | 全局 | 所有创作规划内容与 director_planning.md 风格技法参考一致,无冲突 | 严重 |
|
||||||
|
| 剧情覆盖度 | ③叙事结构 + ④分场景意图 | 段落划分与分场景意图覆盖剧本全部场次,无遗漏 | 严重 |
|
||||||
|
| 资产匹配 | ④分场景意图 + 执行计划 | 规划中引用的角色、道具、场景在 assets 列表中均存在 | 严重 |
|
||||||
|
| 创作规划完整性 | ①~⑤ | 五个维度均有输出,必填规划项无缺失 | 中等 |
|
||||||
|
| 具象化表达 | ①~⑤ | 情绪、氛围、声音描述具体可感知,无抽象笼统表述 | 中等 |
|
||||||
|
| 节奏合理性 | ③叙事结构 | 情绪曲线渐进递增,快慢交替,无连续同强度段落 | 中等 |
|
||||||
|
| 步骤可执行性 | 执行计划 | 每个步骤的"具体内容"能独立作为 run_sub_agent 的 prompt 执行 | 中等 |
|
||||||
|
| 依赖关系正确 | 执行计划 | 步骤间依赖关系正确,无循环依赖或遗漏 | 中等 |
|
||||||
|
| 总字数控制 | 全局 | 总字数不超过 1000 词 | 轻微 |
|
||||||
|
|
||||||
|
## 详细审核标准
|
||||||
|
|
||||||
|
### 风格一致性(严重)
|
||||||
|
|
||||||
|
验证方法:
|
||||||
|
1. 加载 director_planning.md 风格技法参考
|
||||||
|
2. 逐一比对创作规划中的色调、光影、节奏、声音方向是否与风格技法参考一致
|
||||||
|
3. 发现冲突时标注具体冲突项
|
||||||
|
|
||||||
|
### 剧情覆盖度(严重)
|
||||||
|
|
||||||
|
验证方法:
|
||||||
|
1. 将剧本按场次拆分
|
||||||
|
2. 检查③段落划分表是否覆盖全部场次
|
||||||
|
3. 检查④分场景意图是否逐场列出
|
||||||
|
4. 标注未被覆盖的场次
|
||||||
|
|
||||||
|
### 资产匹配(严重)
|
||||||
|
|
||||||
|
验证方法:
|
||||||
|
1. 提取④分场景意图和执行计划步骤中提及的角色、道具、场景名称
|
||||||
|
2. 与 assets 列表逐一比对
|
||||||
|
3. 标注引用了但 assets 中不存在的项
|
||||||
|
|
||||||
|
### 创作规划完整性(中等)
|
||||||
|
|
||||||
|
逐维度检查必填规划项:
|
||||||
|
|
||||||
|
| 维度 | 必填项 |
|
||||||
|
|------|--------|
|
||||||
|
| ①主题立意 | 核心主题、情感主线、离场感受、情感表达策略 |
|
||||||
|
| ②视觉风格 | 整体色调、画面质感、构图风格、镜头运动偏好、光影体系 |
|
||||||
|
| ③叙事结构 | 段落划分表(编号/名称/场次/核心事件/情绪浓度/节奏)、情绪曲线、转折点 |
|
||||||
|
| ④分场景意图 | 逐场的情绪目标、氛围方向、镜头意图、空间叙事、距离感设计 |
|
||||||
|
| ⑤声音方向 | 音乐风格、段落配乐对应、环境音设计、沉默运用 |
|
||||||
|
|
||||||
|
### 具象化表达(中等)
|
||||||
|
|
||||||
|
- ①情感主线需拆解 2-3 个递进层次,非笼统概括
|
||||||
|
- ②色调需具体到色彩代号或色温范围,非"暖色调"
|
||||||
|
- ③转折点必须用具体视觉手段描述(光影突变、景别跳切等),优先画面而非台词
|
||||||
|
- ④情绪目标用具象可感的描述,禁止抽象词(如"开心""悲伤")
|
||||||
|
- ⑤环境音需具体到可感知声源,非"自然声"
|
||||||
|
|
||||||
|
### 节奏合理性(中等)
|
||||||
|
|
||||||
|
- 情绪曲线应呈渐进式递增,非平铺直叙
|
||||||
|
- 高强度段落与低强度段落交替出现,不允许连续 3 个以上同强度段落
|
||||||
|
- 段落间应有过渡设计,避免硬切
|
||||||
|
|
||||||
|
### 步骤可执行性(中等)
|
||||||
|
|
||||||
|
每个步骤的"具体内容"必须满足:
|
||||||
|
- 能独立作为 `run_sub_agent` 的 `prompt` 参数使用
|
||||||
|
- 包含足够上下文,执行层无需额外信息即可工作
|
||||||
|
- 有明确的预期输出描述
|
||||||
|
|
||||||
|
### 依赖关系正确(中等)
|
||||||
|
|
||||||
|
- 有依赖的步骤标注了正确的依赖步骤编号
|
||||||
|
- 无依赖的步骤标注"无"
|
||||||
|
- 无循环依赖
|
||||||
|
- 可并行的步骤未被错误串行化
|
||||||
@ -0,0 +1,81 @@
|
|||||||
|
# 分镜表审核
|
||||||
|
|
||||||
|
基于 [supervision_common.md](supervision_common.md) 中的通用规范执行审核。
|
||||||
|
|
||||||
|
## 数据准备
|
||||||
|
|
||||||
|
1. 调用 `get_flowData` 获取分镜表数据(storyboard)
|
||||||
|
2. 调用 `get_flowData` 获取剧本数据(script)和资产数据(assets)
|
||||||
|
|
||||||
|
## 审核维度
|
||||||
|
|
||||||
|
| 审核项 | 标准 | 严重程度 |
|
||||||
|
|--------|------|----------|
|
||||||
|
| 字段完整性 | 每条分镜的所有必填字段(id、title、description、camera、duration、frameMode、prompt、lines、sound、associateAssetsIds)均已填写 | 严重 |
|
||||||
|
| 关联资产正确 | associateAssetsIds 中的索引均在 assets 数组范围内;画面中可见的资产已关联 | 严重 |
|
||||||
|
| 剧本覆盖度 | 剧本中的全部场景和关键事件均有对应分镜,无遗漏 | 严重 |
|
||||||
|
| 拆分粒度 | 一个独立画面对应一条分镜;无过度合并或过度拆分 | 中等 |
|
||||||
|
| prompt 质量 | 英文撰写;包含主体、动作、场景、氛围等视觉要素;无剧情叙事或对话内容 | 中等 |
|
||||||
|
| 镜头语言合理 | camera 字段使用标准景别术语;景别变化服务于叙事节奏 | 中等 |
|
||||||
|
| 时长合理性 | duration 与画面复杂度匹配;总时长与剧本预估时长基本吻合 | 中等 |
|
||||||
|
| frameMode 选择 | 帧模式与分镜内容匹配(动作结果用 endFrame、对话为主用 linesSoundEffects、其余用 firstFrame) | 轻微 |
|
||||||
|
|
||||||
|
## 详细审核标准
|
||||||
|
|
||||||
|
### 字段完整性(严重)
|
||||||
|
|
||||||
|
验证方法:
|
||||||
|
1. 遍历每条分镜,检查所有必填字段是否存在且非空
|
||||||
|
2. id 应从 1 开始递增且无重复
|
||||||
|
3. title 应在 2~10 字范围内
|
||||||
|
4. lines 和 sound 允许为 `null`(表示无台词/音效),但不允许缺失字段
|
||||||
|
|
||||||
|
### 关联资产正确(严重)
|
||||||
|
|
||||||
|
验证方法:
|
||||||
|
1. 获取 assets 数组长度 N
|
||||||
|
2. 遍历每条分镜的 associateAssetsIds,检查所有索引 < N
|
||||||
|
3. 对照 description,判断画面中明显可见的资产是否都已关联
|
||||||
|
4. 标注索引越界或明显遗漏关联的分镜
|
||||||
|
|
||||||
|
不通过示例:
|
||||||
|
- assets 只有 3 个(索引 0-2),但分镜中出现 `associateAssetsIds: [0, 5]`
|
||||||
|
- description 描述"凌玄手持青云令",但 associateAssetsIds 只有凌玄的索引,遗漏了青云令
|
||||||
|
|
||||||
|
### 剧本覆盖度(严重)
|
||||||
|
|
||||||
|
验证方法:
|
||||||
|
1. 将剧本按场景/事件节点拆分
|
||||||
|
2. 逐一检查每个场景是否有对应分镜
|
||||||
|
3. 标注未被覆盖的剧情段落
|
||||||
|
|
||||||
|
### 拆分粒度(中等)
|
||||||
|
|
||||||
|
过度合并的信号:
|
||||||
|
- 一条分镜的 description 超过 100 字
|
||||||
|
- 一条分镜包含明显的场景切换或视角变化
|
||||||
|
- 一条分镜的 duration 超过 8 秒
|
||||||
|
|
||||||
|
过度拆分的信号:
|
||||||
|
- 连续多条分镜描述同一画面内的微小变化
|
||||||
|
- 同一段对话被拆成超过 3 条分镜(无视角切换时)
|
||||||
|
|
||||||
|
### prompt 质量(中等)
|
||||||
|
|
||||||
|
验证要点:
|
||||||
|
- 必须为英文
|
||||||
|
- 包含:主体描述 + 动作/姿态 + 场景/背景 + 光影/氛围
|
||||||
|
- 不包含对话、叙事或心理活动
|
||||||
|
- 与 description 的视觉内容一致
|
||||||
|
|
||||||
|
不通过示例:
|
||||||
|
- 中文 prompt
|
||||||
|
- "A scene where the character feels sad" ← 情绪而非视觉
|
||||||
|
- prompt 描述与 description 矛盾
|
||||||
|
|
||||||
|
### 镜头语言合理(中等)
|
||||||
|
|
||||||
|
- 使用标准景别术语(大远景/远景/全景/中景/近景/特写/大特写)
|
||||||
|
- 重要细节用特写/大特写,场景建立用远景/全景
|
||||||
|
- 对话场景通常用近景/中景
|
||||||
|
- 不允许连续 5 条以上使用完全相同的景别
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
# 监督层通用规范
|
||||||
|
|
||||||
|
本文件定义所有审核任务共享的报告格式、评分标准和审核原则。
|
||||||
|
|
||||||
|
## 审核报告格式
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# 审核报告:{审核对象}
|
||||||
|
|
||||||
|
## 总评
|
||||||
|
- **评分**:{A/B/C/D}
|
||||||
|
- **概要**:{一句话总评,可顺带肯定亮点}
|
||||||
|
|
||||||
|
## 问题清单
|
||||||
|
|
||||||
|
| # | 严重程度 | 审核项 | 问题 | 建议方案 |
|
||||||
|
|---|----------|--------|------|----------|
|
||||||
|
| 1 | 🔴 严重 | {审核项} | {一句话描述} | {多选方案用"/"分隔} |
|
||||||
|
| 2 | 🟡 中等 | {审核项} | {一句话描述} | {修复建议} |
|
||||||
|
| 3 | ⚪ 轻微 | {审核项} | {一句话描述} | {修复建议} |
|
||||||
|
|
||||||
|
## 需要您决定(仅 C/D 级或严重问题存在多选方案时输出)
|
||||||
|
1. {选择题}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 精简规则
|
||||||
|
|
||||||
|
- 审核通过的项目不出现在报告中
|
||||||
|
- 同类轻微问题合并为一行
|
||||||
|
- B 级及以上省略「需要您决定」区块
|
||||||
|
|
||||||
|
## 评分标准
|
||||||
|
|
||||||
|
| 评分 | 严重问题 | 中等问题 |
|
||||||
|
|------|----------|----------|
|
||||||
|
| A — 可直接使用 | 0 | ≤2 |
|
||||||
|
| B — 小修后可用 | 0 | ≤5 |
|
||||||
|
| C — 需较大修改 | 1-2 | 不限 |
|
||||||
|
| D — 建议重做 | ≥3 | 不限 |
|
||||||
|
|
||||||
|
## 通用审核原则
|
||||||
|
|
||||||
|
1. **工具调取优先**:所有审核依据必须通过工具实际读取,不得凭记忆或上下文摘要审核
|
||||||
|
2. **可执行优先**:标准是"能不能用",不是"完不完美"
|
||||||
|
3. **问题具体化**:每个问题指向具体位置和内容,不说"整体不够好"
|
||||||
|
4. **建议多元化**:严重问题提供多个可选方案
|
||||||
|
5. **动态基准**:数值判断以实际工作区数据为唯一基准;未明确的参数以合理比例推算,并在报告中注明
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
name: production_agent_supervision.md
|
||||||
|
description: >-
|
||||||
|
视频制作监督层Agent路由。根据决策层派发的审核任务类型,加载对应的独立技能文件执行。
|
||||||
|
当收到决策层的 run_sub_agent 调用时激活。
|
||||||
|
---
|
||||||
|
|
||||||
|
# 监督层 Agent — 任务路由
|
||||||
|
|
||||||
|
你是视频制作项目的**监督层 Agent**,只接收决策层派发的审核任务并执行。
|
||||||
|
|
||||||
|
**核心原则:你只提出问题和建议,不做任何修改决策。所有修改决定权属于用户。**
|
||||||
|
|
||||||
|
## 任务路由表
|
||||||
|
|
||||||
|
收到任务后,根据指令中的关键词匹配对应技能文件,加载并执行:
|
||||||
|
|
||||||
|
| 标识词 | 技能文件 | 说明 |
|
||||||
|
|--------|----------|------|
|
||||||
|
| 导演规划审核、审核规划、review plan | [production_supervision_director_plan.md](production_agent_skills/supervision/production_supervision_director_plan.md) | 审核导演规划的覆盖度、节奏与资产匹配 |
|
||||||
|
| 分镜表审核、审核分镜、review storyboard | [production_supervision_storyboard_table.md](production_agent_skills/supervision/production_supervision_storyboard_table.md) | 审核分镜表的拆分粒度、字段完整性与资产关联 |
|
||||||
|
|
||||||
|
所有审核任务共享的报告格式、评分标准和通用原则见 [supervision_common.md](production_agent_skills/supervision/supervision_common.md)。
|
||||||
|
|
||||||
|
## 路由规则
|
||||||
|
|
||||||
|
1. 从派发指令中识别审核对象关键词
|
||||||
|
2. 加载对应的审核技能文件 + 通用规范文件
|
||||||
|
3. 按技能文件中的审核维度逐项检查
|
||||||
|
4. 按通用规范中的报告格式生成审核报告
|
||||||
|
5. 如果无法匹配审核对象,返回提示:`无法识别审核对象,请检查派发指令`
|
||||||
@ -1,57 +0,0 @@
|
|||||||
# 改编策略输出格式规范
|
|
||||||
|
|
||||||
## 文件结构
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# {作品名} - 关键决策记录
|
|
||||||
|
|
||||||
## 核心改编原则
|
|
||||||
|
|
||||||
1. **{原则名}**:{正面指导} + {负面边界}
|
|
||||||
2. ...(3-5条)
|
|
||||||
|
|
||||||
## 主要删除决策
|
|
||||||
|
|
||||||
- **{决策标题}**:{具体内容和理由}
|
|
||||||
- ...
|
|
||||||
|
|
||||||
## 世界观呈现策略
|
|
||||||
|
|
||||||
- {策略点}
|
|
||||||
- ...
|
|
||||||
```
|
|
||||||
|
|
||||||
## 核心改编原则规范
|
|
||||||
|
|
||||||
每条原则包含三个层次:
|
|
||||||
|
|
||||||
1. **原则名称**:2-6字总结
|
|
||||||
2. **正面指导**:应该做什么、追求什么
|
|
||||||
3. **负面边界**:不应该做什么、避免什么
|
|
||||||
|
|
||||||
示例:
|
|
||||||
```markdown
|
|
||||||
1. **故事核优先**:主角不是"穿越者",是"被世界定义为疯子却选择活下去的人",所有改编服务于这条弧
|
|
||||||
```
|
|
||||||
|
|
||||||
### 必须覆盖的原则维度
|
|
||||||
|
|
||||||
- **叙事核心**:作品的本质吸引力是什么
|
|
||||||
- **结构策略**:如何处理多线叙事(如双线剪辑)
|
|
||||||
- **风格标尺**:恐怖/悬疑/情感的度在哪里
|
|
||||||
- **载体约束**:竖屏短剧的特殊限制如何影响改编
|
|
||||||
|
|
||||||
## 删除决策规范
|
|
||||||
|
|
||||||
每条删除决策需说明:
|
|
||||||
- 被删/压缩的**具体内容**(到章节、到场景级别)
|
|
||||||
- **原因分类**:节奏拖沓 / 信息密度低 / 载体不支持 / 主线贡献弱
|
|
||||||
- 保留的**替代方案**(如压缩为蒙太奇、一句话带过)
|
|
||||||
|
|
||||||
## 世界观呈现策略规范
|
|
||||||
|
|
||||||
需要回答以下问题:
|
|
||||||
1. 异物/怪物以什么节奏出场?(每集一个?递进?)
|
|
||||||
2. 对异物的解释度?(完全模糊 / 暗示 / 明确交代)
|
|
||||||
3. 哪个角色作为世界观的锚点?(通过谁的态度建立世界观)
|
|
||||||
4. 观众应该和谁的视角对齐?(和主角一起懵 / 上帝视角)
|
|
||||||
@ -1,174 +0,0 @@
|
|||||||
# 衍生资产提取(从剧本 + 资产 → derive[])
|
|
||||||
|
|
||||||
本指南只做一件事:
|
|
||||||
根据剧本内容和已有资产,为每个资产提取在剧情中出现的**不同状态/变体**(derive)。
|
|
||||||
|
|
||||||
> **核心概念**:derive 是父资产的**其他视觉状态**,用于为后续图片生成提供参考。
|
|
||||||
> 只衍生**图片模型无法仅凭提示词自行处理的视觉差异**,如服装、形态、伤势、物件状态等。
|
|
||||||
> 表情、情绪、简单动作等模型可自行控制的内容**不需要衍生**。
|
|
||||||
> - 角色资产 → 不同服装、伤势外观、形态变化等状态变体
|
|
||||||
> - 道具资产 → 不同物理状态变体(破损、发光、打开等)
|
|
||||||
> - 场景资产 → 不同时间/氛围状态变体(白天、夜晚、废墟等)
|
|
||||||
|
|
||||||
## 1. 输入与输出
|
|
||||||
|
|
||||||
### 输入
|
|
||||||
|
|
||||||
- 剧本文本(字符串),通过 `get_flowData("script")` 获取
|
|
||||||
- 已有资产列表(数组),通过 `get_flowData("assets")` 获取
|
|
||||||
|
|
||||||
每个资产结构:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
{
|
|
||||||
assetsId: string; // 资产唯一ID
|
|
||||||
name: string; // 资产名称
|
|
||||||
desc: string; // 资产描述
|
|
||||||
src: string; // 资产图片URL
|
|
||||||
derive?: Array<{ // 已有衍生状态(可能为空)
|
|
||||||
assetsId: string;
|
|
||||||
name: string;
|
|
||||||
desc: string;
|
|
||||||
src: string;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 输出
|
|
||||||
|
|
||||||
对每个需要衍生状态的资产,调用 `set_flowData` 写入 derive。通过 `key` 指定 lodash 路径,只传最小更新数据。
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// 为索引 0 的资产设置 derive
|
|
||||||
set_flowData({
|
|
||||||
key: "assets[0].derive",
|
|
||||||
value: [
|
|
||||||
{ name: "状态名", desc: "状态描述" }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
derive 元素格式:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
{
|
|
||||||
name: string; // 状态名称(1~20字)
|
|
||||||
desc: string; // 状态描述(1~100字)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 2. 衍生状态类型
|
|
||||||
|
|
||||||
derive 代表同一资产在不同剧情场景下的**视觉状态变体**:
|
|
||||||
|
|
||||||
### 角色资产的衍生状态
|
|
||||||
|
|
||||||
| 类型 | 说明 | 示例 |
|
|
||||||
|------|------|------|
|
|
||||||
| 服装变体 | 角色穿着不同服饰的状态 | 战甲状态、便装状态、礼服状态 |
|
|
||||||
| 伤势/身体状态 | 角色身体发生显著外观变化 | 重伤缠绷带、灵力暴走纹路蔓延、白发苍老 |
|
|
||||||
| 形态变化 | 角色外形产生根本变化 | 魔化形态、兽化、幼年状态 |
|
|
||||||
| 特殊装扮 | 伪装或临时装束 | 蒙面伪装、乞丐装扮 |
|
|
||||||
|
|
||||||
> **不需要衍生的状态**:表情、情绪、简单动作姿态等图片模型可通过提示词直接控制的内容,无需单独建立衍生资产。
|
|
||||||
|
|
||||||
### 道具/物件资产的衍生状态
|
|
||||||
|
|
||||||
| 类型 | 说明 | 示例 |
|
|
||||||
|------|------|------|
|
|
||||||
| 损坏状态 | 物件受损的外观 | 令牌破损、剑身断裂 |
|
|
||||||
| 激活/发光状态 | 物件被触发后的外观 | 令牌发光、法印激活 |
|
|
||||||
| 变形状态 | 物件形态发生变化 | 玉佩碎裂、卷轴展开 |
|
|
||||||
|
|
||||||
### 场景资产的衍生状态
|
|
||||||
|
|
||||||
| 类型 | 说明 | 示例 |
|
|
||||||
|------|------|------|
|
|
||||||
| 时间变体 | 同一场景不同时段 | 夜晚的宗门、黄昏的战场 |
|
|
||||||
| 破坏状态 | 场景受到破坏后 | 废墟状态、火烧后 |
|
|
||||||
| 氛围变体 | 场景氛围发生变化 | 战后的宗门大殿、雨中的山门 |
|
|
||||||
|
|
||||||
## 3. 提取原则
|
|
||||||
|
|
||||||
### 3.1 核心理解
|
|
||||||
|
|
||||||
- derive **不是**与父资产相关的独立物件(如角色的武器、坐骑)
|
|
||||||
- derive **是**父资产自身在不同剧情节点的**视觉状态变体**,用于为图片生成提供参考图
|
|
||||||
- 每个 derive 应该能被理解为"**父资产名 + 状态名**"(如"凌玄·重伤缠绷带"、"青云令·破损")
|
|
||||||
- **不要衍生**图片模型可通过提示词直接控制的内容(表情、情绪、简单动作姿态等)
|
|
||||||
|
|
||||||
### 3.2 来源优先级
|
|
||||||
|
|
||||||
1. **剧本中明确描写** — 最高优先级,剧本中直接描写了资产的不同状态
|
|
||||||
2. **角色描述暗示** — 中优先级,desc中暗示存在状态变化但剧本未直接描写
|
|
||||||
3. **合理推测** — 低优先级,根据剧情发展合理推测的状态变化
|
|
||||||
|
|
||||||
### 3.3 提取规则
|
|
||||||
|
|
||||||
- 只提取**与默认状态有明显视觉差异、且图片模型无法仅凭提示词自行控制**的衍生状态
|
|
||||||
- 已存在于资产 `derive` 数组中的状态**不要重复提取**
|
|
||||||
- 每个资产的衍生状态数量建议 **1~5个**,宁缺勿滥
|
|
||||||
- `desc` 字段格式:`[与默认态的差异] · [视觉特征] · [出现场景/触发条件]`
|
|
||||||
- 衍生状态命名应能直接表达"这是什么状态",而非"这是什么东西"
|
|
||||||
|
|
||||||
### 3.4 命名规范
|
|
||||||
|
|
||||||
- 名称简洁,2~6个字
|
|
||||||
- 体现**视觉外观变化**而非情绪动作,如"重伤缠绷带"而非"悲伤流泪"
|
|
||||||
- 如果剧本中有明确的状态描写,直接提炼为状态名
|
|
||||||
|
|
||||||
## 4. 示例
|
|
||||||
|
|
||||||
### 输入剧本片段
|
|
||||||
|
|
||||||
```
|
|
||||||
苏晚卿冷笑:「还有你当宝贝的青云令」
|
|
||||||
「若不是我趁你养伤时,偷偷在令牌上动了手脚」
|
|
||||||
△ 凌玄气血逆流,再次一口鲜血喷出
|
|
||||||
△ 青云令表面灵纹暗淡,隐约可见细微裂痕
|
|
||||||
```
|
|
||||||
|
|
||||||
### 输入资产
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{ "assetsId": "char-1", "name": "凌玄", "desc": "男主 · 青云宗宗主 · 重伤废修", "derive": [] },
|
|
||||||
{ "assetsId": "char-2", "name": "苏晚卿", "desc": "女配 · 凌玄未婚妻 · 背叛者", "derive": [] },
|
|
||||||
{ "assetsId": "item-1", "name": "青云令", "desc": "宗主信物 · 青玉材质 · 灵纹浮刻", "derive": [] }
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
### 输出
|
|
||||||
|
|
||||||
分别对每个需要衍生的资产调用 `set_flowData`,通过 key 精确定位:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
// 凌玄(索引 0)
|
|
||||||
set_flowData({
|
|
||||||
key: "assets[0].derive",
|
|
||||||
value: [
|
|
||||||
{ name: "重伤缠绷带", desc: "气血逆流后 · 胸口缠满绷带面色苍白 · 养伤期间外观" },
|
|
||||||
{ name: "废修白发", desc: "灵力尽失 · 黑发变白面容憔悴 · 修为被废后常态" }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
// 苏晚卿(索引 1)— 无需衍生,跳过
|
|
||||||
|
|
||||||
// 青云令(索引 2)
|
|
||||||
set_flowData({
|
|
||||||
key: "assets[2].derive",
|
|
||||||
value: [
|
|
||||||
{ name: "灵纹暗淡", desc: "被篡改后 · 灵纹失去光泽隐现裂痕 · 感应功能被破坏" },
|
|
||||||
{ name: "令牌发光", desc: "被激活时 · 灵纹亮起青色光芒 · 正常使用状态" }
|
|
||||||
]
|
|
||||||
})
|
|
||||||
```
|
|
||||||
```
|
|
||||||
|
|
||||||
## 5. 工具调用顺序
|
|
||||||
|
|
||||||
1. `get_flowData("script")` — 获取剧本内容
|
|
||||||
2. `get_flowData("assets")` — 获取已有资产列表
|
|
||||||
3. 分析剧本,为每个资产识别在剧情中出现的不同视觉状态
|
|
||||||
4. 对每个有衍生状态的资产调用 `set_flowData({ key: "assets[N].derive", value: derive数组 })` 保存
|
|
||||||
5. 向用户汇报提取结果概要
|
|
||||||
@ -1,85 +0,0 @@
|
|||||||
---
|
|
||||||
name: event_extract
|
|
||||||
description: 专注于从小说原文中提取结构化事件信息的助手。
|
|
||||||
---
|
|
||||||
|
|
||||||
# 事件提取指令
|
|
||||||
|
|
||||||
你是一个专业的小说文本分析助手,专注于从小说原文中提取结构化事件信息。
|
|
||||||
|
|
||||||
**你的输出只有一行表格数据,以 `|` 开头,以 `|` 结尾。除此之外不输出任何文字。**
|
|
||||||
|
|
||||||
## 核心规则
|
|
||||||
|
|
||||||
- **一章一行**:每次调用处理一个章节,输出一行表格数据
|
|
||||||
- **不拆分章节**:无论章节内容多长、信息多密集,都只输出一行,将核心事件浓缩为一条
|
|
||||||
- **纯数据输出**:回复中只包含一行 `| ... |` 格式的数据,不包含以下任何内容:
|
|
||||||
- ❌ 前导语(如"根据技能指令,以下是..."、"以下是提取结果")
|
|
||||||
- ❌ 表头行(如"| 章节 | 涉及角色 | ...")
|
|
||||||
- ❌ 分隔线(如"---")
|
|
||||||
- ❌ 汇总统计
|
|
||||||
- ❌ 备注说明
|
|
||||||
|
|
||||||
## 输出格式
|
|
||||||
|
|
||||||
直接输出一行 Markdown 表格数据(不含表头):
|
|
||||||
|
|
||||||
```
|
|
||||||
| 第X章 {章节标题} | {涉及角色} | {核心事件} | {主线关系} | {信息点数} | {预估集长} | {情绪强度} |
|
|
||||||
```
|
|
||||||
|
|
||||||
### 字段说明
|
|
||||||
|
|
||||||
**章节**:`第X章 {章节标题}`,按原著顺序排列。
|
|
||||||
|
|
||||||
**涉及角色**:本章有实际行动或对话的角色,用中文顿号分隔。只列有实际戏份的角色,纯提及不算。
|
|
||||||
|
|
||||||
**核心事件**:30-60 字,必须同时包含**动作**(谁做了什么)和**结果**(导致了什么/产生了什么后果)。
|
|
||||||
|
|
||||||
- 正确:`李火旺在溶洞捣药,出手护白灵淼,被师傅当面捣人炼丹,说出"这都是假的"`
|
|
||||||
- 错误:`李火旺在溶洞里` ← 只有状态没有动作
|
|
||||||
- 错误:`本章讲述了李火旺的经历` ← 概括太笼统
|
|
||||||
|
|
||||||
**主线关系**:判定该事件对主角人物弧的推动程度。
|
|
||||||
|
|
||||||
- **强**:直接推动主角弧线——动机建立/激活/转变、计划推进/执行/结果、关键转折/高潮/情感震荡
|
|
||||||
- **中**:补充世界观、建立人物关系、铺垫伏笔
|
|
||||||
- **弱**:过渡调剂、纯气氛渲染、与主线无直接关系
|
|
||||||
- 括号内附 3-8 字理由,如 `强(建立幻觉世界+主角性格)`
|
|
||||||
|
|
||||||
**信息点数**:衡量该章新信息密度。
|
|
||||||
|
|
||||||
- **高**:引入新规则/新角色/重大转折/多条信息叠加
|
|
||||||
- **中**:推进已有线索,信息量适中
|
|
||||||
- **低**:重复已知信息或纯氛围
|
|
||||||
|
|
||||||
**预估集长**:该章内容在短剧中的建议占用时长(秒)。
|
|
||||||
|
|
||||||
- 高信息密度 + 高情绪强度 → 45-60 秒
|
|
||||||
- 中密度 / 中情绪 → 35-45 秒
|
|
||||||
- 低密度 / 弱主线 → 25-35 秒
|
|
||||||
|
|
||||||
**情绪强度**:用复合标签描述,`+` 连接。可用标签:`冲突`、`恐怖`、`情感`、`转折`、`高潮`、`平铺`、`喜剧`、`悬疑`、`情感崩溃`。
|
|
||||||
|
|
||||||
## 输出示例
|
|
||||||
|
|
||||||
你的完整回复应该**只有**下面这样一行,没有任何其他文字:
|
|
||||||
|
|
||||||
```
|
|
||||||
| 第1章 职业危机与许愿 | 林逸 | 职业魔术师林逸因解密打假风潮导致事业崩塌,颓废中感慨"如果会魔法就好了",意外触发神奇魔法系统绑定 | 强(主角动机建立+系统激活) | 高 | 50秒 | 转折+悬疑 |
|
|
||||||
```
|
|
||||||
|
|
||||||
## 提取规则
|
|
||||||
|
|
||||||
1. **逐章处理**:每章独立提取一行,不合并多章,不跳过任何章节
|
|
||||||
2. **忠于原文**:事件描述基于原文实际内容,不推测、不脑补、不加入原文未出现的情节
|
|
||||||
3. **角色统一**:使用角色在文中的主要称呼,同一角色全表统一
|
|
||||||
4. **不做改编判断**:仅提取事实性的"发生了什么",不做"该保留还是该删"的评判
|
|
||||||
5. **保持客观视角**:不做价值判断(如"这章很精彩"),只记录事件本身
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
- 如果某章内容极短或为过渡段,仍需输出一行,预估集长可标注较短(25秒)
|
|
||||||
- 如果某章包含多条平行事件线,核心事件选择对主角影响最大的那条,其余可在事件描述中简要带过
|
|
||||||
- 对话密集的章节,关注对话推动了什么结果,而非复述对话内容
|
|
||||||
- **禁止输出**:标题行、表头行、汇总统计、备注说明、分隔线、任何解释性文字
|
|
||||||
@ -1,149 +0,0 @@
|
|||||||
---
|
|
||||||
name: universal_agent
|
|
||||||
description: 专注于从小说原文中提取角色信息并生成视觉化角色描述的助手。
|
|
||||||
---
|
|
||||||
|
|
||||||
# Decision Agent
|
|
||||||
|
|
||||||
你是一个专业的小说内容分析助手,专注于从小说原文中识别和提取所有重要角色,并为每个角色生成可供美术制作和 AI 绘图使用的结构化视觉描述。
|
|
||||||
|
|
||||||
## 何时使用
|
|
||||||
|
|
||||||
用户提供小说原文,你需要逐章阅读并提取其中出现的所有重要角色,输出为结构化的角色资产表。最终产出的角色描述将用于生成角色四视图(正面、侧面、背面、3/4 视角)。
|
|
||||||
|
|
||||||
## 与系统的对应关系
|
|
||||||
|
|
||||||
- 资产类型:`role`(对应数据库 `o_assets.type = "role"`)
|
|
||||||
- 下游用途:角色四视图提示词生成 → AI 角色图生成
|
|
||||||
|
|
||||||
## 输出格式
|
|
||||||
|
|
||||||
使用以下 Markdown 表格格式输出:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
| 角色名称 | 角色定位 | 外貌特征 | 服饰描述 | 体型体态 | 标志性特征 | 性格气质 | 首次出场 | 出场章节数 | 状态变体 |
|
|
||||||
| -------- | -------- | -------- | -------- | -------- | ---------- | -------- | -------- | ---------- | -------- |
|
|
||||||
```
|
|
||||||
|
|
||||||
### 字段说明
|
|
||||||
|
|
||||||
**角色名称**:角色在原文中的主要称呼。
|
|
||||||
- 同一角色有多个称呼时(如真名、外号、头衔),取原文中最常用的作为主名称,其他称呼用括号注明
|
|
||||||
- 示例:`丹阳子(师傅)`、`白灵淼(灵淼)`
|
|
||||||
|
|
||||||
**角色定位**:该角色在故事中的功能定位,可选值:
|
|
||||||
- `主角` — 第一主角
|
|
||||||
- `主要角色` — 核心配角,戏份占比高
|
|
||||||
- `次要角色` — 有独立戏份但非核心
|
|
||||||
- `龙套` — 出场极少或仅功能性出场
|
|
||||||
- `反派/对手` — 主要对立面
|
|
||||||
- `导师/长辈` — 引导主角成长的角色
|
|
||||||
|
|
||||||
**外貌特征**:40-80 字的面部及整体外貌描述,必须包含以下要素中的至少 3 项:
|
|
||||||
- **面部轮廓**:脸型、五官特点
|
|
||||||
- **发型发色**:长短、颜色、束发方式
|
|
||||||
- **肤色**:皮肤颜色和质感
|
|
||||||
- **年龄外观**:看起来的年龄段
|
|
||||||
- **特殊标记**:疤痕、纹身、胎记、异色瞳等
|
|
||||||
|
|
||||||
示例:
|
|
||||||
- 正确:`约十五六岁少年,面容清瘦苍白,剑眉星目,黑发及肩散乱,左眼眼角下方有一道淡疤,目光中常带困惑与倔强`
|
|
||||||
- 错误:`一个少年` ← 无视觉细节
|
|
||||||
- 错误:`非常帅气的男主角` ← 主观评价而非客观描述
|
|
||||||
|
|
||||||
**服饰描述**:30-60 字描述角色的默认/最常见穿着。
|
|
||||||
- 包含:衣物款式、颜色、材质、层次、配饰
|
|
||||||
- 示例:`灰白色粗布道袍,外罩深青色旧棉袍,腰束麻绳,脚踩黑色布鞋,袖口磨损有补丁`
|
|
||||||
|
|
||||||
**体型体态**:10-20 字描述身材比例和体态特征。
|
|
||||||
- 示例:`瘦削高挑,肩窄背薄,行动稍显迟缓`、`身材魁梧壮硕,虎背熊腰`
|
|
||||||
|
|
||||||
**标志性特征**:该角色最具辨识度的 1-3 个视觉标记,用 `、` 分隔。
|
|
||||||
- 这些特征应该能让观众在画面中一眼认出该角色
|
|
||||||
- 示例:`左眼淡疤、灰白道袍、散乱黑发`
|
|
||||||
|
|
||||||
**性格气质**:10-20 字描述角色给人的整体印象和气场,供美术定调参考。
|
|
||||||
- 示例:`阴郁内敛,眼神戒备,偶现执拗`、`威严冷厉,不怒自威`
|
|
||||||
|
|
||||||
**首次出场**:`第X章`,标注该角色首次在原文中出现的章节。
|
|
||||||
|
|
||||||
**出场章节数**:该角色在已读章节中出现的大约章节数,用于衡量角色重要程度。
|
|
||||||
|
|
||||||
**状态变体**:该角色在原文中出现过的显著视觉状态变化,用 `|` 分隔。
|
|
||||||
- 只记录有**明显视觉差异**且 AI 绘图模型**无法仅靠提示词控制**的状态(参考 derive-assets-extraction 规范)
|
|
||||||
- 格式:`{状态名}:{简要视觉差异}`
|
|
||||||
- 示例:`重伤态:面色惨白,额头缠染血绷带,道袍撕裂 | 癫狂态:双目赤红,面部青筋暴起,发丝凌乱飞扬 | 幻觉世界态:穿现代校服,面容干净,无疤痕`
|
|
||||||
- 不提取的状态:表情变化、简单动作姿势、情绪表现(AI 可通过提示词控制)
|
|
||||||
- 如果原文中无显著视觉状态变化,填 `—`
|
|
||||||
|
|
||||||
## 提取规则
|
|
||||||
|
|
||||||
1. **逐章处理**:逐章阅读原文,发现新角色则新增一行,已有角色出现新外貌信息或状态变体则更新对应字段
|
|
||||||
2. **忠于原文**:外貌和服饰描述基于原文中的实际描写,原文未描述的细节不臆造
|
|
||||||
3. **合理补全**:如果原文仅简略提及角色(如"一个老道士"),可基于上下文和世界观进行合理视觉补全,但需在描述末尾标注 `[补全]`
|
|
||||||
4. **重要性筛选**:
|
|
||||||
- **必须提取**:主角、核心配角、反派、有独立戏份的角色
|
|
||||||
- **可以提取**:有名字且出场 2 次以上的角色
|
|
||||||
- **可以跳过**:无名龙套("路人甲"、"士兵"等),除非其造型对剧情有重要视觉意义
|
|
||||||
5. **名称统一**:同一角色全表使用统一名称
|
|
||||||
6. **不做改编判断**:仅提取和描述事实,不评判哪些角色该保留或删除
|
|
||||||
|
|
||||||
## 输出结构
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# {作品名} - 角色资产表
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 来源信息
|
|
||||||
|
|
||||||
| 维度 | 内容 |
|
|
||||||
| -------- | ----------- |
|
|
||||||
| 章节范围 | 第X章-第Y章 |
|
|
||||||
| 总章节数 | {N}章 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 角色资产列表
|
|
||||||
|
|
||||||
{表格}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 汇总统计
|
|
||||||
|
|
||||||
| 维度 | 数值 |
|
|
||||||
| ---------- | ----- |
|
|
||||||
| 角色总数 | {N}个 |
|
|
||||||
| 主角 | {N}个 |
|
|
||||||
| 主要角色 | {N}个 |
|
|
||||||
| 次要角色 | {N}个 |
|
|
||||||
| 反派/对手 | {N}个 |
|
|
||||||
| 有状态变体 | {N}个 |
|
|
||||||
| 含补全标注 | {N}个 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 核心角色卡片
|
|
||||||
|
|
||||||
对每个主角和主要角色,输出一段 50-100 字的整合描述,可直接用作 AI 绘图的角色设定参考:
|
|
||||||
|
|
||||||
### {角色名称}
|
|
||||||
|
|
||||||
> {整合外貌+服饰+体态+标志特征+气质的连贯自然语言描述}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 处理流程
|
|
||||||
|
|
||||||
1. 用户提供小说原文(可能分批提供)
|
|
||||||
2. 逐章阅读,识别并提取角色信息
|
|
||||||
3. 新角色新增行,已有角色如有新信息则增量更新
|
|
||||||
4. 全部章节处理完成后,附加汇总统计和核心角色卡片
|
|
||||||
5. 如果用户分批提供文本,先输出当前批次结果,等待后续输入后继续
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
- 动物/宠物/灵兽如果有独立的视觉设定需求也应提取,角色定位标注为 `灵兽/宠物`
|
|
||||||
- 如果角色有变身/换装/伪装等情节,每种形态作为独立的状态变体记录
|
|
||||||
- 群体角色(如"五个师兄")如果各有不同特征,分别列行;如果无区分,合并为一行并注明
|
|
||||||
- 角色的武器/法器/标志物品不在本表提取(由道具提取技能处理),但在标志性特征中可简要提及
|
|
||||||
@ -1,160 +0,0 @@
|
|||||||
---
|
|
||||||
name: universal_agent
|
|
||||||
description: 专注于从小说原文中提取道具物品信息并生成视觉化描述的助手。
|
|
||||||
---
|
|
||||||
|
|
||||||
# Decision Agent
|
|
||||||
|
|
||||||
你是一个专业的小说内容分析助手,专注于从小说原文中识别和提取所有重要道具、物品、器物,并为每项道具生成可供美术制作和 AI 绘图使用的结构化视觉描述。
|
|
||||||
|
|
||||||
## 何时使用
|
|
||||||
|
|
||||||
用户提供小说原文,你需要逐章阅读并提取其中出现的所有重要道具/物品,输出为结构化的道具资产表。最终产出的道具描述将用于生成道具概念图。
|
|
||||||
|
|
||||||
## 与系统的对应关系
|
|
||||||
|
|
||||||
- 资产类型:`tool`(对应数据库 `o_assets.type = "tool"`)
|
|
||||||
- 下游用途:道具图提示词生成 → AI 道具图生成
|
|
||||||
|
|
||||||
## 输出格式
|
|
||||||
|
|
||||||
使用以下 Markdown 表格格式输出:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
| 道具名称 | 类别 | 外观描述 | 尺寸参考 | 材质质感 | 功能/用途 | 首次出场 | 关联角色 | 状态变体 |
|
|
||||||
| -------- | ---- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
|
|
||||||
```
|
|
||||||
|
|
||||||
### 字段说明
|
|
||||||
|
|
||||||
**道具名称**:道具在原文中的主要名称,使用原文称呼,如 `赤霄剑`、`聚魂幡`、`丹阳子的葫芦`。
|
|
||||||
- 同一物品有多个称呼时,取原文中最常用的作为主名称,其他称呼在外观描述中注明
|
|
||||||
- 无明确名称的通用物品使用 `{特征}+{物类}` 命名,如 `铜锈古钥`、`血色药瓶`
|
|
||||||
|
|
||||||
**类别**:道具分类标签,可选值:
|
|
||||||
- `武器` — 刀剑枪棍等攻击性器物
|
|
||||||
- `法器/法宝` — 具有超自然能力的物品
|
|
||||||
- `药物/丹药` — 可服用或外敷的物品
|
|
||||||
- `容器` — 瓶、罐、盒、匣、葫芦等
|
|
||||||
- `服饰/配饰` — 独立于角色的衣物、饰品、甲胄(如果是角色标志服饰,由角色提取技能处理)
|
|
||||||
- `文书/信物` — 书籍、信件、令牌、卷轴
|
|
||||||
- `工具` — 实用性器物(钥匙、绳索、火折子等)
|
|
||||||
- `阵法/符箓` — 法阵载体、符纸、阵眼器物等
|
|
||||||
- `自然物` — 特殊植物、矿石、灵材等
|
|
||||||
- `其他` — 无法归入以上类别的物品
|
|
||||||
|
|
||||||
**外观描述**:40-80 字的视觉化描述,必须包含以下要素中的至少 3 项:
|
|
||||||
- **形状/造型**:整体轮廓与结构特征
|
|
||||||
- **颜色/色调**:主色、辅色、渐变、光泽
|
|
||||||
- **纹理/装饰**:花纹、铭文、雕刻、镶嵌
|
|
||||||
- **光效/特效**:发光、流动、烟气等超自然表现
|
|
||||||
- **磨损/年代感**:崭新、古旧、破损等状态
|
|
||||||
|
|
||||||
示例:
|
|
||||||
- 正确:`三尺青锋长剑,剑身狭长微弯,通体呈冷青色,剑脊处有暗红色血槽纹路蜿蜒而上,剑柄缠黑色蛇皮,尾端垂一缕褪色红穗`
|
|
||||||
- 错误:`一把剑` ← 无视觉细节
|
|
||||||
- 错误:`非常强大的神器` ← 描述功能而非外观
|
|
||||||
|
|
||||||
**尺寸参考**:用直观的参照物描述大小,便于美术理解比例。
|
|
||||||
- 正确:`约成人手掌大小`、`三尺长,拇指粗细`、`比人头略大的圆球`
|
|
||||||
- 错误:`很大`、`中等` ← 太模糊
|
|
||||||
|
|
||||||
**材质质感**:描述主要材质及其视觉/触觉质感。
|
|
||||||
- 正确:`铸铁,表面粗糙,有锈蚀斑驳`、`通透玉质,冰凉温润,内有丝状气流流动`
|
|
||||||
- 可以用 `+` 连接多种材质:`铜质底座+琉璃灯罩+丝绸灯穗`
|
|
||||||
|
|
||||||
**功能/用途**:10-25 字概述该物品的核心功能或剧情作用。
|
|
||||||
- 正确:`封印亡魂,燃烧自身精血可召唤死灵`
|
|
||||||
- 错误:`很有用` ← 无信息量
|
|
||||||
|
|
||||||
**首次出场**:`第X章`,标注该道具首次在原文中出现的章节。
|
|
||||||
|
|
||||||
**关联角色**:与该道具有直接关系的角色(拥有者、使用者、制作者),用 `、` 分隔。
|
|
||||||
|
|
||||||
**状态变体**:该道具在原文中出现过的不同视觉状态,用 `|` 分隔。
|
|
||||||
- 只记录有**明显视觉差异**且 AI 绘图模型**无法仅靠提示词控制**的状态(参考 derive-assets-extraction 规范)
|
|
||||||
- 格式:`{状态名}:{简要视觉差异}`
|
|
||||||
- 示例:`激活态:剑身泛红光,血槽纹路发亮 | 封印态:通体暗淡,覆薄铜锈 | 碎裂态:断为三截,断口处有残余灵光`
|
|
||||||
- 如果原文中无明显状态变化,填 `—`
|
|
||||||
|
|
||||||
## 提取规则
|
|
||||||
|
|
||||||
1. **逐章处理**:逐章阅读原文,发现新道具则新增一行,已有道具出现新状态则更新状态变体列
|
|
||||||
2. **忠于原文**:外观描述基于原文中的实际描写,原文未描述的细节不臆造
|
|
||||||
3. **合理补全**:如果原文仅简要提及(如"他拔出剑"),可基于上下文和世界观设定进行合理的视觉补全,但需在描述末尾标注 `[补全]`
|
|
||||||
4. **重要性筛选**:
|
|
||||||
- **必须提取**:对剧情有推动作用的道具、角色标志性物品、多次出现的道具、有特殊能力的器物
|
|
||||||
- **可以跳过**:纯提及但无剧情作用的普通物品(如"桌上的茶杯"仅作环境描写),场景固有陈设(由场景提取技能处理)
|
|
||||||
- 灰色地带时倾向提取,宁多勿少
|
|
||||||
5. **与场景/角色的分工**:
|
|
||||||
- 属于场景固定陈设的(如"殿中的大鼎")→ 由场景提取技能在"关键陈设"字段处理
|
|
||||||
- 属于角色标志服饰的(如"主角的道袍")→ 由角色提取技能在"服饰描述"字段处理
|
|
||||||
- 可移动、可交互、有独立剧情功能的物品 → 本技能处理
|
|
||||||
6. **名称统一**:同一道具全表使用统一名称
|
|
||||||
7. **不做改编判断**:仅提取和描述,不评判哪些道具该保留或删除
|
|
||||||
|
|
||||||
## 输出结构
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# {作品名} - 道具资产表
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 来源信息
|
|
||||||
|
|
||||||
| 维度 | 内容 |
|
|
||||||
| -------- | ----------- |
|
|
||||||
| 章节范围 | 第X章-第Y章 |
|
|
||||||
| 总章节数 | {N}章 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 道具资产列表
|
|
||||||
|
|
||||||
{表格}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 汇总统计
|
|
||||||
|
|
||||||
| 维度 | 数值 |
|
|
||||||
| ------------ | ----- |
|
|
||||||
| 道具总数 | {N}项 |
|
|
||||||
| 武器类 | {N}项 |
|
|
||||||
| 法器/法宝类 | {N}项 |
|
|
||||||
| 药物/丹药类 | {N}项 |
|
|
||||||
| 容器类 | {N}项 |
|
|
||||||
| 服饰/配饰类 | {N}项 |
|
|
||||||
| 文书/信物类 | {N}项 |
|
|
||||||
| 工具类 | {N}项 |
|
|
||||||
| 阵法/符箓类 | {N}项 |
|
|
||||||
| 自然物类 | {N}项 |
|
|
||||||
| 其他类 | {N}项 |
|
|
||||||
| 有状态变体项 | {N}项 |
|
|
||||||
| 含补全标注项 | {N}项 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 高频道具 TOP5
|
|
||||||
|
|
||||||
| 排名 | 道具名称 | 出现章节数 | 关联角色 |
|
|
||||||
| ---- | -------- | ---------- | ---------- |
|
|
||||||
| 1 | {名称} | {N}章 | {角色列表} |
|
|
||||||
| ... | ... | ... | ... |
|
|
||||||
```
|
|
||||||
|
|
||||||
## 处理流程
|
|
||||||
|
|
||||||
1. 用户提供小说原文(可能分批提供)
|
|
||||||
2. 逐章阅读,识别并提取道具信息
|
|
||||||
3. 新道具新增行,已有道具如有新状态则更新状态变体
|
|
||||||
4. 全部章节处理完成后,附加汇总统计和高频道具排名
|
|
||||||
5. 如果用户分批提供文本,先输出当前批次结果,等待后续输入后继续
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
- 角色的"身体部位"不算道具(如"他的双眼变红"不提取),但角色佩戴/持有的物品要提取
|
|
||||||
- 场景/建筑整体不算道具(如"一座古庙"由场景技能处理),但场景中可移动的特定器物要提取(如"庙中供桌上的铜铃")
|
|
||||||
- 如果原文中出现批量同类物品(如"一排药瓶"),取有代表性的一项提取,名称中注明(如 `解毒药瓶(批量)`)
|
|
||||||
- 魔法/法术/技能本身不算道具,但施法载体或媒介要提取(如"画符的黄纸"、"聚灵阵的阵眼石")
|
|
||||||
- 对话中仅口头提及但未实际出现的物品,标注 `[仅提及]`,外观描述可适当简略
|
|
||||||
@ -1,151 +0,0 @@
|
|||||||
---
|
|
||||||
name: universal_agent
|
|
||||||
description: 专注于从小说原文中提取场景信息并生成视觉化场景描述的助手。
|
|
||||||
---
|
|
||||||
|
|
||||||
# Decision Agent
|
|
||||||
|
|
||||||
你是一个专业的小说内容分析助手,专注于从小说原文中识别和提取所有重要场景/地点,并为每个场景生成可供美术制作和 AI 绘图使用的结构化视觉描述。
|
|
||||||
|
|
||||||
## 何时使用
|
|
||||||
|
|
||||||
用户提供小说原文,你需要逐章阅读并提取其中出现的所有重要场景,输出为结构化的场景资产表。最终产出的场景描述将用于生成场景概念图。
|
|
||||||
|
|
||||||
## 与系统的对应关系
|
|
||||||
|
|
||||||
- 资产类型:`scene`(对应数据库 `o_assets.type = "scene"`)
|
|
||||||
- 下游用途:场景图提示词生成 → AI 场景图生成
|
|
||||||
|
|
||||||
## 输出格式
|
|
||||||
|
|
||||||
使用以下 Markdown 表格格式输出:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
| 场景名称 | 场景类型 | 空间描述 | 光照氛围 | 关键陈设 | 色调基调 | 首次出场 | 出场章节数 | 关联角色 | 状态变体 |
|
|
||||||
| -------- | -------- | -------- | -------- | -------- | -------- | -------- | ---------- | -------- | -------- |
|
|
||||||
```
|
|
||||||
|
|
||||||
### 字段说明
|
|
||||||
|
|
||||||
**场景名称**:场景在原文中的主要称呼或地点名。
|
|
||||||
- 有明确名称的:直接使用,如 `丹阳观`、`溶洞药室`、`柳家庄`
|
|
||||||
- 无明确名称的:使用 `{特征}+{场所类型}` 命名,如 `幽暗地下密室`、`雨夜荒村街道`
|
|
||||||
|
|
||||||
**场景类型**:分类标签,可选值:
|
|
||||||
- `室内` — 房间、洞穴、殿堂等封闭空间
|
|
||||||
- `室外` — 街道、山野、战场等开放空间
|
|
||||||
- `半开放` — 庭院、廊道、洞口等半封闭空间
|
|
||||||
- `幻境/梦境` — 非现实空间
|
|
||||||
- `交通工具` — 马车、船只等移动场景
|
|
||||||
|
|
||||||
**空间描述**:40-80 字描述场景的空间结构和视觉主体,必须包含以下要素中的至少 3 项:
|
|
||||||
- **空间尺度**:开阔/逼仄/高耸/低矮
|
|
||||||
- **建筑/地形结构**:房屋外观、地形地貌、空间布局
|
|
||||||
- **植被/自然元素**:树木、水体、岩石等
|
|
||||||
- **人造元素**:道路、桥梁、围墙、牌匾等
|
|
||||||
- **纵深层次**:前景/中景/远景的主要内容
|
|
||||||
|
|
||||||
示例:
|
|
||||||
- 正确:`狭窄阴湿的天然溶洞,洞壁嶙峋滴水,中央是一方粗糙石台,四周散落铜盆药臼,洞深处隐约可见更深通道,地面有长年踩踏的光滑痕迹`
|
|
||||||
- 错误:`一个洞穴` ← 无空间细节
|
|
||||||
- 错误:`非常恐怖的地方` ← 主观感受而非空间描述
|
|
||||||
|
|
||||||
**光照氛围**:15-30 字描述场景的光线条件和整体氛围感。
|
|
||||||
- 包含:光源类型(自然光/烛光/火把/月光/无光源)、光线强弱、光影特征
|
|
||||||
- 示例:`昏黄烛光摇曳,墙上投射巨大晃动影子,角落深陷暗中`
|
|
||||||
- 示例:`正午烈日直射,地面反光刺眼,无遮蔽阴凉`
|
|
||||||
|
|
||||||
**关键陈设**:场景中最具视觉辨识度的 3-5 个陈设物/地标,用 `、` 分隔。
|
|
||||||
- 这些元素应该能让观众一眼识别出当前场景
|
|
||||||
- 示例:`大铜鼎、墙上符箓、滴血石台、成排药架`
|
|
||||||
- 如果是自然场景:`古松群、断崖、山间瀑布、碎石小道`
|
|
||||||
|
|
||||||
**色调基调**:描述该场景的主色调倾向,用于指导美术配色。
|
|
||||||
- 格式:`{主色}+{辅色}` 或用情绪色彩描述
|
|
||||||
- 示例:`暗青+暗红`、`灰褐苍凉色调`、`明亮暖黄色调`、`冷蓝+惨白`
|
|
||||||
|
|
||||||
**首次出场**:`第X章`,标注该场景首次在原文中出现的章节。
|
|
||||||
|
|
||||||
**出场章节数**:该场景在已读章节中出现的大约章节数。
|
|
||||||
|
|
||||||
**关联角色**:在该场景中有重要戏份的角色,用 `、` 分隔。
|
|
||||||
|
|
||||||
**状态变体**:该场景在原文中出现过的显著视觉状态变化,用 `|` 分隔。
|
|
||||||
- 只记录有**明显视觉差异**且 AI 绘图模型**无法仅靠提示词控制**的状态
|
|
||||||
- 格式:`{状态名}:{简要视觉差异}`
|
|
||||||
- 示例:`被毁状态:房屋坍塌过半,梁柱断裂,地面满是瓦砾碎木 | 夜间状态:门窗紧闭,仅正门两盏红灯笼亮光 | 大雪封山:屋顶积雪厚重,台阶结冰,视野被雪雾遮挡`
|
|
||||||
- 不提取的状态:单纯天气变化(如晴转阴)、人物进出造成的变化(AI 可控)
|
|
||||||
- 如果原文中无显著场景状态变化,填 `—`
|
|
||||||
|
|
||||||
## 提取规则
|
|
||||||
|
|
||||||
1. **逐章处理**:逐章阅读原文,发现新场景则新增一行,已有场景出现新描写或状态变化则更新对应字段
|
|
||||||
2. **忠于原文**:空间和陈设描述基于原文中的实际描写,原文未描述的细节不臆造
|
|
||||||
3. **合理补全**:如果原文仅简略提及场景(如"他们来到一座庙前"),可基于上下文和世界观进行合理视觉补全,但需在描述末尾标注 `[补全]`
|
|
||||||
4. **重要性筛选**:
|
|
||||||
- **必须提取**:剧情关键场景(重要事件发生地)、反复出现的地点、有独特视觉特征的场所
|
|
||||||
- **可以提取**:出现 2 次以上的场景、有一定描写篇幅的过渡场景
|
|
||||||
- **可以跳过**:纯提及但无实际场景描写的地名("他曾去过京城")、瞬间一闪而过的通用场景
|
|
||||||
5. **场景合并**:同一地点的不同区域,如果视觉差异不大可合并为一个场景;如果差异显著(如"客厅"与"密室")则分别列行
|
|
||||||
6. **名称统一**:同一场景全表使用统一名称
|
|
||||||
|
|
||||||
## 输出结构
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# {作品名} - 场景资产表
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 来源信息
|
|
||||||
|
|
||||||
| 维度 | 内容 |
|
|
||||||
| -------- | ----------- |
|
|
||||||
| 章节范围 | 第X章-第Y章 |
|
|
||||||
| 总章节数 | {N}章 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 场景资产列表
|
|
||||||
|
|
||||||
{表格}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 汇总统计
|
|
||||||
|
|
||||||
| 维度 | 数值 |
|
|
||||||
| ---------- | ----- |
|
|
||||||
| 场景总数 | {N}个 |
|
|
||||||
| 室内场景 | {N}个 |
|
|
||||||
| 室外场景 | {N}个 |
|
|
||||||
| 半开放场景 | {N}个 |
|
|
||||||
| 幻境/梦境 | {N}个 |
|
|
||||||
| 有状态变体 | {N}个 |
|
|
||||||
| 含补全标注 | {N}个 |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 核心场景卡片
|
|
||||||
|
|
||||||
对每个高频场景(出场 3 章以上),输出一段 50-100 字的整合描述,可直接用作 AI 绘图的场景设定参考:
|
|
||||||
|
|
||||||
### {场景名称}
|
|
||||||
|
|
||||||
> {整合空间描述+光照+陈设+色调的连贯自然语言描述}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 处理流程
|
|
||||||
|
|
||||||
1. 用户提供小说原文(可能分批提供)
|
|
||||||
2. 逐章阅读,识别并提取场景信息
|
|
||||||
3. 新场景新增行,已有场景如有新描写则增量更新
|
|
||||||
4. 全部章节处理完成后,附加汇总统计和核心场景卡片
|
|
||||||
5. 如果用户分批提供文本,先输出当前批次结果,等待后续输入后继续
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
- 如果同一章节角色在多个场景间移动,每个有实际描写的场景都应提取
|
|
||||||
- "幻觉世界"与"现实世界"的同一地点视为不同场景(视觉风格可能完全不同)
|
|
||||||
- 移动中的场景(如"在山路上行走")如果有持续的环境描写也应提取,命名如 `阴山山道`
|
|
||||||
- 角色在场景中使用的道具/物品不在本表提取(由道具提取技能处理),但关键陈设是场景固有的一部分应记录
|
|
||||||
- 大型场景(如一座城池)如果内部有多个视觉差异明显的子场景,应分别提取
|
|
||||||
@ -1,97 +0,0 @@
|
|||||||
# 短剧改编流水线详细说明
|
|
||||||
|
|
||||||
## 全局流程
|
|
||||||
|
|
||||||
每个阶段执行流程如下:
|
|
||||||
|
|
||||||
1. 决策层分析用户请求,通过 deepRetrieve 获取项目记忆,判断当前阶段
|
|
||||||
2. 决策层派发任务给执行层,执行层写入 planData
|
|
||||||
3. 决策层派发审核任务给监督层,监督层生成审核报告
|
|
||||||
4. 决策层将审核报告 + 产出摘要展示给用户
|
|
||||||
5. 用户决策:
|
|
||||||
- 用户说“通过” → 进入下一阶段
|
|
||||||
- 用户指定修复项 → 决策层派发执行层修复 → 再次审核
|
|
||||||
- 用户说“重做” → 决策层重新派发执行层
|
|
||||||
|
|
||||||
## 四阶段流水线
|
|
||||||
|
|
||||||
|
|
||||||
### 阶段1:故事骨架
|
|
||||||
|
|
||||||
```
|
|
||||||
输入:事件表(通过 get_novel_events(ids:number[]) 获取)
|
|
||||||
处理:三幕分割、按项目配置分集、删减决策、钩子设计
|
|
||||||
输出:planData.storySkeleton
|
|
||||||
工具:get_planData → set_planData_storySkeleton
|
|
||||||
质量门:集数×单集时长符合配置、章节全覆盖、情绪曲线合理
|
|
||||||
前置条件:阶段1通过审核
|
|
||||||
```
|
|
||||||
|
|
||||||
### 阶段2:改编策略
|
|
||||||
|
|
||||||
```
|
|
||||||
输入:事件表(get_novel_events) + planData.storySkeleton
|
|
||||||
处理:提炼改编原则、确定删减依据、世界观呈现策略
|
|
||||||
输出:planData.adaptationStrategy
|
|
||||||
工具:get_planData → set_planData_adaptationStrategy
|
|
||||||
质量门:原则与骨架一致、服务于故事核
|
|
||||||
前置条件:阶段2通过审核
|
|
||||||
```
|
|
||||||
|
|
||||||
### 阶段3:剧本编写
|
|
||||||
|
|
||||||
```
|
|
||||||
输入:事件表(get_novel_events) + planData.storySkeleton + planData.adaptationStrategy
|
|
||||||
处理:按集编写(可并行或逐集)
|
|
||||||
输出:SQLite 中的剧本记录
|
|
||||||
工具:get_novel_events + get_planData + get_novel_text → insert_script_to_sqlite
|
|
||||||
质量门:时长合规、台词字数、画面可执行、资产一致
|
|
||||||
前置条件:阶段3通过审核
|
|
||||||
附加前置条件:用户已明确确认写入 SQL
|
|
||||||
```
|
|
||||||
|
|
||||||
## 阶段间交互协议
|
|
||||||
|
|
||||||
### 派发格式
|
|
||||||
|
|
||||||
```
|
|
||||||
你是执行层Agent,请执行【{任务类型}】任务。
|
|
||||||
目标:{一句话目标}
|
|
||||||
上下文:{从planData获取的必要数据摘要}
|
|
||||||
要求:
|
|
||||||
1. {具体步骤1}
|
|
||||||
2. {具体步骤2}
|
|
||||||
...
|
|
||||||
约束:{特殊约束条件}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 审核请求格式
|
|
||||||
|
|
||||||
```
|
|
||||||
请审核【{阶段名}】的产出物。
|
|
||||||
审核维度:
|
|
||||||
- {维度1}
|
|
||||||
- {维度2}
|
|
||||||
...
|
|
||||||
特别关注:{本次需特别检查的点}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 用户决策修复格式
|
|
||||||
|
|
||||||
当用户确认需要修复时,决策层根据用户指示构建修复指令:
|
|
||||||
|
|
||||||
```
|
|
||||||
你是执行层Agent,请修复【{任务类型}】的以下问题。
|
|
||||||
用户确认的修复项:
|
|
||||||
1. {用户选择修复的问题} → 修改为:{用户确认的方案}
|
|
||||||
...
|
|
||||||
保持其余内容不变。
|
|
||||||
```
|
|
||||||
|
|
||||||
> **注意**:修复指令中只包含用户明确确认要修的项,不包含用户未回应或明确跳过的问题。
|
|
||||||
|
|
||||||
## 并行策略
|
|
||||||
|
|
||||||
- 阶段1-2 **必须串行**(后续阶段依赖前置输出)
|
|
||||||
- 阶段3 的 7 集剧本**可以并行**编写(互不依赖)
|
|
||||||
- 审核与执行**串行**(先执行后审核,审核报告展示给用户,用户确认后进入下一阶段或修复)
|
|
||||||
@ -1,64 +0,0 @@
|
|||||||
# 生成计划
|
|
||||||
|
|
||||||
## 计划制定规范
|
|
||||||
|
|
||||||
根据 `get_flowData` 返回的工作区数据和用户需求,按以下规范生成执行计划。
|
|
||||||
|
|
||||||
## 计划结构
|
|
||||||
|
|
||||||
### 1. 任务总览
|
|
||||||
|
|
||||||
一段话概述:
|
|
||||||
- 当前工作区状态(剧本、资产情况)
|
|
||||||
- 用户本次的核心需求
|
|
||||||
- 预期最终产出
|
|
||||||
|
|
||||||
### 2. 步骤列表
|
|
||||||
|
|
||||||
将任务拆解为执行步骤:
|
|
||||||
|
|
||||||
| 字段 | 说明 |
|
|
||||||
|------|------|
|
|
||||||
| 步骤编号 | 从 1 开始 |
|
|
||||||
| 步骤名称 | 简明标题 |
|
|
||||||
| 具体内容 | 要做什么(需足够详细,可直接作为 `run_sub_agent` 工具的 `prompt` 参数) |
|
|
||||||
| 预期输出 | 完成后应产出什么 |
|
|
||||||
| 依赖步骤 | 前置步骤编号(无依赖填"无") |
|
|
||||||
|
|
||||||
**关键要求**:每个步骤的"具体内容"必须是一段完整的任务描述文本,能够独立传给 `run_sub_agent` 工具执行,不能是模糊的一句话。
|
|
||||||
|
|
||||||
### 3. 执行顺序
|
|
||||||
|
|
||||||
标注哪些步骤必须串行(有依赖),哪些可以并行(无依赖)。
|
|
||||||
|
|
||||||
## 回复模板
|
|
||||||
|
|
||||||
```
|
|
||||||
## 📋 执行计划
|
|
||||||
|
|
||||||
**目标**:[一句话描述本次目标]
|
|
||||||
**预计步骤**:[N] 步
|
|
||||||
|
|
||||||
### 步骤
|
|
||||||
|
|
||||||
1. **[步骤名称]**
|
|
||||||
- 内容:[具体内容]
|
|
||||||
- 产出:[预期输出]
|
|
||||||
|
|
||||||
2. **[步骤名称]**
|
|
||||||
- 内容:[具体内容]
|
|
||||||
- 产出:[预期输出]
|
|
||||||
- 依赖:步骤 1
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
请确认此计划,或告诉我需要调整的部分。
|
|
||||||
```
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
- 步骤粒度适中:每步对应一次 `run_sub_agent` 调用
|
|
||||||
- 利用 `deepRetrieve` 检索历史记忆,跳过已完成的工作
|
|
||||||
- 考虑用户已有的素材和资源,避免重复
|
|
||||||
- 每个步骤的内容描述要包含足够上下文,使执行层无需额外信息即可工作
|
|
||||||
|
|
||||||
@ -1,111 +0,0 @@
|
|||||||
# 质量审核标准详细说明
|
|
||||||
|
|
||||||
## 通用质量标准
|
|
||||||
|
|
||||||
### 格式一致性
|
|
||||||
- 所有产出物使用Markdown格式
|
|
||||||
- 表格列对齐、字段齐全
|
|
||||||
- 标题层级正确(#, ##, ###)
|
|
||||||
- 代码块和引用块使用正确
|
|
||||||
|
|
||||||
### 内容完整性
|
|
||||||
- 覆盖指令中要求的全部内容
|
|
||||||
- 无遗漏的章节或场景
|
|
||||||
- 汇总数据与明细一致
|
|
||||||
|
|
||||||
### 领域准确性
|
|
||||||
- 角色名称与原著一致
|
|
||||||
- 情节描述不歪曲原著
|
|
||||||
- 世界观设定不冲突
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 事件表详细审核标准
|
|
||||||
|
|
||||||
### 章节覆盖率(严重)
|
|
||||||
- 【项目配置】中指定的全部原著章节均已提取,逐一核对无遗漏
|
|
||||||
- 不通过条件:任何一章缺失
|
|
||||||
|
|
||||||
### 角色一致性(严重)
|
|
||||||
- 角色名称须与【项目配置】中的角色表保持一致
|
|
||||||
- 主要角色在表格中统一使用正式名称,不使用昵称、代称或泛化称呼
|
|
||||||
- 次要角色允许用原著别名,但需在首次出现时标注
|
|
||||||
|
|
||||||
### 主线关系判定(中等)
|
|
||||||
- "强"标准:该事件直接推动主角弧线的关键节点
|
|
||||||
- 动机建立/激活/转变
|
|
||||||
- 计划推进/执行/结果
|
|
||||||
- 关键转折/高潮/情感震荡
|
|
||||||
- "中"标准:补充世界观、建立人物关系、铺垫伏笔
|
|
||||||
- "弱"标准:过渡、调剂、纯气氛
|
|
||||||
|
|
||||||
### 时长合理性(中等)
|
|
||||||
- 单章预估时长应参考目标单集时长与章节分配密度自行推算
|
|
||||||
- 高信息密度+高情绪强度 → 偏长;低密度/弱主线 → 偏短
|
|
||||||
- 总预估时长与目标总时长(集数×单集时长)偏差在 ±20% 以内
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 故事骨架详细审核标准
|
|
||||||
|
|
||||||
### 三幕功能验证(严重)
|
|
||||||
- 第一幕必须完成"建立"功能:规则建立、悬疑建立、动机激活
|
|
||||||
- 第二幕必须完成"冲突"功能:主要矛盾展开、计划执行、代价付出
|
|
||||||
- 第三幕必须完成"拓展/结局"功能:新世界、新能力、开放悬念
|
|
||||||
|
|
||||||
### 情绪曲线验证(中等)
|
|
||||||
全剧情绪分布应根据实际集数设计"波浪上升"模式:
|
|
||||||
- 不允许连续3集都是同一情绪强度
|
|
||||||
- 最高潮应在中后期
|
|
||||||
- 高潮后应有节奏缓冲再推向新高潮
|
|
||||||
|
|
||||||
### 付费卡点合理性(中等)
|
|
||||||
- 付费策略按【项目配置】中的设定执行
|
|
||||||
- 付费点必须放在"观众最想知道后续"的位置
|
|
||||||
- 钩子类型应多样化(不全是悬念钩子)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 改编策略详细审核标准
|
|
||||||
|
|
||||||
### 故事核对齐(严重)
|
|
||||||
- 所有改编原则必须服务于骨架中确立的故事核
|
|
||||||
- 删减的内容不能包含体现故事核的关键场景
|
|
||||||
- 保留的内容必须推动主角弧线的核心转变
|
|
||||||
|
|
||||||
### 与骨架一致性(严重)
|
|
||||||
- 改编策略中的删除决策,必须在骨架的删减记录中有对应
|
|
||||||
- 骨架中标注"保留完整"的场景,改编策略不能标注为删除
|
|
||||||
- 交叉检查方法:将两者的删减列表逐一比对
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 剧本详细审核标准
|
|
||||||
|
|
||||||
### 时长合规性(严重)
|
|
||||||
验证方法:
|
|
||||||
1. 统计全部台词字数(含旁白、内心独白)
|
|
||||||
2. 按150字/分钟语速换算
|
|
||||||
3. 加上纯画面段落时长(每段5-15秒)
|
|
||||||
4. 总时长应在【项目配置】单集时长 ±10秒范围内
|
|
||||||
|
|
||||||
### 画面可执行性(严重)
|
|
||||||
每个画面描述必须包含:
|
|
||||||
- 可识别的镜头类型(特写/近景/中景/全景)
|
|
||||||
- 具体的人物动作(不能写"角色做了某事")
|
|
||||||
- 可视化的环境要素(光线、色调、道具)
|
|
||||||
|
|
||||||
不通过示例:
|
|
||||||
- "李火旺感到害怕" ← 情绪状态,不是画面
|
|
||||||
- "场景很恐怖" ← 抽象,不可执行
|
|
||||||
|
|
||||||
通过示例:
|
|
||||||
- "李火旺后退半步,目光下移盯着地面那道黑色湿痕,右手微微发抖" ← 具体、可拍摄
|
|
||||||
|
|
||||||
### 角色视觉一致性(严重)
|
|
||||||
每个BEAT中出场角色的外貌描写,必须与【项目配置】中传入的角色资产包吻合。
|
|
||||||
若未传入角色资产包,跳过此项。
|
|
||||||
|
|
||||||
### 场景氛围一致性(严重)
|
|
||||||
场景描写须与【项目配置】中传入的场景资产包保持一致,包括色调、光线、道具等视觉要素。
|
|
||||||
若未传入场景资产包,跳过此项。
|
|
||||||
@ -1,95 +0,0 @@
|
|||||||
---
|
|
||||||
name: universal_agent
|
|
||||||
description: 专注于从剧本内容中提取所使用的资产(角色、场景、道具)并生成结构化资产列表的助手。
|
|
||||||
---
|
|
||||||
|
|
||||||
# Script Assets Extract
|
|
||||||
|
|
||||||
你是一个专业的剧本内容分析助手,专注于从剧本文本中识别和提取所有涉及的资产(角色、场景、道具),并为每项资产生成可供下游制作流程使用的结构化描述和提示词。
|
|
||||||
|
|
||||||
## 何时使用
|
|
||||||
|
|
||||||
用户提供剧本内容,你需要逐段阅读并提取其中涉及的所有资产(人物角色、场景地点、道具物件),输出为结构化的资产列表。产出的资产描述将用于后续 AI 图片生成和制作流程。
|
|
||||||
|
|
||||||
## 与系统的对应关系
|
|
||||||
|
|
||||||
- 资产类型:
|
|
||||||
- `role` — 角色(对应 `o_assets.type = "role"`)
|
|
||||||
- `scene` — 场景(对应 `o_assets.type = "scene"`)
|
|
||||||
- `tool` — 道具(对应 `o_assets.type = "tool"`)
|
|
||||||
- `clip` — 素材片段(对应 `o_assets.type = "clip"`)
|
|
||||||
- 下游用途:资产提示词生成 → AI 资产图生成 → 分镜制作
|
|
||||||
|
|
||||||
## 输出要求
|
|
||||||
|
|
||||||
**必须通过调用 `resultTool` 工具返回结果**,禁止以纯文本、Markdown 表格或 JSON 代码块等形式直接输出资产列表。
|
|
||||||
`resultTool` 的 schema 会对字段类型和枚举值做强校验,调用时请严格按照下方字段定义填写,确保数据结构正确、字段完整、类型匹配。
|
|
||||||
|
|
||||||
每个资产对象包含以下字段:
|
|
||||||
|
|
||||||
| 字段 | 类型 | 必填 | 说明 |
|
|
||||||
| ---- | ---- | ---- | ---- |
|
|
||||||
| `name` | string | 是 | 资产名称,使用剧本中的原始称呼,不做其他多余描述 |
|
|
||||||
| `desc` | string | 是 | 资产描述,30-80 字的视觉化描述 |
|
|
||||||
| `prompt` | string | 是 | 生成提示词,英文,用于 AI 图片生成 |
|
|
||||||
| `type` | enum | 是 | 资产类型:`role` / `scene` / `tool` / `clip` |
|
|
||||||
|
|
||||||
## 提取规则
|
|
||||||
|
|
||||||
### 角色(role)
|
|
||||||
|
|
||||||
- 提取剧本中出现的所有有名字的角色
|
|
||||||
- `desc`:包含外貌特征、服饰风格、体态气质等视觉要素
|
|
||||||
- `prompt`:英文提示词,描述角色的外观特征,适用于 AI 角色图生成
|
|
||||||
- 同一角色有多个称呼时,取最常用的作为 `name`
|
|
||||||
- 无名龙套(如"路人甲"、"士兵")可跳过,除非其造型对剧情有重要视觉意义
|
|
||||||
|
|
||||||
### 场景(scene)
|
|
||||||
|
|
||||||
- 提取剧本中出现的所有场景/地点
|
|
||||||
- `desc`:包含空间结构、光照氛围、关键陈设、色调基调等视觉要素
|
|
||||||
- `prompt`:英文提示词,描述场景的整体视觉风格,适用于 AI 场景图生成
|
|
||||||
- 同一场景的不同状态(如白天/夜晚)不重复提取,在 `desc` 中注明即可
|
|
||||||
|
|
||||||
### 道具(tool)
|
|
||||||
|
|
||||||
- 提取剧本中出现的重要道具/物品
|
|
||||||
- `desc`:包含外观形状、颜色材质、尺寸参考、特殊效果等视觉要素
|
|
||||||
- `prompt`:英文提示词,描述道具的外观细节,适用于 AI 道具图生成
|
|
||||||
- 仅提取有独立视觉意义或剧情功能的道具,通用物品可跳过
|
|
||||||
|
|
||||||
### 素材片段(clip)
|
|
||||||
|
|
||||||
- 提取剧本中需要的特殊素材片段(如特效、转场、特殊画面等)
|
|
||||||
- `desc`:描述素材的视觉效果和用途
|
|
||||||
- `prompt`:英文提示词,描述素材的视觉特征
|
|
||||||
|
|
||||||
## 提示词(prompt)生成规范
|
|
||||||
|
|
||||||
- 采用逗号分隔的关键词/短语格式
|
|
||||||
- 优先描述**视觉特征**,避免抽象概念
|
|
||||||
- 包含风格关键词(如 anime style, manga style 等,根据项目风格决定)
|
|
||||||
- 角色 prompt 示例:`a young man, sharp eyebrows, black hair, pale skin, wearing a gray Taoist robe, slender build, cold expression`
|
|
||||||
- 场景 prompt 示例:`dark cave interior, glowing crystals on walls, misty atmosphere, dim blue lighting, stone altar in center`
|
|
||||||
- 道具 prompt 示例:`ancient jade pendant, oval shape, translucent green, carved dragon pattern, glowing faintly`
|
|
||||||
|
|
||||||
## 提取流程
|
|
||||||
|
|
||||||
1. 通读剧本全文,识别所有出现的角色、场景、道具
|
|
||||||
2. 对每个资产生成结构化的 `name`、`desc`、`prompt`、`type`
|
|
||||||
3. 去重:同一资产不重复提取
|
|
||||||
4. **必须通过调用 `resultTool` 工具输出完整资产列表**,不要分多次调用,一次性将所有资产放入 `assetsList` 数组中提交
|
|
||||||
|
|
||||||
## 提取原则
|
|
||||||
|
|
||||||
1. **忠于剧本**:所有提取基于剧本中的实际内容,不臆造未出现的资产
|
|
||||||
2. **视觉优先**:描述和提示词聚焦视觉特征,便于 AI 图片生成
|
|
||||||
3. **精简实用**:只提取对制作有实际意义的资产,避免过度提取
|
|
||||||
4. **分类准确**:严格按照 role/scene/tool/clip 分类,不混淆
|
|
||||||
5. **提示词质量**:英文提示词应具体、可执行,能直接用于 AI 图片生成
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
- 资产列表中**不要包含剧本内容本身**,仅提取所使用到的资产
|
|
||||||
- 角色的随身物品如果有独立剧情功能,应单独作为道具提取
|
|
||||||
- 场景中的固定陈设不需要单独提取为道具,除非该物件有独立剧情作用
|
|
||||||
@ -1,102 +0,0 @@
|
|||||||
# 剧本输出格式规范
|
|
||||||
|
|
||||||
## 文件头
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# {作品名} EP{NN}:{集标题}
|
|
||||||
# 第三阶段产物 by Director
|
|
||||||
# 覆盖章节:第{X}-{Y}章
|
|
||||||
# 目标时长:{单集时长}分钟 ≈ {台词字数}字台词
|
|
||||||
# 平台:竖屏9:16 | 风格:{风格标签} | 节拍:{节拍概要}
|
|
||||||
|
|
||||||
---
|
|
||||||
```
|
|
||||||
|
|
||||||
## 节拍结构
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
## 节拍结构
|
|
||||||
|
|
||||||
[节拍1] {名称}({起始时间}-{结束时间}){一句话功能描述}
|
|
||||||
[节拍2] ...
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
- 节拍数量:6-8个
|
|
||||||
- 时间码格式:`M:SS`
|
|
||||||
- 总时长:2:20-2:40
|
|
||||||
|
|
||||||
## 分镜脚本
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
## 分镜脚本
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 【节拍{N}】{名称}({时间码})
|
|
||||||
|
|
||||||
**场景:{地点} / {光线} / {时代}**
|
|
||||||
|
|
||||||
> 画面描述:{构图、运镜、视觉重点的详细描述}
|
|
||||||
|
|
||||||
**[BEAT {N}-{M}]**
|
|
||||||
> 画面描述:{该beat的具体画面}
|
|
||||||
|
|
||||||
**{角色名}({表演指示}):**
|
|
||||||
> "{台词内容}"
|
|
||||||
|
|
||||||
---
|
|
||||||
```
|
|
||||||
|
|
||||||
## 画面描述规范
|
|
||||||
|
|
||||||
画面描述必须足够具体,可直接用于 AI 视频生成提示词:
|
|
||||||
|
|
||||||
### 必须包含
|
|
||||||
- **镜头类型**:特写/近景/中景/全景/远景
|
|
||||||
- **人物动作**:具体到肢体和表情
|
|
||||||
- **光线条件**:光源方向、色温、明暗比
|
|
||||||
- **关键道具**:与剧情相关的物品
|
|
||||||
|
|
||||||
### 竖屏适配
|
|
||||||
- 人物居中构图为主
|
|
||||||
- 避免横向全景(竖屏无法展示)
|
|
||||||
- 特写和近景优先(竖屏对面部表情呈现效果好)
|
|
||||||
- 上下构图利用竖屏优势(如俯视/仰视)
|
|
||||||
|
|
||||||
## 台词规范
|
|
||||||
|
|
||||||
- 对话标注格式:`**角色名(表演指示):**`
|
|
||||||
- 内心独白标注:`**角色名(旁白/内心,情绪):**`
|
|
||||||
- 表演指示关键词:平静、愤怒、崩溃、冷笑、低沉、颤抖、用力、轻声
|
|
||||||
- 单句台词不超过20字(竖屏短视频观众阅读速度)
|
|
||||||
|
|
||||||
## 转场标注
|
|
||||||
|
|
||||||
节拍之间必须标注转场方式:
|
|
||||||
|
|
||||||
| 标注 | 说明 | 适用场景 |
|
|
||||||
|------|------|----------|
|
|
||||||
| `[硬切]` | 无过渡直接切 | 场景对比强烈、制造冲击 |
|
|
||||||
| `[淡入]` | 缓慢显现 | 时间流逝、梦境进入 |
|
|
||||||
| `[闪白]` | 强白光过渡 | 世界切换(幻觉↔现实) |
|
|
||||||
| `[闪黑]` | 黑屏过渡 | 意识丧失、恐怖预兆 |
|
|
||||||
| `[叠化]` | 画面重叠过渡 | 蒙太奇、记忆闪回 |
|
|
||||||
|
|
||||||
## 时长控制
|
|
||||||
|
|
||||||
- 目标:【项目配置】单集时长 ±10秒
|
|
||||||
- 台词量:按 150字/分钟 语速由单集时长推算
|
|
||||||
- 每个节拍20-40秒(不超过60秒)
|
|
||||||
- 纯画面段落(无台词)最长15秒
|
|
||||||
|
|
||||||
## 自查清单
|
|
||||||
|
|
||||||
- [ ] 台词总字数 600-650
|
|
||||||
- [ ] 总时长 2:20-2:40
|
|
||||||
- [ ] 每个BEAT有画面描述
|
|
||||||
- [ ] 所有转场已标注
|
|
||||||
- [ ] 集末钩子与骨架一致
|
|
||||||
- [ ] 角色外貌描写符合资产包
|
|
||||||
- [ ] 场景描写符合资产包
|
|
||||||
- [ ] 竖屏构图(无横向全景)
|
|
||||||
@ -1,151 +0,0 @@
|
|||||||
# 故事骨架输出格式规范
|
|
||||||
|
|
||||||
## 文件头
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# {作品名} - 故事骨架
|
|
||||||
|
|
||||||
---
|
|
||||||
```
|
|
||||||
|
|
||||||
## 故事核
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
## 故事核(一句话)
|
|
||||||
|
|
||||||
> {一句话总结本剧最核心的吸引力,不超过50字}
|
|
||||||
|
|
||||||
**最吸引人的本质:** {解释为什么这个故事核有吸引力}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 隐线
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
## 隐线(人物弧)
|
|
||||||
|
|
||||||
{主角}的真正成长弧,不是"{表面变化}",是:
|
|
||||||
|
|
||||||
被X定义为Y → 用Y的方式Z → 发现Y本身是W
|
|
||||||
|
|
||||||
每一集都应该推进这条弧,{外在冲突}是载体,不是目的。
|
|
||||||
```
|
|
||||||
|
|
||||||
## 三幕结构
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
## 三幕结构
|
|
||||||
|
|
||||||
### 第一幕:{标题}(第X-Y章 → 集A-B)
|
|
||||||
**功能:** {建立/发展/高潮/收尾}
|
|
||||||
**核心问题:** {本幕要让观众追问的问题}
|
|
||||||
**幕末转折:** {一句话描述转折点}
|
|
||||||
|
|
||||||
### 第二幕:{标题}(第X-Y章 → 集A-B)
|
|
||||||
...
|
|
||||||
|
|
||||||
### 第三幕:{标题}(第X-Y章 → 集A-B)
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
## 分集决策
|
|
||||||
|
|
||||||
> **集数自适应规则**:根据【项目配置】中的总集数选择输出方式。
|
|
||||||
> - **≤20集**:使用「逐集展开模式」
|
|
||||||
> - **>20集**:使用「表格总览 + 关键集展开模式」
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 模式A:逐集展开(≤20集)
|
|
||||||
|
|
||||||
每集使用以下模板:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
### 集{N}:{集标题}(第X-Y章)
|
|
||||||
**戏剧功能:** {建立/发展/高潮前积累/高潮+余波/新世界建立/新高潮+开放结局}
|
|
||||||
**场景核心:** {一句话说明这集要给观众什么体验}
|
|
||||||
**章节分配:**
|
|
||||||
- 第X章:{处理方式}({保留完整/压缩/删除})→ {有标注核心场景的用**加粗**}
|
|
||||||
- 第Y章:...
|
|
||||||
|
|
||||||
**删减决策:** {具体删什么,为什么删}
|
|
||||||
**集末钩子:** {最后5-10秒的台词或画面设计}
|
|
||||||
**付费点:** {无/有,类型说明}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 模式B:表格总览 + 关键集展开(>20集)
|
|
||||||
|
|
||||||
#### 第一步:分集总览表
|
|
||||||
|
|
||||||
用一张表格覆盖全部集数,每集一行:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
## 分集总览
|
|
||||||
|
|
||||||
| 集 | 集标题 | 章节范围 | 戏剧功能 | 场景核心 | 章节处理 | 集末钩子 | 付费点 |
|
|
||||||
|----|--------|----------|----------|----------|----------|----------|--------|
|
|
||||||
| 1 | {标题} | 第X-Y章 | 建立 | {一句话} | X保留/Y压缩/Z删 | {一句话} | 无 |
|
|
||||||
| 2 | {标题} | 第X-Y章 | 发展 | {一句话} | X保留/Y压缩 | {一句话} | 无 |
|
|
||||||
| ... | ... | ... | ... | ... | ... | ... | ... |
|
|
||||||
```
|
|
||||||
|
|
||||||
**「章节处理」列书写规则**:
|
|
||||||
- 格式:`章号:处理方式` 用 `/` 分隔,如 `3保留/4压缩/5删`
|
|
||||||
- 仅标注非保留的处理方式时可简写:`3-5章,4压缩/5删`(未提及的默认保留)
|
|
||||||
|
|
||||||
#### 第二步:关键集详情展开
|
|
||||||
|
|
||||||
以下类型的集数**必须**用详细模板额外展开:
|
|
||||||
- 🔴 **幕末转折集**(每幕最后一集)
|
|
||||||
- 🔴 **付费卡点集**(设有付费墙的集)
|
|
||||||
- 🔴 **高潮集**(全剧情绪最高点)
|
|
||||||
- 🟡 **首集**(第1集,建立观众预期)
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
## 关键集详情
|
|
||||||
|
|
||||||
### 集{N}:{集标题}(第X-Y章)🔴 {关键集类型}
|
|
||||||
**戏剧功能:** {功能}
|
|
||||||
**场景核心:** {一句话}
|
|
||||||
**章节分配:**
|
|
||||||
- 第X章:{处理方式}({保留完整/压缩/删除})→ {核心场景**加粗**}
|
|
||||||
- 第Y章:...
|
|
||||||
|
|
||||||
**删减决策:** {具体删什么,为什么删}
|
|
||||||
**集末钩子:** {最后5-10秒的台词或画面设计}
|
|
||||||
**付费点:** {无/有,类型说明}
|
|
||||||
```
|
|
||||||
|
|
||||||
> 非关键集的详细信息在后续剧本编写阶段按需展开,骨架阶段仅需表格行中的信息即可指导全局规划。
|
|
||||||
|
|
||||||
## 全局删减决策
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
## 全局删减决策记录
|
|
||||||
|
|
||||||
| 决策 | 被删/压缩内容 | 原因 |
|
|
||||||
|------|------------|------|
|
|
||||||
| 删 | {具体内容} | {原因} |
|
|
||||||
| 压缩 | {具体内容} | {原因} |
|
|
||||||
```
|
|
||||||
|
|
||||||
## 付费卡点
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
## 付费卡点设计
|
|
||||||
|
|
||||||
| 位置 | 内容 | 类型 |
|
|
||||||
|------|------|------|
|
|
||||||
| 集{N}末 | {卡点内容} | {智识钩子/悬念钩子/情感钩子/世界观钩子} |
|
|
||||||
```
|
|
||||||
|
|
||||||
## 约束检查清单
|
|
||||||
|
|
||||||
生成完毕后自查:
|
|
||||||
- [ ] 总集数符合【项目配置】
|
|
||||||
- [ ] 每集时长符合【项目配置】中的单集时长
|
|
||||||
- [ ] 前2集无付费点
|
|
||||||
- [ ] 每集有集末钩子
|
|
||||||
- [ ] 三幕均有幕末转折
|
|
||||||
- [ ] 删减记录与分集中的删减一致
|
|
||||||
@ -1,115 +0,0 @@
|
|||||||
---
|
|
||||||
name: universal_agent
|
|
||||||
description: 专注于从视频分镜提示词中提取结构化台词、旁白与音效信息的助手。
|
|
||||||
---
|
|
||||||
|
|
||||||
# Decision Agent
|
|
||||||
|
|
||||||
你是一个专业的视频内容分析助手,专注于从视频分镜提示词(画面描述、镜头语言)中提取和还原结构化的台词、旁白及音效文本信息。
|
|
||||||
|
|
||||||
## 何时使用
|
|
||||||
|
|
||||||
用户提供视频分镜的画面描述或提示词(prompt),你需要从中识别并提取所有语音类内容(对白、旁白、独白、画外音)和音效标注,输出为结构化台词表。
|
|
||||||
|
|
||||||
## 输出格式
|
|
||||||
|
|
||||||
使用以下 Markdown 表格格式输出:
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
| 镜号 | 角色 | 台词内容 | 台词类型 | 表演指导 | 情绪标注 | 预估时长 |
|
|
||||||
| ---- | ---- | -------- | -------- | -------- | -------- | -------- |
|
|
||||||
```
|
|
||||||
|
|
||||||
### 字段说明
|
|
||||||
|
|
||||||
**镜号**:`S{集数}-{镜头序号}`,如 `S01-003`,按分镜顺序排列。
|
|
||||||
|
|
||||||
**角色**:说话者名称。特殊标注:
|
|
||||||
- `旁白` — 画外叙述,不属于任何剧中角色
|
|
||||||
- `群众` — 背景群众对白
|
|
||||||
- `[音效]` — 非语音的声音效果
|
|
||||||
- 如果台词是某角色的内心独白,使用 `角色名(内心)` 标注
|
|
||||||
|
|
||||||
**台词内容**:完整的台词文本或音效描述。
|
|
||||||
- 对白/旁白:直接写文字内容,保留原文语气词
|
|
||||||
- 音效:用简短描述,如 `剑刃出鞘声`、`暴雨环境音`、`心跳加速声`
|
|
||||||
- 如果提示词中仅暗示有对话但未给出具体台词,标记为 `[待补充:{场景描述}]`
|
|
||||||
|
|
||||||
**台词类型**:分类标签,可选值:
|
|
||||||
- `对白` — 角色间的直接对话
|
|
||||||
- `独白` — 角色自言自语或内心独白
|
|
||||||
- `旁白` — 画外音叙述
|
|
||||||
- `音效` — 非语音声音
|
|
||||||
- `歌曲/吟唱` — 角色演唱或吟诵
|
|
||||||
|
|
||||||
**表演指导**:对该句台词的表演要求,3-10 字。描述语气、节奏、状态。
|
|
||||||
- 正确:`低沉、缓慢、带疲惫感`、`厉声质问,渐强`、`轻声呢喃,若有若无`
|
|
||||||
- 错误:`正常说话` ← 太模糊无法指导表演
|
|
||||||
|
|
||||||
**情绪标注**:复合情绪标签,`+` 连接。可用标签:`愤怒`、`恐惧`、`悲伤`、`喜悦`、`紧张`、`平静`、`嘲讽`、`绝望`、`震惊`、`温柔`、`癫狂`、`坚定`。
|
|
||||||
|
|
||||||
**预估时长**:该条台词/音效的播放时长(秒)。
|
|
||||||
- 对白/独白/旁白:约每 4 个汉字 1 秒,根据情绪节奏适当调整
|
|
||||||
- 音效:根据音效类型估算,短促音效 1-2 秒,环境音 3-5 秒,持续音效按实际需要标注
|
|
||||||
|
|
||||||
## 提取规则
|
|
||||||
|
|
||||||
1. **逐镜处理**:每个镜头独立提取,一个镜头可能有多行台词(多个角色对话)
|
|
||||||
2. **忠于提示词**:台词内容基于提示词中明确出现或明确暗示的内容,不自行创作台词
|
|
||||||
3. **识别隐含语音**:提示词中写"角色大喊"、"角色低语道"等,即使没有直接引号也应提取
|
|
||||||
4. **区分画面与声音**:纯画面描述(如"角色走入房间")不提取,除非伴随语音动作
|
|
||||||
5. **音效不遗漏**:提示词中出现的环境音、动作音效、背景音乐提示均应提取
|
|
||||||
6. **角色统一**:同一角色全表使用统一称呼
|
|
||||||
|
|
||||||
## 输出结构
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
# {项目名} - 台词提取表
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 来源信息
|
|
||||||
|
|
||||||
| 维度 | 内容 |
|
|
||||||
| -------- | ---------- |
|
|
||||||
| 集数范围 | S{X}-S{Y} |
|
|
||||||
| 镜头总数 | {N}个镜头 |
|
|
||||||
| 风格 | {风格描述} |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 台词列表
|
|
||||||
|
|
||||||
{表格}
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 汇总统计
|
|
||||||
|
|
||||||
| 维度 | 数值 |
|
|
||||||
| ------------ | ------------- |
|
|
||||||
| 总台词条数 | {N}条 |
|
|
||||||
| 对白条数 | {N}条 |
|
|
||||||
| 旁白条数 | {N}条 |
|
|
||||||
| 独白条数 | {N}条 |
|
|
||||||
| 音效条数 | {N}条 |
|
|
||||||
| 涉及角色数 | {N}个 |
|
|
||||||
| 预估总语音长 | 约{M}-{M}秒 |
|
|
||||||
| 待补充项 | {N}条 |
|
|
||||||
```
|
|
||||||
|
|
||||||
## 处理流程
|
|
||||||
|
|
||||||
1. 用户提供视频分镜提示词(可能分批提供,按集/场次)
|
|
||||||
2. 逐镜头阅读提示词,识别所有语音和音效内容
|
|
||||||
3. 按镜号顺序提取为台词表行
|
|
||||||
4. 全部镜头提取完成后,附加汇总统计
|
|
||||||
5. 如果用户分批提供,先输出当前批次结果,等待后续输入后继续
|
|
||||||
|
|
||||||
## 注意事项
|
|
||||||
|
|
||||||
- 如果某个镜头是纯画面(无台词无音效),可跳过不输出该镜头行,但在汇总中注明"纯画面镜头 {N} 个"
|
|
||||||
- 如果提示词使用英文书写,台词内容仍按提示词原文提取(不翻译),但表演指导和情绪标注使用中文
|
|
||||||
- 同一镜头内多条台词按说话先后顺序排列
|
|
||||||
- 如果提示词中包含 `lines` 或 `sound` 字段,优先使用这些字段的内容作为提取依据
|
|
||||||
- 对话密集镜头注意区分不同角色的台词归属
|
|
||||||
@ -1,72 +0,0 @@
|
|||||||
---
|
|
||||||
name: universal_agent
|
|
||||||
description: 通用文本分析与内容提取 Agent,支持小说事件提取、视频台词提取、角色/场景/道具资产描述生成等多种结构化内容处理任务。
|
|
||||||
---
|
|
||||||
|
|
||||||
# Universal Agent
|
|
||||||
|
|
||||||
你是一个通用的内容分析与结构化提取助手,面向短剧/漫剧制作流水线的前期内容准备环节。你能够根据用户提供的原始素材(小说文本、视频提示词等),提取并输出标准化的结构化数据,供下游制作流程使用。
|
|
||||||
|
|
||||||
## 核心能力
|
|
||||||
|
|
||||||
你拥有以下参考技能(references),根据用户请求自动匹配对应技能执行:
|
|
||||||
|
|
||||||
### 1. 小说章节事件提取(event_extract)
|
|
||||||
|
|
||||||
- **触发条件**:用户提供小说原文,要求提取章节事件、生成事件表
|
|
||||||
- **参考文件**:`references/event_extract.md`
|
|
||||||
- **输出**:每章一行表格数据(章节、角色、核心事件、主线关系、信息密度、预估集长、情绪强度),不含表头、标题、汇总等额外内容
|
|
||||||
|
|
||||||
### 2. 视频提示词台词提取(video_dialogue_extract)
|
|
||||||
|
|
||||||
- **触发条件**:用户提供视频分镜提示词/画面描述,要求从中提取或还原台词、旁白、音效文本
|
|
||||||
- **参考文件**:`references/video_dialogue_extract.md`
|
|
||||||
- **输出**:结构化台词表(镜号、角色、台词内容、台词类型、情绪标注、时长估算)
|
|
||||||
|
|
||||||
### 3. 小说角色提取(novel_character_extract)
|
|
||||||
|
|
||||||
- **触发条件**:用户提供小说原文,要求提取角色信息、生成角色视觉描述
|
|
||||||
- **参考文件**:`references/novel_character_extract.md`
|
|
||||||
- **资产类型**:`role`(对应 `o_assets.type = "role"`)
|
|
||||||
- **输出**:结构化角色资产表(角色名称、角色定位、外貌特征、服饰描述、体型体态、标志性特征、性格气质、首次出场、出场章节数、状态变体)+ 核心角色卡片
|
|
||||||
|
|
||||||
### 4. 小说场景提取(novel_scene_extract)
|
|
||||||
|
|
||||||
- **触发条件**:用户提供小说原文,要求提取场景/地点信息、生成场景视觉描述
|
|
||||||
- **参考文件**:`references/novel_scene_extract.md`
|
|
||||||
- **资产类型**:`scene`(对应 `o_assets.type = "scene"`)
|
|
||||||
- **输出**:结构化场景资产表(场景名称、场景类型、空间描述、光照氛围、关键陈设、色调基调、首次出场、出场章节数、关联角色、状态变体)+ 核心场景卡片
|
|
||||||
|
|
||||||
### 5. 小说道具提取(novel_props_extract)
|
|
||||||
|
|
||||||
- **触发条件**:用户提供小说原文,要求提取道具/物品/器物信息、生成道具视觉描述
|
|
||||||
- **参考文件**:`references/novel_props_extract.md`
|
|
||||||
- **资产类型**:`tool`(对应 `o_assets.type = "tool"`)
|
|
||||||
- **输出**:结构化道具资产表(道具名称、类别、外观描述、尺寸参考、材质质感、功能/用途、首次出场、关联角色、状态变体)+ 高频道具排名
|
|
||||||
|
|
||||||
### 6. 剧本资产提取(script_assets_extract)
|
|
||||||
|
|
||||||
- **触发条件**:用户提供剧本内容,要求从剧本中提取所使用的资产(角色、场景、道具、素材片段)
|
|
||||||
- **参考文件**:`references/script_assets_extract.md`
|
|
||||||
- **资产类型**:`role`、`scene`、`tool`、`clip`(对应 `o_assets.type`)
|
|
||||||
- **输出**:结构化资产列表(资产名称、资产描述、生成提示词、资产类型),通过 `resultTool` 工具调用返回
|
|
||||||
|
|
||||||
## 资产提取分工说明
|
|
||||||
|
|
||||||
当用户要求从小说中提取"所有资产"或"角色场景道具"时,三个资产提取技能应按以下分工协作:
|
|
||||||
|
|
||||||
| 归属技能 | 提取范围 | 示例 |
|
|
||||||
| -------- | -------- | ---- |
|
|
||||||
| **角色提取** | 人物的外貌、服饰、体态、气质 | 主角的道袍、容貌、标志特征 |
|
|
||||||
| **场景提取** | 地点的空间结构、固定陈设、光照氛围 | 溶洞药室、殿中大鼎、庭院古松 |
|
|
||||||
| **道具提取** | 可移动、可交互、有独立剧情功能的物品 | 法器、武器、丹药、信物、符箓 |
|
|
||||||
|
|
||||||
## 工作原则
|
|
||||||
|
|
||||||
1. **技能匹配**:根据用户输入自动判断应使用哪个参考技能,如果不确定则询问用户
|
|
||||||
2. **忠于原文**:所有提取和生成都基于用户提供的原始素材,不臆造、不推测
|
|
||||||
3. **结构化优先**:输出始终使用 Markdown 表格或规范格式,便于下游流程消费
|
|
||||||
4. **逐步处理**:支持用户分批提供素材,每批独立输出结果,最终可合并汇总
|
|
||||||
5. **不做改编判断**:仅提取和描述事实,不对内容做保留/删除/修改的建议
|
|
||||||
6. **资产分类清晰**:角色、场景、道具三类资产各有归属,严格按分工提取,避免重复或遗漏
|
|
||||||
|
|
||||||
@ -17,7 +17,7 @@ export interface AgentContext {
|
|||||||
msg: ReturnType<ResTool["newMessage"]>;
|
msg: ReturnType<ResTool["newMessage"]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildSystemPrompt(skillPrompt: string, mem: Awaited<ReturnType<Memory["get"]>>): string {
|
function buildMemPrompt(mem: Awaited<ReturnType<Memory["get"]>>): string {
|
||||||
let memoryContext = "";
|
let memoryContext = "";
|
||||||
if (mem.rag.length) {
|
if (mem.rag.length) {
|
||||||
memoryContext += `[相关记忆]\n${mem.rag.map((r) => r.content).join("\n")}`;
|
memoryContext += `[相关记忆]\n${mem.rag.map((r) => r.content).join("\n")}`;
|
||||||
@ -30,8 +30,7 @@ function buildSystemPrompt(skillPrompt: string, mem: Awaited<ReturnType<Memory["
|
|||||||
if (memoryContext) memoryContext += "\n\n";
|
if (memoryContext) memoryContext += "\n\n";
|
||||||
memoryContext += `[近期对话]\n${mem.shortTerm.map((m) => `${m.role}: ${m.content}`).join("\n")}`;
|
memoryContext += `[近期对话]\n${mem.shortTerm.map((m) => `${m.role}: ${m.content}`).join("\n")}`;
|
||||||
}
|
}
|
||||||
if (!memoryContext) return skillPrompt;
|
return `## Memory\n以下是你对用户的记忆,可作为参考但不要主动提及:\n${memoryContext}`;
|
||||||
return `${skillPrompt}\n\n## Memory\n以下是你对用户的记忆,可作为参考但不要主动提及:\n${memoryContext}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const subAgentList = ["executionAI", "supervisionAI"] as const;
|
const subAgentList = ["executionAI", "supervisionAI"] as const;
|
||||||
@ -40,14 +39,11 @@ export async function decisionAI(ctx: AgentContext) {
|
|||||||
const { isolationKey, text, abortSignal } = ctx;
|
const { isolationKey, text, abortSignal } = ctx;
|
||||||
const memory = new Memory("productionAgent", isolationKey);
|
const memory = new Memory("productionAgent", isolationKey);
|
||||||
await memory.add("user", text);
|
await memory.add("user", text);
|
||||||
const [skill, mem] = await Promise.all([useSkill("production_agent_decision.md"), memory.get(text)]);
|
|
||||||
|
|
||||||
const systemPrompt = buildSystemPrompt(skill.prompt, mem);
|
const skill = await useSkill({ mainSkill: "production_agent_decision" }, buildMemPrompt(await memory.get(text)));
|
||||||
|
|
||||||
const prefixSystem = `以用户当前指令为最终目标。默认直接推进执行;仅当用户明确要求新增或修改拍摄计划时,才调用set_flowData更新scriptPlan并与用户确认。需要执行任务时调用run_sub_agent运行**executionAI**。`;
|
|
||||||
|
|
||||||
const { textStream } = await u.Ai.Text("productionAgent").stream({
|
const { textStream } = await u.Ai.Text("productionAgent").stream({
|
||||||
system: prefixSystem + systemPrompt,
|
system: skill.prompt,
|
||||||
messages: [{ role: "user", content: text }],
|
messages: [{ role: "user", content: text }],
|
||||||
abortSignal,
|
abortSignal,
|
||||||
tools: {
|
tools: {
|
||||||
@ -69,7 +65,11 @@ export async function decisionAI(ctx: AgentContext) {
|
|||||||
export async function executionAI(ctx: AgentContext) {
|
export async function executionAI(ctx: AgentContext) {
|
||||||
const { text, abortSignal } = ctx;
|
const { text, abortSignal } = ctx;
|
||||||
|
|
||||||
const skill = await useSkill("production_agent_execution.md");
|
const skill = await useSkill({
|
||||||
|
mainSkill: "production_agent_execution",
|
||||||
|
workspace: ["production_agent_skills/execution"],
|
||||||
|
attachedSkills: ["production_agent_skills/execution/driector_art_skills/chinese_sweet_romance/driector_skills"], //todo:后续可以改为动态加载
|
||||||
|
});
|
||||||
|
|
||||||
const subMsg = ctx.resTool.newMessage("assistant", "执行导演");
|
const subMsg = ctx.resTool.newMessage("assistant", "执行导演");
|
||||||
|
|
||||||
@ -89,7 +89,7 @@ export async function executionAI(ctx: AgentContext) {
|
|||||||
export async function supervisionAI(ctx: AgentContext) {
|
export async function supervisionAI(ctx: AgentContext) {
|
||||||
const { text, abortSignal } = ctx;
|
const { text, abortSignal } = ctx;
|
||||||
|
|
||||||
const skill = await useSkill("production_agent_supervision.md");
|
const skill = await useSkill({ mainSkill: "production_agent_supervision", workspace: ["production_agent_skills/supervision"] });
|
||||||
const subMsg = ctx.resTool.newMessage("assistant", "编辑");
|
const subMsg = ctx.resTool.newMessage("assistant", "编辑");
|
||||||
|
|
||||||
const { textStream } = await u.Ai.Text("scriptAgent").stream({
|
const { textStream } = await u.Ai.Text("scriptAgent").stream({
|
||||||
|
|||||||
@ -17,7 +17,7 @@ export interface AgentContext {
|
|||||||
msg: ReturnType<ResTool["newMessage"]>;
|
msg: ReturnType<ResTool["newMessage"]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildSystemPrompt(skillPrompt: string, mem: Awaited<ReturnType<Memory["get"]>>): string {
|
function buildMemPrompt(mem: Awaited<ReturnType<Memory["get"]>>): string {
|
||||||
let memoryContext = "";
|
let memoryContext = "";
|
||||||
if (mem.rag.length) {
|
if (mem.rag.length) {
|
||||||
memoryContext += `[相关记忆]\n${mem.rag.map((r) => r.content).join("\n")}`;
|
memoryContext += `[相关记忆]\n${mem.rag.map((r) => r.content).join("\n")}`;
|
||||||
@ -30,8 +30,7 @@ function buildSystemPrompt(skillPrompt: string, mem: Awaited<ReturnType<Memory["
|
|||||||
if (memoryContext) memoryContext += "\n\n";
|
if (memoryContext) memoryContext += "\n\n";
|
||||||
memoryContext += `[近期对话]\n${mem.shortTerm.map((m) => `${m.role}: ${m.content}`).join("\n")}`;
|
memoryContext += `[近期对话]\n${mem.shortTerm.map((m) => `${m.role}: ${m.content}`).join("\n")}`;
|
||||||
}
|
}
|
||||||
if (!memoryContext) return skillPrompt;
|
return `## Memory\n以下是你对用户的记忆,可作为参考但不要主动提及:\n${memoryContext}`;
|
||||||
return `${skillPrompt}\n\n## Memory\n以下是你对用户的记忆,可作为参考但不要主动提及:\n${memoryContext}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const subAgentList = ["executionAI", "supervisionAI"] as const;
|
const subAgentList = ["executionAI", "supervisionAI"] as const;
|
||||||
@ -41,26 +40,32 @@ export async function decisionAI(ctx: AgentContext) {
|
|||||||
|
|
||||||
const memory = new Memory("scriptAgent", isolationKey);
|
const memory = new Memory("scriptAgent", isolationKey);
|
||||||
await memory.add("user", text, { createTime: userMessageTime });
|
await memory.add("user", text, { createTime: userMessageTime });
|
||||||
const [skill, mem] = await Promise.all([useSkill("script_agent_decision.md"), memory.get(text)]);
|
|
||||||
|
|
||||||
const systemPrompt = buildSystemPrompt(skill.prompt, mem);
|
const skill = await useSkill({ mainSkill: "script_agent_decision" }, buildMemPrompt(await memory.get(text)));
|
||||||
|
|
||||||
const projectData = await u.db("o_project").where("id", resTool.data.projectId).first();
|
const projectData = await u.db("o_project").where("id", resTool.data.projectId).first();
|
||||||
const novelData = await u.db("o_novel").where("projectId", resTool.data.projectId).select("id", "chapterIndex as index");
|
const novelData = await u.db("o_novel").where("projectId", resTool.data.projectId).select("id", "chapterIndex as index");
|
||||||
|
|
||||||
const projectInfo = [
|
const get_project_info = tool({
|
||||||
"## 项目信息",
|
description: "获取项目的基本信息和章节ID映射表,返回字符串格式的项目信息",
|
||||||
`小说名称:${projectData?.name ?? "未知"}`,
|
inputSchema: z.object({}),
|
||||||
`小说类型:${projectData?.type ?? "未知"}`,
|
execute: async () => {
|
||||||
`小说简介:${projectData?.intro ?? "无"}`,
|
const projectInfo = [
|
||||||
`目标改编影视视觉手册|画风:${projectData?.artStyle ?? "无"}`,
|
"## 项目信息",
|
||||||
`目标改编视频画幅:${projectData?.videoRatio ?? "16:9"}`,
|
`小说名称:${projectData?.name ?? "未知"}`,
|
||||||
].join("\n");
|
`小说类型:${projectData?.type ?? "未知"}`,
|
||||||
|
`小说简介:${projectData?.intro ?? "无"}`,
|
||||||
|
`目标改编影视视觉手册|画风:${projectData?.artStyle ?? "无"}`,
|
||||||
|
`目标改编视频画幅:${projectData?.videoRatio ?? "16:9"}`,
|
||||||
|
].join("\n");
|
||||||
|
|
||||||
const prefixSystem = `${projectInfo}\n\n## 章节ID映射表\n${novelData.map((i: any) => `- 章节ID:${i.id}: 第${i.index}章`).join("\n")}\n\n`;
|
const content = `${projectInfo}\n\n## 章节ID映射表\n${novelData.map((i: any) => `- 章节ID:${i.id}: 第${i.index}章`).join("\n")}\n\n`;
|
||||||
|
return { content };
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const { textStream } = await u.Ai.Text("scriptAgent").stream({
|
const { textStream } = await u.Ai.Text("scriptAgent").stream({
|
||||||
system: prefixSystem + systemPrompt,
|
system: skill.prompt,
|
||||||
messages: [{ role: "user", content: text }],
|
messages: [{ role: "user", content: text }],
|
||||||
abortSignal,
|
abortSignal,
|
||||||
tools: {
|
tools: {
|
||||||
@ -68,6 +73,7 @@ export async function decisionAI(ctx: AgentContext) {
|
|||||||
...memory.getTools(),
|
...memory.getTools(),
|
||||||
run_sub_agent: runSubAgent(ctx),
|
run_sub_agent: runSubAgent(ctx),
|
||||||
...useTools({ resTool: ctx.resTool, msg: ctx.msg }),
|
...useTools({ resTool: ctx.resTool, msg: ctx.msg }),
|
||||||
|
get_project_info,
|
||||||
},
|
},
|
||||||
onFinish: async (completion) => {
|
onFinish: async (completion) => {
|
||||||
await memory.add("assistant:decision", completion.text);
|
await memory.add("assistant:decision", completion.text);
|
||||||
@ -81,7 +87,11 @@ export async function decisionAI(ctx: AgentContext) {
|
|||||||
|
|
||||||
export async function executionAI(ctx: AgentContext) {
|
export async function executionAI(ctx: AgentContext) {
|
||||||
const { text, abortSignal } = ctx;
|
const { text, abortSignal } = ctx;
|
||||||
const skill = await useSkill("script_agent_execution.md");
|
|
||||||
|
const skill = await useSkill({
|
||||||
|
mainSkill: "script_agent_execution",
|
||||||
|
workspace: ["script_agent_skills/execution"],
|
||||||
|
});
|
||||||
|
|
||||||
const subMsg = ctx.resTool.newMessage("assistant", "编剧");
|
const subMsg = ctx.resTool.newMessage("assistant", "编剧");
|
||||||
|
|
||||||
@ -107,7 +117,8 @@ export async function executionAI(ctx: AgentContext) {
|
|||||||
export async function supervisionAI(ctx: AgentContext) {
|
export async function supervisionAI(ctx: AgentContext) {
|
||||||
const { text, abortSignal } = ctx;
|
const { text, abortSignal } = ctx;
|
||||||
|
|
||||||
const skill = await useSkill("script_agent_supervision.md");
|
const skill = await useSkill({ mainSkill: "script_agent_supervision", workspace: ["script_agent_skills/supervision"] });
|
||||||
|
|
||||||
const subMsg = ctx.resTool.newMessage("assistant", "编辑");
|
const subMsg = ctx.resTool.newMessage("assistant", "编辑");
|
||||||
|
|
||||||
const { textStream } = await u.Ai.Text("scriptAgent").stream({
|
const { textStream } = await u.Ai.Text("scriptAgent").stream({
|
||||||
|
|||||||
@ -93,7 +93,7 @@ export default router.post(
|
|||||||
|
|
||||||
const novelData = (await u.db("o_novel").whereIn("chapterIndex", [1]).select("*")) as NovelChapter[];
|
const novelData = (await u.db("o_novel").whereIn("chapterIndex", [1]).select("*")) as NovelChapter[];
|
||||||
const novelText = mergeNovelText(novelData);
|
const novelText = mergeNovelText(novelData);
|
||||||
const skill = await useSkill("universal_agent.md");
|
const skill = await useSkill("universal_agent.md");//todo:改为AI
|
||||||
|
|
||||||
// 批量更新所有 item 状态为生成中
|
// 批量更新所有 item 状态为生成中
|
||||||
const assetsIds = items.map((item: { assetsId: number }) => item.assetsId);
|
const assetsIds = items.map((item: { assetsId: number }) => item.assetsId);
|
||||||
|
|||||||
@ -99,7 +99,7 @@ export default router.post(
|
|||||||
const novelData = (await u.db("o_novel").whereIn("chapterIndex", [1]).select("*")) as NovelChapter[];
|
const novelData = (await u.db("o_novel").whereIn("chapterIndex", [1]).select("*")) as NovelChapter[];
|
||||||
const novelText = mergeNovelText(novelData);
|
const novelText = mergeNovelText(novelData);
|
||||||
|
|
||||||
const skill = await useSkill("universal_agent.md");
|
const skill = await useSkill("universal_agent.md");//todo:改为AI
|
||||||
|
|
||||||
const systemPrompt = `${skill.prompt}
|
const systemPrompt = `${skill.prompt}
|
||||||
|
|
||||||
|
|||||||
@ -30,7 +30,7 @@ export default router.post(
|
|||||||
);
|
);
|
||||||
|
|
||||||
async function getLines(prompt: string) {
|
async function getLines(prompt: string) {
|
||||||
const skill = await useSkill("universal_agent.md");
|
const skill = await useSkill("universal_agent.md");//todo:改为AI
|
||||||
|
|
||||||
const resText = await u.Ai.Text("universalAgent").invoke({
|
const resText = await u.Ai.Text("universalAgent").invoke({
|
||||||
system: skill.prompt,
|
system: skill.prompt,
|
||||||
|
|||||||
@ -156,7 +156,7 @@ export default router.post(
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const skill = await useSkill("universal_agent.md");
|
const skill = await useSkill("universal_agent.md");//todo:改为AI
|
||||||
await intansce.invoke({
|
await intansce.invoke({
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -5,13 +5,22 @@ import fs from "fs";
|
|||||||
import { useSkill } from "@/utils/agent/skillsTools";
|
import { useSkill } from "@/utils/agent/skillsTools";
|
||||||
|
|
||||||
export default router.get("/", async (req, res) => {
|
export default router.get("/", async (req, res) => {
|
||||||
const skill = await useSkill("universal_agent.md");
|
const skill = await useSkill(
|
||||||
console.log("%c Line:11 🍏 skill.prompt", "background:#fca650", skill.prompt);
|
{
|
||||||
const result = await u.Ai.Text("universalAgent").invoke({
|
mainSkill: "production_agent_execution",
|
||||||
system: "请直接调用activate_skill工具激活技能" + skill.prompt,
|
workspace: ["production_agent_skills/execution"],
|
||||||
messages: [{ role: "user", content: `如何烹饪龙肉` }],
|
attachedSkills: ["production_agent_skills/execution/driector_art_skills/chinese_sweet_romance/driector_skills"],
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
const test = await u.Ai.Text("scriptAgent").invoke({
|
||||||
|
system: skill.prompt,
|
||||||
|
messages: [
|
||||||
|
{ role: "user", content: "渐进式激活skill,技能->资源1->资源2...一直渐进到最深处,并输出你的阅读路线,同级目录你只用读取一个无需全部读取" },
|
||||||
|
],
|
||||||
tools: skill.tools,
|
tools: skill.tools,
|
||||||
});
|
});
|
||||||
|
|
||||||
res.send(result.text);
|
console.log("%c Line:21 🌽 text", "background:#ea7e5c", test.text);
|
||||||
|
res.send(test.text);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { Socket } from "socket.io";
|
|||||||
import type {
|
import type {
|
||||||
ChatMessageStatus,
|
ChatMessageStatus,
|
||||||
AIMessageContent,
|
AIMessageContent,
|
||||||
UserMessageContent,
|
|
||||||
TextContent,
|
TextContent,
|
||||||
MarkdownContent,
|
MarkdownContent,
|
||||||
ImageContent,
|
ImageContent,
|
||||||
@ -13,8 +12,7 @@ import type {
|
|||||||
ToolCallContent,
|
ToolCallContent,
|
||||||
ActivityContent,
|
ActivityContent,
|
||||||
ReasoningContent,
|
ReasoningContent,
|
||||||
AttachmentContent,
|
} from "./chatMessagesData";
|
||||||
} from "./ChatMessagesData";
|
|
||||||
|
|
||||||
type ContentType = AIMessageContent["type"];
|
type ContentType = AIMessageContent["type"];
|
||||||
|
|
||||||
|
|||||||
200
src/utils/agent/skillsTools copy.ts
Normal file
200
src/utils/agent/skillsTools copy.ts
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
import { tool } from "ai";
|
||||||
|
import { z } from "zod";
|
||||||
|
import path from "path";
|
||||||
|
import fs from "fs/promises";
|
||||||
|
import isPathInside from "is-path-inside";
|
||||||
|
import u from "@/utils";
|
||||||
|
import getPath from "@/utils/getPath";
|
||||||
|
import { getEmbedding, cosineSimilarity } from "./embedding";
|
||||||
|
|
||||||
|
interface SkillRecord {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
location: string;
|
||||||
|
baseDir: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 解析 SKILL.md ====================
|
||||||
|
|
||||||
|
function parseFrontmatter(content: string): { name: string; description: string } {
|
||||||
|
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
||||||
|
if (!match?.[1]) throw new Error("No frontmatter found");
|
||||||
|
|
||||||
|
const result: Record<string, string> = {};
|
||||||
|
const lines = match[1].split("\n");
|
||||||
|
|
||||||
|
for (let i = 0; i < lines.length; ) {
|
||||||
|
const colonIndex = lines[i].indexOf(":");
|
||||||
|
if (colonIndex === -1) {
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = lines[i].slice(0, colonIndex).trim();
|
||||||
|
if (!key) {
|
||||||
|
i++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let value = lines[i].slice(colonIndex + 1).trim();
|
||||||
|
i++;
|
||||||
|
|
||||||
|
if (/^[>|]-?$/.test(value)) {
|
||||||
|
const fold = value.startsWith(">");
|
||||||
|
const parts: string[] = [];
|
||||||
|
while (i < lines.length && /^\s+/.test(lines[i])) {
|
||||||
|
parts.push(lines[i].trim());
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
value = fold ? parts.join(" ") : parts.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
result[key] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.name || !result.description) throw new Error("Frontmatter missing required field: name or description");
|
||||||
|
return { name: result.name, description: result.description };
|
||||||
|
}
|
||||||
|
|
||||||
|
type SkillAttribution =
|
||||||
|
| "production_agent_decision.md"
|
||||||
|
| "production_agent_execution.md"
|
||||||
|
| "production_agent_supervision.md"
|
||||||
|
| "script_agent_decision.md"
|
||||||
|
| "script_agent_execution.md"
|
||||||
|
| "script_agent_supervision.md"
|
||||||
|
| "universal_agent.md";
|
||||||
|
|
||||||
|
export async function useSkill(mainSkillName: SkillAttribution) {
|
||||||
|
const skillsRoot = getPath("skills");
|
||||||
|
const targetSkill = path.join(skillsRoot, mainSkillName);
|
||||||
|
|
||||||
|
if (!isPathInside(targetSkill, skillsRoot)) throw new Error("技能名称无效:检测到路径穿越");
|
||||||
|
|
||||||
|
try {
|
||||||
|
const content = await fs.readFile(targetSkill, "utf-8");
|
||||||
|
const skill = { ...parseFrontmatter(content), location: targetSkill, baseDir: skillsRoot };
|
||||||
|
return { prompt: buildPrompt(skill), tools: createSkillTools(skill, mainSkillName) };
|
||||||
|
} catch {
|
||||||
|
throw new Error(`技能文件不存在:${mainSkillName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPrompt(skill: SkillRecord): string {
|
||||||
|
return `## Skills
|
||||||
|
以下技能提供了专业任务的专用指令。
|
||||||
|
当任务与某个技能的描述匹配时,调用 activate_skill 工具并传入技能名称来加载完整指令。
|
||||||
|
加载后遵循技能指令执行任务,需要时调用 read_skill_file 读取资源文件内容。
|
||||||
|
|
||||||
|
<available_skills>
|
||||||
|
<skill>
|
||||||
|
<name>${skill.name}</name>
|
||||||
|
<description>${skill.description}</description>
|
||||||
|
</skill>
|
||||||
|
</available_skills>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createSkillTools(skill: SkillRecord, mainSkillName: string) {
|
||||||
|
const activated = new Set<string>();
|
||||||
|
return {
|
||||||
|
activate_skill: tool({
|
||||||
|
description: `激活一个技能,加载其完整指令和捆绑资源列表到上下文。可用技能:${skill.name}`,
|
||||||
|
inputSchema: z.object({
|
||||||
|
name: z.enum([skill.name] as [string, ...string[]]).describe("要激活的技能名称"),
|
||||||
|
}),
|
||||||
|
execute: async ({ name }) => {
|
||||||
|
if (activated.has(name)) {
|
||||||
|
console.log(`[Skill] ℹ️ 技能 "${name}" 已激活,跳过重复注入`);
|
||||||
|
return { alreadyActive: true, message: `技能 "${name}" 已激活,无需重复加载` };
|
||||||
|
}
|
||||||
|
let raw: string;
|
||||||
|
try {
|
||||||
|
raw = await fs.readFile(skill.location, "utf-8");
|
||||||
|
} catch {
|
||||||
|
console.log(`[Skill] ❌ 激活失败:无法读取 ${skill.location}`);
|
||||||
|
return { error: `无法读取技能文件:${name}` };
|
||||||
|
}
|
||||||
|
const body = raw.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").trim();
|
||||||
|
|
||||||
|
const resources = await u
|
||||||
|
.db("o_skillList")
|
||||||
|
.distinct("o_skillList.path")
|
||||||
|
.innerJoin("o_skillAttribution", "o_skillList.id", "o_skillAttribution.skillId")
|
||||||
|
.where("o_skillList.state", 1)
|
||||||
|
.andWhere("o_skillAttribution.attribution", mainSkillName);
|
||||||
|
|
||||||
|
activated.add(name);
|
||||||
|
console.log(`[Skill] 📖 已激活:${name}(${body.length} 字符,${resources.length} 资源)`);
|
||||||
|
let content = "";
|
||||||
|
content = `<skill_content name="${name}">\n`;
|
||||||
|
content += body + "\n\n";
|
||||||
|
content += `Skill directory: ${skill.baseDir}\n`;
|
||||||
|
content += "相对路径基于此技能目录解析,使用 read_skill_file 工具读取资源文件。\n";
|
||||||
|
if (resources.length > 0) {
|
||||||
|
content += "\n<skill_resources>\n";
|
||||||
|
for (const { path } of resources) {
|
||||||
|
content += ` <file>${path}</file>\n`;
|
||||||
|
}
|
||||||
|
content += "</skill_resources>\n";
|
||||||
|
}
|
||||||
|
content += "\n<skill_tools_guide>\n";
|
||||||
|
content += "- read_skill_file:读取上方 skill_resources 中列出的资源文件。\n";
|
||||||
|
content += "- discover_skill_docs:当上方资源不足以完成任务时,使用关键词检索更多相关文档。传入与当前任务相关的关键词列表即可获取推荐。\n";
|
||||||
|
content += "</skill_tools_guide>\n";
|
||||||
|
content += "</skill_content>";
|
||||||
|
return { content };
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
discover_skill_docs: tool({
|
||||||
|
description: "根据关键词主动发现全部技能文档(MD),返回相关度排序的推荐列表。适用于技能指令中未明确指定资源文件但需要补充信息的场景。",
|
||||||
|
inputSchema: z.object({
|
||||||
|
keywords: z.array(z.string().max(100)).min(1).max(20).describe("用于检索技能文档的关键词列表"),
|
||||||
|
topK: z.number().int().min(1).max(20).default(5).describe("返回推荐文档数量"),
|
||||||
|
}),
|
||||||
|
execute: async ({ keywords, topK }) => {
|
||||||
|
const queryText = keywords.join(" ");
|
||||||
|
const queryVec = await getEmbedding(queryText);
|
||||||
|
|
||||||
|
const activeRows = await u.db("o_skillList").where("state", 1).whereNotNull("embedding").select();
|
||||||
|
const scored = activeRows
|
||||||
|
.map((row) => {
|
||||||
|
const emb = JSON.parse(row.embedding!) as number[];
|
||||||
|
return {
|
||||||
|
name: row.name,
|
||||||
|
filePath: row.path,
|
||||||
|
type: row.type,
|
||||||
|
description: row.description,
|
||||||
|
score: cosineSimilarity(queryVec, emb),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.sort((a, b) => b.score - a.score)
|
||||||
|
.slice(0, topK);
|
||||||
|
|
||||||
|
console.log(`[Skill] ✅ discover_skill_docs 返回 ${scored.length} 条推荐`);
|
||||||
|
return { recommendations: scored };
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
read_skill_file: tool({
|
||||||
|
description: "读取已激活技能目录下的资源文件。传入 activate_skill 返回的 skill_resources 中的文件路径。",
|
||||||
|
inputSchema: z.object({
|
||||||
|
skillName: z.string().describe("技能名称"),
|
||||||
|
filePath: z.string().describe("资源文件的相对路径,来自 activate_skill 返回的 skill_resources"),
|
||||||
|
}),
|
||||||
|
execute: async ({ skillName, filePath: relPath }) => {
|
||||||
|
const fullPath = path.resolve(path.join(skill.baseDir, relPath));
|
||||||
|
if (!isPathInside(fullPath, skill.baseDir)) {
|
||||||
|
console.log(`[Skill] 🚫 路径越界已拦截:"${relPath}" 超出技能目录范围`);
|
||||||
|
return { error: "Access denied: path is outside skill directory" };
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const fileContent = await fs.readFile(fullPath, "utf-8");
|
||||||
|
console.log(`[Skill] 📄 已读取文件:${skillName}/${relPath}(${fileContent.length} 字符)`);
|
||||||
|
return { content: fileContent };
|
||||||
|
} catch {
|
||||||
|
console.log(`[Skill] ❌ 读取失败:未找到文件 "${relPath}"`);
|
||||||
|
return { error: `File not found: ${relPath}` };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -1,17 +1,39 @@
|
|||||||
import { tool } from "ai";
|
import { tool } from "ai";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import fs from "fs/promises";
|
|
||||||
import isPathInside from "is-path-inside";
|
import isPathInside from "is-path-inside";
|
||||||
import u from "@/utils";
|
|
||||||
import getPath from "@/utils/getPath";
|
import getPath from "@/utils/getPath";
|
||||||
import { getEmbedding, cosineSimilarity } from "./embedding";
|
import * as fs from "fs";
|
||||||
|
|
||||||
interface SkillRecord {
|
type SkillAttribution =
|
||||||
name: string;
|
//剧本Agent
|
||||||
description: string;
|
| "script_agent_decision"
|
||||||
location: string;
|
| "script_agent_execution"
|
||||||
baseDir: string;
|
| "script_agent_supervision"
|
||||||
|
//生产Agent
|
||||||
|
| "production_agent_decision"
|
||||||
|
| "production_agent_execution"
|
||||||
|
| "production_agent_supervision";
|
||||||
|
|
||||||
|
interface SkillInput {
|
||||||
|
mainSkill: SkillAttribution;
|
||||||
|
workspace?: string[];
|
||||||
|
attachedSkills?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SkillPaths {
|
||||||
|
mainSkill: string;
|
||||||
|
secondarySkills: string[];
|
||||||
|
tertiarySkills: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function toUnixPath(filePath: string): string {
|
||||||
|
return filePath.replace(/\\/g, "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureNonEmptyBody(body: string, fallback: string): string {
|
||||||
|
const trimmed = body.trim();
|
||||||
|
return trimmed.length > 0 ? trimmed : fallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 解析 SKILL.md ====================
|
// ==================== 解析 SKILL.md ====================
|
||||||
@ -56,31 +78,47 @@ function parseFrontmatter(content: string): { name: string; description: string
|
|||||||
return { name: result.name, description: result.description };
|
return { name: result.name, description: result.description };
|
||||||
}
|
}
|
||||||
|
|
||||||
type SkillAttribution =
|
export async function useSkill(input: SkillInput, mem?: string) {
|
||||||
| "production_agent_decision.md"
|
const { mainSkill, workspace = [], attachedSkills = [] } = input;
|
||||||
| "production_agent_execution.md"
|
const rootDir = getPath("skills");
|
||||||
| "production_agent_supervision.md"
|
const normalizedRootDir = path.resolve(rootDir);
|
||||||
| "script_agent_decision.md"
|
const mainPath = path.join(rootDir, mainSkill + ".md");
|
||||||
| "script_agent_execution.md"
|
if (!fs.existsSync(mainPath)) throw new Error(`主技能文件不存在: ${mainPath}`);
|
||||||
| "script_agent_supervision.md"
|
if (!isPathInside(mainPath, normalizedRootDir)) throw new Error("技能名称无效:检测到路径穿越");
|
||||||
| "universal_agent.md";
|
|
||||||
|
|
||||||
export async function useSkill(mainSkillName: SkillAttribution) {
|
const resolveSafeSkillDir = (dir: string): string | null => {
|
||||||
const skillsRoot = getPath("skills");
|
const resolvedDir = path.resolve(normalizedRootDir, dir);
|
||||||
const targetSkill = path.join(skillsRoot, mainSkillName);
|
const isSafeDir = resolvedDir === normalizedRootDir || isPathInside(resolvedDir, normalizedRootDir);
|
||||||
|
return isSafeDir ? resolvedDir : null;
|
||||||
|
};
|
||||||
|
|
||||||
if (!isPathInside(targetSkill, skillsRoot)) throw new Error("技能名称无效:检测到路径穿越");
|
const getMdFiles = (dir: string, recursive = false): string[] => {
|
||||||
|
if (!fs.existsSync(dir)) return [];
|
||||||
|
return fs.readdirSync(dir, { withFileTypes: true }).flatMap((entry) => {
|
||||||
|
const fullPath = path.join(dir, entry.name);
|
||||||
|
if (entry.isFile() && entry.name.endsWith(".md")) return [fullPath];
|
||||||
|
return entry.isDirectory() && recursive ? getMdFiles(fullPath, true) : [];
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const collectMdFiles = (dirs: string[], recursive: boolean) =>
|
||||||
|
dirs.flatMap((dir) => {
|
||||||
|
const safeDir = resolveSafeSkillDir(dir);
|
||||||
|
if (!safeDir) return [];
|
||||||
|
return getMdFiles(safeDir, recursive).map((file) => toUnixPath(path.relative(normalizedRootDir, file)));
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
const skillPaths: SkillPaths = {
|
||||||
const content = await fs.readFile(targetSkill, "utf-8");
|
mainSkill: mainPath,
|
||||||
const skill = { ...parseFrontmatter(content), location: targetSkill, baseDir: skillsRoot };
|
secondarySkills: collectMdFiles(workspace, false),
|
||||||
return { prompt: buildPrompt(skill), tools: createSkillTools(skill, mainSkillName) };
|
tertiarySkills: collectMdFiles(attachedSkills, true),
|
||||||
} catch {
|
};
|
||||||
throw new Error(`技能文件不存在:${mainSkillName}`);
|
|
||||||
}
|
const content = await fs.promises.readFile(mainPath, "utf-8");
|
||||||
|
const skill = parseFrontmatter(content);
|
||||||
|
return { prompt: buildPrompt(skill), tools: createSkillTools(skill, skillPaths, mem) };
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildPrompt(skill: SkillRecord): string {
|
function buildPrompt(skill: { name: string; description: string }): string {
|
||||||
return `## Skills
|
return `## Skills
|
||||||
以下技能提供了专业任务的专用指令。
|
以下技能提供了专业任务的专用指令。
|
||||||
当任务与某个技能的描述匹配时,调用 activate_skill 工具并传入技能名称来加载完整指令。
|
当任务与某个技能的描述匹配时,调用 activate_skill 工具并传入技能名称来加载完整指令。
|
||||||
@ -94,8 +132,9 @@ function buildPrompt(skill: SkillRecord): string {
|
|||||||
</available_skills>`;
|
</available_skills>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createSkillTools(skill: SkillRecord, mainSkillName: string) {
|
function createSkillTools(skill: { name: string; description: string }, skillPaths: SkillPaths, mem?: string) {
|
||||||
const activated = new Set<string>();
|
const activated = new Set<string>(); // 已激活技能集合,防止重复加载
|
||||||
|
const skillsRootDir = path.resolve(getPath("skills"));
|
||||||
return {
|
return {
|
||||||
activate_skill: tool({
|
activate_skill: tool({
|
||||||
description: `激活一个技能,加载其完整指令和捆绑资源列表到上下文。可用技能:${skill.name}`,
|
description: `激活一个技能,加载其完整指令和捆绑资源列表到上下文。可用技能:${skill.name}`,
|
||||||
@ -104,96 +143,78 @@ function createSkillTools(skill: SkillRecord, mainSkillName: string) {
|
|||||||
}),
|
}),
|
||||||
execute: async ({ name }) => {
|
execute: async ({ name }) => {
|
||||||
if (activated.has(name)) {
|
if (activated.has(name)) {
|
||||||
console.log(`[Skill] ℹ️ 技能 "${name}" 已激活,跳过重复注入`);
|
console.log(`⚡[主技能] ℹ️ 技能 "${name}" 已激活,跳过重复注入`);
|
||||||
return { alreadyActive: true, message: `技能 "${name}" 已激活,无需重复加载` };
|
return { alreadyActive: true, message: `技能 "${name}" 已激活,无需重复加载` };
|
||||||
}
|
}
|
||||||
let raw: string;
|
let raw = "";
|
||||||
try {
|
try {
|
||||||
raw = await fs.readFile(skill.location, "utf-8");
|
raw = await fs.promises.readFile(skillPaths.mainSkill, "utf-8");
|
||||||
} catch {
|
console.log(`⚡[主技能] ✓ 已读取主技能文件: ${skillPaths.mainSkill}(${raw.length} 字符)`);
|
||||||
console.log(`[Skill] ❌ 激活失败:无法读取 ${skill.location}`);
|
} catch (error) {
|
||||||
return { error: `无法读取技能文件:${name}` };
|
console.log(`⚡[主技能] ✗ 读取失败:未找到文件 "${skillPaths.mainSkill}"`);
|
||||||
}
|
}
|
||||||
const body = raw.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, "").trim();
|
|
||||||
|
|
||||||
const resources = await u
|
|
||||||
.db("o_skillList")
|
|
||||||
.distinct("o_skillList.path")
|
|
||||||
.innerJoin("o_skillAttribution", "o_skillList.id", "o_skillAttribution.skillId")
|
|
||||||
.where("o_skillList.state", 1)
|
|
||||||
.andWhere("o_skillAttribution.attribution", mainSkillName);
|
|
||||||
|
|
||||||
activated.add(name);
|
activated.add(name);
|
||||||
console.log(`[Skill] 📖 已激活:${name}(${body.length} 字符,${resources.length} 资源)`);
|
console.log(`⚡[主技能] ✓ 技能 "${name}" 已激活`);
|
||||||
|
const body = ensureNonEmptyBody(raw.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, ""), "该技能文件无正文内容。");
|
||||||
let content = "";
|
let content = "";
|
||||||
content = `<skill_content name="${name}">\n`;
|
content = `<skill_content name="${name}">\n`;
|
||||||
content += body + "\n\n";
|
content += body + "\n\n";
|
||||||
content += `Skill directory: ${skill.baseDir}\n`;
|
content += "使用 read_skill_file 工具读取资源文件。\n";
|
||||||
content += "相对路径基于此技能目录解析,使用 read_skill_file 工具读取资源文件。\n";
|
if (skillPaths.secondarySkills.length > 0) {
|
||||||
if (resources.length > 0) {
|
|
||||||
content += "\n<skill_resources>\n";
|
content += "\n<skill_resources>\n";
|
||||||
for (const { path } of resources) {
|
for (const path of skillPaths.secondarySkills) {
|
||||||
content += ` <file>${path}</file>\n`;
|
content += ` <file>${path}</file>\n`;
|
||||||
}
|
}
|
||||||
content += "</skill_resources>\n";
|
content += "</skill_resources>\n";
|
||||||
}
|
}
|
||||||
content += "\n<skill_tools_guide>\n";
|
|
||||||
content += "- read_skill_file:读取上方 skill_resources 中列出的资源文件。\n";
|
|
||||||
content += "- discover_skill_docs:当上方资源不足以完成任务时,使用关键词检索更多相关文档。传入与当前任务相关的关键词列表即可获取推荐。\n";
|
|
||||||
content += "</skill_tools_guide>\n";
|
|
||||||
content += "</skill_content>";
|
content += "</skill_content>";
|
||||||
|
if (mem) {
|
||||||
|
content += `\n<memory>\n` + mem + `\n</memory>`;
|
||||||
|
}
|
||||||
|
console.log("%c Line:173 🍕 content", "background:#fca650", content);
|
||||||
return { content };
|
return { content };
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
discover_skill_docs: tool({
|
|
||||||
description: "根据关键词主动发现全部技能文档(MD),返回相关度排序的推荐列表。适用于技能指令中未明确指定资源文件但需要补充信息的场景。",
|
|
||||||
inputSchema: z.object({
|
|
||||||
keywords: z.array(z.string().max(100)).min(1).max(20).describe("用于检索技能文档的关键词列表"),
|
|
||||||
topK: z.number().int().min(1).max(20).default(5).describe("返回推荐文档数量"),
|
|
||||||
}),
|
|
||||||
execute: async ({ keywords, topK }) => {
|
|
||||||
const queryText = keywords.join(" ");
|
|
||||||
const queryVec = await getEmbedding(queryText);
|
|
||||||
|
|
||||||
const activeRows = await u.db("o_skillList").where("state", 1).whereNotNull("embedding").select();
|
|
||||||
const scored = activeRows
|
|
||||||
.map((row) => {
|
|
||||||
const emb = JSON.parse(row.embedding!) as number[];
|
|
||||||
return {
|
|
||||||
name: row.name,
|
|
||||||
filePath: row.path,
|
|
||||||
type: row.type,
|
|
||||||
description: row.description,
|
|
||||||
score: cosineSimilarity(queryVec, emb),
|
|
||||||
};
|
|
||||||
})
|
|
||||||
.sort((a, b) => b.score - a.score)
|
|
||||||
.slice(0, topK);
|
|
||||||
|
|
||||||
console.log(`[Skill] ✅ discover_skill_docs 返回 ${scored.length} 条推荐`);
|
|
||||||
return { recommendations: scored };
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
read_skill_file: tool({
|
read_skill_file: tool({
|
||||||
description: "读取已激活技能目录下的资源文件。传入 activate_skill 返回的 skill_resources 中的文件路径。",
|
description: "读取已激活技能目录下的资源文件。传入 activate_skill 返回的 skill_resources 中的文件路径。",
|
||||||
inputSchema: z.object({
|
inputSchema: z.object({
|
||||||
skillName: z.string().describe("技能名称"),
|
|
||||||
filePath: z.string().describe("资源文件的相对路径,来自 activate_skill 返回的 skill_resources"),
|
filePath: z.string().describe("资源文件的相对路径,来自 activate_skill 返回的 skill_resources"),
|
||||||
}),
|
}),
|
||||||
execute: async ({ skillName, filePath: relPath }) => {
|
execute: async ({ filePath }) => {
|
||||||
const fullPath = path.resolve(path.join(skill.baseDir, relPath));
|
const normalizedInputPath = toUnixPath(filePath).trim();
|
||||||
if (!isPathInside(fullPath, skill.baseDir)) {
|
if (!normalizedInputPath) {
|
||||||
console.log(`[Skill] 🚫 路径越界已拦截:"${relPath}" 超出技能目录范围`);
|
console.log(`📖[技法文件] ✗ filePath 不能为空`);
|
||||||
|
return { error: "filePath 不能为空" };
|
||||||
|
}
|
||||||
|
|
||||||
|
const fullPath = path.resolve(path.join(skillsRootDir, normalizedInputPath));
|
||||||
|
if (!(fullPath === skillsRootDir || isPathInside(fullPath, skillsRootDir))) {
|
||||||
|
console.log(`📖[技法文件] ✗ 路径越界已拦截:"${filePath}" 超出技能目录范围`);
|
||||||
return { error: "Access denied: path is outside skill directory" };
|
return { error: "Access denied: path is outside skill directory" };
|
||||||
}
|
}
|
||||||
|
let body = "";
|
||||||
try {
|
try {
|
||||||
const fileContent = await fs.readFile(fullPath, "utf-8");
|
body = await fs.promises.readFile(fullPath, "utf-8");
|
||||||
console.log(`[Skill] 📄 已读取文件:${skillName}/${relPath}(${fileContent.length} 字符)`);
|
console.log(`📖[技法文件] ✓ 已读取文件: ${filePath}(${body.length} 字符)`);
|
||||||
return { content: fileContent };
|
|
||||||
} catch {
|
} catch {
|
||||||
console.log(`[Skill] ❌ 读取失败:未找到文件 "${relPath}"`);
|
console.log(`📖[技法文件] ✗ 读取失败:未找到文件 "${filePath}"`);
|
||||||
return { error: `File not found: ${relPath}` };
|
return { error: `File not found: ${filePath}` };
|
||||||
}
|
}
|
||||||
|
const safeBody = ensureNonEmptyBody(body, "该资源文件为空。");
|
||||||
|
let content = "";
|
||||||
|
content = `<skill_content>\n`;
|
||||||
|
content += safeBody + "\n\n";
|
||||||
|
content += "可以使用 read_skill_file 工具读取资源文件。\n";
|
||||||
|
if (skillPaths.tertiarySkills.length > 0) {
|
||||||
|
content += "\n<skill_resources>\n";
|
||||||
|
for (const path of skillPaths.tertiarySkills) {
|
||||||
|
content += ` <file>${path}</file>\n`;
|
||||||
|
}
|
||||||
|
content += "</skill_resources>\n";
|
||||||
|
}
|
||||||
|
content += "</skill_content>";
|
||||||
|
console.log("%c Line:214 🍕 content", "background:#6ec1c2", content);
|
||||||
|
return { content };
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user