外观
戏剧工坊:从 AI Town 与 MiroFish 到角色自主演戏
一个工程案例:先运行 AI Town 和 MiroFish,再把两者有用的部分拆出来,重组为面向剧本生产的角色演化系统。文中的角色一律用占位名,不绑定具体剧本。
1. 我们没有直接复制任何一个项目
三个项目解决的是不同问题:
| 项目 | 基本单位 | 产出 | 我们吸收的部分 |
|---|---|---|---|
| AI Town | 世界 tick、移动、双人对话 | 一个持续活动的世界 | 持续状态、角色记忆、服务端事实 |
| MiroFish | 发帖、评论、点赞、舆论 | 群体预测报告 | 图谱事件、角色信息边界、长期记忆 |
| 戏剧工坊 | 一集、多段交锋、旁白 | 剧本和分镜 | 导演调度、角色表演、动态场次 |
我们没有把社交平台模拟硬改成剧场,也没有把 2D 小镇硬改成剧本。
2. 戏剧工坊的基本循环
一次「集」的结构是:
text
开场旁白:时间 / 地点 / 前情
↓
段 1:角色 A 出招,角色 B 接
↓
段 2:角色 B 反击,角色 A 接
↓
段 3:压力升级、秘密露一角或场景转移
↓
收尾旁白:留下下一集钩子这解决了早期版本的两个问题:
- 一拍只有一两句,像零散聊天;
- 角色说完之后没有余波,剧情不向前走。
现在一集通常是 10-15 行台词,可以直接作为一集短剧的基础单元。
3. 两种使用方式
自动剧
text
用户给背景 + 角色
↓
导演自动推进
↓
角色按人设表演
↓
一集结束,进入下一集互动剧
text
用户选择一个角色
↓
用户输入台词
↓
其他角色按自己的记忆和目的回应
↓
导演继续安排下一步系统会拦截一种典型错位:玩家刚说完,又被安排替对方回答。实现上是一个 exclude 护栏,把刚发言的角色排除出下一次焦点候选。
4. 场次不是一开始写死的
早期的 YAML 模式仍然保留,适合明确排好的剧本:
yaml
scenes:
scene1:
characters: [角色A, 角色B, 角色C]
scene2:
characters: [角色A, 角色B]但自由演化模式不要求预先列出全部场次:
text
导演每集决定:
continue 继续当前冲突
new_arc 开一个新单元
new_scene 换地点或换人
close_arc 收掉一条支线
finale 进入收尾主线 + 单元线
text
主线:一件贯穿全剧、迟迟没有答案的事
├─ 单元 A:某个物件引出的支线
├─ 单元 B:某个职业角色手上的流程
├─ 单元 C:某个外人带来的噪音
├─ 单元 D:某段关系里的旧账
└─ 单元 E:某个被忽略的细节related=true:给主线一块拼图;related=false:人物塑造、节奏喘息或干扰;- 单元可以并行,导演决定何时重新打开或收掉。
5. 图谱记忆:把「谁知道什么」单独建模
戏剧工坊没有把完整真相塞进每个角色的 prompt,而是按事件建立见证关系:
text
(Char)-[:WITNESSED]->(Beat)
(Char)-[:SAID]->(Beat)
(Char)-[:KNOWS]->(Fact)
(SceneSummary)角色看到的内容
角色生成台词前,记忆由三块组成:
text
短期记忆:最近几条
+
场景摘要:过去一段戏发生了什么
+
事实权限:这个角色自己见过、听过或被告知什么隐私隔离的关键
text
scene.characters = [角色A, 角色B]
↓
本场产生的事实只发给角色A和角色B
↓
不在场的角色,事实查询拿不到这场内容判定依据不是「他能不能猜到」,而是「他有没有见证边」。
| 角色 | 事实数 | 能看到的场次 |
|---|---|---|
| 一直在场的角色 | 80 | 全部 |
| 只参与两场的角色 | 50 | 现场 + 单独问话 |
| 只出现一场的角色 | 35 | 现场 |
这个设计来自 MiroFish 的图谱思路,但比「向量相似检索」多了一条明确的权限边界:不在场,就没有见证边。
6. 旁白不是第四个演员
旁白层单独存在,不占用人物身份:
| 旁白职责 | 触发方式 |
|---|---|
| 开场 | 每集必有:时间、地点、前情 |
| 中段 | 只有换地点、时间跳跃或镜头外发生事时才出现 |
| 摘要 | 每隔若干行压缩已发生事实 |
| 收尾 | 每集必有:一个下一集钩子 |
| 裁决 | 可选:检查角色是否说出了不可能知道的事 |
例如:
text
【开场】夜里的走廊,灯只亮了一半。
角色对话……
【收尾】门外的那个人,一直没有走。如果每句台词都加旁白,戏会被切碎。因此中段旁白是事件驱动,不是固定频率插入。
7. 运行中遇到的真实问题
| 问题 | 表现 | 处理方式 |
|---|---|---|
| 角色连续盯同一个人 | 连续多集都在问同一个人 | 引擎级焦点轮换 |
| 单元线不增长 | 导演重复返回旧 arc id | 强制生成新 id |
| 一拍太短 | 只有一两句台词 | 焦点出招 + 反应者接招 |
| 台词重复 | 后一段复述上一段 | 传入本集 prior + 相似度过滤 |
| 所有人都知道秘密 | 场景里信息污染 | WITNESSED / KNOWS 过滤 |
| 思考模型返回空 JSON | 旁白、事实抽取静默失败 | 工具调用换非思考模型 + 重试 |
| 长戏容易忘 | 最近窗口被截断 | Beat + Summary + Fact 三层记忆 |
| 主线过早到顶 | 进度很快 100% | 按产出行数调节增量 |
这些问题说明:自由演化不能只靠提示词,必须有引擎级状态和护栏。
8. 导出到短剧产线
戏剧工坊输出两种文件:
text
screenplay.md
场次
开场旁白
动作
台词
收尾钩子
storyboard.csv
镜号
场景
说话人
对象
动作
台词
建议景别
建议时长示例:
csv
S001,第一场,角色A,,走到门口停下,你先说,我听着。,近景,4.0后续可以把 storyboard.csv 接到已有短剧产线,继续生成:
text
分镜 → 首帧 → 视频 → 配音 → 口型 → BGM → 成片戏剧工坊负责的是剧情和角色行为层,不替代下游图像、视频和音频引擎。
9. 这套系统真正有什么用
它不是为了替人「一键写完一部剧」,而是提供一个可反复运行的剧情实验场:
- 测试一个角色在压力下会不会说漏秘密;
- 看一条线索先会撞到谁;
- 发现人工大纲里没想到的支线;
- 比较不同角色组合会产生什么冲突;
- 先拿到一版活的剧本,再人工剪辑成正式结构;
- 把角色行为和镜头素材产线连接起来。
10. 一句话总结
戏剧工坊不是让模型替作者写一篇故事,而是给角色一个世界、给每个人一份不完整的记忆,再让导演、演员和记忆账本一起把故事推出来。