项目地址:https://devflow.aiyly.com/

它的业务目标很简单:输入小说章节或短剧文本,系统拆解角色、空间、道具、场次和分镜,再生成资产图、3D 运镜预览和图生视频任务。功能层面可以概括成“文本 -> 世界资产 -> 分镜 -> 3D 运镜 -> 视频片段”。

系统实现重点包括:Next.js 全栈架构、Route Handlers 后端接口、Prisma + MySQL 数据模型、AgentRun / AgentStep 状态机、AI Provider 抽象、Three.js 预览、任务恢复,以及 Docker + GitLab CI/CD 部署。

整体技术架构

项目是一个 Next.js 全栈应用,前端页面、服务端 API、Agent 编排、数据库访问、OAuth 登录和部署配置都在同一个工程里。

核心技术栈:

  • Next.js 16 App Router
  • React 19
  • TypeScript
  • Tailwind CSS
  • Three.js
  • Prisma 7
  • MySQL
  • Google OAuth
  • Qwen 文本模型
  • Doubao Seedream 图片模型
  • Doubao Seedance 图生视频任务
  • Docker
  • GitLab CI/CD
  • Nginx

整体链路如下:

1
2
3
4
5
6
7
React 页面
-> Next.js Route Handlers
-> AgentRun / AgentStep 状态机
-> AI Provider 层
-> Prisma / MySQL
-> 本地资产存储与缩略图
-> Three.js 预览 / 图片与视频任务结果

系统按职责拆成几层:

  • UI 层:负责工作台交互、任务进度展示、资产编辑和 3D 预览。
  • API 层:使用 Next Route Handlers 承接登录、剧本解析、生成任务、Agent 执行和资产管理。
  • Agent 层:负责任务计划、步骤执行、状态流转、人工确认和失败重试。
  • Provider 层:屏蔽不同 AI 模型的请求参数、返回结构和错误处理。
  • Data 层:用 Prisma + MySQL 持久化用户、任务、资产、分镜和 Agent 状态。
  • Deploy 层:使用 Docker 镜像、migrator 镜像、GitLab CI/CD 和 Nginx 发布到生产环境。

前端:App Router 下的工作台式页面组织

这个项目不是营销页,而是偏工具型的工作台,所以前端信息架构按“主流程 + 可插入工具”组织。

主要页面包括:

  • /:产品入口和主流程说明。
  • /agent:Agent 导演台,创建和执行 AgentRun。
  • /scripts:剧本解析工作台。
  • /scripts/[id]/world:世界资产编辑页面。
  • /storyboard-3d:3D 运镜预览器。
  • /generate:图片和视频生成工作台。
  • /login:Google OAuth 登录入口。
  • /about:项目架构介绍。

前端比较关键的不是 UI 本身,而是状态展示方式。Agent 任务不是一次请求立即返回结果,而是分步骤执行,所以页面需要展示:

  • 当前 AgentRun 状态。
  • 每个 AgentStep 的状态。
  • 正在执行的步骤。
  • 等待用户确认的步骤。
  • 失败步骤和错误信息。
  • 可重试入口。
  • 已生成的中间产物。

这类页面不能只靠一个简单 loading。它更像任务控制台,需要把后端状态机完整映射到前端。

后端:Route Handlers 作为 BFF 和任务入口

项目后端没有单独起 Express 或 Nest 服务,而是使用 Next.js Route Handlers。

这种选择的好处是:

  • 页面和 API 可以共享 TypeScript 类型。
  • 服务端代码可以直接访问 Prisma、Cookie、环境变量和 Provider。
  • 部署时可以走 Next standalone 镜像。
  • 对个人项目来说工程复杂度更低。

典型 API 包括:

  • POST /api/agent/runs:创建 AgentRun 和 AgentStep。
  • POST /api/agent/runs/[id]/start:启动 Agent 执行。
  • POST /api/agent/steps/[id]/confirm:确认等待中的步骤。
  • POST /api/agent/steps/[id]/retry:重试失败步骤。
  • POST /api/scripts/analyze:解析剧本。
  • POST /api/scripts/novel/condense:精简小说。
  • POST /api/scripts/novel/to-script:小说转短剧剧本。
  • POST /api/world/assets/[id]/generate-image:生成世界资产图。
  • POST /api/world/storyboards/[id]/generate-image:生成分镜图。
  • POST /api/world/storyboards/[id]/sync-video:同步视频结果。
  • GET /api/auth/me:读取当前登录用户。

Route Handler 里会做几件事:

  1. 读取并校验请求参数。
  2. 通过 Cookie Session 获取当前用户。
  3. 调用 Prisma 查询或写入数据库。
  4. 调用 Agent executor 或 AI Provider。
  5. 返回结构化 JSON 给前端。

API Key、模型调用、文件转存都只在服务端发生,前端不直接接触模型密钥。

数据建模:把 AI 过程拆成可恢复的业务对象

AIGC 项目如果只保存最终图片或视频,很快会遇到两个问题:

  1. 中间过程不可追踪。
  2. 失败后只能重来。

所以这个项目的数据库设计重点不是“保存结果”,而是“保存生成过程”。

核心模型包括:

模型 作用
User 用户、登录身份和额度余额
GenerationJob 图片生成、图片编辑等生成任务
GenerationCandidate 一次生成返回的候选结果
Asset 图片、视频等统一资产
AssetVersion 图生图或二次编辑的版本关系
CreditTransaction 成本记录和额度流水
ScriptAnalysis 剧本解析结果
WorldAsset 角色、空间、道具
WorldAssetImage 世界资产和图片资产的关联
WorldScene 场次
SceneStoryboard 分镜、图片提示词、视频提示词和镜头信息
AgentRun 一次 Agent 任务
AgentStep Agent 任务中的一个执行步骤

这里最关键的是 AgentRunAgentStep

AgentRun 记录一次完整目标,比如“把这段小说转成视频预视觉”。AgentStep 则记录每一个可执行节点,比如“精简小说”“生成资产图”“等待用户确认”“生成分镜图”。

这种设计让系统具备几个能力:

  • 可以看到任务执行进度。
  • 可以保存每一步输入和输出。
  • 可以从失败步骤重试。
  • 可以保留已成功的中间产物。
  • 可以让用户在关键节点确认后再继续。
  • 可以把长任务拆成多个短事务。

Agent 设计:模板计划 + 状态机,而不是黑盒自由 Agent

这个项目没有一开始引入复杂 Agent 框架,而是先实现了一个轻量可控的工作流 Agent。

原因很直接:短剧生成链路强依赖业务数据、资产关系、用户确认和成本控制。如果完全交给模型自由规划,很容易出现不可控的工具调用和难以恢复的状态。

所以当前 Agent 采用:

1
2
3
4
5
6
AgentRunType
-> buildInitialAgentPlan
-> AgentStep[]
-> executor 按顺序执行
-> 写入 outputJson / errorMessage
-> 更新 run.status 和 currentStepId

当前支持的 AgentRunType:

  • novel_to_video_preview
  • script_to_video_preview
  • asset_generation
  • storyboard_generation
  • video_generation

当前支持的 AgentStepType:

  • condense_novel
  • convert_novel_to_script
  • analyze_script
  • confirm_world_assets
  • generate_world_asset_images
  • generate_scene_storyboards
  • build_scene_director_plan
  • generate_video_clips
  • generate_final_report

AgentStep 状态:

1
2
3
4
5
6
pending
running
waiting_user_confirm
succeeded
failed
skipped

AgentRun 状态:

1
2
3
4
5
6
7
draft
pending
running
waiting_user_confirm
succeeded
failed
cancelled

最重要的设计点是 waiting_user_confirm。当系统解析出角色、空间和道具后,不会立刻消耗模型资源继续生成图片,而是暂停等待用户确认。

这一步让 Agent 从“全自动黑盒”变成“可控半自动工作流”。对生产工具来说,这比单纯追求自动化更实用。

Executor:长任务拆步执行与失败恢复

Agent executor 的职责是找到当前待执行步骤,执行对应工具,然后把结果写回数据库。

执行过程可以抽象成:

1
2
3
4
5
6
7
8
9
读取 AgentRun + AgentStep
-> 构建 ExecutionContext
-> 找到下一个 pending step
-> step.status = running
-> 调用对应 tool
-> 成功:写 outputJson,step.status = succeeded
-> 需要确认:step.status = waiting_user_confirm,run.status = waiting_user_confirm
-> 失败:写 errorMessage,step.status = failed,run.status = failed
-> 更新 currentStepId

ExecutionContext 会把前面步骤的输出合并起来,让后续步骤可以复用:

  • 精简后的小说。
  • 转换后的剧本。
  • scriptAnalysisId
  • 已生成资产数量。
  • 分镜数量。
  • 视频任务数量。

失败恢复也基于这个模型。重试失败步骤时,只需要把当前步骤及后续未完成步骤重置为 pending,已成功步骤保持不变。这样用户不会因为某一步模型失败而丢掉所有产物。

AI Provider 抽象:把模型差异隔离到服务端

项目目前主要接了三类模型能力:

  • Qwen:文本理解、小说精简、小说转剧本、剧本结构化解析、分镜生成。
  • Doubao Seedream:文生图、图生图、角色/空间/道具主图、分镜主视觉图。
  • Doubao Seedance:图生视频任务。

Provider 层解决的问题是:业务代码不应该关心不同模型的具体请求格式、字段名、错误结构和 URL 处理。

因此页面和 Agent executor 只关心标准化后的能力:

1
2
3
4
5
analyzeScript()
generateImage()
editImage()
createVideoTask()
syncVideoTask()

模型层内部再处理:

  • 模型名称归一化。
  • Prompt 安全规则。
  • 请求参数拼装。
  • 响应结构解析。
  • 错误信息转换。
  • 图片或视频 URL 本地化。
  • 额度耗尽、配置缺失等异常提示。

这样后续如果要增加新模型,只需要扩展 Provider,不需要改 Agent 主流程。

Prompt 与结构化输出

这个项目里 Prompt 不是随手拼字符串,而是围绕“结构化结果可入库”设计。

文本模型输出需要服务于数据库结构:

  • 剧本解析要产出角色、空间、道具和场次。
  • 世界资产要有名称、类型、描述、关键词、提示词和负面提示词。
  • 场次要有地点、情绪、关键动作、空间关系和视觉重点。
  • 分镜要有镜号、原文、图片提示词、视频提示词、镜头运动和对白。

也就是说,Prompt 的目标不是“生成一段好看的文字”,而是生成后端可以继续处理的数据。

这种设计有两个好处:

  • 前端能稳定渲染编辑表单。
  • 后续步骤能稳定引用前一步结果。

Three.js:把分镜信息转成可视化运镜草案

Three.js 在这个项目里不是做炫技 3D 场景,而是承担“运镜预览”的角色。

它消费的数据来自后端:

  • 场次地点。
  • 角色列表。
  • 道具列表。
  • 空间关系。
  • 分镜信息。
  • 镜头运动。
  • 视觉焦点。

前端再把这些信息转成基础站位、摄像机位置、注视点和路径预览。这样用户可以更直观看到“这场戏大概怎么拍”。

这个模块的价值在于:后续视频模型不只拿到一句自然语言描述,还可以拿到结构化的镜头关系。即使当前 3D 预览还不是完整制作工具,它也为后续更精细的视频生成预留了数据接口。

资产存储:外部 URL 本地化与缩略图

AI 图片和视频模型返回的 URL 往往有有效期,不能直接当长期资产使用。

所以项目做了本地化存储:

  • 生成图片后保存原图。
  • 生成 WebP 缩略图。
  • 列表和卡片优先读取 thumbnailUrl
  • 详情页和下载再读取原图。
  • 下载通过服务端代理,减少浏览器 CORS 问题。
  • /uploads/ 在生产环境中用 volume 挂载。
  • Nginx 可以直接服务静态资源,避免所有图片流量都打到 Next 容器。

这部分虽然不如 Agent 听起来亮眼,但对 AIGC 项目很关键。否则图片链接过期后,历史记录就会失效。

登录、额度与安全

登录使用 Google OAuth。用户登录后自动创建账户,并通过 Cookie Session 识别当前用户。

额度系统目前主要用于管理 AI 生成成本和任务使用边界:

  • 图片生成、图片编辑、资产主图、分镜图和视频任务都有独立成本记录。
  • 生成失败、配置错误或额度耗尽时不会记录为成功消耗。
  • 当前项目还没有开放付费购买额度,后续会接入订单和支付模块。

安全上主要做了几个约束:

  • API Key 只放在服务端环境变量。
  • 前端不直接请求模型 API。
  • 所有模型调用都通过 Route Handlers。
  • 生产环境变量通过 GitLab CI/CD File 类型变量注入。
  • OAuth 回调地址通过环境变量区分本地和生产环境。

Docker 与 GitLab CI/CD

项目已经部署到生产环境,线上地址是:https://devflow.aiyly.com/

部署方案不是直接在服务器上拉代码构建,而是走 Docker 镜像:

1
2
3
4
5
6
7
8
GitLab CI
-> docker build 应用镜像
-> docker build migrator 镜像
-> push 到 GitLab Registry
-> SSH 到服务器
-> docker compose pull
-> migrator 执行 prisma db push
-> docker compose up -d

Dockerfile 分为几个阶段:

  • deps:安装依赖。
  • migrator:只保留 Prisma 迁移需要的内容。
  • builder:生成 Next standalone 产物。
  • runner:运行生产服务。

Docker Compose 里有两个服务:

  • aigc-video-studio:主应用。
  • aigc-video-studio-migrate:数据库迁移服务。

这种拆法的好处是生产镜像不会携带完整开发环境,迁移也不会和应用启动强耦合。

这个项目里比较有价值的技术点

从技术角度看,这个项目最值得总结的是下面几件事:

1. Agent 不一定要一开始就上复杂框架

对于业务步骤明确、状态要求强、需要用户确认的系统,模板计划 + 状态机 + 工具函数,比自由 Agent 更稳定。

后续如果分支、循环、并行工具调用变复杂,再引入 LangGraph.js 这类状态图框架会更自然。

2. AI 应用要优先设计“中间态”

很多 AI Demo 只保存最终结果,但真实工具更需要中间态:

  • 用户输入。
  • 模型增强后的 Prompt。
  • 结构化解析结果。
  • 候选图。
  • 资产版本。
  • 分镜。
  • 视频任务。
  • 错误信息。
  • 每一步执行状态。

这些中间态决定了系统是否能恢复、复用和迭代。

3. 生成任务要从第一天考虑失败

AIGC 调用不稳定是常态。网络、额度、内容安全、模型输出格式、图片地址有效期,都可能导致失败。

所以任务系统应该默认支持:

  • 失败记录。
  • 单步重试。
  • 状态回滚。
  • 已成功结果保留。
  • 用户确认后继续执行。

4. Provider 抽象能降低后续换模型成本

模型变化很快,业务代码如果直接绑定某个厂商的字段,很快会变得难维护。

把模型能力封装成 Provider 后,Agent 和页面只依赖内部统一接口,后续替换模型会轻很多。

后续技术规划

后续优先补齐这些技术能力:

  • 额度购买、订单支付和套餐系统。
  • 更完整的视频任务队列。
  • 视频状态轮询和回调同步。
  • 角色语音包、配音和字幕。
  • 多视频片段排序和成片导出。
  • 更强的角色一致性策略。
  • 更细粒度的镜头参数控制。
  • 引入对象存储,替换本地 uploads。
  • 当 Agent 流程复杂后,引入 LangGraph.js 或自研更完整的 state graph。

最终目标不是做一个“点按钮生成视频”的页面,而是做一个可持续扩展的 AI 短剧制作系统:前端能承载复杂工作流,后端能管理长任务和资产,Agent 能稳定编排多模型,数据层能保存完整创作过程。