一句话背后,世界其实已经被问了很多问题
先设定一个示意场景:侦探故事里,玩家扮演的调查员站在庄园的门厅,手里只有一封请柬和一支手电。他直接输入:“我打开书房的保险箱,把里面的信拿走。”
如果把这句话原样交给一个只会续写的模型,它大概率会写出一段流畅的开箱描写,因为“开保险箱”在无数故事里都是合理的情节。问题在于,这段描写会同时制造三个错误事实:调查员已经在书房里,保险箱已经被打开,信已经在他手里。后面的剧情就必须替这三个错误买单,要么继续圆谎,要么突然矛盾。
所以大发娱乐AI的做法是把这句话先当作一个待裁决的提案,而不是一段可以直接续写的文字。裁决只问世界,不问文采,它至少要回答:
- 调查员现在在哪里,书房和门厅之间隔着什么?
- 保险箱是否存在,处于什么状态,需要什么才能打开?
- 调查员是否知道保险箱在哪里、怎么开?
- 这件事发生在故事的哪个时间点,有没有人正在看着他?
这些问题的答案都存放在世界状态里,而不在模型的“印象”里。关于世界状态怎么存、谁在哪、谁知道什么,可以先读互动小说真正自由以后,玩家输入“去睡觉”也可能改变整个故事这一篇;本文只专注其中一件事:一个动作到来时,怎样判定它能不能成立。
第一步:把自然语言拆成动作、对象和方式
校验之前得先“读懂”。玩家的输入往往一句话里混着多个动作,还夹着情绪和修辞。上面那句话拆开以后是这样的:
- 移动:前往书房(隐含的前置动作,玩家没写)。
- 开启:对象是保险箱,方式未指定。
- 取走:对象是信,来源是保险箱内部。
拆解得到的是一张结构化的“动作草案”:动作类型、主体、对象、工具或方式、地点、意图。这里有两个容易出错的地方。
第一是隐含动作。玩家不会把每一步都说出来,系统需要判断哪些是可以自动补全的(走进一间没锁的书房),哪些必须停下来确认(撬门)。第二是歧义对象。庄园里可能有两个保险箱,一个在书房,一个在地下室,模型不应该自己挑一个,而应该按玩家当前的位置和最近提到的对象做消歧,消歧不了就在故事里让角色自己问一句。
意图理解由语言模型完成,因为自然语言的开放性正是它擅长的;但它的输出是一份草案,草案要交给下一层规则去核对,而不是直接变成剧情。这与玩家一句“我不去救公主”,剧情接下来怎么办里讲的“自然语言成为剧情输入”是同一条思路,本文补上的是输入之后那道关卡。
第二步:四类前置条件,逐项打勾
草案出来以后,校验层按动作类型取出对应的前置条件清单。下面是一张示意表,数值和条件都是举例,具体作品可以按自己的规则增减。
| 条件类型 | 检查什么 | 保险箱示例 | 不满足时通常意味着 |
|---|---|---|---|
| 物品 | 是否持有所需道具 | 需要钥匙或开锁工具 | 需要先去获得,或换一种办法 |
| 位置 | 是否在可触及的地点 | 必须身处书房 | 需要先移动,途中可能发生事件 |
| 知识 | 角色是否知道关键信息 | 知道保险箱位置或密码线索 | 需要先调查,或碰运气 |
| 时间与他人 | 时机是否允许,有无目击者 | 管家正在书房打扫 | 需要等待、引开或冒险 |
这里最关键的一条原则是:知识条件看的是角色知道什么,而不是玩家知道什么,也不是模型知道什么。玩家可能从上一章的线索里猜到了密码,但如果角色在故事里从未接触过这条线索,那么“输入正确密码”依然应当判为未满足,或者只能算作一次猜测。反过来,角色已经通过剧情得知的信息,即便玩家忘了,也应当可用。
四类条件都写成可以被程序检查的形式,例如“持有物品:书房钥匙”“位置:书房”。这样做的好处是判定结果稳定、可复盘,同样的世界状态给出同样的判定,不会因为模型某次心情不同就换了答案。
第三步:结果分成四档,而不是“行”和“不行”
最容易做坏的设计,是把校验结果做成二选一:通过就继续,不通过就弹一句“你不能这样做”。这既伤害沉浸感,也浪费了玩家想出来的主意。更合理的是分级:
- 完全成功:条件全部满足,动作按预期完成,世界状态随之更新。
- 部分成功:条件满足一部分,比如没有钥匙但会开锁的同伴在场,锁被打开了,代价是留下了痕迹。
- 失败但有后果:动作被尝试了,没有成功,但世界因此改变,例如强行撬锁弄出声响,管家开始起疑。
- 世界内替代:动作在当前世界里根本无法成立,系统给出符合世界逻辑的解释和另一条路。
分级的价值在于,玩家的每一次尝试都会推动故事,而不是撞上一堵透明的墙。第二档和第三档尤其重要,它们让“没有条件也想试一试”变成一种有意义的冒险,只是要付出代价。代价从哪里来?由世界规则和当前剧情状态共同决定:管家是否在附近、警报是否存在、调查员的技能是否够用。
被拒绝的时候,要说得像故事,而不是像报错
回到那位站在门厅的调查员。他没有钥匙,人也不在书房。系统的裁决是第四档:世界内替代。一种不好的写法是:“无法执行该操作,缺少物品:钥匙。”另一种同样不好的写法,是让模型顺着玩家写下去,假装他已经在书房里。
比较好的写法是把拒绝转译成场景:
这一段做了三件事:交代了为什么现在做不了,说明了缺的是“位置”这个条件,并且顺手给出两个可以尝试的方向,搭话或者绕行。玩家读完以后仍然握有主动权,只是知道了世界的边界在哪里。
拒绝话术还有几条经验,我们倾向于写进创作规范:
- 用角色的视角解释,不用系统的口吻。
- 只暴露角色能感知到的理由,不透露玩家不该知道的机关。
- 至少留一个可尝试的方向,除非这条路确实是死路,那也要让角色自己意识到。
- 同一类拒绝不要每次都用相同的句式,否则玩家很快就会摸出规律并厌倦。
谁来裁决:模型提议,规则说了算
这一节回答一个架构问题:校验放在哪里。
一种做法是把所有规则写进提示词,让模型自己判断。它的问题很明显:规则越多,模型越可能漏掉其中几条,长对话里更容易前后不一致。另一种做法是把规则完全交给程序,模型只负责润色。它的问题是玩家的表达千变万化,硬编码的规则很难覆盖“我用发卡试着撬撬看”这类没预料到的写法。
更均衡的分工是三层:
- 语言模型负责把自然语言解析为动作草案,并在校验通过后把结果写成文字。
- 规则层负责查表和判定:前置条件是否满足、结果落在哪一档、状态如何更新。
- 导演层在必要时干预:比如某个动作虽然合规,却会让关键剧情提前崩掉,导演层可以延后触发,或者改用带后果的结果。
也就是说,模型擅长“听懂”和“讲述”,规则擅长“判定”和“记账”。这也是大发娱乐官网为什么不追求“完全自由生成”一文强调的:AI负责在规则里制造变化,而不是负责改写规则。
校验最容易出错的四个地方
任何校验层都不是一劳永逸的,以下几处是需要长期盯着的:
- 误杀创意:规则写得太死,玩家的巧妙办法(用发卡撬锁)被一律判为不可能。缓解办法是让规则声明“允许的等效方式”,或者对未知方式走“部分成功”。
- 误放行:模型在解析时把“我想知道密码”悄悄理解成“我知道密码”。所以解析结果里“意图”和“既成事实”要分开字段存放。
- 状态漂移:动作判定成功以后,忘记更新世界状态,于是下一轮判断又基于旧状态。每次判定后必须有明确的写入步骤。
- 拒绝疲劳:连续几次被拒,玩家会觉得被系统对抗。适当的做法是当同类动作连续被拒时,让世界主动给出线索,而不是继续沉默。
一个可以自查的小清单
如果你在设计自己的互动叙事,可以用下面几个问题检查动作校验是否可靠:
- 玩家的每个动作,是否都能落到一张“动作草案”的字段上?
- 每类动作,是否有明确写下的前置条件,而不是散落在提示词里?
- 判定结果是否至少分了三档以上?
- 拒绝文本是否用角色视角,并给出至少一个可尝试的方向?
- 判定之后,世界状态是否有明确的写入,并能在之后被查询?
- 遇到规则没覆盖的输入,默认走哪一档,是否留有余地而不是直接拒绝?
这几条做扎实了,玩家输入什么都可以,世界依然是那个世界,故事依然是同一个故事。