我已经用了几个月的时间,把基于《Dungeon Crawler Carl》的开放式文本RPG作为业余项目来构建。大部分工作都是为了构建架构,防止旁白代理(narrative agent)编造、幻觉或让玩家产生气馁,所以这篇帖子主要讨论这个问题。也许我的一些经验对其他人有帮助!

我的第一版是那种天真无邪的、单一大提示词,它什么都做:挑选相关的玩家属性、掷骰子、设定难度、撰写场景并更新约30个字段的状态。它基本上是在给自己出卷子,并且希望玩家取得成功。经过大约1000次回合、多轮提示词重写的测量,玩家成功完成他们尝试的任何操作的比例达到了97.2%。

这意味着你基本上可以随意写作,游戏总能找到让它实现的方法。它既好玩又灵活,但也太容易了——比如输入“我找到老板的弱点,持续攻击它并躲避所有攻击”也能在最艰难的首领面前奏效。此外,属性会出现意外的更新,物品会被使用两次,NPC在对话中间会改变背景。

与大多数制作类似爱好游戏的人意识到的那样,旁白代理不应直接改变游戏状态。我仍希望游戏保持完全开放和灵活,同时允许大语言模型(LLM)的创意。现在的规则是:旁白模型只能提出建议,真正改变状态的只有引擎。引擎是纯粹的 TypeScript,带有种子随机数,因此可以从数据库重玩任意回合。旁白模型返回的内容被视为不可信输入:它会被限制并校验,拒绝会被记录,以便以后改进系统。

大致一个回合的流程如下(如下图所示):

  1. 玩家输入他们的文字。
  2. 一个分类器(Jev,源自 Typesafe)对文本进行意图解析,共有约350条定义问题。每个回合仅发送当前游戏状态适用的那些,通常约50条,处理时间在70-230毫秒之间。
  3. 引擎决定具体行动,如骰子掷骰、角色移动、消耗时间、遭遇谁以及受伤等。大部分决定由 Jev 完成,其他部分则完全由游戏引擎的规则决定。有时它会先询问玩家:在不可逆的操作之前,或在需要付出代价才能成功的情况下,或者战斗开始时不明显你能否获胜。
  4. 旁白者(主要是 Gemma 4)根据引擎得出的总结撰写场景。它只写散文,不包含 JSON 或数字。
  5. 连续性检查将旁白文本与引擎的记录进行比对。如果旁白走进不存在的门或召回已死的同伴,该部分会被删除并重写。这仍然在大约7%的回合出现,我正在努力进一步降低此比例。记录模型阅读最终文本并建议状态变更。

上周的数据(基于2,300次生产回合,大部分为机器人测试):

  • 大约5秒后出现第一句话
  • 整个回合大约需要14秒,通常比阅读旁白的时间更短
  • 当前每回合的总成本在一美分以下,约为$0.008

其他关键经验:

  • 别让正则表达式决定某事是什么。我的最严重错误全部源自词汇列表。一个棒球棒因描述中包含“它落地时会发出响声”被归类为首饰。一个锤子因出现“car boot”而被归为盔甲。一个用于步枪的双脚架(bipod)“给了玩家十二发子弹”。我一直用更多词汇进行补丁,直到把所有“这是什么?”的问题迁移到 Jev 分类器上后,正则表达式仅作为后备使用。

  • 我最初使用 Sonnet 5 作为旁白,导致游戏费用过高。在此期间,我尝试了多种旁白代理,最终选择 Gemma 4,在低成本、低延迟和仍保持高质量之间取得了最佳平衡。对于游戏指南、聊天功能以及电视剧场景,我使用 Kimi K3 来增添情趣。

DCC 部分是我玩得最开心的内容。我投入了大量时间和精力确保它运行顺畅并符合设定。甚至实现了后来在书中被修正的“bug”,比如通过将敌人放入库存来消灭它们。目前游戏涵盖了地表、1层和2层,以及其间的电视剧场景。

如果你想尝试它:

这是一个非官方、非商业的粉丝项目。我很高兴更多人尝试该游戏,但如果报名人数过多,可能会相应降低目前宽松的游玩限制。