From ec57055bd40ba6d6c334af179cd86c42ff077fd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ACT=E4=B8=B6=E6=B5=81=E6=98=9F=E9=9B=A8?= <1340145680@qq.com> Date: Mon, 23 Mar 2026 21:10:03 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BF=AE=E5=A4=8DscriptAgent=E5=B7=A5?= =?UTF-8?q?=E5=85=B7=EF=BC=8C=E5=B9=B6=E6=B7=BB=E5=8A=A0ctx=E4=B8=8A?= =?UTF-8?q?=E4=B8=8B=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/skills/script-agent/decision/SKILL.md | 26 ++++++++++--------- .../decision/references/pipeline.md | 16 ++++++------ data/skills/script-agent/execution/SKILL.md | 19 +++++++------- src/agents/scriptAgent/tools.ts | 26 +++++++++++-------- src/socket/resTool.ts | 7 ++++- src/socket/routes/scriptAgent.ts | 4 ++- 6 files changed, 56 insertions(+), 42 deletions(-) diff --git a/data/skills/script-agent/decision/SKILL.md b/data/skills/script-agent/decision/SKILL.md index 90c7d62..525e77e 100644 --- a/data/skills/script-agent/decision/SKILL.md +++ b/data/skills/script-agent/decision/SKILL.md @@ -29,6 +29,7 @@ description: >- | 集数 | 总共拆分为几集 | 7集 | | 单集时长 | 每集目标时长(分钟) | 2.5分钟 | | 原著范围 | 改编覆盖的章节范围 | 第1-35章 | +| 章节ID列表 | 本次任务涉及的章节ID(用于事件检索) | [1,2,3,4,5] | | 平台规格 | 画面比例(竖屏/横屏) | 竖屏9:16 | | 风格定位 | 短剧整体风格标签 | 诡异修仙+心理悬疑 | | 付费策略 | 前几集免费、从第几集设付费点 | 前2集免费,第3集起付费 | @@ -50,6 +51,7 @@ description: >- - 集数:{totalEpisodes}集 - 单集时长:{episodeDuration}分钟(约{wordsPerEpisode}字台词) - 原著范围:第{startChapter}-{endChapter}章 +- 章节ID列表:{chapterIds} - 平台规格:{platform} - 风格定位:{style} - 付费策略:{paywall} @@ -72,24 +74,24 @@ description: >- ### 阶段1:事件提取(Event Extraction) - **触发词**:提取事件、分析章节、事件列表、event -- **输入**:原著小说章节文本 +- **输入**:章节ID列表(由系统提示词中的章节映射表解析) - **输出**:结构化事件表(章节、角色、核心事件、主线关系、信息点数、预估时长、情绪强度) - **派发指令模板**: ``` 你是执行层Agent,请执行【事件提取】任务。 -目标:从原著第{start}章到第{end}章提取结构化事件表。 +目标:基于章节ID获取结构化事件表。 要求: -1. 调用 get_novel_text 获取原著文本 -2. 按事件提取规范逐章分析 -3. 输出标准事件表格式 -4. 调用 set_planData_event 保存结果 +1. 按系统提示词中的章节映射表,确定章节ID数组 ids:number[] +2. 调用 get_novel_events 获取事件数据(传入 ids:number[]) +3. 按事件提取规范整理为标准事件表格式 +4. 将事件表作为后续阶段上下文返回(不写入 planData) ``` ### 阶段2:故事骨架(Story Skeleton) - **触发词**:故事骨架、分集、三幕结构、skeleton -- **输入**:事件表(阶段1产出) +- **输入**:事件表(通过 get_novel_events 获取) - **输出**:三幕结构 + 分集决策 + 全局删减记录 + 付费卡点设计 - **前置条件**:事件表已完成 - **派发指令模板**: @@ -98,7 +100,7 @@ description: >- 你是执行层Agent,请执行【故事骨架搭建】任务。 目标:基于已有事件表构建故事骨架。 要求: -1. 调用 get_planData 获取当前事件表 +1. 调用 get_novel_events 获取当前任务章节的事件表(传入 ids:number[]) 2. 设计三幕结构,明确每幕功能、核心问题、幕末转折 3. 制定分集决策({totalEpisodes}集×{episodeDuration}分钟),每集包含戏剧功能、核心场景、章节分配、删减决策、集末钩子、付费点 4. 记录全局删减决策 @@ -108,7 +110,7 @@ description: >- ### 阶段3:改编策略(Adaptation Strategy) - **触发词**:改编策略、改编决策、改编原则、adaptation -- **输入**:事件表 + 故事骨架(阶段1-2产出) +- **输入**:事件表(通过 get_novel_events 获取) + 故事骨架(阶段2产出) - **输出**:核心改编原则 + 删除决策 + 世界观呈现策略 - **前置条件**:故事骨架已完成 - **派发指令模板**: @@ -117,7 +119,7 @@ description: >- 你是执行层Agent,请执行【改编策略制定】任务。 目标:基于事件表和故事骨架制定改编策略。 要求: -1. 调用 get_planData 获取事件表和故事骨架 +1. 调用 get_novel_events 获取事件表(传入 ids:number[]),并调用 get_planData 获取故事骨架 2. 确立核心改编原则(故事核优先、双线剪辑策略、恐怖克制原则) 3. 列出主要删除决策及理由 4. 制定世界观呈现策略 @@ -127,7 +129,7 @@ description: >- ### 阶段4:剧本编写(Script Writing) - **触发词**:写剧本、编剧、分镜脚本、script -- **输入**:事件表 + 故事骨架 + 改编策略(阶段1-3产出) +- **输入**:事件表(通过 get_novel_events 获取) + 故事骨架 + 改编策略(阶段2-3产出) - **输出**:分集剧本(节拍结构 + 分镜脚本) - **前置条件**:改编策略已完成 - **派发指令模板**: @@ -137,7 +139,7 @@ description: >- 目标:编写第{ep}集剧本。 集信息:{从骨架获取的该集信息} 要求: -1. 调用 get_planData 获取全部工作区数据 +1. 调用 get_novel_events 获取该集对应章节事件(传入 ids:number[]),并调用 get_planData 获取故事骨架与改编策略 2. 按节拍结构编写,严格控制总时长在{episodeDuration}分钟(约{wordsPerEpisode}字台词) 3. 每个节拍包含:场景描述、画面描述、台词、内心独白 4. 竖屏9:16格式,注意画面构图适配 diff --git a/data/skills/script-agent/decision/references/pipeline.md b/data/skills/script-agent/decision/references/pipeline.md index 2a44cd3..09d69b2 100644 --- a/data/skills/script-agent/decision/references/pipeline.md +++ b/data/skills/script-agent/decision/references/pipeline.md @@ -18,17 +18,17 @@ ### 阶段1:事件提取 ``` -输入:原著小说文本(第1-35章) -处理:逐章提取结构化事件 -输出:planData.event(Markdown事件表) -工具:get_novel_text → set_planData_event +输入:章节ID数组 ids:number[](由系统提示词中的章节映射表提供) +处理:调用事件检索工具并整理为标准事件表 +输出:事件表(Markdown,作为后续阶段上下文,不写入 planData) +工具:get_novel_events(ids:number[]) 质量门:章节覆盖率100%、角色名统一、强主线≥20章 ``` ### 阶段2:故事骨架 ``` -输入:planData.event +输入:事件表(通过 get_novel_events(ids:number[]) 获取) 处理:三幕分割、按项目配置分集、删减决策、钩子设计 输出:planData.storySkeleton 工具:get_planData → set_planData_storySkeleton @@ -39,7 +39,7 @@ ### 阶段3:改编策略 ``` -输入:planData.event + planData.storySkeleton +输入:事件表(get_novel_events) + planData.storySkeleton 处理:提炼改编原则、确定删减依据、世界观呈现策略 输出:planData.adaptationStrategy 工具:get_planData → set_planData_adaptationStrategy @@ -50,10 +50,10 @@ ### 阶段4:剧本编写 ``` -输入:planData.event + planData.storySkeleton + planData.adaptationStrategy +输入:事件表(get_novel_events) + planData.storySkeleton + planData.adaptationStrategy 处理:按集编写(可并行或逐集) 输出:planData.script -工具:get_planData + get_novel_text → set_planData_script +工具:get_novel_events + get_planData + get_novel_text → set_planData_script 质量门:时长合规、台词字数、画面可执行、资产一致 前置条件:阶段3通过审核 ``` diff --git a/data/skills/script-agent/execution/SKILL.md b/data/skills/script-agent/execution/SKILL.md index 0b014e6..b824a49 100644 --- a/data/skills/script-agent/execution/SKILL.md +++ b/data/skills/script-agent/execution/SKILL.md @@ -3,7 +3,7 @@ name: execution description: >- 短剧改编执行层Agent技能。负责接收决策层派发的具体任务并执行,涵盖事件提取、 故事骨架搭建、改编策略制定、剧本编写四大任务类型。使用 get_novel_text 读取原著, - 使用 get_planData 获取工作区状态,使用 set_planData 系列工具保存产出物。 + 使用 get_novel_events 获取章节事件数据,使用 get_planData 获取工作区状态,使用 set_planData 系列工具保存产出物。 当收到决策层的 run_sub_agent 调用时激活。 --- @@ -16,7 +16,6 @@ description: >- ```typescript const planData = { - event: string, // 章节事件表 storySkeleton: string, // 故事骨架 adaptationStrategy: string, // 改编策略 script: string, // 剧本内容 @@ -24,7 +23,8 @@ const planData = { ``` - 读取:`get_planData` → 返回完整 planData 对象 -- 写入:`set_planData_event` / `set_planData_storySkeleton` / `set_planData_adaptationStrategy` / `set_planData_script` +- 事件读取:`get_novel_events(ids:number[])` → 返回指定章节ID的事件数据 +- 写入:`set_planData_storySkeleton` / `set_planData_adaptationStrategy` / `set_planData_script` ## 项目背景 @@ -52,7 +52,8 @@ const planData = { **执行流程**: -1. 调用 `get_novel_text` 获取指定章节范围的原著文本 +1. 根据决策层传入的章节ID,构建 `ids:number[]` +2. 调用 `get_novel_events(ids:number[])` 获取结构化事件数据 2. 逐章分析,提取以下维度: | 字段 | 说明 | 示例 | @@ -66,7 +67,7 @@ const planData = { | 情绪强度 | 情绪标签 | 冲突+恐怖 | 3. 生成汇总统计(总章节、强主线章节数、可压缩章节、预估总时长、目标时长、压缩比) -4. 调用 `set_planData_event` 保存 Markdown 表格格式的事件表 +4. 输出 Markdown 表格格式的事件表,作为后续任务上下文(不写入 planData) **输出格式**:参考 [event-format.md](references/event-format.md) @@ -83,7 +84,7 @@ const planData = { **执行流程**: -1. 调用 `get_planData` 获取已有事件表 +1. 调用 `get_novel_events(ids:number[])` 获取已有事件表 2. 确定故事核(一句话总结整部剧的核心吸引力) 3. 提炼隐线(人物弧:主角的内在成长轨迹) 4. 设计三幕结构: @@ -122,7 +123,7 @@ const planData = { **执行流程**: -1. 调用 `get_planData` 获取事件表和故事骨架 +1. 调用 `get_novel_events(ids:number[])` 获取事件表,并调用 `get_planData` 获取故事骨架 2. 制定核心改编原则(3-5条),每条原则必须: - 明确优先级 - 给出正面指导("应该做什么")和负面边界("不应该做什么") @@ -152,7 +153,7 @@ const planData = { **执行流程**: -1. 调用 `get_planData` 获取全部工作区数据(事件表、骨架、改编策略) +1. 调用 `get_novel_events(ids:number[])` 获取事件表,并调用 `get_planData` 获取骨架与改编策略 2. 根据指定集数,从骨架中获取该集的: - 覆盖章节范围 - 戏剧功能 @@ -181,7 +182,7 @@ const planData = { ## 通用执行规范 -1. **先读后写**:执行任何任务前,先调用 `get_planData` 了解当前工作区状态 +1. **先读后写**:执行任何任务前,先调用 `get_planData` 了解工作区状态,并按需调用 `get_novel_events(ids:number[])` 获取事件数据 2. **增量更新**:如果工作区已有内容,在其基础上修改而非全部覆盖(除非指令明确要求重写) 3. **格式一致**:严格按照对应的输出格式规范,使用 Markdown 格式 4. **任务边界**:只执行指令中明确要求的任务,不越权执行其他阶段 diff --git a/src/agents/scriptAgent/tools.ts b/src/agents/scriptAgent/tools.ts index edc0831..96f3bda 100644 --- a/src/agents/scriptAgent/tools.ts +++ b/src/agents/scriptAgent/tools.ts @@ -1,10 +1,10 @@ import { tool, Tool } from "ai"; +import u from "@/utils"; import { z } from "zod"; import _ from "lodash"; import ResTool from "@/socket/resTool"; export const planData = z.object({ - event: z.string().describe("章节事件"), storySkeleton: z.string().describe("故事骨架"), adaptationStrategy: z.string().describe("改编策略"), script: z.string().describe("剧本内容"), @@ -20,6 +20,20 @@ const planDataKeyLabels = Object.fromEntries( export default (resTool: ResTool, toolsNames?: string[]) => { const { socket } = resTool; const tools: Record = { + get_novel_events: tool({ + description: "获取章节事件", + inputSchema: z.object({ + ids: z.array(z.number()).describe("章节id"), + }), + execute: async ({ ids }) => { + const data = await u + .db("o_novel") + .select("id", "chapterIndex as index", "reel", "chapter", "chapterData", "event", "eventState") + .whereIn("id", ids); + const eventString = data.map((i: any) => [`第${i.index}章,标题:${i.chapter},事件:${i.event}`].join("\n")).join("\n"); + return eventString; + }, + }), get_planData: tool({ description: "获取工作区数据", inputSchema: z.object({ @@ -42,16 +56,6 @@ export default (resTool: ResTool, toolsNames?: string[]) => { return ""; }, }), - set_planData_event: tool({ - description: "保存章节事件到工作区", - inputSchema: z.object({ value: planData.shape.event }), - execute: async ({ value }) => { - console.log("[tools] set_planData event", value); - resTool.systemMessage("正在保存 章节事件 数据"); - socket.emit("setPlanData", { key: "event", value }); - return true; - }, - }), set_planData_storySkeleton: tool({ description: "保存故事骨架到工作区", inputSchema: z.object({ value: planData.shape.storySkeleton }), diff --git a/src/socket/resTool.ts b/src/socket/resTool.ts index 91d2e47..fbac487 100644 --- a/src/socket/resTool.ts +++ b/src/socket/resTool.ts @@ -2,7 +2,12 @@ import u from "@/utils"; import { Socket } from "socket.io"; class ResTool { - constructor(public socket: Socket) {} + public socket: Socket; + public data: Record; + constructor(socket: Socket, data: Record = {}) { + this.socket = socket; + this.data = data; + } textMessage(name: string = "AI") { const messageId = u.uuid(); diff --git a/src/socket/routes/scriptAgent.ts b/src/socket/routes/scriptAgent.ts index a543f78..18071c9 100644 --- a/src/socket/routes/scriptAgent.ts +++ b/src/socket/routes/scriptAgent.ts @@ -35,7 +35,9 @@ export default (nsp: Namespace) => { console.log("[scriptAgent] 已连接:", socket.id); - const resTool = new ResTool(socket); + const resTool = new ResTool(socket, { + projectId: socket.handshake.auth.projectId, + }); let abortController: AbortController | null = null; socket.on("message", async (text: string) => { From 7183d56805946d6088d00cdc9c0f9f15292b7c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?ACT=E4=B8=B6=E6=B5=81=E6=98=9F=E9=9B=A8?= <1340145680@qq.com> Date: Mon, 23 Mar 2026 22:56:36 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=89=A7=E6=9C=ACAgent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- data/skills/script-agent/decision/SKILL.md | 21 ++++- .../decision/references/pipeline.md | 5 +- data/skills/script-agent/execution/SKILL.md | 25 ++++-- src/agents/productionAgent/tools.ts | 2 + src/agents/scriptAgent/index.ts | 28 +++++-- src/agents/scriptAgent/tools.ts | 37 +++++++-- src/lib/initDB.ts | 2 +- src/router.ts | 78 ++++++++++--------- src/routes/production/getFlowData.ts | 2 +- src/routes/production/saveFlowData.ts | 6 +- src/routes/scriptAgent/getPlanData.ts | 36 +++++++++ src/routes/scriptAgent/setPlanData.ts | 28 +++++++ src/types/database.d.ts | 12 ++- 13 files changed, 213 insertions(+), 69 deletions(-) create mode 100644 src/routes/scriptAgent/getPlanData.ts create mode 100644 src/routes/scriptAgent/setPlanData.ts diff --git a/data/skills/script-agent/decision/SKILL.md b/data/skills/script-agent/decision/SKILL.md index 525e77e..6f921c9 100644 --- a/data/skills/script-agent/decision/SKILL.md +++ b/data/skills/script-agent/decision/SKILL.md @@ -130,7 +130,7 @@ description: >- - **触发词**:写剧本、编剧、分镜脚本、script - **输入**:事件表(通过 get_novel_events 获取) + 故事骨架 + 改编策略(阶段2-3产出) -- **输出**:分集剧本(节拍结构 + 分镜脚本) +- **输出**:分集剧本(节拍结构 + 分镜脚本)并写入 SQLite - **前置条件**:改编策略已完成 - **派发指令模板**: @@ -143,9 +143,20 @@ description: >- 2. 按节拍结构编写,严格控制总时长在{episodeDuration}分钟(约{wordsPerEpisode}字台词) 3. 每个节拍包含:场景描述、画面描述、台词、内心独白 4. 竖屏9:16格式,注意画面构图适配 -5. 调用 set_planData_script 保存结果 +5. 仅在用户确认后调用 insert_script_to_sqlite 写入剧本(SQL操作) ``` +### 阶段4额外安全规则(SQL写入) + +`insert_script_to_sqlite` 为 SQL 写入操作,决策层必须先与用户明确对话确认,再允许执行层写入: + +1. 先向用户展示待写入剧本摘要,并询问:`是否确认写入数据库?` +2. 仅当用户明确回复“确认/是/同意”后,才可派发写入指令 +3. 派发剧本写入任务时,指令中必须包含:`用户已确认写入SQL: 是` +4. 若用户未确认或拒绝,禁止派发 SQL 写入 + +当用户要求删除剧本时,决策层必须提醒:`剧本删除请在道具本管理中手动删除`。 + ## 调度规则 ### 派发执行任务 @@ -208,8 +219,10 @@ run_sub_agent( 1. **进度汇报**:每完成一个阶段,向用户汇报结果摘要和下一步计划 2. **审核结果展示**:将监督层的完整审核报告展示给用户,包括问题、建议和亮点 3. **等待用户决策**:审核发现问题时,**必须等待用户明确指示**后再执行修复,不可自行决定 -4. **确认关键决策**:涉及大幅偏离既定策略的修改时,先咨询用户 -5. **不暴露内部机制**:不向用户提及 Agent 名称、工具名称等实现细节 +4. **SQL写入确认**:调用 `insert_script_to_sqlite` 前,必须完成用户明确确认 +5. **删除请求提醒**:用户要求删除剧本时,提醒其在道具本管理中手动删除 +6. **确认关键决策**:涉及大幅偏离既定策略的修改时,先咨询用户 +7. **不暴露内部机制**:不向用户提及 Agent 名称、工具名称等实现细节 ## 错误处理 diff --git a/data/skills/script-agent/decision/references/pipeline.md b/data/skills/script-agent/decision/references/pipeline.md index 09d69b2..513040b 100644 --- a/data/skills/script-agent/decision/references/pipeline.md +++ b/data/skills/script-agent/decision/references/pipeline.md @@ -52,10 +52,11 @@ ``` 输入:事件表(get_novel_events) + planData.storySkeleton + planData.adaptationStrategy 处理:按集编写(可并行或逐集) -输出:planData.script -工具:get_novel_events + get_planData + get_novel_text → set_planData_script +输出:SQLite 中的剧本记录 +工具:get_novel_events + get_planData + get_novel_text → insert_script_to_sqlite 质量门:时长合规、台词字数、画面可执行、资产一致 前置条件:阶段3通过审核 +附加前置条件:用户已明确确认写入 SQL ``` ## 阶段间交互协议 diff --git a/data/skills/script-agent/execution/SKILL.md b/data/skills/script-agent/execution/SKILL.md index b824a49..16c7567 100644 --- a/data/skills/script-agent/execution/SKILL.md +++ b/data/skills/script-agent/execution/SKILL.md @@ -3,14 +3,14 @@ name: execution description: >- 短剧改编执行层Agent技能。负责接收决策层派发的具体任务并执行,涵盖事件提取、 故事骨架搭建、改编策略制定、剧本编写四大任务类型。使用 get_novel_text 读取原著, - 使用 get_novel_events 获取章节事件数据,使用 get_planData 获取工作区状态,使用 set_planData 系列工具保存产出物。 + 使用 get_novel_events 获取章节事件数据,使用 get_planData 获取工作区状态,使用写入工具保存产出物。 当收到决策层的 run_sub_agent 调用时激活。 --- # 执行层 Agent 技能指令 你是短剧改编项目的**执行层 Agent**,只接收决策层派发的任务指令并执行。 -你不与用户直接交互,所有产出物通过 `set_planData_*` 写入工作区。 +你不与用户直接交互,骨架与策略写入 planData,剧本写入 SQLite。 ## 工作区数据结构 @@ -24,7 +24,15 @@ const planData = { - 读取:`get_planData` → 返回完整 planData 对象 - 事件读取:`get_novel_events(ids:number[])` → 返回指定章节ID的事件数据 -- 写入:`set_planData_storySkeleton` / `set_planData_adaptationStrategy` / `set_planData_script` +- 写入:`set_planData_storySkeleton` / `set_planData_adaptationStrategy` / `insert_script_to_sqlite` + +### SQL写入安全约束(剧本) + +`insert_script_to_sqlite` 属于 SQL 写入操作,执行前必须满足以下条件: + +1. 决策层指令中明确包含:`用户已确认写入SQL: 是` +2. 若未包含该确认标记,执行层必须拒绝写入并返回:`缺少用户确认,未执行 SQL 写入` +3. 执行层不处理剧本删除请求;如收到删除诉求,返回提醒:`请在道具本管理中手动删除剧本` ## 项目背景 @@ -54,7 +62,7 @@ const planData = { 1. 根据决策层传入的章节ID,构建 `ids:number[]` 2. 调用 `get_novel_events(ids:number[])` 获取结构化事件数据 -2. 逐章分析,提取以下维度: +3. 逐章分析,提取以下维度: | 字段 | 说明 | 示例 | |------|------|------| @@ -66,8 +74,8 @@ const planData = { | 预估集长 | 秒数 | 45秒 | | 情绪强度 | 情绪标签 | 冲突+恐怖 | -3. 生成汇总统计(总章节、强主线章节数、可压缩章节、预估总时长、目标时长、压缩比) -4. 输出 Markdown 表格格式的事件表,作为后续任务上下文(不写入 planData) +4. 生成汇总统计(总章节、强主线章节数、可压缩章节、预估总时长、目标时长、压缩比) +5. 输出 Markdown 表格格式的事件表,作为后续任务上下文(不写入 planData) **输出格式**:参考 [event-format.md](references/event-format.md) @@ -169,7 +177,7 @@ const planData = { - 画面描述(构图、运镜、视觉重点) - 台词/旁白/内心独白 - 表演指示(情绪、动作细节) -6. 调用 `set_planData_script` 保存 +6. 仅当指令中包含 `用户已确认写入SQL: 是` 时,调用 `insert_script_to_sqlite` 写入剧本 **输出格式**:参考 [script-format.md](references/script-format.md) @@ -186,4 +194,5 @@ const planData = { 2. **增量更新**:如果工作区已有内容,在其基础上修改而非全部覆盖(除非指令明确要求重写) 3. **格式一致**:严格按照对应的输出格式规范,使用 Markdown 格式 4. **任务边界**:只执行指令中明确要求的任务,不越权执行其他阶段 -5. **异常上报**:遇到无法处理的情况(如缺少前置数据),在返回结果中明确说明 \ No newline at end of file +5. **异常上报**:遇到无法处理的情况(如缺少前置数据),在返回结果中明确说明 +6. **SQL安全执行**:未收到明确用户确认时,禁止调用 `insert_script_to_sqlite` \ No newline at end of file diff --git a/src/agents/productionAgent/tools.ts b/src/agents/productionAgent/tools.ts index 8483193..97684c6 100644 --- a/src/agents/productionAgent/tools.ts +++ b/src/agents/productionAgent/tools.ts @@ -6,10 +6,12 @@ import ResTool from "@/socket/resTool"; export const deriveAssetSchema = z.object({ id: z.number().describe("衍生资产ID,如果新增则为空").optional(), assetsId: z.string().describe("关联的资产ID"), + prompt: z.string().describe("生成提示词"), name: z.string().describe("衍生资产名称"), desc: z.string().describe("衍生资产描述"), src: z.string().describe("衍生资产资源路径"), state: z.enum(["未生成", "生成中", "已完成", "生成失败"]).describe("衍生资产生成状态"), + type: z.enum(["role", "tool", "scene", "clip"]).describe("衍生资产类型"), }); export const assetItemSchema = z.object({ assetsId: z.string().describe("资产唯一标识"), diff --git a/src/agents/scriptAgent/index.ts b/src/agents/scriptAgent/index.ts index bffc0b9..1bd4dda 100644 --- a/src/agents/scriptAgent/index.ts +++ b/src/agents/scriptAgent/index.ts @@ -35,14 +35,30 @@ function buildSystemPrompt(skillPrompt: string, mem: Awaited `- ${i.id}: 第${i.index}章`).join("\n")}\n\n`; + console.log("%c Line:57 🍧 prefixSystem", "background:#ea7e5c", prefixSystem); const { textStream } = await u.Ai.Text("scriptAgent").stream({ system: prefixSystem + systemPrompt, @@ -92,7 +108,10 @@ export async function executionAI(ctx: AgentContext) { } export async function supervisionAI(ctx: AgentContext) { - const { isolationKey, text, abortSignal } = ctx; + const { isolationKey, text, abortSignal, resTool } = ctx; + + resTool.systemMessage("监督层AI 接管聊天"); + const memory = new Memory("scriptAgent", isolationKey); const [skill, mem] = await Promise.all([useSkill("script-agent", "supervision"), memory.get(text)]); @@ -127,11 +146,10 @@ function runSubAgent(parentCtx: AgentContext) { //运行子Agent const subTextStream = await fn({ ...parentCtx, text: prompt }); - let msg: ReturnType; + let msg = parentCtx.resTool.textMessage(); let fullResponse = ""; for await (const chunk of subTextStream) { - if (!msg!) msg = parentCtx.resTool.textMessage(); msg.send(chunk); fullResponse += chunk; } diff --git a/src/agents/scriptAgent/tools.ts b/src/agents/scriptAgent/tools.ts index 96f3bda..800850e 100644 --- a/src/agents/scriptAgent/tools.ts +++ b/src/agents/scriptAgent/tools.ts @@ -4,6 +4,17 @@ import { z } from "zod"; import _ from "lodash"; import ResTool from "@/socket/resTool"; +export const AssetSchema = z.object({ + id: z.number().describe("衍生资产ID,如果新增则为空").optional(), + assetsId: z.string().describe("关联的资产ID"), + prompt: z.string().describe("生成提示词"), + name: z.string().describe("衍生资产名称"), + desc: z.string().describe("衍生资产描述"), + src: z.string().describe("衍生资产资源路径").optional(), + state: z.enum(["未生成", "生成中", "已完成", "生成失败"]).describe("衍生资产生成状态,新增默认未生成"), + type: z.enum(["role", "tool", "scene", "clip"]).describe("衍生资产类型"), +}); + export const planData = z.object({ storySkeleton: z.string().describe("故事骨架"), adaptationStrategy: z.string().describe("改编策略"), @@ -26,6 +37,8 @@ export default (resTool: ResTool, toolsNames?: string[]) => { ids: z.array(z.number()).describe("章节id"), }), execute: async ({ ids }) => { + resTool.systemMessage(`正在阅读 章节事件 数据...`); + console.log("[tools] get_novel_events", ids); const data = await u .db("o_novel") .select("id", "chapterIndex as index", "reel", "chapter", "chapterData", "event", "eventState") @@ -76,13 +89,23 @@ export default (resTool: ResTool, toolsNames?: string[]) => { return true; }, }), - set_planData_script: tool({ - description: "保存剧本内容到工作区", - inputSchema: z.object({ value: planData.shape.script }), - execute: async ({ value }) => { - console.log("[tools] set_planData script", value); - resTool.systemMessage("正在保存 剧本 数据"); - socket.emit("setPlanData", { key: "script", value }); + insert_script_to_sqlite: tool({ + description: "将剧本内容插入sqlite数据库,供后续业务使用", + inputSchema: z.object({ + list: z.array(AssetSchema), + }), + execute: async ({ list }) => { + console.log("[tools] insert_script_to_sqlite", list); + await u.db("o_assets").insert( + list.map((i) => ({ + name: i.name, + prompt: i.prompt, + type: i.type, + describe: i.desc, + projectId: resTool.data.projectId, + state: "未生成", + })), + ); return true; }, }), diff --git a/src/lib/initDB.ts b/src/lib/initDB.ts index 12e4d2a..03e12bb 100644 --- a/src/lib/initDB.ts +++ b/src/lib/initDB.ts @@ -328,7 +328,7 @@ export default async (knex: Knex, forceInit: boolean = false): Promise => }, //flowData-剧本 { - name: "o_flowData", + name: "o_agentWorkData", builder: (table) => { table.integer("id").notNullable(); table.integer("projectId"); diff --git a/src/router.ts b/src/router.ts index 3841909..1dea5f9 100644 --- a/src/router.ts +++ b/src/router.ts @@ -1,4 +1,4 @@ -// @routes-hash cd0c360844a1f493a1cf5deb6ab906b0 +// @routes-hash 0ea539b96a91286c69cc811893902704 import { Express } from "express"; import route1 from "./routes/agents/clearMemory"; @@ -60,24 +60,26 @@ import route56 from "./routes/script/delScript"; import route57 from "./routes/script/exportScript"; import route58 from "./routes/script/getScrptApi"; import route59 from "./routes/script/updateScript"; -import route60 from "./routes/setting/agentDeploy/deployAgentModel"; -import route61 from "./routes/setting/agentDeploy/getAgentDeploy"; -import route62 from "./routes/setting/agentDeploy/updateKey"; -import route63 from "./routes/setting/dbConfig/clearData"; -import route64 from "./routes/setting/getTextModel"; -import route65 from "./routes/setting/loginConfig/getUser"; -import route66 from "./routes/setting/loginConfig/updateUserPwd"; -import route67 from "./routes/setting/memoryConfig/getMemory"; -import route68 from "./routes/setting/memoryConfig/sureMemory"; -import route69 from "./routes/setting/vendorConfig/addVendor"; -import route70 from "./routes/setting/vendorConfig/deleteVendor"; -import route71 from "./routes/setting/vendorConfig/getVendorList"; -import route72 from "./routes/setting/vendorConfig/modelTest"; -import route73 from "./routes/setting/vendorConfig/updateVendor"; -import route74 from "./routes/task/getTaskApi"; -import route75 from "./routes/task/getTaskCategories"; -import route76 from "./routes/task/taskDetails"; -import route77 from "./routes/test/test"; +import route60 from "./routes/scriptAgent/getPlanData"; +import route61 from "./routes/scriptAgent/setPlanData"; +import route62 from "./routes/setting/agentDeploy/deployAgentModel"; +import route63 from "./routes/setting/agentDeploy/getAgentDeploy"; +import route64 from "./routes/setting/agentDeploy/updateKey"; +import route65 from "./routes/setting/dbConfig/clearData"; +import route66 from "./routes/setting/getTextModel"; +import route67 from "./routes/setting/loginConfig/getUser"; +import route68 from "./routes/setting/loginConfig/updateUserPwd"; +import route69 from "./routes/setting/memoryConfig/getMemory"; +import route70 from "./routes/setting/memoryConfig/sureMemory"; +import route71 from "./routes/setting/vendorConfig/addVendor"; +import route72 from "./routes/setting/vendorConfig/deleteVendor"; +import route73 from "./routes/setting/vendorConfig/getVendorList"; +import route74 from "./routes/setting/vendorConfig/modelTest"; +import route75 from "./routes/setting/vendorConfig/updateVendor"; +import route76 from "./routes/task/getTaskApi"; +import route77 from "./routes/task/getTaskCategories"; +import route78 from "./routes/task/taskDetails"; +import route79 from "./routes/test/test"; export default async (app: Express) => { app.use("/api/agents/clearMemory", route1); @@ -139,22 +141,24 @@ export default async (app: Express) => { app.use("/api/script/exportScript", route57); app.use("/api/script/getScrptApi", route58); app.use("/api/script/updateScript", route59); - app.use("/api/setting/agentDeploy/deployAgentModel", route60); - app.use("/api/setting/agentDeploy/getAgentDeploy", route61); - app.use("/api/setting/agentDeploy/updateKey", route62); - app.use("/api/setting/dbConfig/clearData", route63); - app.use("/api/setting/getTextModel", route64); - app.use("/api/setting/loginConfig/getUser", route65); - app.use("/api/setting/loginConfig/updateUserPwd", route66); - app.use("/api/setting/memoryConfig/getMemory", route67); - app.use("/api/setting/memoryConfig/sureMemory", route68); - app.use("/api/setting/vendorConfig/addVendor", route69); - app.use("/api/setting/vendorConfig/deleteVendor", route70); - app.use("/api/setting/vendorConfig/getVendorList", route71); - app.use("/api/setting/vendorConfig/modelTest", route72); - app.use("/api/setting/vendorConfig/updateVendor", route73); - app.use("/api/task/getTaskApi", route74); - app.use("/api/task/getTaskCategories", route75); - app.use("/api/task/taskDetails", route76); - app.use("/api/test/test", route77); + app.use("/api/scriptAgent/getPlanData", route60); + app.use("/api/scriptAgent/setPlanData", route61); + app.use("/api/setting/agentDeploy/deployAgentModel", route62); + app.use("/api/setting/agentDeploy/getAgentDeploy", route63); + app.use("/api/setting/agentDeploy/updateKey", route64); + app.use("/api/setting/dbConfig/clearData", route65); + app.use("/api/setting/getTextModel", route66); + app.use("/api/setting/loginConfig/getUser", route67); + app.use("/api/setting/loginConfig/updateUserPwd", route68); + app.use("/api/setting/memoryConfig/getMemory", route69); + app.use("/api/setting/memoryConfig/sureMemory", route70); + app.use("/api/setting/vendorConfig/addVendor", route71); + app.use("/api/setting/vendorConfig/deleteVendor", route72); + app.use("/api/setting/vendorConfig/getVendorList", route73); + app.use("/api/setting/vendorConfig/modelTest", route74); + app.use("/api/setting/vendorConfig/updateVendor", route75); + app.use("/api/task/getTaskApi", route76); + app.use("/api/task/getTaskCategories", route77); + app.use("/api/task/taskDetails", route78); + app.use("/api/test/test", route79); } diff --git a/src/routes/production/getFlowData.ts b/src/routes/production/getFlowData.ts index f9a81e7..b7fa46f 100644 --- a/src/routes/production/getFlowData.ts +++ b/src/routes/production/getFlowData.ts @@ -14,7 +14,7 @@ export default router.post( }), async (req, res) => { const { projectId, episodesId } = req.body; - const sqlData = await u.db("o_flowData").where({ projectId, episodesId }).first(); + const sqlData = await u.db("o_agentWorkData").where({ projectId, episodesId }).first(); const scriptData = await u.db("o_script").where("projectId", projectId).first(); diff --git a/src/routes/production/saveFlowData.ts b/src/routes/production/saveFlowData.ts index 14fd7e1..73b7670 100644 --- a/src/routes/production/saveFlowData.ts +++ b/src/routes/production/saveFlowData.ts @@ -15,16 +15,16 @@ export default router.post( }), async (req, res) => { const { projectId, episodesId } = req.body; - const sqlData = await u.db("o_flowData").where({ projectId, episodesId }).first(); + const sqlData = await u.db("o_agentWorkData").where({ projectId, episodesId }).first(); if (!sqlData) { - await u.db("o_flowData").insert({ + await u.db("o_agentWorkData").insert({ projectId, episodesId, data: JSON.stringify(req.body.data), }); } else { await u - .db("o_flowData") + .db("o_agentWorkData") .where({ projectId, episodesId }) .update({ data: JSON.stringify(req.body.data), diff --git a/src/routes/scriptAgent/getPlanData.ts b/src/routes/scriptAgent/getPlanData.ts new file mode 100644 index 0000000..9ca8ad6 --- /dev/null +++ b/src/routes/scriptAgent/getPlanData.ts @@ -0,0 +1,36 @@ +import express from "express"; +import { success } from "@/lib/responseFormat"; +import u from "@/utils"; +import { z } from "zod"; +import { validateFields } from "@/middleware/middleware"; +const router = express.Router(); + +export default router.post( + "/", + validateFields({ + projectId: z.number(), + agentType: z.enum(["scriptAgent"]), + }), + async (req, res) => { + const { projectId, agentType } = req.body; + const data = await u.db("o_agentWorkData").where({ id: projectId, key: agentType }).first(); + + if (!data) { + await u.db("o_agentWorkData").insert({ + id: projectId, + key: agentType, + data: JSON.stringify({ + storySkeleton: "", + adaptationStrategy: "", + }), + }); + return res.status(200).send( + success({ + storySkeleton: "", + adaptationStrategy: "", + }), + ); + } + res.status(200).send(success(JSON.parse(data.data ?? "{}"))); + }, +); diff --git a/src/routes/scriptAgent/setPlanData.ts b/src/routes/scriptAgent/setPlanData.ts new file mode 100644 index 0000000..e7ac9e3 --- /dev/null +++ b/src/routes/scriptAgent/setPlanData.ts @@ -0,0 +1,28 @@ +import express from "express"; +import { success } from "@/lib/responseFormat"; +import u from "@/utils"; +import { z } from "zod"; +import { validateFields } from "@/middleware/middleware"; +const router = express.Router(); + +export default router.post( + "/", + validateFields({ + projectId: z.number(), + agentType: z.enum(["scriptAgent"]), + data: z.object({ + storySkeleton: z.string(), + adaptationStrategy: z.string(), + }), + }), + async (req, res) => { + const { projectId, agentType, data } = req.body; + await u + .db("o_agentWorkData") + .where({ id: projectId, key: agentType }) + .update({ + data: JSON.stringify(data), + }); + res.status(200).send(success()); + }, +); diff --git a/src/types/database.d.ts b/src/types/database.d.ts index 7e0021e..83d0f35 100644 --- a/src/types/database.d.ts +++ b/src/types/database.d.ts @@ -1,4 +1,4 @@ -// @db-hash 3cdc2f747dac456ddd4bbfd877efe991 +// @db-hash f73a46df6ee14425a01df5c1ff88fcb2 //该文件由脚本自动生成,请勿手动修改 export interface memories { @@ -22,6 +22,15 @@ export interface o_agentDeploy { 'name'?: string | null; 'vendorId'?: number | null; } +export interface o_agentWorkData { + 'createTime'?: number | null; + 'data'?: string | null; + 'espisodeId'?: number | null; + 'id'?: number; + 'key'?: string | null; + 'projectId'?: number | null; + 'updateTime'?: number | null; +} export interface o_artStyle { 'id'?: number; 'name'?: string | null; @@ -193,6 +202,7 @@ export interface o_videoConfig { export interface DB { "memories": memories; "o_agentDeploy": o_agentDeploy; + "o_agentWorkData": o_agentWorkData; "o_artStyle": o_artStyle; "o_assets": o_assets; "o_assets2Storyboard": o_assets2Storyboard;