和 ChatGPT 聊天几十轮后,我发现 ChatGPT 虽然很能写,但脑洞有时候开的巨大,完全看不出主角想要做什么。
我在以红楼梦为背景设计文字 RPG 游戏时,就遇到了 token 超限、人物的语言和混乱等问题。多次尝试后,使用「规划-校准-推理」思想设计的大模型创作流程,可将剧情发展约束在可控范围内。测试中,我以「刘姥姥一进荣国府」的故事为背景,用大模型扩展出了刘姥姥在荣国府门口摆地摊以曲线进入荣国府的小作文。
此方法仍在实验中,与理想状态还有很大差距。现在先分享下实验的收获和踩过的坑。
07 👥 参考资料 学会如何思考
下图是整个方案的运行逻辑,其中黄色单元是本方案与传统 prompt 方式的主要差异:
核心处理单元的功能及其目的如下:
故事背景:通过截取红楼梦的片段,告诉大模型目前在什么场景、有什么人、在干什么事。选取红楼梦💃是因为大模型的训练数据中一定包含了许多对红楼梦解析的文章,我假设这些训练数据可以让大模型在仅有少量提示的情况下就可以知道时代背景、人物性格、社会地位等基本画像。
人物解析:故事背景仅仅是原书的摘取,我需要借助大模型,从文本中提取到结构化的信息,例如人物名字、位置、和谁在一起等。
行为规划:目前对红楼梦文本的应用仅仅是作为初始设定的资料,故事发展希望由大模型生成。剧情是由人物推动的,所以我先让人物“想”做点什么。规划会使用大模型猜测人物的几种可能会做的事情, 比如刘姥姥可能会去荣国府,也可能继续跟家人吵架..
行为校准:「规划」时产生的几种方案是大模型一次性输出的。我学习其他项目的思想,让大模型对自己的输出做一次检查。检查的结果是每一个行为都有了多维度的评分,我从中选择得分最高的一个行为进入下一步。
状态推理:此时希望得到 人物状态+人物行为 的结果,也就是从想做什么,到做完之后怎么样。这个过程也是由大模型完成。
剧本生成:推理后的状态是对人物画像的结构化描述,例如新的位置(进城了)、新的事情(遇到了老和尚)、新的想法(想去打听事)。为了让读者看起来更符合小说的味道,我让大模型以指定的文笔把这个场景🎬描述出来。
主角状态历史:这些历史,目的是告诉大模型近一段时间内的上下文,有助于大模型生成连续的故事。
下面分别介绍这些处理单元的实现细节和踩过的坑。
[已阅读30% 加油🦆]
02
探究人物特征
我摘取红楼梦第六回「刘老老一进荣国府」中的片段作为故事的背景:
刘老老道:“我也知道。只是许多时不走动,知道他如今是怎样?——这也说不得了!你又是个男人,这么个嘴脸,自然去不得;我们姑娘年轻的媳妇儿,也难卖头卖脚的。倒还是舍着我这副老脸去碰碰, 果然有好处,大家也有益。”当晚计议已定。
这段文字介绍了刘姥姥的家庭成员、家中缺钱、荣国府里有远房亲戚等几件事情,我需要从中提取以下信息:
故事背景:这相当于是对上述文字做一个概括。因为红楼梦的故事太宏大了,刘姥姥也进出大观园 3 次,重复叙述背景 也许🤔️ 可以限制大模型随意穿越。
地理位置:作为故事背景的补充,故事+位置组成了场景。希望以后可以设计多场景的剧本,人物可以在多个场景之间走动。
人物的长期目标和短期目标:长期目标实际上是为主线剧情服务的,解决在缺少生成目标时,大模型的发挥太过于灵活的问题。我分了短期目标和长期目标,是希望长期目标服务于整个章节的剧情,短期目标用于1~2个段落的扩写。短期目标可能被大模型自主更改,但长期目标通过程序控制保持住不变。
人物的名字、位置、和谁在一起、在做什么:这几个是人物的初始信息,随着剧情的发展会变化的。其中和谁在一起有助于大模型去想象人物在说什么事情、以什么方式交流。例如刘姥姥跟狗儿(刘姥姥的女婿)说话的语气必然跟在荣府说话时不一样。另外在最后「剧本生成」时,大模型竟然还会自己发挥出其他人说的话,可能也与提示信息中赋予的人物关系有关。
用于提取故事背景和人物解析的 prompt 如下:
阅读下面的故事段落,并从中提取以下信息,然后以JSON格式输出:
- **环境信息**:
- 故事背景
- 地理位置
- **人物信息**(对每个主要角色):
- 长期目标是什么
- 短期目标是什么
- 名字
- 位置
- 他们和谁在一起
- 正在做什么
故事段落:
{{story_content}}
请以JSON格式提取以上信息:
{
"story_info": {
"scene_background": "[请填写]",
"scene_location": "[请填写]"
},
"role_status": [{
"long_term_goal": "[请填写]",
"short_term_goal": "[请填写]",
"role_name": "[请填写]",
"role_location": "[请填写]",
"role_with": "[请填写]",
"role_do_or_say": "[请填写]"
}]
}
现在,刘姥姥的状态是:
{
"long_term_goal": "改善生活",
"short_term_goal": "",
"role_name": "刘老老",
"role_location": "小小之家,位于城外乡村",
"role_with": "儿子狗儿和儿媳刘氏",
"role_do_or_say": "劝说狗儿找机会改善生活,提出去拜访荣府的主意"
}
03.
脑暴剧情走向
为了让大模型可以根据当前的人物状态、人物关联的环境推测人物的下一个动作,我尝试了多种 prompt,但效果都不好。例如会多次出现同一个想法(如 告诉王家自己的困境并请求帮助)但没有任何行动,就像是剧情卡住了。这可能是由于 temperature 不合理导致的, temperature 调小后确实多了许多想象力,但也带来了副作用。
最终我使用 prompt 一次生成 3 个可能的行为,通过后面的处理对行为进行筛选,效果还不错。用来生成行为的 prompt 如下:
阅读下面的故事段落,和其中一个人物的过往经历,设计人物3个有可能的行为。然后以JSON格式输出:
故事段落:
{{story_content}}
过往经历
{{role_history}}
行为需要遵循:
1. 不可与过往经历相同
2. 可以适当发挥想象力,但不可脱离红楼梦的时代背景
3. 行为需要延续故事段落
请以JSON格式输出3个可能的行为:
{
"thoughts": [{
"id": "[a random string of 10 characters]",
"reasoning": "[请填写]"
"plan": "[请填写]",
"criticism": "[请填写]",
"speak": "[请填写]"
}]
}
其中 story_content 填充的是人物的状态,也就是长期目标、短期目标、名字、位置等信息。这其实也是测试出来的结果,填充这样的信息管用。role_history 是人物的状态序列。
某一次生成的计划如下:
刘老老决定去城里的茶楼或酒楼,寻找与贾府有关系的人,希望能够通过他们进入荣府。
刘老老决定去城里的书店,购买一些书籍,学习有关礼仪、文化和艺术的知识,以便能够在与贾府的人交流时表现得更加得体。
刘老老决定去乡村的集市或庙会,与其他乡村的人交流,寻找与贾府有关系的人,希望能够通过他们进入荣府。
这样看起来有点意思了。刘姥姥的终极目的是弄到钱,哪一个方法好呢?
[已阅读60% 加油🦆🦆]
04
匹配主线目标
前面的 3 个行动计划,仅从文字我是看不出来好坏的,但是好在大模型可以帮忙。使用下面的 prompt,可以对 3 个计划进行打分:
阅读下面人物的当前状态、过往经历和人物的目标。给你当前人物的计划,请评估每一个计划对长期目标和短期目标的影响,然后以JSON格式输出:
当前状态:
{{story_content}}
过往经历
{{role_history}}
长期目标
{{long_term_goal}}
短期目标
{{short_term_goal}}
计划
{{thoughts}}
行为需要遵循:
1. 以 0~10 对每个计划打分。0 分代表计划对目标没有任何帮助,10 分代表计划非常有助于达成目标
2. 每一个计划,需要有短期目标和长期目标的贡献值.
请以JSON格式输出3个可能的行为:
{
"thoughts": [{
"id": "copy from thought",
"long_term_value": "int value from 0 to 10, contribution for long term goal",
"short_term_value": "int value from 0 to 10, contribution for short term goal",
}]
}
这个打分的逻辑参考了「斯坦福的虚拟小镇」中对 npc 记忆重要性的评价。通过这个打分,我每次都选择最有助于长期目标或者短期目标的一个行动。
05.
引领情节发展
大模型给「去城里的茶楼」这一计划评价颇高,于是我选择了这个计划:
{
"id": "abcdefg123",
"reasoning": "刘老老意识到拜访荣府是一个很好的机会,但他也意识到自己和儿子的身份地位有限,很难得到进入荣府的机会。因此,他决定通过其他途径接触到贾府的人,以便能够得到更多的机会。",
"plan": "刘老老决定去城里的茶楼或酒楼,寻找与贾府有关系的人,希望能够通过他们进入荣府。",
"criticism": "这个计划存在一定的风险,因为刘老老并不了解城里的茶楼或酒楼的情况,也不知道如何找到与贾府有关系的人。而且,即使找到了这样的人,也不一定能够得到进入荣府的机会。",
"speak": "刘老老对儿子狗儿说:“我们去城里的茶楼或酒楼,寻找与贾府有关系的人,希望能够通过他们进入荣府。这样一来,我们就有机会改善生活了。”"
}
现在看一下执行计划之前人物的状态:
{
"long_term_goal": "改善生活",
"short_term_goal": "",
"role_name": "刘老老",
"role_location": "小小之家,位于城外乡村",
"role_with": "儿子狗儿和儿媳刘氏",
"role_do_or_say": "劝说狗儿找机会改善生活,提出去拜访荣府的主意"
}
我们需要让模型执行这个计划,并观察人物有没有更新位置、有没有产生下一个目标。通过下面这个 prompt ,我们可以让角色进入到下一个状态:
阅读下面的故事段落,和其中一个人物的经历和当前动作。请推测动作完成后,人物的最新状态,然后以JSON格式输出:
故事段落:
{{story_content}}
过往经历
{{role_history}}
当前动作
{{role_action}}
推测过程需要遵循
1. role_with 是 role_do_or_say 的对象
2. role_reason 尽量遵循当前动作
请以JSON格式输出人物的最新状态:
{
"role_name": "[请填写]",
"role_location": "[请填写]",
"role_with": "[请填写]",
"role_goal": "[请填写]",
"role_reason": "[请填写]",
"role_do_or_say": "[请填写]"
}
现在人物已经来到了「城里的茶楼或酒楼」🎉,并且准备好「寻找与贾府有关系的人」:
{
"long_term_goal": "改善生活",
"short_term_goal": "进入荣府",
"role_name": "刘老老",
"role_location": "城里的茶楼或酒楼",
"role_with": "儿子狗儿和儿媳刘氏",
"role_do_or_say": "刘老老对儿子狗儿说:“我们去城里的茶楼或酒楼,寻找与贾府有关系的人,希望能够通过他们进入荣府。这样一来,我们就有机会改善生活了。”"
}
另外,注意这里的短期目标已经发生变化了!大模型的神奇力量!
微调说话风格
在上面👆生成的人物状态中,我放了一个 role_do_or_say 字段,本是调试用的。无意中发现模型实际上已经理解了人物关系、位置、目的,并生成了一句话来描述这些信息片段。但是跑了多次程序后,看到这句话的句式非常的单调,都是”刘姥姥说xxx”。从逻辑上倒也合理,因为我每次都是让大模型以刘姥姥的身份进行续写,只是如果把这样的字显示给读者显然是非常傻的。
我通过一个 prompt 让大模型优化一下表达方式:
System: 我是红楼梦RPG游戏的策划,正在设计角色的对话。我会给你一段角色的描述,请以剧本作者的身份,描述发生了什么。
场景和角色已经固定,不再需要额外说明
Human: 当前状态:
{{story_content}}
经过改造,输出的文案如下:
剧本作者:刘老老带着儿子狗儿和儿媳刘氏来到城里的茶楼或酒楼。他们坐在一张靠窗的桌子旁,刘老老满怀期待地对儿子狗儿说:
刘老老:我们来到这里,是为了寻找与贾府有关系的人。希望能够通过他们进入荣府,这样一来,我们就有机会改善生活了。
儿子狗儿:父亲,您真是明智之人。我也希望能够进入荣府,给您和儿媳过上好日子。
刘氏:是啊,公公,我也希望能够改善我们的生活。贾府的人应该会有办法帮助我们。
正当他们谈论着未来的美好时,一个陌生人走到他们桌前,看起来有些疲惫。
陌生人:请问你们在找与贾府有关系的人吗?
刘老老:是的,我们正是。您是贾府的人吗?
陌生人:不是,但我认识一位与贾府有关系的人,他是我朋友的朋友。我可以帮你们联系上他。
刘老老:太好了!非常感谢您的帮助。
陌生人:不客气,我也希望能帮助到你们。我会给你们他的联系方式,你们可以与他取得联系。
刘老老感激地接过陌生人递过来的纸条,上面写着联系方式。
刘老老:谢谢你,真是太感谢了。我们会尽快与他联系。
陌生人:希望你们能够成功进入荣府,实现你们的目标。祝你们好运!
陌生人离开后,刘老老一家人充满了希望和期待。他们决定立即与那位与贾府有关系的人取得联系,希望能够打开新的机会,改善他们的生活。
文字是基本能看了,但还是有人物关系混乱的问题(姥姥变父亲、贾府荣府错乱)。也许,让大模型多检查一次会好一些,下个版本再优化。
07
学会如何思考
以上就是一个非常简单的剧本写作机器人,有机会扩展成剧本杀、RPG 游戏的玩法。但是在跑了多遍之后,还是有一定概率出现剧情的拖沓和肤浅,质量堪忧。
例如有一次生成中,刘姥姥先是决定去茶楼,然后从茶楼里出来去了寺庙找高僧,从寺庙里出来又去了茶楼...多次辗转,终于决定在荣国府门口摆摊了。这三个行为都与赚钱有关系,更理想的情况是茶楼里的故事应该有一些延续,起码在尝试之后再去庙里摇人。
设计过程中其实推翻了许多次,也参考了一些他人的思考。这里有一文记录了那些精彩的设计,相比起来本文只是对他们的拙劣模仿。
欢迎戳「在看 · 分享」👇将本文传播给更多人。
欢迎订阅 #AI 专题 、提出建议,感谢各位的鼓励。