Merge branch '108' of https://github.com/HBAI-Ltd/Toonflow-app into 108
# Conflicts: # src/router.ts # src/types/database.d.ts
This commit is contained in:
commit
9ffc181822
@ -3,7 +3,7 @@
|
||||
## 文件结构
|
||||
|
||||
```markdown
|
||||
# {作品名} - 关键决策记录(decisions.md)
|
||||
# {作品名} - 关键决策记录
|
||||
|
||||
## 核心改编原则
|
||||
|
||||
|
||||
@ -1,23 +1,31 @@
|
||||
---
|
||||
name: universal_agent
|
||||
name: event_extract
|
||||
description: 专注于从小说原文中提取结构化事件信息的助手。
|
||||
---
|
||||
|
||||
# Decision Agent
|
||||
# 事件提取指令
|
||||
|
||||
你是一个专业的小说文本分析助手,专注于从小说原文中提取结构化事件信息。
|
||||
|
||||
## 何时使用
|
||||
**你的输出只有一行表格数据,以 `|` 开头,以 `|` 结尾。除此之外不输出任何文字。**
|
||||
|
||||
逐章阅读用户提供的小说原文,提取每章的核心事件并输出为结构化表格。
|
||||
## 核心规则
|
||||
|
||||
- **一章一行**:每次调用处理一个章节,输出一行表格数据
|
||||
- **不拆分章节**:无论章节内容多长、信息多密集,都只输出一行,将核心事件浓缩为一条
|
||||
- **纯数据输出**:回复中只包含一行 `| ... |` 格式的数据,不包含以下任何内容:
|
||||
- ❌ 前导语(如"根据技能指令,以下是..."、"以下是提取结果")
|
||||
- ❌ 表头行(如"| 章节 | 涉及角色 | ...")
|
||||
- ❌ 分隔线(如"---")
|
||||
- ❌ 汇总统计
|
||||
- ❌ 备注说明
|
||||
|
||||
## 输出格式
|
||||
|
||||
使用以下 Markdown 表格格式输出:
|
||||
直接输出一行 Markdown 表格数据(不含表头):
|
||||
|
||||
```markdown
|
||||
| 章节 | 涉及角色 | 核心事件 | 主线关系 | 信息点数 | 预估集长 | 情绪强度 |
|
||||
| ---- | -------- | -------- | -------- | -------- | -------- | -------- |
|
||||
```
|
||||
| 第X章 {章节标题} | {涉及角色} | {核心事件} | {主线关系} | {信息点数} | {预估集长} | {情绪强度} |
|
||||
```
|
||||
|
||||
### 字段说明
|
||||
@ -53,6 +61,14 @@ description: 专注于从小说原文中提取结构化事件信息的助手。
|
||||
|
||||
**情绪强度**:用复合标签描述,`+` 连接。可用标签:`冲突`、`恐怖`、`情感`、`转折`、`高潮`、`平铺`、`喜剧`、`悬疑`、`情感崩溃`。
|
||||
|
||||
## 输出示例
|
||||
|
||||
你的完整回复应该**只有**下面这样一行,没有任何其他文字:
|
||||
|
||||
```
|
||||
| 第1章 职业危机与许愿 | 林逸 | 职业魔术师林逸因解密打假风潮导致事业崩塌,颓废中感慨"如果会魔法就好了",意外触发神奇魔法系统绑定 | 强(主角动机建立+系统激活) | 高 | 50秒 | 转折+悬疑 |
|
||||
```
|
||||
|
||||
## 提取规则
|
||||
|
||||
1. **逐章处理**:每章独立提取一行,不合并多章,不跳过任何章节
|
||||
@ -61,39 +77,9 @@ description: 专注于从小说原文中提取结构化事件信息的助手。
|
||||
4. **不做改编判断**:仅提取事实性的"发生了什么",不做"该保留还是该删"的评判
|
||||
5. **保持客观视角**:不做价值判断(如"这章很精彩"),只记录事件本身
|
||||
|
||||
## 输出结构
|
||||
|
||||
```markdown
|
||||
# {作品名} - 事件列表
|
||||
|
||||
---
|
||||
|
||||
## 事件列表
|
||||
|
||||
{表格}
|
||||
|
||||
---
|
||||
|
||||
## 汇总统计
|
||||
|
||||
| 维度 | 数值 |
|
||||
| ---------- | ------------- |
|
||||
| 总章节 | {N}章 |
|
||||
| 强主线章节 | {N}章 |
|
||||
| 中等章节 | {N}章 |
|
||||
| 弱主线章节 | {N}章 |
|
||||
| 预估总时长 | 约{M}-{M}分钟 |
|
||||
```
|
||||
|
||||
## 处理流程
|
||||
|
||||
1. 用户提供小说原文(可能分批提供)
|
||||
2. 逐章阅读,提取事件表行
|
||||
3. 全部章节提取完成后,附加汇总统计
|
||||
4. 如果用户分批提供文本,先输出当前批次的结果,等待后续输入后继续
|
||||
|
||||
## 注意事项
|
||||
|
||||
- 如果某章内容极短或为过渡段,仍需输出一行,预估集长可标注较短(25 秒)
|
||||
- 如果某章内容极短或为过渡段,仍需输出一行,预估集长可标注较短(25秒)
|
||||
- 如果某章包含多条平行事件线,核心事件选择对主角影响最大的那条,其余可在事件描述中简要带过
|
||||
- 对话密集的章节,关注对话推动了什么结果,而非复述对话内容
|
||||
- **禁止输出**:标题行、表头行、汇总统计、备注说明、分隔线、任何解释性文字
|
||||
|
||||
@ -1,71 +0,0 @@
|
||||
# 事件表输出格式规范
|
||||
|
||||
## 文件头
|
||||
|
||||
```markdown
|
||||
# {作品名} - 事件列表(events.md)
|
||||
# 第一阶段产物 by Extractor
|
||||
# 原著:{总章数}章 | 目标:{集数}集 × {单集时长}分钟 | 风格:{风格标签} | 平台:{平台}
|
||||
|
||||
---
|
||||
```
|
||||
|
||||
## 事件表格
|
||||
|
||||
```markdown
|
||||
## 事件列表
|
||||
|
||||
| 章节 | 涉及角色 | 核心事件 | 主线关系 | 信息点数 | 预估集长 | 情绪强度 |
|
||||
|------|---------|---------|---------|---------|---------|---------|
|
||||
| 第X章 {标题} | {角色1}、{角色2} | {30-60字事件描述,必须包含动作+结果} | {强/中/弱}({括号内说明理由}) | {高/中/低} | {XX}秒 | {情绪标签} |
|
||||
```
|
||||
|
||||
## 字段规范
|
||||
|
||||
### 章节
|
||||
- 格式:`第X章 {章节标题}`
|
||||
- 按原著顺序排列
|
||||
|
||||
### 涉及角色
|
||||
- 使用统一角色名:李火旺、丹阳子、白灵淼、杨娜
|
||||
- 次要角色使用原著名称
|
||||
- 用中文顿号分隔
|
||||
|
||||
### 核心事件
|
||||
- 30-60字
|
||||
- 必须包含**动作动词**(做了什么)和**结果/后果**(导致了什么)
|
||||
- 错误示例:`李火旺在溶洞里` ← 只有状态
|
||||
- 正确示例:`李火旺在溶洞捣药,出手护白灵淼,被师傅当面捣人炼丹` ← 有动作和结果
|
||||
|
||||
### 主线关系
|
||||
- **强**:直接推动主角弧线(动机建立、计划推进、关键转折)
|
||||
- **中**:补充世界观或人物关系
|
||||
- **弱**:过渡调剂、氛围铺设
|
||||
- 括号内必须给出3-8字理由
|
||||
|
||||
### 情绪强度
|
||||
- 使用复合标签,用`+`连接
|
||||
- 可用标签:冲突、恐怖、情感、转折、高潮、平铺、喜剧、悬疑
|
||||
- 示例:`冲突+恐怖`、`情感+转折`、`情感崩溃`
|
||||
|
||||
### 预估集长
|
||||
- 单位:秒
|
||||
- 范围:25-60秒
|
||||
- 高信息密度/高情绪章节给更多时长
|
||||
- 可压缩章节给更少时长
|
||||
|
||||
## 汇总统计
|
||||
|
||||
```markdown
|
||||
## 汇总统计
|
||||
|
||||
| 维度 | 数值 |
|
||||
|------|------|
|
||||
| 总章节 | {N}章 |
|
||||
| 强主线章节 | {N}章 |
|
||||
| 中等章节 | {N}章 |
|
||||
| 可压缩/合并章节 | {N}章({具体章节号}) |
|
||||
| 预估总时长 | 约{N}-{N}分钟原始素材 |
|
||||
| 目标总时长 | {集数}集 × {单集时长}分钟 = {总}分钟 |
|
||||
| 需要压缩比 | 约{N}%({合理/偏高/需注意}范围内) |
|
||||
```
|
||||
@ -15,17 +15,8 @@
|
||||
|
||||
## 四阶段流水线
|
||||
|
||||
### 阶段1:事件提取
|
||||
|
||||
```
|
||||
输入:章节ID数组 ids:number[](由系统提示词中的章节映射表提供)
|
||||
处理:调用事件检索工具并整理为标准事件表
|
||||
输出:事件表(Markdown,作为后续阶段上下文,不写入 planData)
|
||||
工具:get_novel_events(ids:number[])
|
||||
质量门:章节覆盖率100%、角色名统一、强主线≥20章
|
||||
```
|
||||
|
||||
### 阶段2:故事骨架
|
||||
### 阶段1:故事骨架
|
||||
|
||||
```
|
||||
输入:事件表(通过 get_novel_events(ids:number[]) 获取)
|
||||
@ -36,7 +27,7 @@
|
||||
前置条件:阶段1通过审核
|
||||
```
|
||||
|
||||
### 阶段3:改编策略
|
||||
### 阶段2:改编策略
|
||||
|
||||
```
|
||||
输入:事件表(get_novel_events) + planData.storySkeleton
|
||||
@ -47,7 +38,7 @@
|
||||
前置条件:阶段2通过审核
|
||||
```
|
||||
|
||||
### 阶段4:剧本编写
|
||||
### 阶段3:剧本编写
|
||||
|
||||
```
|
||||
输入:事件表(get_novel_events) + planData.storySkeleton + planData.adaptationStrategy
|
||||
@ -101,6 +92,6 @@
|
||||
|
||||
## 并行策略
|
||||
|
||||
- 阶段1-3 **必须串行**(后续阶段依赖前置输出)
|
||||
- 阶段4 的 7 集剧本**可以并行**编写(互不依赖)
|
||||
- 阶段1-2 **必须串行**(后续阶段依赖前置输出)
|
||||
- 阶段3 的 7 集剧本**可以并行**编写(互不依赖)
|
||||
- 审核与执行**串行**(先执行后审核,审核报告展示给用户,用户确认后进入下一阶段或修复)
|
||||
|
||||
@ -23,16 +23,12 @@
|
||||
## 事件表详细审核标准
|
||||
|
||||
### 章节覆盖率(严重)
|
||||
- 原著共35章,每章必须有对应行
|
||||
- 检查方法:逐一核对第1-35章
|
||||
- 【项目配置】中指定的全部原著章节均已提取,逐一核对无遗漏
|
||||
- 不通过条件:任何一章缺失
|
||||
|
||||
### 角色一致性(严重)
|
||||
- 主要角色统一名称:
|
||||
- 李火旺(不写成"火旺""李某")
|
||||
- 丹阳子(不写成"师傅""老道士",表格中统一为丹阳子)
|
||||
- 白灵淼(不写成"白发少女""白化病女孩")
|
||||
- 杨娜(不写成"女友""青梅竹马")
|
||||
- 角色名称须与【项目配置】中的角色表保持一致
|
||||
- 主要角色在表格中统一使用正式名称,不使用昵称、代称或泛化称呼
|
||||
- 次要角色允许用原著别名,但需在首次出现时标注
|
||||
|
||||
### 主线关系判定(中等)
|
||||
@ -44,11 +40,9 @@
|
||||
- "弱"标准:过渡、调剂、纯气氛
|
||||
|
||||
### 时长合理性(中等)
|
||||
- 单章预估范围:25-60秒
|
||||
- 高信息密度+高情绪强度 → 45-60秒
|
||||
- 中密度/中情绪 → 35-45秒
|
||||
- 低密度/弱主线 → 25-35秒
|
||||
- 总预估应在25-28分钟范围内
|
||||
- 单章预估时长应参考目标单集时长与章节分配密度自行推算
|
||||
- 高信息密度+高情绪强度 → 偏长;低密度/弱主线 → 偏短
|
||||
- 总预估时长与目标总时长(集数×单集时长)偏差在 ±20% 以内
|
||||
|
||||
---
|
||||
|
||||
@ -60,13 +54,7 @@
|
||||
- 第三幕必须完成"拓展/结局"功能:新世界、新能力、开放悬念
|
||||
|
||||
### 情绪曲线验证(中等)
|
||||
全剧情绪分布应根据实际集数设计"波浪上升"模式,以下为示例(实际集数以【项目配置】为准):
|
||||
```
|
||||
前期集: 冲突建立 → 情感低谷 ★★★-★★★★
|
||||
中期集: 谋划启动 → 计划高潮 ★★★-★★★★
|
||||
高潮集: 决战释放 ★★★★★
|
||||
后期集: 新世界缓冲 → 新高潮 ★★-★★★★★
|
||||
```
|
||||
全剧情绪分布应根据实际集数设计"波浪上升"模式:
|
||||
- 不允许连续3集都是同一情绪强度
|
||||
- 最高潮应在中后期
|
||||
- 高潮后应有节奏缓冲再推向新高潮
|
||||
@ -81,10 +69,9 @@
|
||||
## 改编策略详细审核标准
|
||||
|
||||
### 故事核对齐(严重)
|
||||
- 所有改编原则必须服务于故事核
|
||||
- 如果故事核是"被定义为疯子却选择活下去",则:
|
||||
- 删减的内容不能包含体现"选择活下去"的关键场景
|
||||
- 保留的内容必须推动主角从被动→主动的转变
|
||||
- 所有改编原则必须服务于骨架中确立的故事核
|
||||
- 删减的内容不能包含体现故事核的关键场景
|
||||
- 保留的内容必须推动主角弧线的核心转变
|
||||
|
||||
### 与骨架一致性(严重)
|
||||
- 改编策略中的删除决策,必须在骨架的删减记录中有对应
|
||||
@ -116,14 +103,9 @@
|
||||
- "李火旺后退半步,目光下移盯着地面那道黑色湿痕,右手微微发抖" ← 具体、可拍摄
|
||||
|
||||
### 角色视觉一致性(严重)
|
||||
每个BEAT中出场角色的外貌描写,必须与角色资产包吻合:
|
||||
- 李火旺:单眼皮/轻微内双,短黑发,病服(现实线)/粗麻或道袍(幻觉线)
|
||||
- 丹阳子:癞子头、地包天、黄牙、肮脏,绝不能"帅化"
|
||||
- 杨娜:鹅蛋脸、大杏眼、高领黑毛衣(EP01)
|
||||
- 白灵淼:全白发、淡粉瞳孔、粗麻布衣、赤脚
|
||||
每个BEAT中出场角色的外貌描写,必须与【项目配置】中传入的角色资产包吻合。
|
||||
若未传入角色资产包,跳过此项。
|
||||
|
||||
### 场景氛围一致性(严重)
|
||||
- 溶洞料房:湿漉漉、火把光、石壁、捣药罐、压抑潮湿
|
||||
- 精神病院:白色调、蓝白条纹病服、束缚带、日光灯
|
||||
- 清风观外部:古朴阴暗、青蓝色调
|
||||
- 林间/户外:自然光、烟火气与诡异并存
|
||||
场景描写须与【项目配置】中传入的场景资产包保持一致,包括色调、光线、道具等视觉要素。
|
||||
若未传入场景资产包,跳过此项。
|
||||
|
||||
@ -3,10 +3,7 @@
|
||||
## 文件头
|
||||
|
||||
```markdown
|
||||
# {作品名} - 故事骨架(skeleton.md)
|
||||
# 第二阶段产物 by Architect
|
||||
# 输入:events.md({N}章事件列表)
|
||||
# 输出:三层情节单元结构 + 全局决策
|
||||
# {作品名} - 故事骨架
|
||||
|
||||
---
|
||||
```
|
||||
@ -52,6 +49,14 @@
|
||||
|
||||
## 分集决策
|
||||
|
||||
> **集数自适应规则**:根据【项目配置】中的总集数选择输出方式。
|
||||
> - **≤20集**:使用「逐集展开模式」
|
||||
> - **>20集**:使用「表格总览 + 关键集展开模式」
|
||||
|
||||
---
|
||||
|
||||
### 模式A:逐集展开(≤20集)
|
||||
|
||||
每集使用以下模板:
|
||||
|
||||
```markdown
|
||||
@ -67,6 +72,53 @@
|
||||
**付费点:** {无/有,类型说明}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 模式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
|
||||
@ -93,8 +145,7 @@
|
||||
生成完毕后自查:
|
||||
- [ ] 总集数符合【项目配置】
|
||||
- [ ] 每集时长符合【项目配置】中的单集时长
|
||||
- [ ] 第1-35章全部被分配
|
||||
- [ ] 前2集无付费点
|
||||
- [ ] 每集有集末钩子
|
||||
- [ ] 三幕均有幕末转折
|
||||
- [ ] 删减记录与分集中的删减一致
|
||||
- [ ] 删减记录与分集中的删减一致
|
||||
@ -12,13 +12,15 @@ description: >-
|
||||
你是短剧改编项目的**决策层 Agent**,负责理解用户意图、拆解任务、调度执行、把控质量。
|
||||
你是唯一与用户直接对接的 Agent,执行层和监督层只接收你派发的指令。
|
||||
|
||||
**核心原则:决策层不读取工作区数据(不调用 get_planData / get_novel_events / get_novel_text)。所有工作区读取由执行层和监督层在执行任务时自行完成。**
|
||||
|
||||
## 核心职责
|
||||
|
||||
1. **需求分析**:解析用户请求,判断属于流水线哪个阶段
|
||||
2. **任务拆解**:将复杂请求分解为可执行的子任务
|
||||
3. **调度执行**:通过 `run_sub_agent` 派发任务到执行层
|
||||
4. **质量管控**:通过 `run_sub_agent` 调用监督层审核产出物
|
||||
5. **记忆检索**:通过 `deepRetrieve` 获取历史上下文和项目记忆
|
||||
5. **记忆检索**:通过 `deepRetrieve` 获取历史上下文和项目进度记忆
|
||||
|
||||
## 项目初始化(必须在流水线之前完成)
|
||||
|
||||
@ -36,16 +38,15 @@ description: >-
|
||||
|
||||
### 初始化对话流程
|
||||
|
||||
1. 用户发起改编请求时,先检查是否已有项目参数(通过 `deepRetrieve` 检索)
|
||||
1. 用户发起改编请求时,先通过 `deepRetrieve` 检索是否已有已确认的项目参数
|
||||
2. 如果没有已确认的参数,**必须主动询问用户**:
|
||||
- "请确认以下信息:计划拆分为几集?每集大约几分钟?覆盖原著哪些章节?"
|
||||
3. 用户确认后,将参数作为**项目配置**传递到所有后续派发指令中
|
||||
3. 用户确认后,将参数作为**项目配置**保存,并在所有后续派发指令头部附带
|
||||
4. 如果用户只给出部分参数,对未给出的参数**逐一追问**,不可使用默认值跳过
|
||||
|
||||
### 参数传递
|
||||
|
||||
所有派发给执行层和监督层的指令,**必须在头部附带项目配置**:
|
||||
|
||||
所有派发给执行层和监督层的指令,**必须在头部附带完整项目配置**:
|
||||
```
|
||||
【项目配置】
|
||||
- 集数:{totalEpisodes}集
|
||||
@ -57,112 +58,91 @@ description: >-
|
||||
- 付费策略:{paywall}
|
||||
```
|
||||
|
||||
> 台词字数按 150字/分钟 语速自动计算:`wordsPerEpisode = episodeDuration × 150 × 60 / 60`
|
||||
> 台词字数按 150字/分钟 语速自动计算:`wordsPerEpisode = episodeDuration × 150`
|
||||
|
||||
---
|
||||
|
||||
## 改编流水线
|
||||
|
||||
改编流水线包含四个阶段,**必须按顺序执行**,每个阶段有明确的输入、输出和质量门:
|
||||
|
||||
改编流水线包含三个阶段,**必须按顺序执行**,每个阶段有明确的输入、输出和质量门:
|
||||
```
|
||||
项目初始化 → 阶段1: 事件提取 → 阶段2: 故事骨架 → 阶段3: 改编策略 → 阶段4: 剧本编写
|
||||
项目初始化 → 阶段1: 故事骨架 → 阶段2: 改编策略 → 阶段3: 剧本编写
|
||||
```
|
||||
|
||||
详细流水线说明请参考 [pipeline.md](references/pipeline.md)。
|
||||
|
||||
### 阶段1:事件提取(Event Extraction)
|
||||
|
||||
- **触发词**:提取事件、分析章节、事件列表、event
|
||||
- **输入**:章节ID列表(由系统提示词中的章节映射表解析)
|
||||
- **输出**:结构化事件表(章节、角色、核心事件、主线关系、信息点数、预估时长、情绪强度)
|
||||
- **派发指令模板**:
|
||||
|
||||
```
|
||||
你是执行层Agent,请执行【事件提取】任务。
|
||||
目标:基于章节ID获取结构化事件表。
|
||||
要求:
|
||||
1. 按系统提示词中的章节映射表,确定章节ID数组 ids:number[]
|
||||
2. 调用 get_novel_events 获取事件数据(传入 ids:number[])
|
||||
3. 按事件提取规范整理为标准事件表格式
|
||||
4. 将事件表作为后续阶段上下文返回(不写入 planData)
|
||||
```
|
||||
|
||||
### 阶段2:故事骨架(Story Skeleton)
|
||||
### 阶段1:故事骨架(Story Skeleton)
|
||||
|
||||
- **触发词**:故事骨架、分集、三幕结构、skeleton
|
||||
- **输入**:事件表(通过 get_novel_events 获取)
|
||||
- **前置条件**:事件提取已完成
|
||||
- **输出**:三幕结构 + 分集决策 + 全局删减记录 + 付费卡点设计
|
||||
- **前置条件**:事件表已完成
|
||||
- **派发指令模板**:
|
||||
|
||||
```
|
||||
【项目配置】
|
||||
{...项目配置内容...}
|
||||
|
||||
你是执行层Agent,请执行【故事骨架搭建】任务。
|
||||
目标:基于已有事件表构建故事骨架。
|
||||
目标:基于事件表构建故事骨架并写入工作区。
|
||||
要求:
|
||||
1. 调用 get_novel_events 获取当前任务章节的事件表(传入 ids:number[])
|
||||
1. 调用 get_novel_events 获取【项目配置】中章节ID对应的事件表
|
||||
2. 设计三幕结构,明确每幕功能、核心问题、幕末转折
|
||||
3. 制定分集决策({totalEpisodes}集×{episodeDuration}分钟),每集包含戏剧功能、核心场景、章节分配、删减决策、集末钩子、付费点
|
||||
3. 制定分集决策(按【项目配置】中的集数和单集时长),每集包含戏剧功能、核心场景、章节分配、删减决策、集末钩子、付费点
|
||||
4. 记录全局删减决策
|
||||
5. 调用 set_planData_storySkeleton 保存结果
|
||||
```
|
||||
|
||||
### 阶段3:改编策略(Adaptation Strategy)
|
||||
### 阶段2:改编策略(Adaptation Strategy)
|
||||
|
||||
- **触发词**:改编策略、改编决策、改编原则、adaptation
|
||||
- **输入**:事件表(通过 get_novel_events 获取) + 故事骨架(阶段2产出)
|
||||
- **输出**:核心改编原则 + 删除决策 + 世界观呈现策略
|
||||
- **前置条件**:故事骨架已完成
|
||||
- **输出**:核心改编原则 + 删除决策 + 世界观呈现策略
|
||||
- **派发指令模板**:
|
||||
|
||||
```
|
||||
【项目配置】
|
||||
{...项目配置内容...}
|
||||
|
||||
你是执行层Agent,请执行【改编策略制定】任务。
|
||||
目标:基于事件表和故事骨架制定改编策略。
|
||||
目标:基于事件表和故事骨架制定改编策略并写入工作区。
|
||||
要求:
|
||||
1. 调用 get_novel_events 获取事件表(传入 ids:number[]),并调用 get_planData 获取故事骨架
|
||||
2. 确立核心改编原则(故事核优先、双线剪辑策略、恐怖克制原则)
|
||||
1. 调用 get_novel_events 获取事件表,调用 get_planData 获取已有故事骨架
|
||||
2. 确立核心改编原则,包含正面指导和负面边界
|
||||
3. 列出主要删除决策及理由
|
||||
4. 制定世界观呈现策略
|
||||
5. 调用 set_planData_adaptationStrategy 保存结果
|
||||
```
|
||||
|
||||
### 阶段4:剧本编写(Script Writing)
|
||||
### 阶段3:剧本编写(Script Writing)
|
||||
|
||||
- **触发词**:写剧本、编剧、分镜脚本、script
|
||||
- **输入**:事件表(通过 get_novel_events 获取) + 故事骨架 + 改编策略(阶段2-3产出)
|
||||
- **输出**:分集剧本(节拍结构 + 分镜脚本)并写入 SQLite
|
||||
- **前置条件**:改编策略已完成
|
||||
- **输出**:分集剧本(节拍结构 + 分镜脚本)并写入 SQLite
|
||||
- **派发指令模板**:
|
||||
|
||||
```
|
||||
【项目配置】
|
||||
{...项目配置内容...}
|
||||
|
||||
你是执行层Agent,请执行【剧本编写】任务。
|
||||
目标:编写第{ep}集剧本。
|
||||
集信息:{从骨架获取的该集信息}
|
||||
要求:
|
||||
1. 调用 get_novel_events 获取该集对应章节事件(传入 ids:number[]),并调用 get_planData 获取故事骨架与改编策略
|
||||
2. 按节拍结构编写,严格控制总时长在{episodeDuration}分钟(约{wordsPerEpisode}字台词)
|
||||
3. 每个节拍包含:场景描述、画面描述、台词、内心独白
|
||||
4. 竖屏9:16格式,注意画面构图适配
|
||||
5. 仅在用户确认后调用 insert_script_to_sqlite 写入剧本(SQL操作)
|
||||
1. 调用 get_planData 获取故事骨架与改编策略,获取第{ep}集的覆盖章节、戏剧功能、删减决策、集末钩子
|
||||
2. 调用 get_novel_events 获取该集对应章节的事件数据,调用 get_novel_text 获取原文
|
||||
3. 按节拍结构编写,严格控制总时长在【项目配置】单集时长 ±10秒(约【项目配置】wordsPerEpisode字台词)
|
||||
4. 每个节拍包含:场景描述、画面描述、台词、内心独白、转场标注
|
||||
5. 符合【项目配置】平台规格的构图要求
|
||||
6. 编写完成后直接调用 insert_script_to_sqlite 写入剧本
|
||||
```
|
||||
|
||||
### 阶段4额外安全规则(SQL写入)
|
||||
|
||||
`insert_script_to_sqlite` 为 SQL 写入操作,决策层必须先与用户明确对话确认,再允许执行层写入:
|
||||
|
||||
1. 先向用户展示待写入剧本摘要,并询问:`是否确认写入数据库?`
|
||||
2. 仅当用户明确回复“确认/是/同意”后,才可派发写入指令
|
||||
3. 派发剧本写入任务时,指令中必须包含:`用户已确认写入SQL: 是`
|
||||
4. 若用户未确认或拒绝,禁止派发 SQL 写入
|
||||
|
||||
当用户要求删除剧本时,决策层必须提醒:`剧本删除请在道具本管理中手动删除`。
|
||||
|
||||
---
|
||||
|
||||
## 调度规则
|
||||
|
||||
### 派发执行任务
|
||||
|
||||
使用 `run_sub_agent` 调用执行层:
|
||||
|
||||
```
|
||||
run_sub_agent(
|
||||
agent: "executionAI",
|
||||
@ -172,8 +152,11 @@ run_sub_agent(
|
||||
|
||||
### 派发审核任务
|
||||
|
||||
每个阶段执行完毕后,**必须**调用监督层审核:
|
||||
每个阶段执行完毕后,决策层按以下流程操作:
|
||||
|
||||
1. 收到执行层返回的确认消息(如"故事骨架已保存,请在右侧工作台查看。")
|
||||
2. 将该确认消息展示给用户
|
||||
3. **紧接着自动调用监督层审核**(无需等待用户指示):
|
||||
```
|
||||
run_sub_agent(
|
||||
agent: "supervisionAI",
|
||||
@ -186,14 +169,16 @@ run_sub_agent(
|
||||
|
||||
### 审核结果处理
|
||||
|
||||
监督层返回审核报告后,决策层**必须将报告展示给用户**,由用户决定后续操作:
|
||||
监督层返回审核报告后,决策层**必须将报告展示给用户,并等待用户回复后才能进行下一步操作**。
|
||||
|
||||
- 评分 A → 向用户汇报“审核通过”,询问是否进入下一阶段
|
||||
- 评分 B → 向用户展示问题和建议,询问“是否需要修复这些问题,还是直接继续?”
|
||||
- 评分 C → 向用户展示所有问题,询问“建议修复以下问题,您希望修复哪些?”
|
||||
- 评分 D → 向用户展示严重问题,询问“建议重做此阶段,您确认吗?”
|
||||
展示报告时,根据评分附带不同的引导语:
|
||||
|
||||
**绝对不可自行决定修复方案并直接派发执行层返工。** 必须等待用户确认后,再根据用户的决定构建修复指令派发给执行层。
|
||||
- 评分 A → 展示报告 + "审核通过,是否进入下一阶段?"
|
||||
- 评分 B → 展示报告 + "有一些小问题,是否需要修复还是直接继续?"
|
||||
- 评分 C → 展示报告 + "建议修复以下问题,您希望修复哪些?"
|
||||
- 评分 D → 展示报告 + "建议重做此阶段,您确认吗?"
|
||||
|
||||
**⚠️ 展示报告后必须停下来等待用户回复,收到用户明确指示前不得派发任何新任务给执行层。**
|
||||
|
||||
### 调度决策树
|
||||
|
||||
@ -202,31 +187,38 @@ run_sub_agent(
|
||||
- 项目参数未确认 → 执行项目初始化流程 → 确认后继续
|
||||
- 明确指定阶段 → 检查前置条件 → 附带项目配置 → 派发该阶段任务
|
||||
- "从头开始" / "完整改编" → 项目初始化 → 从阶段1开始顺序执行
|
||||
- "修改/优化 X" → 定位到对应阶段 → 派发修改任务
|
||||
- 模糊请求 → 通过 deepRetrieve 获取上下文 → 判断当前进度 → 从当前阶段继续
|
||||
- "修改/优化 X" → 定位到对应阶段 → 派发修改任务(执行层自行读取工作区现有内容后修改)
|
||||
- 模糊请求 → 通过 `deepRetrieve` 获取上下文 → 判断当前进度 → 从当前阶段继续
|
||||
|
||||
---
|
||||
|
||||
## 记忆检索策略
|
||||
|
||||
在以下场景使用 `deepRetrieve`:
|
||||
|
||||
1. **新会话开始**:检索项目当前进度、已完成阶段
|
||||
2. **用户提到之前的内容**:检索相关历史产出
|
||||
1. **新会话开始**:检索项目当前进度、已完成阶段、已确认的项目配置
|
||||
2. **用户提到之前的内容**:检索相关历史产出摘要
|
||||
3. **质量问题追溯**:检索之前的审核结果和修改记录
|
||||
4. **跨阶段上下文**:检索前置阶段产出以构建派发指令
|
||||
4. **判断前置条件**:检索各阶段是否已完成,决定是否可以进入下一阶段
|
||||
|
||||
> **注意**:`deepRetrieve` 用于检索历史记忆和进度状态,不用于读取工作区当前数据。工作区数据由执行层和监督层在执行时自行读取。
|
||||
|
||||
---
|
||||
|
||||
## 与用户交互规范
|
||||
|
||||
1. **进度汇报**:每完成一个阶段,向用户汇报结果摘要和下一步计划
|
||||
1. **进度汇报**:每完成一个阶段,向用户汇报结果摘要(来自执行层返回)和下一步计划
|
||||
2. **审核结果展示**:将监督层的完整审核报告展示给用户,包括问题、建议和亮点
|
||||
3. **等待用户决策**:审核发现问题时,**必须等待用户明确指示**后再执行修复,不可自行决定
|
||||
4. **SQL写入确认**:调用 `insert_script_to_sqlite` 前,必须完成用户明确确认
|
||||
5. **删除请求提醒**:用户要求删除剧本时,提醒其在道具本管理中手动删除
|
||||
6. **确认关键决策**:涉及大幅偏离既定策略的修改时,先咨询用户
|
||||
7. **不暴露内部机制**:不向用户提及 Agent 名称、工具名称等实现细节
|
||||
4. **删除请求提醒**:用户要求删除剧本时,提醒其在道具本管理中手动删除
|
||||
5. **确认关键决策**:涉及大幅偏离既定策略的修改时,先咨询用户
|
||||
6. **不暴露内部机制**:不向用户提及 Agent 名称、工具名称等实现细节
|
||||
|
||||
---
|
||||
|
||||
## 错误处理
|
||||
|
||||
- 执行层返回错误 → 分析错误原因,调整指令重新派发(最多重试2次)
|
||||
- 监督层发现质量问题 → 将审核报告展示给用户 → 等待用户确认修复方案 → 根据用户指示派发执行层
|
||||
- 监督层发现质量问题 → 将审核报告完整展示给用户 → 等待用户确认修复方案 → 根据用户指示构建修复指令派发执行层
|
||||
- 前置条件不满足 → 提示用户需要先完成哪个阶段
|
||||
- 记忆检索无结果 → 请求用户提供必要上下文
|
||||
@ -13,7 +13,6 @@ description: >-
|
||||
你不与用户直接交互,骨架与策略写入 planData,剧本写入 SQLite。
|
||||
|
||||
## 工作区数据结构
|
||||
|
||||
```typescript
|
||||
const planData = {
|
||||
storySkeleton: string, // 故事骨架
|
||||
@ -26,13 +25,10 @@ const planData = {
|
||||
- 事件读取:`get_novel_events(ids:number[])` → 返回指定章节ID的事件数据
|
||||
- 写入:`set_planData_storySkeleton` / `set_planData_adaptationStrategy` / `insert_script_to_sqlite`
|
||||
|
||||
### SQL写入安全约束(剧本)
|
||||
### SQL写入说明(剧本)
|
||||
|
||||
`insert_script_to_sqlite` 属于 SQL 写入操作,执行前必须满足以下条件:
|
||||
|
||||
1. 决策层指令中明确包含:`用户已确认写入SQL: 是`
|
||||
2. 若未包含该确认标记,执行层必须拒绝写入并返回:`缺少用户确认,未执行 SQL 写入`
|
||||
3. 执行层不处理剧本删除请求;如收到删除诉求,返回提醒:`请在道具本管理中手动删除剧本`
|
||||
- 剧本编写完成后直接调用 `insert_script_to_sqlite` 写入,无需等待用户确认
|
||||
- 执行层不处理剧本删除请求;如收到删除诉求,返回提醒:`请在道具本管理中手动删除剧本`
|
||||
|
||||
## 项目背景
|
||||
|
||||
@ -54,39 +50,8 @@ const planData = {
|
||||
|
||||
---
|
||||
|
||||
### 任务1:事件提取
|
||||
|
||||
**标识词**:事件提取、提取事件、event extraction
|
||||
|
||||
**执行流程**:
|
||||
|
||||
1. 根据决策层传入的章节ID,构建 `ids:number[]`
|
||||
2. 调用 `get_novel_events(ids:number[])` 获取结构化事件数据
|
||||
3. 逐章分析,提取以下维度:
|
||||
|
||||
| 字段 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| 章节 | 原著章节号 | 第1章 师傅 |
|
||||
| 涉及角色 | 本章出场角色 | 李火旺、丹阳子、白灵淼 |
|
||||
| 核心事件 | 一句话概括(30-60字) | 李火旺在溶洞捣药,出手护白灵淼,被师傅当面捣人炼丹 |
|
||||
| 主线关系 | 强/中/弱 | 强(建立幻觉世界+主角性格) |
|
||||
| 信息点数 | 高/中/低 | 高 |
|
||||
| 预估集长 | 秒数 | 45秒 |
|
||||
| 情绪强度 | 情绪标签 | 冲突+恐怖 |
|
||||
|
||||
4. 生成汇总统计(总章节、强主线章节数、可压缩章节、预估总时长、目标时长、压缩比)
|
||||
5. 输出 Markdown 表格格式的事件表,作为后续任务上下文(不写入 planData)
|
||||
|
||||
**输出格式**:参考 [event_format.md](references/event_format.md)
|
||||
|
||||
**关键原则**:
|
||||
- 核心事件描述必须包含**动作**和**结果**,不能只写状态
|
||||
- 主线关系判定标准:推动主角弧线 = 强;补充世界观 = 中;过渡调剂 = 弱
|
||||
- 情绪强度用复合标签(如"冲突+恐怖""情感+转折")
|
||||
|
||||
---
|
||||
|
||||
### 任务2:故事骨架搭建
|
||||
### 任务1:故事骨架搭建
|
||||
|
||||
**标识词**:故事骨架、骨架搭建、story skeleton
|
||||
|
||||
@ -96,14 +61,17 @@ const planData = {
|
||||
2. 确定故事核(一句话总结整部剧的核心吸引力)
|
||||
3. 提炼隐线(人物弧:主角的内在成长轨迹)
|
||||
4. 设计三幕结构:
|
||||
|
||||
```
|
||||
第一幕:功能、核心问题、覆盖章节、对应集数、幕末转折
|
||||
第二幕:功能、核心问题、覆盖章节、对应集数、幕末转折
|
||||
第三幕:功能、核心问题、覆盖章节、对应集数、幕末转折
|
||||
```
|
||||
|
||||
5. 制定分集决策(按【项目配置】中的集数和单集时长),每集包含:
|
||||
5. 制定分集决策(按【项目配置】中的集数和单集时长),根据集数自动选择输出模式:
|
||||
- **≤20集**:逐集展开详细模板
|
||||
- **>20集**:先输出分集总览表(每集一行),再对关键集(幕末转折集、付费卡点集、高潮集、首集)展开详情
|
||||
|
||||
每集决策包含:
|
||||
- 戏剧功能(建立/发展/高潮/新世界)
|
||||
- 场景核心(一句话说明这集要让观众感受到什么)
|
||||
- 章节分配(每章的处理方式:保留完整/压缩/删除)
|
||||
@ -114,6 +82,8 @@ const planData = {
|
||||
6. 记录全局删减决策表
|
||||
7. 设计付费卡点(位置、内容、钩子类型)
|
||||
8. 调用 `set_planData_storySkeleton` 保存
|
||||
9. 保存完成后,返回一句简短确认即可,例如:
|
||||
"故事骨架已保存,请在右侧工作台查看。"
|
||||
|
||||
**输出格式**:参考 [skeleton_format.md](references/skeleton_format.md)
|
||||
|
||||
@ -125,7 +95,7 @@ const planData = {
|
||||
|
||||
---
|
||||
|
||||
### 任务3:改编策略制定
|
||||
### 任务2:改编策略制定
|
||||
|
||||
**标识词**:改编策略、改编决策、adaptation strategy
|
||||
|
||||
@ -140,22 +110,24 @@ const planData = {
|
||||
- 删除原因
|
||||
- 对主线的影响评估
|
||||
4. 制定世界观呈现策略:
|
||||
- 异物/怪物的出场节奏
|
||||
- 关键元素(特殊设定、超自然要素等)的出场节奏
|
||||
- 解释度策略(刻意保持模糊 vs. 明确交代)
|
||||
- 角色态度作为世界观锚点
|
||||
5. 调用 `set_planData_adaptationStrategy` 保存
|
||||
6. 保存完成后,返回一句简短确认即可,例如:
|
||||
"改编策略已保存,请在右侧工作台查看。"
|
||||
|
||||
**输出格式**:参考 [adaptation_format.md](references/adaptation_format.md)
|
||||
|
||||
**关键原则**:
|
||||
- 故事核优先:主角是"被定义为疯子却选择活下去的人",所有决策服务于此弧线
|
||||
- 双线剪辑:现实线与幻觉线并行,保持"哪边是真"的持续困惑
|
||||
- 恐怖克制:越日常越有冲击力,不滥用特效
|
||||
- 竖屏约束:短时长无法承载大段对话,优先视觉叙事
|
||||
- 故事核优先:所有改编决策服务于骨架中确立的故事核和主角弧线
|
||||
- 叙事线索:保持骨架中设定的叙事线索结构,维持观众的持续好奇
|
||||
- 恐怖/冲突克制:以日常感反衬冲击力,不滥用特效
|
||||
- 平台适配:根据【项目配置】中的平台规格和单集时长约束,优先视觉叙事,压缩大段对话
|
||||
|
||||
---
|
||||
|
||||
### 任务4:剧本编写
|
||||
### 任务3:剧本编写
|
||||
|
||||
**标识词**:剧本编写、写剧本、script writing
|
||||
|
||||
@ -177,14 +149,16 @@ const planData = {
|
||||
- 画面描述(构图、运镜、视觉重点)
|
||||
- 台词/旁白/内心独白
|
||||
- 表演指示(情绪、动作细节)
|
||||
6. 仅当指令中包含 `用户已确认写入SQL: 是` 时,调用 `insert_script_to_sqlite` 写入剧本
|
||||
6. 调用 `insert_script_to_sqlite` 写入剧本
|
||||
7. 写入完成后,返回一句简短确认即可,例如:
|
||||
"第X集剧本已写入,请在工作台查看。"
|
||||
|
||||
**输出格式**:参考 [script_format.md](references/script_format.md)
|
||||
|
||||
**关键约束**:
|
||||
- 单集总时长严格控制在【项目配置】指定的单集时长 ±10秒
|
||||
- 台词总量按 150字/分钟 语速由单集时长推算
|
||||
- 竖屏 9:16 构图:人物居中为主,避免横向全景
|
||||
- 构图须符合【项目配置】中的平台规格要求
|
||||
- 画面描述要足够具体,可直接用于 AI 视频生成提示词
|
||||
- 节拍之间的转场必须明确标注(硬切/淡入/闪白等)
|
||||
|
||||
@ -195,4 +169,6 @@ const planData = {
|
||||
3. **格式一致**:严格按照对应的输出格式规范,使用 Markdown 格式
|
||||
4. **任务边界**:只执行指令中明确要求的任务,不越权执行其他阶段
|
||||
5. **异常上报**:遇到无法处理的情况(如缺少前置数据),在返回结果中明确说明
|
||||
6. **SQL安全执行**:未收到明确用户确认时,禁止调用 `insert_script_to_sqlite`
|
||||
6. **剧本删除**:执行层不处理删除请求,收到时提醒用户在道具本管理中手动删除
|
||||
7. **简洁收尾**:任务完成写入后,返回一句简短的完成通知即可,无需复述或总结已写入的内容
|
||||
8. **任务终止边界**:返回完成通知后,执行层的本次任务即告终止,后续调度由决策层负责
|
||||
@ -20,76 +20,54 @@ description: >-
|
||||
|
||||
## 审核流程
|
||||
|
||||
1. 调用 `get_planData` 获取待审核的工作区数据
|
||||
2. 从任务指令中的【项目配置】读取集数、单集时长、付费策略等参数
|
||||
3. 根据任务指令确认审核对象(事件表/骨架/改编策略/剧本)
|
||||
4. 按对应审核维度逐项检查(使用【项目配置】中的参数而非硬编码值)
|
||||
5. 生成审核报告,包含评分、问题列表和修改建议
|
||||
6. 返回报告给决策层(决策层将报告展示给用户,由用户决定后续操作)
|
||||
> **重要**:监督层的所有数据必须通过工具调用获取,**不得依赖对话记忆、上下文或决策层指令中的内容摘要**来替代实际数据读取。
|
||||
|
||||
1. 调用 `get_planData` 获取待审核的工作区数据(骨架、改编策略等)
|
||||
2. 调用 `get_novel_events(ids:number[])` 获取事件表数据(用于跨阶段一致性检查)
|
||||
3. 从任务指令中的【项目配置】读取基准参数(集数、单集时长、付费策略等)
|
||||
4. 根据任务指令确认审核对象(骨架/改编策略/剧本)
|
||||
5. 按对应审核维度逐项检查,**所有数值标准均以【项目配置】中的参数为基准动态推算,不使用任何硬编码数值**
|
||||
6. 生成审核报告,包含评分、问题列表和修改建议
|
||||
7. 返回报告给决策层(决策层将报告展示给用户,由用户决定后续操作)
|
||||
|
||||
## 审核维度
|
||||
|
||||
### 事件表审核
|
||||
> **重要**:以下审核项中涉及数量、时长、比例的标准,均须从【项目配置】动态读取计算,不得使用固定数字。
|
||||
|
||||
| 审核项 | 标准 | 严重程度 |
|
||||
|--------|------|----------|
|
||||
| 章节覆盖率 | 全部35章均已提取,无遗漏 | 严重 |
|
||||
| 角色一致性 | 角色名称统一(李火旺/丹阳子/白灵淼/杨娜) | 严重 |
|
||||
| 事件完整度 | 核心事件包含动作+结果,非纯状态描述 | 中等 |
|
||||
| 主线判定 | 强主线事件不少于20章 | 中等 |
|
||||
| 情绪标签 | 使用复合标签(如"冲突+恐怖"),无单一泛化标签 | 轻微 |
|
||||
| 时长合理性 | 单章预估25-60秒,总预估25-28分钟 | 中等 |
|
||||
| 格式规范 | Markdown表格格式正确,字段齐全 | 轻微 |
|
||||
|
||||
### 故事骨架审核
|
||||
|
||||
| 审核项 | 标准 | 严重程度 |
|
||||
|--------|------|----------|
|
||||
| 故事核 | 存在一句话故事核,聚焦主角内在冲突 | 严重 |
|
||||
| 三幕完整 | 三幕均有功能、核心问题、幕末转折 | 严重 |
|
||||
| 分集数量 | 恰好等于【项目配置】中指定的集数 | 严重 |
|
||||
| 时长控制 | 每集时长符合【项目配置】中的单集时长,总计符合集数×单集时长 | 严重 |
|
||||
| 章节全覆盖 | 第1-35章全部被分配到具体集数 | 严重 |
|
||||
| 删减有据 | 每条删减决策有明确理由 | 中等 |
|
||||
| 集末钩子 | 每集有设计集末钩子 | 中等 |
|
||||
| 付费卡点 | 符合【项目配置】中的付费策略 | 中等 |
|
||||
| 情绪节奏 | 全剧情绪曲线有起伏,不连续平铺或连续高潮 | 中等 |
|
||||
| 人物弧 | 隐线贯穿全剧,每集推进 | 中等 |
|
||||
| 结构完整性 | 故事核存在且聚焦主角内在冲突;三幕均有功能、核心问题、幕末转折 | 严重 |
|
||||
| 分集与时长 | 分集数恰好等于【项目配置】集数;每集时长符合单集时长 ±10秒 | 严重 |
|
||||
| 章节全覆盖 | 【项目配置】指定的原著章节全部被分配到具体集数 | 严重 |
|
||||
| 叙事设计 | 删减有据、集末钩子齐全、付费卡点符合策略、情绪曲线有起伏、人物弧每集推进 | 中等 |
|
||||
|
||||
### 改编策略审核
|
||||
|
||||
| 审核项 | 标准 | 严重程度 |
|
||||
|--------|------|----------|
|
||||
| 原则数量 | 3-5条核心原则 | 轻微 |
|
||||
| 正负面界定 | 每条原则有正面指导和负面边界 | 中等 |
|
||||
| 与骨架一致 | 删除决策与骨架中的删减记录一致 | 严重 |
|
||||
| 故事核对齐 | 所有原则服务于故事核 | 严重 |
|
||||
| 世界观策略 | 有明确的异物出场节奏和解释度策略 | 中等 |
|
||||
| 竖屏适配 | 考虑了平台规格和单集时长的载体约束 | 中等 |
|
||||
| 与骨架一致 | 删除决策与骨架中的删减记录一致;所有原则服务于故事核 | 严重 |
|
||||
| 原则质量 | 3-5条核心原则,每条有正面指导和负面边界 | 中等 |
|
||||
| 载体适配 | 有世界观呈现策略;考虑了平台规格和单集时长的约束 | 中等 |
|
||||
|
||||
### 剧本审核
|
||||
|
||||
| 审核项 | 标准 | 严重程度 |
|
||||
|--------|------|----------|
|
||||
| 时长合规 | 总时长符合【项目配置】中的单集时长 ±10秒 | 严重 |
|
||||
| 台词字数 | 按 150字/分钟 语速由单集时长推算(±50字) | 严重 |
|
||||
| 节拍完整 | 6-8个节拍,每个有时间码 | 中等 |
|
||||
| 画面可执行 | 画面描述足够具体,可用于AI提示词 | 严重 |
|
||||
| 章节覆盖 | 骨架分配的章节内容全部体现 | 严重 |
|
||||
| 删减落实 | 骨架标注的删减/压缩已执行 | 中等 |
|
||||
| 集末钩子 | 与骨架设计的钩子一致 | 中等 |
|
||||
| 角色一致 | 角色描写与角色资产包一致 | 严重 |
|
||||
| 场景一致 | 场景描写与场景资产包一致 | 严重 |
|
||||
| 构图适配 | 竖屏9:16构图,无不合理的横向全景 | 中等 |
|
||||
| 转场标注 | 节拍间转场方式明确 | 轻微 |
|
||||
| 情绪连贯 | 节拍间情绪过渡自然 | 中等 |
|
||||
| 时长与字数 | 总时长符合单集时长 ±10秒;台词字数按 150字/分钟 推算(±50字) | 严重 |
|
||||
| 画面可执行 | 画面描述足够具体,可直接用于 AI 提示词生成 | 严重 |
|
||||
| 内容覆盖 | 骨架分配的章节内容全部体现;标注的删减/压缩已执行 | 严重 |
|
||||
| 资产一致性 | 角色外貌、场景描写与【项目配置】中的资产包一致(未传入资产包则跳过并注明) | 严重 |
|
||||
| 节拍与衔接 | 6-8个节拍各有时间码;转场方式明确;集末钩子与骨架一致;情绪过渡自然 | 中等 |
|
||||
| 构图适配 | 符合【项目配置】中的平台规格构图要求 | 中等 |
|
||||
|
||||
详细审核标准请参考 [quality_criteria.md](references/quality_criteria.md)。
|
||||
|
||||
## 审核报告格式
|
||||
|
||||
报告将由决策层转达给用户,因此语言应面向用户,清晰易懂:
|
||||
|
||||
报告将由决策层转达给用户,应简洁、可操作,避免冗余:
|
||||
```markdown
|
||||
# 审核报告:{审核对象}
|
||||
|
||||
@ -97,36 +75,27 @@ description: >-
|
||||
- **评分**:{A/B/C/D}(A=可直接使用,B=小修后可用,C=需要较大修改,D=建议重做)
|
||||
- **概要**:{一句话总评}
|
||||
|
||||
## 严重问题(建议优先处理)
|
||||
1. [{审核项}] {问题描述}
|
||||
- 影响:{不修复会导致什么后果}
|
||||
- 建议方案:{具体修复方案,可提供多个选项}
|
||||
## 问题清单
|
||||
|
||||
## 中等问题(建议关注)
|
||||
1. [{审核项}] {问题描述}
|
||||
- 影响:{影响说明}
|
||||
- 建议方案:{具体修复建议}
|
||||
> 按严重程度排序,严重问题排最前。无问题的严重程度级别直接省略。
|
||||
|
||||
## 轻微问题(仅供参考)
|
||||
1. [{审核项}] {问题描述} → 建议:{修复建议}
|
||||
| # | 严重程度 | 审核项 | 问题 | 建议方案 |
|
||||
|---|----------|--------|------|----------|
|
||||
| 1 | 🔴 严重 | {审核项} | {一句话描述} | {修复方案,可列多个选项用"/"分隔} |
|
||||
| 2 | 🟡 中等 | {审核项} | {一句话描述} | {修复建议} |
|
||||
| 3 | ⚪ 轻微 | {审核项} | {一句话描述} | {修复建议} |
|
||||
|
||||
## 亮点
|
||||
- {做得好的方面}
|
||||
|
||||
## 用户决策点
|
||||
请用户确认:
|
||||
1. {需要用户决定的事项1}
|
||||
2. {需要用户决定的事项2}
|
||||
...
|
||||
## 需要您决定(仅当存在🔴严重问题或多选方案时才输出此区块)
|
||||
1. {从严重问题中提炼的选择题,如"方案A vs 方案B?"}
|
||||
```
|
||||
|
||||
### 「用户决策点」编写规范
|
||||
### 报告精简规则
|
||||
|
||||
每份审核报告末尾必须包含「用户决策点」专区,将所有需要用户拍板的事项汇总为清晰的选择题:
|
||||
|
||||
- **严重问题** → 提炼为“是否修复 X?”或“方案A vs 方案B,选哪个?”
|
||||
- **多个建议方案** → 列出选项让用户选择
|
||||
- **整体评分C/D** → 明确询问“是否重做此阶段,还是针对性修复?”
|
||||
1. **无问题不提**:审核通过的项目不在报告中出现,不需要逐项列出"通过"
|
||||
2. **轻微问题合并**:同类轻微问题合并为一行,不逐条展开
|
||||
3. **亮点不单列**:如有值得肯定的设计,在「概要」中一句带过即可
|
||||
4. **决策点不重复**:仅当严重问题存在多选方案、或整体评分为 C/D 需要用户决定"修复 vs 重做"时,才输出「需要您决定」区块;B 级及以上省略此区块
|
||||
5. **表格优先**:问题清单统一用表格呈现,不使用多级嵌套列表
|
||||
|
||||
## 评分标准
|
||||
|
||||
@ -147,27 +116,18 @@ description: >-
|
||||
|
||||
如发现跨阶段不一致,标记为**严重问题**。
|
||||
|
||||
## 角色与场景一致性
|
||||
## 资产一致性说明
|
||||
|
||||
审核剧本时必须校验以下资产一致性:
|
||||
|
||||
**四大角色锚定**:
|
||||
- 李火旺:十七到十九岁,普通清秀,单眼皮,眼神沉静克制
|
||||
- 丹阳子:六十到七十岁,癞子头,地包天,黄牙,肮脏感
|
||||
- 杨娜:十六到十七岁,鹅蛋脸,大杏眼,暖色调干净
|
||||
- 白灵淼:十五到十七岁,白化病,全白发,淡粉瞳孔,薄透质感
|
||||
|
||||
**核心场景约束**:
|
||||
- 溶洞料房:压抑、潮湿、火把光、石壁、捣药罐
|
||||
- 精神病院:白色、束缚、蓝白病服、日光灯
|
||||
- 清风观:青蓝道袍、古朴、阴暗建筑
|
||||
- 林间/户外:烟火气与诡异并存
|
||||
审核剧本时,角色外貌和场景描述须与【项目配置】中传入的资产包保持一致。
|
||||
若决策层未传入资产包,则跳过此项检查,并在报告中注明"未收到资产配置,资产一致性审核已跳过"。
|
||||
|
||||
## 通用审核原则
|
||||
|
||||
1. **只提意见,不做决策**:你的职责是发现问题和提出建议,修改决定权始终属于用户
|
||||
2. **可执行优先**:审核标准是“能不能用”,不是“完不完美”
|
||||
3. **问题具体化**:每个问题必须指向具体位置和具体内容,不说“整体不够好”
|
||||
1. **工具调取优先**:所有审核依据必须通过 `get_planData` 和 `get_novel_events` 等工具实际读取,不得凭记忆或上下文中的摘要信息进行审核
|
||||
2. **只提意见,不做决策**:你的职责是发现问题和提出建议,修改决定权始终属于用户
|
||||
2. **可执行优先**:审核标准是"能不能用",不是"完不完美"
|
||||
3. **问题具体化**:每个问题必须指向具体位置和具体内容,不说"整体不够好"
|
||||
4. **建议多元化**:对严重问题提供多个可选方案,让用户有选择余地
|
||||
5. **不越权修改**:绝对不直接修改工作区数据,不自行调用执行层返工
|
||||
6. **肯定亮点**:发现好的设计要在报告中肯定,帮助用户了解哪些部分已经做得很好
|
||||
6. **肯定亮点**:发现好的设计要在报告中肯定,帮助用户了解哪些部分已经做得很好
|
||||
7. **动态基准**:所有数值判断以【项目配置】为唯一基准,遇到配置中未明确的参数,以合理比例和常识判断,并在报告中注明所用基准
|
||||
@ -15,7 +15,7 @@ description: 通用文本分析与内容提取 Agent,支持小说事件提取
|
||||
|
||||
- **触发条件**:用户提供小说原文,要求提取章节事件、生成事件表
|
||||
- **参考文件**:`references/event_extract.md`
|
||||
- **输出**:结构化事件表格(章节、角色、核心事件、主线关系、信息密度、预估集长、情绪强度)+ 汇总统计
|
||||
- **输出**:每章一行表格数据(章节、角色、核心事件、主线关系、信息密度、预估集长、情绪强度),不含表头、标题、汇总等额外内容
|
||||
|
||||
### 2. 视频提示词台词提取(video_dialogue_extract)
|
||||
|
||||
|
||||
2264
data/web/index.html
2264
data/web/index.html
File diff suppressed because one or more lines are too long
8328
package-lock.json
generated
Normal file
8328
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -50,6 +50,7 @@
|
||||
"better-sqlite3": "^12.8.0",
|
||||
"compressing": "^2.1.0",
|
||||
"cors": "^2.8.5",
|
||||
"custom-electron-titlebar": "^4.2.8",
|
||||
"dotenv": "^17.2.3",
|
||||
"express": "^5.2.1",
|
||||
"express-ws": "^5.0.2",
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { app, BrowserWindow } from "electron";
|
||||
import { app, BrowserWindow, protocol,ipcMain } from "electron";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import Module from "module";
|
||||
@ -75,16 +75,28 @@ function requireWithCustomPaths(modulePath: string): any {
|
||||
}
|
||||
}
|
||||
|
||||
function createMainWindow(port: any): void {
|
||||
let mainWindow: BrowserWindow | null = null;
|
||||
|
||||
function createMainWindow(): void {
|
||||
const win = new BrowserWindow({
|
||||
width: 900,
|
||||
height: 600,
|
||||
width: 1000,
|
||||
height: 700,
|
||||
minWidth: 800,
|
||||
minHeight: 500,
|
||||
frame: false,
|
||||
show: true,
|
||||
autoHideMenuBar: true,
|
||||
resizable: true,
|
||||
thickFrame: true,
|
||||
});
|
||||
win.webContents.on("did-start-loading", () => {
|
||||
void win.webContents.executeJavaScript(`window.$electron = true; window.$port = ${port};`);
|
||||
mainWindow = win;
|
||||
win.setMenuBarVisibility(false);
|
||||
win.removeMenu();
|
||||
|
||||
win.on("closed", () => {
|
||||
mainWindow = null;
|
||||
});
|
||||
|
||||
const isDev = process.env.NODE_ENV === "dev" || !app.isPackaged;
|
||||
if (process.env.VITE_DEV) {
|
||||
void win.loadURL("http://localhost:50188");
|
||||
@ -96,6 +108,17 @@ function createMainWindow(port: any): void {
|
||||
|
||||
let closeServeFn: (() => Promise<void>) | undefined;
|
||||
|
||||
protocol.registerSchemesAsPrivileged([
|
||||
{
|
||||
scheme: "toonflow",
|
||||
privileges: {
|
||||
secure: true,
|
||||
supportFetchAPI: true,
|
||||
corsEnabled: true,
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
app.whenReady().then(async () => {
|
||||
try {
|
||||
let servePath: string;
|
||||
@ -111,12 +134,44 @@ app.whenReady().then(async () => {
|
||||
const mod = requireWithCustomPaths(servePath);
|
||||
closeServeFn = mod.closeServe;
|
||||
const port = await mod.default(true);
|
||||
console.log("%c Line:112 🍇 port", "background:#2eafb0", port);
|
||||
createMainWindow(port);
|
||||
// 注册协议处理器
|
||||
protocol.handle("toonflow", (request) => {
|
||||
const url = new URL(request.url);
|
||||
const pathname = url.hostname.toLowerCase();
|
||||
const handlers: Record<string, () => object> = {
|
||||
getport: () => ({ port: port }),
|
||||
windowminimize: () => {
|
||||
mainWindow?.minimize();
|
||||
return { ok: true };
|
||||
},
|
||||
windowmaximize: () => {
|
||||
if (mainWindow?.isMaximized()) {
|
||||
mainWindow.unmaximize();
|
||||
} else {
|
||||
mainWindow?.maximize();
|
||||
}
|
||||
return { ok: true };
|
||||
},
|
||||
windowclose: () => {
|
||||
app.exit(0);
|
||||
return { ok: true };
|
||||
},
|
||||
windowismaximized: () => ({
|
||||
maximized: mainWindow?.isMaximized() ?? false,
|
||||
}),
|
||||
};
|
||||
const handler = handlers[pathname];
|
||||
const responseData = handler ? handler() : { error: "未知接口" };
|
||||
return new Response(JSON.stringify(responseData), {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
});
|
||||
});
|
||||
|
||||
createMainWindow();
|
||||
} catch (err) {
|
||||
console.error("[服务启动失败]:", err);
|
||||
// 如果服务启动失败,使用默认端口创建窗口
|
||||
createMainWindow(defaultPort);
|
||||
// 如果服务启动失败,仍然创建窗口
|
||||
createMainWindow();
|
||||
}
|
||||
});
|
||||
|
||||
@ -126,8 +181,7 @@ app.on("window-all-closed", () => {
|
||||
|
||||
app.on("activate", () => {
|
||||
if (BrowserWindow.getAllWindows().length === 0) {
|
||||
// 重新激活时使用默认端口
|
||||
createMainWindow(defaultPort);
|
||||
createMainWindow();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@ -104,7 +104,7 @@ export async function supervisionAI(ctx: AgentContext) {
|
||||
abortSignal,
|
||||
tools: {
|
||||
...skill.tools,
|
||||
...memory.getTools(),
|
||||
...useTools(ctx.resTool),
|
||||
},
|
||||
onFinish: async (completion) => {
|
||||
await memory.add("assistant:supervision", completion.text);
|
||||
|
||||
@ -122,7 +122,7 @@ export async function supervisionAI(ctx: AgentContext) {
|
||||
abortSignal,
|
||||
tools: {
|
||||
...skill.tools,
|
||||
...memory.getTools(),
|
||||
...useTools(ctx.resTool),
|
||||
},
|
||||
onFinish: async (completion) => {
|
||||
await memory.add("assistant:supervision", completion.text);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// @routes-hash 8097a5206252be753261d3f059243260
|
||||
// @routes-hash 557dfd43a824a4bd4170d0e2c9a6b45c
|
||||
import { Express } from "express";
|
||||
|
||||
import route1 from "./routes/agents/clearMemory";
|
||||
@ -94,10 +94,11 @@ import route90 from "./routes/setting/vendorConfig/deleteVendor";
|
||||
import route91 from "./routes/setting/vendorConfig/getVendorList";
|
||||
import route92 from "./routes/setting/vendorConfig/modelTest";
|
||||
import route93 from "./routes/setting/vendorConfig/updateVendor";
|
||||
import route94 from "./routes/task/getTaskApi";
|
||||
import route95 from "./routes/task/getTaskCategories";
|
||||
import route96 from "./routes/task/taskDetails";
|
||||
import route97 from "./routes/test/test";
|
||||
import route94 from "./routes/task/getProject";
|
||||
import route95 from "./routes/task/getTaskApi";
|
||||
import route96 from "./routes/task/getTaskCategories";
|
||||
import route97 from "./routes/task/taskDetails";
|
||||
import route98 from "./routes/test/test";
|
||||
|
||||
export default async (app: Express) => {
|
||||
app.use("/api/agents/clearMemory", route1);
|
||||
@ -193,8 +194,9 @@ export default async (app: Express) => {
|
||||
app.use("/api/setting/vendorConfig/getVendorList", route91);
|
||||
app.use("/api/setting/vendorConfig/modelTest", route92);
|
||||
app.use("/api/setting/vendorConfig/updateVendor", route93);
|
||||
app.use("/api/task/getTaskApi", route94);
|
||||
app.use("/api/task/getTaskCategories", route95);
|
||||
app.use("/api/task/taskDetails", route96);
|
||||
app.use("/api/test/test", route97);
|
||||
app.use("/api/task/getProject", route94);
|
||||
app.use("/api/task/getTaskApi", route95);
|
||||
app.use("/api/task/getTaskCategories", route96);
|
||||
app.use("/api/task/taskDetails", route97);
|
||||
app.use("/api/test/test", route98);
|
||||
}
|
||||
|
||||
@ -29,7 +29,8 @@ export default router.post(
|
||||
}),
|
||||
async (req, res) => {
|
||||
const { scriptId, projectId, storyboardId, prompt, data, model, duration, resolution, audio, mode } = req.body;
|
||||
|
||||
//获取生成视频比例
|
||||
const ratio = await u.db("o_project").select("videoRatio").where("id", projectId).first();
|
||||
const videoPath = `/${projectId}/video/${uuidv4()}.mp4`; //视频保存路径
|
||||
//新增
|
||||
const videoData = {
|
||||
@ -109,6 +110,7 @@ export default router.post(
|
||||
imageBase64: base64.filter((item) => item !== null) as string[],
|
||||
mode,
|
||||
duration,
|
||||
aspectRatio: (ratio?.videoRatio as `${number}:${number}`) || "16:9",
|
||||
resolution,
|
||||
audio,
|
||||
taskClass: "视频生成",
|
||||
|
||||
@ -14,7 +14,6 @@ export default router.post(
|
||||
async (req, res) => {
|
||||
const { scriptId, specifyIds } = req.body;
|
||||
const data = await u.db("o_video").where("scriptId", scriptId).whereIn("id", specifyIds).andWhere("state", "生成中").select("*");
|
||||
console.log("%c Line:17 🧀 data", "background:#93c0a4", data.length);
|
||||
res.status(200).send(success(data));
|
||||
},
|
||||
);
|
||||
@ -28,43 +28,55 @@ export default router.post(
|
||||
if (search) {
|
||||
const searchPattern = `%${search}%`;
|
||||
const whereBuilder = (builder: any) => {
|
||||
builder.where("name", "like", searchPattern).orWhere("path", "like", searchPattern).orWhere("description", "like", searchPattern);
|
||||
builder
|
||||
.where("name", "like", searchPattern)
|
||||
.orWhere("path", "like", searchPattern)
|
||||
.orWhere("description", "like", searchPattern);
|
||||
};
|
||||
query = query.where(whereBuilder);
|
||||
countQuery = countQuery.where(whereBuilder);
|
||||
}
|
||||
|
||||
// 查询总数
|
||||
const [{ count }]: any = await countQuery.count("* as count");
|
||||
|
||||
// 查询列表
|
||||
// type 筛选条件
|
||||
if (type) {
|
||||
query = query.where("type", type);
|
||||
countQuery = countQuery.where("type", type);
|
||||
}
|
||||
|
||||
// attributions 筛选条件
|
||||
if (attributions && attributions.length > 0) {
|
||||
query = query.whereIn("id", function () {
|
||||
this.select("skillId").from("o_skillAttribution").whereIn("attribution", attributions);
|
||||
});
|
||||
countQuery = countQuery.whereIn("id", function () {
|
||||
this.select("skillId").from("o_skillAttribution").whereIn("attribution", attributions);
|
||||
});
|
||||
const attributionSubQuery = function (this: any) {
|
||||
this.select("skillId")
|
||||
.from("o_skillAttribution")
|
||||
.whereIn("attribution", attributions);
|
||||
};
|
||||
query = query.whereIn("id", attributionSubQuery);
|
||||
countQuery = countQuery.whereIn("id", attributionSubQuery);
|
||||
}
|
||||
|
||||
// 查询总数(在所有筛选条件应用后)
|
||||
const [{ count }]: any = await countQuery.count("* as count");
|
||||
|
||||
// 查询列表
|
||||
const list = await query
|
||||
.select("*")
|
||||
.orderByRaw(`
|
||||
.orderByRaw(
|
||||
`
|
||||
CASE type WHEN 'main' THEN 1 ELSE 0 END ASC,
|
||||
CASE WHEN id NOT IN (SELECT skillId FROM o_skillAttribution) THEN 0 ELSE 1 END ASC,
|
||||
CASE WHEN state = 1 THEN 1 ELSE 0 END ASC,
|
||||
updateTime DESC
|
||||
`)
|
||||
`
|
||||
)
|
||||
.limit(limit)
|
||||
.offset(offset);
|
||||
|
||||
// 查询每个技能的归属
|
||||
const skillIds = list.map((item: any) => item.id);
|
||||
const attributionsList = await u.db("o_skillAttribution").whereIn("skillId", skillIds).select("skillId", "attribution");
|
||||
const attributionsList = await u
|
||||
.db("o_skillAttribution")
|
||||
.whereIn("skillId", skillIds)
|
||||
.select("skillId", "attribution");
|
||||
|
||||
// 将归属信息合并到列表中
|
||||
const attributionMap = new Map<string, string[]>();
|
||||
@ -75,6 +87,9 @@ export default router.post(
|
||||
attributionMap.get(attr.skillId!)!.push(attr.attribution!);
|
||||
}
|
||||
|
||||
// 记录需要更新state的技能id
|
||||
const missingFileIds: string[] = [];
|
||||
|
||||
const listWithAttributions = list.map((item: any) => {
|
||||
const normalizedPath = (item.path || "").replace(/\\/g, "/");
|
||||
const isPrefixedReferencePath = normalizedPath.startsWith("references/");
|
||||
@ -83,19 +98,41 @@ export default router.post(
|
||||
? path.join(u.getPath(["skills", "references"]), item.path!)
|
||||
: path.join(u.getPath("skills"), item.path!);
|
||||
|
||||
let content = "";
|
||||
let state = item.state;
|
||||
|
||||
// 检查文件是否存在
|
||||
if (fs.existsSync(skillFilePath)) {
|
||||
content = fs.readFileSync(skillFilePath, "utf-8");
|
||||
} else {
|
||||
state = -1;
|
||||
if (item.state !== -1) {
|
||||
missingFileIds.push(item.id);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...item,
|
||||
state,
|
||||
attributions: attributionMap.get(item.id) || [],
|
||||
content: fs.readFileSync(skillFilePath, "utf-8"),
|
||||
content,
|
||||
embedding: item.embedding ? true : false,
|
||||
};
|
||||
});
|
||||
|
||||
// 批量更新文件不存在的技能状态
|
||||
if (missingFileIds.length > 0) {
|
||||
await u
|
||||
.db("o_skillList")
|
||||
.whereIn("id", missingFileIds)
|
||||
.update({ state: -1 });
|
||||
}
|
||||
|
||||
res.status(200).send(
|
||||
success({
|
||||
list: listWithAttributions,
|
||||
total: Number(count),
|
||||
}),
|
||||
})
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
);
|
||||
10
src/routes/task/getProject.ts
Normal file
10
src/routes/task/getProject.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import express from "express";
|
||||
import u from "@/utils";
|
||||
import { success } from "@/lib/responseFormat";
|
||||
const router = express.Router();
|
||||
|
||||
export default router.post("/", async (req, res) => {
|
||||
const list = await u.db("o_project").select("id", "name").groupBy("name");
|
||||
const data = list.filter((item) => item.name);
|
||||
res.status(200).send(success(data));
|
||||
});
|
||||
@ -9,11 +9,12 @@ export default router.post(
|
||||
validateFields({
|
||||
state: z.string().optional().nullable(),
|
||||
taskClass: z.string().optional().nullable(),
|
||||
projectId: z.number().optional().nullable(),
|
||||
page: z.number(),
|
||||
limit: z.number(),
|
||||
}),
|
||||
async (req, res) => {
|
||||
const { taskClass, state, page = 1, limit = 10 }: any = req.body;
|
||||
const { taskClass, state, projectId, page = 1, limit = 10 }: any = req.body;
|
||||
const offset = (page - 1) * limit;
|
||||
const data = await u
|
||||
.db("o_tasks")
|
||||
@ -25,6 +26,9 @@ export default router.post(
|
||||
if (state) {
|
||||
qb.andWhere("o_tasks.state", state);
|
||||
}
|
||||
if (projectId) {
|
||||
qb.andWhere("o_tasks.projectId", projectId);
|
||||
}
|
||||
})
|
||||
.select("o_tasks.*", "o_project.* ")
|
||||
.offset(offset)
|
||||
@ -36,6 +40,9 @@ export default router.post(
|
||||
if (taskClass) {
|
||||
qb.andWhere("o_tasks.taskClass", taskClass);
|
||||
}
|
||||
if (projectId) {
|
||||
qb.andWhere("o_tasks.projectId", projectId);
|
||||
}
|
||||
if (state) {
|
||||
qb.andWhere("o_tasks.state", state);
|
||||
}
|
||||
|
||||
@ -1,17 +1,10 @@
|
||||
import express from "express";
|
||||
import u from "@/utils";
|
||||
import { success } from "@/lib/responseFormat";
|
||||
import { validateFields } from "@/middleware/middleware";
|
||||
import { number, z } from "zod";
|
||||
const router = express.Router();
|
||||
|
||||
export default router.post(
|
||||
"/",
|
||||
validateFields({
|
||||
projectId: z.number(),
|
||||
}),
|
||||
async (req, res) => {
|
||||
const data = await u.db("o_tasks").where("projectId", req.body.projectId).select("taskClass").groupBy("taskClass");
|
||||
res.status(200).send(success(data));
|
||||
},
|
||||
);
|
||||
export default router.post("/", async (req, res) => {
|
||||
const list = await u.db("o_tasks").select("taskClass").groupBy("taskClass");
|
||||
const data = list.filter((item) => item.taskClass);
|
||||
res.status(200).send(success(data));
|
||||
});
|
||||
|
||||
@ -6,6 +6,7 @@ import { useSkill } from "@/utils/agent/skillsTools";
|
||||
|
||||
export default router.get("/", async (req, res) => {
|
||||
const skill = await useSkill("universal_agent.md");
|
||||
console.log("%c Line:11 🍏 skill.prompt", "background:#fca650", skill.prompt);
|
||||
const result = await u.Ai.Text("universalAgent").invoke({
|
||||
system: "请直接调用activate_skill工具激活技能" + skill.prompt,
|
||||
messages: [{ role: "user", content: `如何烹饪龙肉` }],
|
||||
|
||||
34
src/types/database.d.ts
vendored
34
src/types/database.d.ts
vendored
@ -1,36 +1,6 @@
|
||||
// @db-hash 579a004cc745580469a24ee71f5f51c3
|
||||
// @db-hash d807205fbb27fc5ddb04cae060fb4430
|
||||
//该文件由脚本自动生成,请勿手动修改
|
||||
|
||||
export interface _o_project_old_20260326 {
|
||||
'artStyle'?: string | null;
|
||||
'createTime'?: number | null;
|
||||
'id'?: number | null;
|
||||
'intro'?: string | null;
|
||||
'name'?: string | null;
|
||||
'projectType'?: string | null;
|
||||
'type'?: string | null;
|
||||
'userId'?: number | null;
|
||||
'videoRatio'?: string | null;
|
||||
}
|
||||
export interface _o_storyboard_old_20260325 {
|
||||
'camera'?: string | null;
|
||||
'createTime'?: number | null;
|
||||
'description'?: string | null;
|
||||
'duration'?: string | null;
|
||||
'filePath'?: string | null;
|
||||
'frameMode'?: string | null;
|
||||
'id'?: number;
|
||||
'lines'?: string | null;
|
||||
'mode'?: string | null;
|
||||
'model'?: string | null;
|
||||
'prompt'?: string | null;
|
||||
'reason'?: string | null;
|
||||
'resolution'?: string | null;
|
||||
'scriptId'?: number | null;
|
||||
'sound'?: string | null;
|
||||
'state'?: string | null;
|
||||
'title'?: string | null;
|
||||
}
|
||||
export interface memories {
|
||||
'content': string;
|
||||
'createTime': number;
|
||||
@ -250,8 +220,6 @@ export interface o_videoConfig {
|
||||
}
|
||||
|
||||
export interface DB {
|
||||
"_o_project_old_20260326": _o_project_old_20260326;
|
||||
"_o_storyboard_old_20260325": _o_storyboard_old_20260325;
|
||||
"memories": memories;
|
||||
"o_agentDeploy": o_agentDeploy;
|
||||
"o_agentWorkData": o_agentWorkData;
|
||||
|
||||
@ -66,7 +66,7 @@ class AiText {
|
||||
async invoke(input: Omit<Parameters<typeof generateText>[0], "model">) {
|
||||
const modelName = await resolveModelName(this.AiType);
|
||||
return generateText({
|
||||
...(input.tools && { stopWhen: stepCountIs(Object.keys(input.tools).length * 5) }),
|
||||
...(input.tools && { stopWhen: stepCountIs(Object.keys(input.tools).length * 50) }),
|
||||
...input,
|
||||
model: await getVendorTemplateFn("textRequest", modelName),
|
||||
} as Parameters<typeof generateText>[0]);
|
||||
@ -74,7 +74,7 @@ class AiText {
|
||||
async stream(input: Omit<Parameters<typeof streamText>[0], "model">) {
|
||||
const modelName = await resolveModelName(this.AiType);
|
||||
return streamText({
|
||||
...(input.tools && { stopWhen: stepCountIs(Object.keys(input.tools).length * 5) }),
|
||||
...(input.tools && { stopWhen: stepCountIs(Object.keys(input.tools).length * 50) }),
|
||||
...input,
|
||||
model: wrapLanguageModel({
|
||||
model: await getVendorTemplateFn("textRequest", modelName),
|
||||
@ -137,8 +137,6 @@ class AiVideo {
|
||||
async run(input: VideoConfig) {
|
||||
return withTaskRecord(this.key, input.taskClass, input.describe, input.relatedObjects, input.projectId, async (modelName) => {
|
||||
const fn = await getVendorTemplateFn("videoRequest", modelName);
|
||||
|
||||
console.log("%c Line:142 🎂 input", "background:#42b983", input);
|
||||
this.result = await fn(input);
|
||||
if (this.result.startsWith("http")) this.result = await urlToBase64(this.result);
|
||||
return this;
|
||||
|
||||
@ -30,6 +30,7 @@ export default function runCode(code: string) {
|
||||
urlToBase64,
|
||||
mergeImages,
|
||||
pollTask,
|
||||
fetch,
|
||||
exports,
|
||||
axios,
|
||||
FormData,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user