跳到主要内容

架构说明

本文档描述 ArcReel 的稳定架构边界、主要数据流和扩展点。它不替代代码级 API 文档,也不记录临时实现计划。

1. 架构目标​

ArcReel 的核心目标不是绑定某个模型,而是提供一条:

  • 可编排;
  • 可审核;
  • 可中断恢复;
  • 可替换供应商;
  • 可追踪成本;
  • 可保留版本;
  • 可继续后期编辑

的 AI 视频生产流水线。

2. 总体架构​

3. 前端层​

前端使用 React 19 和 TypeScript,主要职责包括:

  • 项目列表和创建;
  • 项目工作台;
  • 素材预览;
  • Agent(智能体)对话;
  • 任务状态;
  • 费用统计;
  • 设置和供应商管理;
  • 版本历史;
  • 项目导入和导出。

前端不应直接处理供应商密钥或绕过后端调用模型。

4. API 与实时状态​

FastAPI 提供:

  • REST API;
  • 认证;
  • 项目和资产操作;
  • 任务创建与查询;
  • Agent 对话;
  • Agent 与项目事件 SSE;
  • 生成任务查询;
  • 外部 API Key 接入。

Agent 回复通过对话 SSE 流式返回;项目终态变化通过项目事件 SSE 触发界面刷新,生成任务的中间状态和断线兜底由任务查询补充。部署反向代理时必须关闭 SSE 代理缓冲并设置足够长的读取超时。

5. Agent Runtime​

Agent Runtime 基于 Claude Agent SDK,并采用“编排 Skill + 聚焦子智能体”的结构。

5.1 编排 Skill​

负责:

  • 判断项目当前状态;
  • 选择下一步;
  • 调用确定性工具;
  • 分发子智能体;
  • 控制阶段边界;
  • 在需要时等待用户确认。

编排层不应承担所有内容推理,否则会让主上下文快速膨胀。

5.2 聚焦子智能体​

每个子智能体聚焦一个目标,例如:

  • 角色、场景和道具提取;
  • 旁白/解说片段拆分;
  • 剧情演绎剧本规范化;
  • 单集结构化剧本;
  • 资产生成。

大量小说原文和中间推理尽量保留在子智能体内部,主 Agent 接收摘要和结果引用。

子智能体 .md 中的速查或浓缩清单不得省略规则的例外分支;无法完整保留时只引用规则的真相源,不复述规则。

5.3 确定性工具​

确定性操作更适合由工具或 Skill 执行,例如:

  • 读取和写入项目文件;
  • 创建任务;
  • 查询状态;
  • 生成结构化文件;
  • 合成视频;
  • 导出归档。

这类操作不应反复交给语言模型自由生成。

6. 应用服务层​

应用服务协调:

  • 项目;
  • 剧集;
  • 角色、场景和道具;
  • 分镜;
  • 媒体任务;
  • 文件上传;
  • 项目导入和导出;
  • 剪映草稿;
  • 费用和用量;
  • 诊断信息。

服务层应依赖稳定协议,而不是直接向上层泄漏供应商 SDK 的具体对象。

6.1 核心库与服务端的分界​

后端由核心库 lib/ 与服务端 server/ 两个包组成,依赖只能由服务端指向核心库。

  • 核心库是领域逻辑与基础设施:项目与资产、脚本与分镜、生成队列、供应商调用、计费、数据库访问等。它不知道 HTTP、Agent SDK、SSE 等交付方式的存在。
  • 服务端是交付层(HTTP 路由、Agent 工具、MCP)加上多个入口共用的用例编排,后者即应用服务(server/services/)。
  • 归属的判据是「模块是什么」,不是「谁在用它」:只被服务端使用的领域模块仍属核心库;不依赖服务端的纯领域服务应移入核心库。
  • 路由层可以直接调用核心库,不强制经过应用服务。应用服务只在两种情况下需要:同一用例被多个入口共用;需要跨多个领域包协调事务或补偿。
  • 与 Web 框架绑定的胶水归服务端。例如文案表与按语言成文在 lib/i18n/,从请求的 Accept-Language 解析语言、向路由注入 translator 的依赖在 server/i18n.py。

这条分界由依赖检查强制(import-linter,契约写在 pyproject.toml):「核心库不依赖服务端」与「核心库不依赖 HTTP 框架」(fastapi / starlette)。两条契约都没有豁免。核心库需要服务端的能力时由应用装配处注入,例如生成 Worker 的任务执行器与续跑执行器由 server/app.py 构造 Worker 时传入。

7. 供应商抽象​

ArcReel 使用:

  • TextBackend
  • ImageBackend
  • VideoBackend
  • AudioBackend

统一不同供应商的调用方式。

抽象层负责统一:

  • 请求输入;
  • 任务创建;
  • 任务轮询;
  • 输出位置;
  • 统一错误;
  • 用量信息;
  • 费用计算入口。

供应商差异仍然存在,例如:

  • 参数;
  • 时长;
  • 参考图数量;
  • 异步任务状态;
  • 失败语义;
  • 计费单位。

正确做法是把这些差异封装在后端适配器和能力描述中,而不是假装所有供应商完全相同。

8. 生成任务队列​

图像、视频和音频任务具有不同的成本和延迟特征,因此使用独立并发通道。

主要能力:

  • 异步执行;
  • Image / Video / Audio 独立并发;
  • 状态持久化;
  • 中断恢复;
  • 失败记录;
  • 排队中任务的取消;
  • 项目事件通知与任务状态刷新。

8.1 为什么需要持久化任务​

模型调用可能持续数分钟。任务不能只存在于内存,否则进程重启会丢失:

  • 已提交的远程任务 ID;
  • 当前状态;
  • 费用;
  • 输出路径;
  • 错误信息。

8.2 幂等性​

创建和重试任务时应避免:

  • 同一个分镜重复扣费;
  • 远程任务已成功但本地重复提交;
  • SSE 断开导致任务被认为失败;
  • 重复点击产生相同的生成任务。

任务身份、持久化状态和供应商任务 ID 是处理这些问题的关键。

9. 项目和资产模型​

ArcReel 的项目不仅是一条数据库记录,还包括文件系统中的媒体资产。

典型内容:

  • 原始小说、剧本或商品素材;
  • 项目配置;
  • 角色、场景和道具定义;
  • 参考图;
  • 分镜;
  • 视频片段;
  • 音频;
  • 合成输出;
  • 历史版本;
  • 导出归档。

应用数据根目录解析顺序:

  1. ARCREEL_DATA_DIR
  2. 兼容变量 AI_ANIME_PROJECTS
  3. 默认 <仓库根>/projects/

数据根下的布局(ADR 0088):

<数据根>/
├── projects/<项目名>/ 项目与生成资产
├── global_assets/ 全局资产库
├── users/<user_id>/memory/ Agent 用户记忆
├── arcreel.db 默认 SQLite 数据库
├── logs/ 文件日志
├── vertex_keys/ Vertex 凭据
├── trial_runs/ 端点「测试连接」的产物
└── runtime/ 生成准入锁、迁移完成标记、迁移错误日志
  • 各条目的位置只由 lib/infra/data_root_layout.py 的 DataRootLayout 给出,其它代码不自行拼接,也不从项目目录反推数据根。
  • 「什么是项目」只由 is_project_dir 回答:projects/ 下名字符合项目名规则、并且带 project.json 的目录。数据根里的其它条目一概不是项目,新增系统目录不需要前缀或登记清单。
  • Agent 读访问对数据根默认拒绝,只放行当前项目和当前用户的记忆。
  • 代码目录只放代码与配置,运行时不向其中写数据。
  • 从旧布局升级时,启动阶段的数据根布局迁移(lib/infra/data_root_layout_migration.py)把条目搬到上述位置,完成后写入 runtime/ 下的完成标记。

10. 数据库​

ArcReel 使用 SQLAlchemy 2.0 异步 ORM。

SQLite​

适合:

  • 个人体验;
  • 本地开发;
  • 轻量单实例。

默认使用 WAL、忙等待超时和外键约束。

PostgreSQL​

适合:

  • 生产环境;
  • 较高并发;
  • 长期运行;
  • 更成熟的备份和恢复。

应用启动时运行 Alembic 迁移,将数据库升级到当前版本。

11. 版本历史​

媒体生成具有不确定性,因此“重新生成”不应简单覆盖旧文件。

版本历史用于:

  • 对比不同生成结果;
  • 回滚;
  • 保留已审核版本;
  • 降低试错风险;
  • 为项目归档提供完整上下文。

服务层应通过统一的资产版本接口操作,而不是让各供应商适配器自行决定如何覆盖文件。

12. 用量和费用​

用量追踪跨越:

  • 文本;
  • 图片;
  • 视频;
  • TTS;
  • 不同供应商;
  • 不同币种;
  • 预估和实际。

设计原则:

  • 供应商适配器提供原始用量;
  • 费用策略负责转换;
  • 不同币种默认分开统计;
  • 失败任务是否计费按供应商语义处理;
  • ArcReel 记录不替代供应商官方账单。

13. 视频合成与剪映导出​

媒体生成完成后有两种输出路径。

成片合成​

使用 FFmpeg 处理:

  • 片段拼接;
  • 转场;
  • 音频;
  • 最终编码。

剪映草稿​

导出可继续编辑的项目结构,用于:

  • 调整片段;
  • 编辑字幕;
  • 替换配音;
  • 增加音乐;
  • 修改转场;
  • 人工精修。

“可继续编辑”是 ArcReel 与只输出单个视频文件的生成工具之间的重要差异。

成片读取模型​

浏览器预览、可编辑包下载和剪映草稿不各自推导声音、字幕或时长,而是共同消费成片读取模型。该模型固定已选视频版本、可选 TTS 版本、实际媒体时长、原音开关、字幕时序以及当前或历史状态;当前成片的字幕和呈现描述分别物化到 subtitles/ 与 presentations/,并登记到项目 Artifact Manifest。历史选择只读,不覆盖当前物化结果。

手动上传但尚无生成来源证明的视频走显式 raw-only 分支:保留原始视频,不猜测来源基点或新旧状态,不生成 TTS 和字幕,也不登记派生成片。这样三个输出入口仍共享同一选择,同时把来源未知与来源已验证区分开。

14. 认证和外部集成​

ArcReel 提供:

  • 用户名和密码登录;
  • JWT;
  • arc- 前缀 API Key;
  • 外部 Agent 同步对话端点。

API Key 应使用哈希存储,不应在创建后以明文持续返回。

外部 Agent 集成应:

  • 最小化权限;
  • 限制可访问的项目;
  • 记录调用;
  • 支持撤销;
  • 避免把管理员密码提供给第三方平台。

15. 沙箱和安全边界​

Agent 工具可能访问:

  • 文件系统;
  • 网络;
  • 子进程;
  • FFmpeg;
  • Bash 工具。

ArcReel 在支持的环境中使用 bwrap 等机制限制这些能力。Docker Compose 为沙箱配置了额外权限,因此生产部署需要在功能和宿主机隔离之间做清晰取舍。

安全原则:

  • 默认最小权限;
  • 文件和网络白名单;
  • 不挂载 Docker Socket;
  • 不挂载不必要的宿主机路径;
  • 对外只暴露反向代理;
  • 使用 HTTPS;
  • 定期更新;
  • 把未知项目输入视为不可信数据。

16. 扩展一个新供应商​

一个完整的新供应商接入通常需要:

  1. 定义能力和配置模型;
  2. 实现对应 Backend 协议;
  3. 统一错误类型;
  4. 实现同步或异步任务生命周期;
  5. 保存远程任务 ID;
  6. 解析输出和用量;
  7. 实现费用策略;
  8. 接入设置页;
  9. 添加单元和集成测试;
  10. 更新供应商文档;
  11. 验证超时和重试。

不要只实现“成功路径”。视频供应商的轮询、超时、失败和重复提交往往比创建请求更复杂。

17. 扩展一个新工作流阶段​

新阶段应回答:

  • 输入是什么;
  • 输出是什么;
  • 是否可重复执行;
  • 如何判断已完成;
  • 是否需要用户确认;
  • 失败后如何恢复;
  • 是否产生费用;
  • 是否需要版本历史;
  • 主 Agent、Skill、子智能体和确定性工具各负责什么。

一个阶段只有在完成条件可以由项目状态明确判断时,才能可靠地被编排和恢复。

18. 架构约束​

建议长期保持以下约束:

  • UI 不直接调用供应商;
  • 业务服务不依赖供应商 SDK 返回对象;
  • Agent 不直接拼接数据库 SQL;
  • 供应商适配器不决定产品工作流;
  • 重试不绕过幂等性;
  • 费用记录与生成任务关联;
  • 项目文件和数据库状态可共同备份;
  • 具体模型名称不进入稳定领域接口;
  • 长文本推理不无限累积在主 Agent 上下文;
  • 确定性操作优先使用工具而不是自然语言生成;
  • 核心库不依赖服务端与 Web 框架(见 6.1)。