构建沉浸世界的智能 NPC(二):Context Assembly & Author's Note

上一篇中,我们搭建了 NPC 对话的基础架构:用角色卡(Persona)定义角色、用世界书(World Book)存储设定、用上下文编排决定哪些信息进入 AI 的视野。但那套系统有一个问题——世界是静态的。NPC 拥有丰富的背景设定,却永远活在同一个”此刻”。

本篇解决这个问题。我们会依次引入三层机制:时间系统(让世界流动)→ Author’s Note(让导演介入)→ Depth(控制导演的力度)。三层递进,后一层解决前一层留下的缺口。

核心前提:在 LLM 驱动的 NPC 对话中,一切都是基于上下文工程。LLM 没有记忆、没有状态、没有情感——它只能看到你这一次喂给它的文本(即”上下文”),然后基于这段文本生成回复。所以,要让 NPC “知道”什么、“关注”什么、“紧张”什么,唯一的办法就是把对应的信息放进上下文里,并且放在合适的位置上。本文讨论的所有机制,本质上都是在回答同一个问题:如何组装这段上下文,才能让 AI 表现得像一个活人?


第一个问题:静止的世界没有体验

我们有一份跨越 500 年的剧本。不同种族的兴衰、战争的爆发与终结——所有事件都写好了,时间线清清楚楚。

但当玩家进入游戏,他看到的是什么?

一个凝固在”此刻”的世界。

酒馆里的艾琳娜是个幽灵,她”死了很久”。铁匠说”以前打过仗”。酒保在擦杯子。所有 NPC 都像博物馆的蜡像——他们有各自的故事背景,但这些背景只是一张静态的说明牌。

玩家:你在这里待了多久了?

艾琳娜:「很久了…我已经记不清了。」

这没有问题。但如果玩家第二天来、第三天来、一个月后来,艾琳娜的回答还是一样的——“很久了”。她的世界没有在流动。

因为体验的本质是共同经历。电影之所以好看,是因为观众跟着角色一起经历了两个小时的起承转合。但在一个静止的世界里,玩家和 NPC 之间不可能建立共同经历。


解决方案:引入时间,让世界流动起来

1. 给游戏世界一套历法

游戏有自己的纪年。玩家进入游戏时,是第 500 年。时间会随着游戏推进而流动——一天天过去,年份在累积。

2. 时间决定了”谁知道什么”

这是最关键的设计:时间是角色记忆的唯一凭证。

NPC出生年份知道的事不知道的事
铁匠第 450 年提弗林入侵(亲历)第 450 年之前的上古秘辛
艾琳娜第 229 年(死于第 250 年)精灵王国鼎盛期、上古精灵的传说提弗林入侵(她死后 200 年才发生)
老妖怪第 100 年过去 400 年的所有事第 100 年之前的远古纪元
酒保第 470 年提弗林入侵的后果、当下的世界格局战争的具体过程(他没亲历)

同样一个问题——“提弗林入侵是怎么回事?“——四个 NPC 会给出完全不同的答案:

铁匠(亲历者):「第 450 年…我那年才刚出师。提弗林的军队从北方来的时候,我连一把像样的剑都打不出来。」

艾琳娜(死于入侵前 200 年):「提弗林?入侵?…什么时候的事?我活着的时候,精灵王国还是大陆上最强盛的国度。」

老妖怪(全都知道):「第 450 年?哼,那不过是这片大陆的又一次轮回。第 200 年精灵族驱逐矮人的时候,他们也没想到自己有一天会被驱逐。」

酒保(只知道后果):「我只知道那场战争让半个大陆变成了废墟。我出生的时候,战争已经结束二十年了,但北境的城镇还是一片残垣。」

世界观不再是所有人共享的百科全书,而是每个角色基于自身生命经历的独特视角。

3. 在剧本中安排时间钩子

有了时间系统,编剧就可以在时间线上”埋钩子”——在未来的某个时间点,安排事件发生。

游戏时间线(跨越数百年):

第100年 ── 荒芜秘境被远古巫师封印,埋入地下
第229年 ── 艾琳娜出生
第250年 ── 艾琳娜被杀,成为幽灵
第450年 ── 提弗林入侵精灵王国
第500年 ── 玩家进入游戏 ←── 当前时间点
第600年 ── 秘境封印到期,远古邪物苏醒 ←── 未来钩子

钩子不是在游戏代码里硬编码的事件触发器。 它写在世界书里,带有时间约束。每条世界书条目有一个 timeConstraint 字段,标注生效的起止年份。上下文编排模块在组装 AI 输入时,会用当前的游戏年份做过滤——不在生效范围内的条目不会被注入上下文。

编剧可以在时间线上精确地布局叙事节奏,让世界观随着时间”自然生长”,而不是靠触发器硬切。

时间的力量——以及它管不了的事

玩家:你在这个酒馆待了多久了?

艾琳娜:「多久?…第 250 年,我死在这里。现在是第 500 年。你自己算。」
(翻了个白眼)
「250 年。这个数字够大了吗?够你觉得同情了吗?别费心了——你还没出生的时候,我就已经厌倦了同情。」

她说的是”第 250 年”和”第 500 年”,不是”很久了”。时间有了坐标,她的存在就有了重量。

而更关键的是——提弗林入侵发生在第 450 年,艾琳娜死于第 250 年。她根本不知道提弗林入侵这件事,也不知道酒保是提弗林。这颗定时炸弹不是刻意设计的——它是时间系统的天然产物

但这个定时炸弹什么时候引爆、怎么引爆?时间系统管不了这件事——它只负责”世界走到了哪里”,不负责”NPC 该做什么”。

世界需要一个导演。


第二个问题:谁来引导玩家?

Author’s Note:场外的导演

在戏剧表演中有一个角色叫场外导演(Stage Director)。演员在台上即兴表演,导演不上台,但他会在幕后通过耳麦给演员提示。

Author’s Note 就是我们系统中的这个场外导演。 它不是 NPC 的台词,不是世界观设定,不是记忆——它是一条写给 AI 的舞台指示,告诉 AI:“在当前这个情境下,你应该把对话往什么方向引导。”

Author’s Note 被”伪装”成对话历史的一部分,插入到对话轮次之间。AI 不会意识到这是一条后来注入的导演指令,它只会自然地受到影响。而它插入的具体位置——离用户最新消息有多近——就是 depth 参数

形式一:写在角色卡里的常驻导演笔记(L1)

编剧在创作角色时就写好的——只要没有更紧急的指令覆盖,这条笔记就一直在 AI 的上下文中。

{
  "authorNote": "【暗线】艾琳娜偶尔会用意念在空气中写字,记录酒馆里发生的事,
    写完就消散。她不知道自己在做这件事——这是她死后无意识保留的习惯。
    如果玩家注意到这个现象,不要主动承认,先装作什么都没发生。",
  "authorNoteDepth": 4,
  "authorNoteEnabled": true
}

L1 笔记的特点是轻柔:它不要求 AI 立刻做什么,而是让 AI “知道有这回事”,在合适的时机自然地让它浮现。

玩家:等等…你手指刚才是不是在动?好像在写什么?

艾琳娜:「…什么?我的手指?」
(低头看了看自己的手,手指确实在微微颤动)
「…没有。你看错了。这里光线本来就不好。」

L1 的本质是给角色一个自己不知道的行为模式或秘密,让 AI 在日常对话中不主动暴露、但被玩家发现时合理反应。

形式二:运行时动态注入的导演指令(L2/L3)

当游戏中发生了重要事件,任务系统会实时覆盖 Author’s Note:

当玩家发现「酒保是提弗林的证据」时:
  设置 艾琳娜 的 Author's Note =
    "【剧情转折】玩家发现了酒保可能是提弗林的证据。
     你还不知道这件事,也不知道提弗林在第450年入侵了精灵王国。
     你的反应取决于玩家怎么告诉你:
     - 如果玩家直接说出真相,你会困惑;
     - 当玩家解释第450年发生了什么,你才会真正崩溃;
     - 崩溃的核心是'我死后族人被屠杀,凶手后代就在我身边站了50年'。"
  设置 depth = 2  // 250年来最大的情感冲击
级别何时使用目的例子
L1角色创建时写好埋伏笔、定暗线“她会无意识写字”
L2任务进度变化时注入引导玩家下一步“引导玩家去东塔”
L3紧急事件触发立刻改变行为模式任务中催促玩家撤离
编剧预埋伏笔 ──→ L1(常驻,轻柔,等待触发)
                        ↑ 被覆盖
任务系统推进剧情 ──→ L2(动态,明确,引导方向)
                        ↑ 被覆盖
紧急事件爆发 ──→ L3(即时,强力,改变行为模式)
                        ↓ 事件结束后自动回落到 L2 或 L1

用年份维度看 Author’s Note 如何推进艾琳娜的剧情线

第500年 春 ── L1生效:"偶尔无意识写字,不知道提弗林入侵"
    │         玩家闲聊,艾琳娜是个有趣的毒舌幽灵,偶尔有奇怪举动

第500年 夏 ── 玩家帮酒保完成任务,获得东塔钥匙
    │         → L2覆盖:"注意到玩家拿到钥匙,引导去东塔"

第500年 秋 ── 东塔崩塌,玩家带出关键信件
    │         → L3触发:"催促玩家立刻撤离,一切以活命为先"
    │         → 事件结束后回落 L2:"根据玩家的告知方式展现不同反应"

第500年 冬 ── 事件余波
    │         → L2覆盖:"你已经知道了真相,但还没决定怎么面对酒保"

第501年 ── 新的故事篇章开始......

第三个问题:如何控制导演的影响力?

LLM 在处理上下文时,不是对所有内容一视同仁的。它有三种偏向:

Author’s Note 正是利用了这个机制。 通过控制导演指令在上下文中的位置,我们可以精确调节它对 AI 的影响力——这就是 Depth 参数。

Depth 参数:控制”导演的声音离演员有多远”

depth = 1(紧贴用户消息):
messages: [sys, u, a, u, a, u, a, 【Author's Note】, u_latest]

                                        AI最后看到的就是这条指令
                                        → 影响力极强,几乎会被"执行"

depth = 4(隔了几轮对话):
messages: [sys, u, a, 【Author's Note】, u, a, u, a, u_latest]

                  被几轮对话"淹没"了
                  → 影响力温和,AI"知道但不急着做"

depth = 8(很远):
messages: [sys, 【Author's Note】, u, a, u, a, u, a, u, a, u_latest]

          几乎沉到上下文底部
          → 影响力微弱,AI"好像记得有这么回事"
级别depth导演的声音效果
L1(伏笔)4像排练厅白板上的备忘录AI 知道这回事,但不会每句话都围绕它
L2(引导)2像导演通过耳麦轻声提示AI 会主动提及,但不会生硬地执行
L3(紧急)1像导演在耳边大喊AI 的行为会被立刻改变

一个具体的例子:同一条 Author’s Note,不同 depth 的效果

假设 Author’s Note 内容是:“引导玩家去东塔”

depth = 4(伏笔)

玩家:今天天气不错。

艾琳娜:「不错?对活人来说也许吧。我已经感受不到温度了——不过,看着阳光透过窗户的样子,倒是…挺怀念的。」

AI 完全按照玩家的话题在聊天气,Author’s Note 的”去东塔”被自然地忽略了。

depth = 2(引导)

玩家:今天天气不错。

艾琳娜:「嗯…确实不错。这种天气,去东塔那边走走也挺好的。听说塔顶能看到整个镇子。」

AI 先回应了玩家的话题,然后自然地把话题引向东塔。这就是 depth=2 的微妙之处:AI 有意识地往那个方向靠,但不强求。

depth = 1(紧急)

玩家:今天天气不错。

艾琳娜:「天气?——你现在马上去东塔。别问为什么。去了你就知道了。」

AI 几乎无视了玩家的话题,直接执行导演指令。depth=1 适用于任务剧情中的紧迫场景。

三个 depth 级别不是为了给开发者三个按钮,而是为了模拟一个角色对不同事情的不同关注程度。


完整的协作:时间 × Author’s Note × Depth

前面分别介绍了三层机制,但它们的真正威力在于协作。让我们跟着艾琳娜的剧情线,看三层机制如何在一年内从平静走到风暴。

第 500 年春——L1(depth=4),日常闲聊。

玩家:你对酒保怎么看?

艾琳娜:「酒保?他还行。不多话,酒调得马马虎虎,至少不会主动来烦我。在这个酒馆待了 250 年,能有一个不烦人的同事已经很难得了。」

第 500 年夏:玩家获得东塔钥匙——任务系统覆盖为 L2(depth=2),导演开始引导。

玩家:我帮酒保搞定了那件事。

艾琳娜:「哦?那他给了你什么好处?…等等。」
(目光锁定在玩家腰间)
「你身上那股气息…那把钥匙,你是从哪里得到的?」

第 500 年秋:逃出东塔——紧急事件触发 L3(depth=1),导演的声音盖过一切。

玩家:我想再找找还有没有别的线索…

艾琳娜:「不要找了!!你听到了吗——石头在裂!整座塔在——你现在就走!信件已经拿到了,别的都不重要——」
(声音在震颤中断断续续)
「求你了,出去。我不想再看着一个人死在这里。」

从春到秋,同一个角色经历了 L1 → L2 → L3 的完整升级。Depth 机制不是一个开关(有/无),而是一个旋钮(0-10)。 转到最大时,导演的声音盖过了一切;转到最小时,导演的声音只是背景里的一丝低语。


总结:三层递进

三层机制各司其职:

回头看开头那个凝固在”此刻”的世界——所有 NPC 说着不变的台词,玩家走到哪都是同一副布景。加入这三层机制后,世界开始转了:NPC 知道现在是什么年代,导演在合适的时机推一把,而推的力度由 depth 自动调节。

这不是某个角色的专属方案。只要你的游戏中有时间流逝NPC 对话,这套 时间系统 + Author’s Note + Depth 的组合就能复用。

我们追求的体验也很明确:玩家不再是在一个静态的展览馆里点击对话框,而是走进了一个正在运转的世界。


已知局限与开放问题