返回博客
官小西

DramaClaw 源码拆解:一条「源码可得」的 AIGC 短剧流水线,到底开了什么、留了什么

DramaClaw 源码拆解:一条「源码可得」的 AIGC 短剧流水线,到底开了什么、留了什么

「source-available」的 AI 项目这两年越来越多,但真正把仓库整个拉下来读完的人很少。差别通常不在 README 写了什么,而在它没写什么:哪一层被抽走了、哪个招牌功能在公开版里默认关着、哪条依赖的权重许可证禁止商用。

DramaClaw 是个好样本。它把一条完整的短剧工业化产线开了出来——从小说原文到成片,中间的角色提取、分集规划、剧本生成、分镜首帧、配音合成、成片剪辑全在仓库里。我之前分别写过 AI 短剧的角色一致性场景一致性,这次是第一次看到有人把这两件事的解法同时放进一个可读的仓库里。截至 2026 年 8 月 9 日,它在 GitHub 上有 3,428 Star、297 Fork、19 个 Open Issue、57 个 Closed Issue。仓库创建于 2026 年 3 月 27 日,7 月 2 日发出第一个 release,到 8 月 7 日已经是 v1.3.2——五周 20 个版本

我把仓库完整 clone 下来读了一遍。以下是源码层面的发现。

一、先看盘子有多大

维度 数值(截至 2026-08-09)
后端 Python 约 157,000 行(src/ 下)
前端 TS/TSX 约 256,000 行
后端测试 232 个文件 / 约 77,000 行
前端测试 322 个文件
近 50 次提交的实际作者 5 个账号(README 的贡献墙列了 11 人)
GitHub Discussions 0 条

测试与源码的行数比接近 1:2,这个比例在同龄的 AI 应用项目里相当少见。

技术栈是 Python 3.11–3.12 + FastAPI + React 19 + TanStack Router/Query + XYFlow + Zustand + PlayCanvas,包管理用 uv 和 pnpm。故事知识图谱挂在 Cognee 上,Agent 框架用 pydantic-ai,且版本被死死钉住

# pydantic-ai 2.x 移除了 Agent(output_retries=...),而这里的
# scene/prop/asset planner 和各类 verifier 仍在传这个参数。
"pydantic-ai-slim[anthropic,google,openai,openrouter]==1.107.0",

这条注释本身值得一读——它记录了一次真实事故:不加上限的 >= 让两个发行版的镜像构建漂移到了不同的大版本,直接炸在 unexpected keyword argument。把事故写进依赖注释,比写进 issue 有用得多。

二、最值钱的一块:一份代码、两个发行版、零 fork

这是整个仓库里普适价值最高的设计,也是我认为其他做开源商业化的团队最该抄的部分。

公开仓库是 CE(社区版)。商业版 EE 不在仓库里。但它们不是两份代码——共用同一个引擎,靠 Ports & Adapters(六边形架构)在运行时注入不同实现。

一份代码两个发行版:端口与适配器的注入结构

核心是 12 个 Protocol 端口:auth / auth_session / project_registry / project_access / audit_sink / credit_quote / usage_meter / provider_instrumentation / task_backend / cancellation_store / lifecycle / product_surface_access

启动时的分派逻辑是fail-closed 的,三条路里没有一条会静默降级:

dsn = os.environ.get("ST_CONTROL_PLANE_DSN", "").strip()
edition = os.environ.get("ST_EDITION", "").strip().lower()
if dsn and edition == "ce":
    raise RuntimeError("有控制面 DSN 即 EE,声明 CE 即应无 DSN——请二选一")
if dsn:
    # 从 entry-point 组加载 EE 适配器
    missing = [name for name in _EE_REQUIRED_PORTS if name not in _PORTS]
    if missing:
        raise RuntimeError("EE 端口不完整,缺失: " + ", ".join(missing))
    ...
if edition == "ce":
    register_local_ports(); ...
raise RuntimeError("缺 DSN 且未显式声明 CE,拒绝启动")

矛盾配置直接拒启动、EE 端口缺一个就拒启动、两个都不设也拒启动。没有「默认走 CE」这种好心的兜底——而好心的兜底正是这类双发行版架构最常见的翻车点:一次配置漏写,生产环境静默降级成单用户无鉴权模式。

真正让这套东西成立的是 CI 门禁,不是架构本身。 仓库里有一条 import lint,用 AST 扫描 src/ 下每一个静态 import、惰性 import、importlib.import_module() 的字面量字符串,以及 pyproject.toml 里所有字符串(entry-point 元数据也算),确认核心代码从不引用商业版的三个模块前缀。规则文件自己写着:

硬零:命中即失败,无 baseline 留存。

对照一下:同一个仓库里的 ruff lint 是允许存量豁免的(per-file-ignores 基线,逐文件治理),而 import lint 一条豁免都不给。这个区别是有意的——代码风格可以慢慢还债,架构边界不能。另有一条 check_ce_port_closure.py 在 CI 里校验:CE 模式下所有端口必须由本地实现满足,不得有外部适配器。

所有守栏的豁免集中在一个 ce-allowlist.toml 里,每条必须写明 path / guards / reason,而且有测试防腐——登记了一个不存在的路径就直接失败。这条细节很能说明团队的成色:他们知道豁免清单最终会腐烂成一堆没人敢删的僵尸条目,所以提前给它加了体检。

我的判断:这是我读过的开源/商业分割里做得最彻底的一个。 大多数项目的「开源版」是从内部仓库手工裁剪出来的,两边很快分叉;这里的核心代码从来只有一份,商业版只提供实现 + 启动时注册。

三、「源码可得」不是开源——他们自己用 CI 卡这件事

仓库采用 Elastic License 2.0:可自由使用、修改、再分发,唯一限制是不得把软件作为托管/受管服务提供给第三方。

有意思的是他们怎么执行这个措辞。仓库里有一条 lint_banned_words.py,扫描所有被 git 跟踪的文档文本,命中「开源 / open source / opensource」就构建失败(大小写不敏感,连字符和下划线变体都覆盖)。规则文件的 docstring 写得很直白:

采用 Elastic License v2(源码可得 / source-available),不是开源。对外文本不得把本项目称为「开源」,以免与 OSI 开源定位混淆。

豁免只给三种情况:许可说明文档本身(需要写「这不是开源」)、第三方许可证全文汇编、以及 DCO 标准法律文本——后两者是上游原文含这个措辞,不可改,且不是本项目自称。

把品牌措辞做成 CI 门禁,这个做法我第一次见。它解决的是一个真实问题:一个五周发 20 版、多人协作的项目,靠 code review 提醒「别写开源」必然会漏。但它同时也暴露了这类许可的尴尬——你必须持续花力气阻止别人(和自己人)误以为它是开源的。

四、公开版里被拿掉了什么

这是读 README 读不出来、必须看代码的部分。

4.1 招牌 AI 助手在 CE 里默认不可见

README 把「导演智能体」列为核心能力之一,还配了图。但产品面(product surface)的可见性默认值硬编码在端口契约里:

{"surface_code": "mainline",           "default_available": True},
{"surface_code": "freezone",           "default_available": True},
{"surface_code": "assistant",          "default_available": False},   # ← AI 助手
{"surface_code": "freezone_assistant", "default_available": False},   # ← 画布里的助手

CE 的本地实现直接返回这份默认值,没有任何环境变量可以翻转它;前端的应用外壳、项目导航、画布外壳三处都按这个接口决定入口是否渲染。也就是说,公开版拿到的是一个隐藏入口——想开只能改代码重新构建。

要精确一点说:这是入口可见性层面的关闭,不是 API 层面的封锁。助手的聊天路由本身在 CE 里会通过(额度检查器是 no-op)。但对一个不读源码的自托管用户来说,这个功能就是不存在的。

4.2 22 个模型别名只在官方网关上解析

.env.example 里定义了 83 个环境变量,其中 22 个是形如 DC-* 的模型别名:文本改写、身份规划、场景构建、分镜提示词合成、风格分析、剧本规范化……每个环节一个专属别名。

这些别名只在官方网关上有映射。README 说项目「模型中立」——这话没错,任何 OpenAI 兼容端点都能接。但「开箱即用地模型中立」是另一回事:自带网关(BYO)的用户要把 22 个角色一个个映射到真实模型上。官方给了 272 行的配置文档,可这仍然是实打实的摩擦。

代码里还有一处佐证:官方媒体目录(哪些图像/视频模型可选)现在从对象存储拉取,本地只保留一个可选的自动更新轮询(默认关闭)。BYO 路径的测试覆盖也明显更薄——open issue 里就有一条是「用自建渠道走首尾帧模式生成的视频不正确」。

4.3 单机单用户,且没有登录

CE 固定单本地用户,鉴权端口的实现无条件返回一个 owner 角色的本地用户。官方架构文档写明了这一点(「Local single user, no login」),所以不算隐瞒。但含义要说清楚:把 Web 端口暴露到公网,等于任何人都能用你的网关密钥烧钱。

同样地,多人协作、分布式任务调度、计量计费全在 EE。README 的能力对比表里「团队生产(共享、角色、任务、成本)」打了 ✅——那是产品的能力,不是公开版的能力。

五、「3D 片场」的真面目:它没那么 3D

这是我读这个仓库最反直觉的一处发现。

项目主推一个叫「导演世界」的功能:用 3D 高斯泼溅(3DGS)搭一个虚拟片场,锁住空间结构、人物走位和机位,让同一个场景在不同镜头间保持一致。听起来像是把一套实时 3D 管线塞进了 AI 视频工具。

实际读完是这样:

「3D 片场」四个阶段的真实数据流

三个必须点破的事实:

其一,3D 对成片的唯一贡献是一张控制截图。 用户在浏览器里飞相机、摆人模、选画幅,然后截图。这张截图作为参考图喂给图像模型重画成正式分镜稿,而下游(首帧、视频)只吃重画后的分镜稿——文件头注释直接写明「downstream 消费 sketch,绝不消费原始 3D 截图」。更关键的是,prompt 里明令丢弃截图的风格、材质、噪点和游戏渲染感。它锁的是「相机在哪、谁站在画面的哪个位置」,别的什么都不留。

其二,那个被反复提及的「空间契约」根本不含 3D。 它是一次 VLM 调用产出的 JSON,schema 版本号自己就叫 topology_only,且明确要求「不要输出 bbox 和原图坐标,下游拓扑指引是坐标无关的」。它只约束「360 全景图上,哪个方位角该有哪些固定陈设」——不约束机位、不约束走位、不约束光照。零 GPU、零模型权重。

这块有三个工程细节值得单独拿出来讲,因为它们体现了「不信 LLM 一次成型」的正确姿势:

  1. 确定性后处理凌驾于 LLM 输出之上。 LLM 出的原始 JSON 会被代码强制改写:重叠分析认定为共享的锚点,从前后墙里删掉、移进侧墙;方向排除锁(「书柜只能出现在后墙,不得出现在前墙或接缝」)完全由代码依据物件类型和尺寸白名单生成,LLM 说了不算
  2. 契约是可失效的派生物,不是永久事实。 契约文件的修改时间早于参考图就直接丢弃,schema 版本不匹配也丢弃。
  3. 注入用的 prompt 头由代码确定性合成,不是 LLM 写的。 这保证了措辞不会跨次漂移。

其三,生产默认路径不跑那个最出名的 3D 重建网络。 几何模式默认走「全景 + 深度反投影」,只用一个 Apache-2.0 的全景深度模型;那个来自大厂研究院的 3D 重建库只被 import 了两个纯代码工具函数(点云数据结构和 PLY 序列化),权重下载函数只在另一个非默认分支里被调用。这一点对法务影响巨大,下一节说。

最后:这套东西是可选增强,不是必需。 主分镜流水线里那个开关默认是关的,官方文档也写着「可选」,前端合同测试的标题直接把 optional 写死了。不开它的时候,镜头背景来自「从全景图裁切」。所以 3D 真正的独有增量只有两样:任意机位(而不是几个固定裁切锚点),以及人物道具的三维遮挡关系

六、生成后验证闭环:让检查器比生成器便宜

这是仓库里另一块值得抄的东西,尤其对任何做「AI 生成 + 质量把关」的人。

先说一个容易误判的结构事实:验证代码不在生成主链路上。 那个近 7,700 行的验证包,在整个后端只有两个外部导入方;生成流水线的几个 runner 完全不 import 它。它是手动 CLI + 用户点按钮触发的旁路,闸门卡在「候选产物 → 正式产物」的晋升环节,而不是卡生成。失败的语义是「不晋升,保留旧产物,并登记」——不自动重试、不自动修复。

6.1 失败模式注册表:一条定义喂三个消费者

整个系统的心脏是一张「失败模式」表。注意粒度:登记的是反复出现的缺陷类别,不是失败实例。每条模式有七个字段,其中三个分别喂给三个完全不同的消费者:

字段 消费者 作用
detection 视觉闸门 渲染成「这张图有没有 X?」的提问
negative_prompt_clause 生成 prompt 拼成负面约束块,注入到下一次生成
correction_template 修复指令 教修复者怎么写编辑指令
gate_enabled 成本开关 决定这条值不值得花一次模型调用去问

第二行是关键:观测到的失败 → 登记成模式 → 自动进入之后每一次生成的 prompt。这是一条预防回路,不是同轮修复回路。而 gate_enabled 把「值得花钱问」和「只做预防」分开——内置的十条种子模式里只有五条参与提问。

存储做了切分:定义放在跨项目共享的库里,命中计数放在项目级库里。这个切分是对的——定义是共享知识,计数是项目事实。

6.2 成本控制:七个杠杆叠在一起

「每次验证都是一次模型调用」是这类系统最容易失控的地方。他们的解法是级联剪枝加上一堆朴素手段:

四层级联:每层只看上一层的幸存者

除了图里的级联,还有几条:一次调用问全部缺陷模式(不是每个模式一次调用);把候选图和多张参考图拼成一张图发出去;整集审片直接把所有分镜拼成一张编号网格,整集一次调用;上下文图像压到质量 35、长边 512;小模型 + 温度 0 + 输出上限 512 token。

还有一条判断值得单独说:没有参考图时,自动剔除依赖参考的检查项。没有基线就不问——这比问了然后得到一个编造的答案要诚实得多。

6.3 一个我不认同的取舍

闸门把模型返回的 unsure 当作通过,只有明确的 yes 才计入命中。文件头写明这是刻意保守。

我认为这个取舍是错的,或者至少方向反了。这套系统存在的理由是「生成完就当成功」这个失败模式;而把「模型看不清/不确定」判为通过,恰恰在最需要人介入的场景里放行了。「未验证」和「已通过」是两种状态,把前者折叠进后者,等于在报告里制造假阳性——用户会比系统更早发现这一点。

更保守的做法是把不确定单独标成第三种状态,交给调用方决定,而不是在闸门里就替它做主。

6.4 数据飞轮:目前只写不读

仓库里有一套完整的回放捕获和训练数据库:每个 beat 每次尝试一行记录,含注册表版本、prompt 版本、格式版本三个哈希,加上 prompt 原文和响应的内容寻址存储(同一批次的多个 beat 天然共享同一份 prompt 副本,自动去重)。被闸门判失败的候选图进一个「拒绝缓冲区」,注释里明写是给负样本挖掘用的。

三个版本号都不许手填:注册表版本是定义的规范化 JSON 哈希,prompt 版本是实际发出的字节的哈希(不是模板哈希——这个区别很重要,模板一样但变量不同就是两次不同的实验)。

但要如实说:代码里没有任何训练或微调的消费方。 这套飞轮目前只写不读,是为「几个月后有更多数据时做策略对比」准备的。收敛日志的注释里甚至写着控制器「upcoming」——也就是说,自动收敛循环还没实现,今天的终止条件是人看趋势表自己决定停。

七、三条会咬人的许可证地雷

如果你打算把这套东西里的任何部分拿去商用,这一节比前面所有内容都重要。我逐个拉了上游的许可证原文。

组件 代码许可 权重许可 商用托管服务
项目本体 Elastic License 2.0 ⛔ 明文禁止作为托管/受管服务提供给第三方
全景深度模型(本项目默认用的那个) Apache-2.0 Apache-2.0 ✅ 全绿
大厂研究院的 3D 重建库 允许商用,但零专利授权 研究用途,明文排除任何商业开发或商业产品/服务,且微调派生同样锁死,许可可撤销
PLY 点云序列化库 GPL-3.0-or-later ⚠️ 纯 SaaS 不触发 copyleft;分发镜像或安装包就触发

三条要点:

  1. 权重许可和代码许可是两回事,而且常常方向相反。 那个 3D 重建库的代码是可商用的,权重是明文禁商用的,且「模型派生物」条款把微调版一并锁死,许可还是可撤销的。更麻烦的是权重在首次运行时自动下载——「我没打包进镜像」不构成抗辩。
  2. 同一个模型家族里,不同尺寸的许可可能完全不同。 我在核查时撞见一个典型例子:某个常用深度模型的 Small 版是 Apache-2.0,Large 版是 CC-BY-NC-4.0。按家族名做许可判断会翻车。
  3. GPL 依赖在纯 SaaS 下不触发,一旦分发容器镜像就触发。 这条特别容易漏,因为「我们是 SaaS」这个判断在团队开始发 Docker 镜像的那天就失效了。

顺带一提:这个项目自己的默认几何路径不需要那个禁商用的权重。也就是说,即使有人想复刻这条管线,避开它在技术上完全可行——这不是巧合,更像是他们自己也做过同样的核查。

八、代码质量:干净的边界,脏的内部

前面夸了不少,这里说反面。

文件 行数
自由画布的 API 路由 13,060
网格出图生成器(九宫格分镜 那一路做法) 7,552
主生成路由 6,189
前端设置对话框 5,852
前端画布组件 5,080

后端有 33 个文件超过 1,000 行。一个 13,000 行的 HTTP 路由文件意味着那一整块领域逻辑根本没有从传输层里分出来——这和他们在端口层表现出的洁癖形成刺眼反差。

我的解读:这是「五周发 20 版」的代价,而且是一次有意识的取舍。架构边界(端口、CI 门禁、许可合规)是不可逆的,错了以后极难改;模块内部的臃肿是可逆的,任何时候都能拆。他们把纪律花在了不可逆的地方。这个优先级我认为是对的——虽然那个 13,000 行的文件迟早会还债。

另外两处观察:

  • 注释语言。 后端约 2,600 条注释里有 1,600 多条是中文。对目标市场(中文短剧)没问题,对海外贡献者是实质门槛。
  • 文档厚度。 中英文档加起来约 2,170 行——对一个 40 万行的产品来说相当薄。真正的产品手册是一个外链文档,且只有中文。README 里链的 Discussions 至今 0 条。

九、评分

维度 分数 理由
架构边界设计 9/10 端口注入 + fail-closed + 零豁免 import lint,是这类分割的教科书
合规基建 9/10 SBOM、许可清单、密钥扫描、DCO、REUSE 规范齐全,远超同龄项目
垂直领域深度 8/10 分集节拍、身份一致性、空间契约都是真 know-how,不是通用工作流套壳
验证闭环设计 7/10 级联剪枝和失败模式注册表很扎实;unsure 当通过是错的,自动收敛控制器未实现
模块内部质量 4/10 13,000 行的路由文件,33 个千行以上文件
公开版完整度 5/10 招牌助手默认隐藏、22 个别名绑定官方网关、单机单用户无鉴权
文档 5/10 2,170 行文档撑不起 40 万行代码;主手册外链且单语言
运行时可靠性 5/10 Open issue 里有超时导致重复生成与重复计费、结果丢失、重启后媒体消失

结论

值得读的是它的边界,不是它的内部。

如果你在做开源商业化,第二节和第三节值得完整照搬一遍思路:端口协议 + 运行时注入 + fail-closed 启动 + 零豁免的 import lint + 有防腐测试的豁免清单。这套组合让「社区版和商业版永不分叉」从一句 slogan 变成了 CI 会拦住你的硬约束。大多数团队做到「架构上分开」就停了,而架构上的分开只要没有门禁,半年内一定会被一次赶工的 import 打穿。

如果你在做 AI 生成类产品,第六节的成本模型比架构更有即时价值:零模型调用的前置过滤 → 便宜模型的事实核查 → 打分 → 只在分差过小时才做的对比,加上「一次调用问全部缺陷」和「拼图代替多图」。这套东西的精髓不是某一条技巧,而是承认验证本身有成本,并为它设计预算。太多团队在生成侧精打细算,在验证侧却毫无节制地调用最贵的模型去回答一道判断题。

至于产品本身——不要把它当成可以直接商用的底座。 Elastic License 2.0 禁止托管服务这一条足以否掉大部分商业场景,而公开版被抽掉的协作层、被隐藏的助手入口、被绑定的模型别名,共同说明它的定位是「开源引流 + 官方网关抽成 + 企业版授权」,不是「你可以拿去卖」。

最后一个观察,与技术无关但值得记:这个项目在五周内拿到 3,400 Star 和 297 Fork,靠的不是某项技术突破——它的每一块技术都能在 2026 年的 AI 视频生成工具版图 里找到对应物。它拿到的是对「AI 短剧流水线」这个词的定义权。在一个还没有标准答案的赛道上,第一个把完整链路摆到台面上的人,会顺手定义后来者的词汇表。

参考资料

  1. DramaClaw — GitHub 仓库(本文数据截至 2026-08-09:3,428 Star / 297 Fork / 19 Open Issue / 57 Closed Issue)
  2. DramaClaw — 英文文档目录
  3. DramaClaw — 安装文档
  4. DramaClaw — 官方站点
  5. Elastic — Elastic License 2.0 全文
  6. Open Source Initiative — The Open Source Definition(用于对照「source-available ≠ open source」的界定)
  7. Linux Foundation — Developer Certificate of Origin 1.1
  8. FSFE — REUSE Specification(仓库采用的许可元数据规范)