需求
小朋友的拼插乐高城堡玩具时,突然带来这样一个项目,也就是让 AI 完成长篇的童话小说。其实生成文本是 LLM 的那首好戏了,但是对于篇幅较短,几百字内容可能会得心应手,不过对于长篇的确是不小挑战。虽然大家都说什么什么写论文,写日报。
内容来自 个人主页
故事展开的源头
这是一个故事的背景,小岛密林中隐藏一座古老的古堡,在古堡中存放着一个具有魔法的红宝石,以千面怪盗和其同伙飞爪为首的反派盗贼们一心想要偷取古堡的红宝石,古堡的主人和古堡夫人和他们仆人们,与千面怪盗一伙展开了一场斗志斗勇的大戏,最终凭借古堡主人的勇敢和古堡夫人的智慧成功把千面怪盗盗走的红宝石夺了回来。
面临挑战
长篇童话需要保持情节的连贯性,角色行为的逻辑性,以及世界观的一致性。
AI 模型容易出现"幻觉",生成不符合情理的内容,导致故事脱节。
- 这种逻辑性会体现在时间和空间上
- 内容符合物理性质,客观世界
- 遵从人物之间的关系
早期工作对于故事背景进行设定以及人物进行设定
xml
<storyoutline>
<introduction>
这是一个故事的背景,小岛密林中隐藏一座古老的古堡,在古堡中存放着一个具有魔法的红宝石,以千面怪盗和其同伙飞爪为首的反派盗贼们一心想要偷取古堡的红宝石,古堡的主人和古堡夫人和他们仆人们,与千面怪盗一伙展开了一场斗志斗勇的大戏,最终凭借古堡主人的勇敢和古堡夫人的智慧成功把千面怪盗盗走的红宝石夺了回来。
</introduction>
<plotoutline>
<beginning>
<scene>
宁静祥和的小岛上,一座古老的古堡静静地矗立在密林深处。古堡中珍藏着一颗拥有神秘力量的红宝石,它是古堡世代守护的宝物。古堡的主人和夫人过着平静的生活,仆人们也尽忠职守。然而,这份平静即将被打破。
</scene>
双向人物设定
生成人物文本形式,xml 表示人物的外貌、性格、身世等信息,然后生成角色的外貌来绘制图像,图像再次输入到 VLM 模型模型生成文本,通过对比源文本以及图像描述文本达到一致性
对于人物进行设定
- 基于 LLM 生成结构化文本,文本包含对角色外貌描写
- 基于外貌描写文本生成角色图像
- 角色图像交给 VLM 生成图像描述
- 通过对比图像生成文本和源文本(对于人物描述)更新描述
python
<character>
<name>
飞爪
</name>
<appearance>
身手矫健,动作敏捷,常常穿着便于行动的紧身衣,目光锐利。
</appearance>
<personality>
性格冲动,行动果断,是千面怪盗的得力助手,擅长执行各种高难度任务。
</personality>
<backstory>
从小在街头巷尾长大,练就了一身攀爬和抓取的本领,后被千面怪盗招募。
</backstory>
<motivation>
追随千面怪盗,追求刺激和冒险,以及获得丰厚的报酬。
</motivation>
生成情节
基于故事情节大纲以及故事人物设定来展开一个一个情节,这些情节包含发生时间、地点以及出场人物,在生成过程在 reuslt 会对于生成内容是否遵从规则,也就是地点和给定地点以及登场人物是否和给定的登场人物一致。
python
class StoryPlot(BaseModel):
plot_time:str = Field(title="story plot time",description="故事发生的时间,故事发生时间线是在 30 天,所以时间格式为 {{第 x 天 白天(或者夜晚)}} 这样时间输出",examples=['第1天 白天','第20天 夜晚'])
scene:str = Field(title='story plot scene',description="故事发生地点,请在 <scenes> 提供地点中进行选择",examples=['古堡内','古堡外','古堡大厅'])
title:str = Field(title='title of story plot',description="该故事情节的标题,对于该情节发生的内容进行简单总结",examples=['利用红宝石的力量击退敌人','面怪盗再次潜入,雇佣狼人'])
characters:List[str] = Field(title='characters of plot',description="故事情节的登场角色,请在<characters> 提供角色进行选择",examples=[[
"千面怪盗",
"飞爪"
]])
content:str = Field(title='content of story plot',description="故事情节概要描述,在该情节发生了什么内容",examples=['千面怪盗和飞爪再次潜入古堡,这次他们准备更充分,不仅带来了最新的盗窃工具,还雇佣了一群会变身的狼人作为帮手。'])
生成情节对话
有了一段一段情节,接下来就可以根据情节进行展开,内容主要是人物对话,对于对话一致性以及一段一段之间合理性,需要通过接下来复核Agent进行复核并给出建议从而修复不合理和不一致的内容。
python
class DialogueLine(BaseModel):
character:str = Field(title="dialogue character",description="也就是对话中角色,该角色从给定 storyPlot 中 characters 提供的角色中选取",examples=['content'])
content:str = Field(title="dialogue content",description="对话中角色的对话的内容,需要根据给定 storyPlot 中 content,并且符合故事背景,以及符合 characters 中该 character 的 personality",examples=['是的,夫人。我看到主人们已经休息了。但是我可以去叫醒他们,如果您需要見面的话。(疑惑地看了一眼夫人的方向)夫人,您......看起来似乎有点不像平常的您。您今天晚上没事吧?(更加留意地观察着,试图判断是否有异常)'])
class StoryPlotDialogue(BaseModel):
lines:List[DialogueLine] = Field(title="dialogue of story plot",description="多轮连续的场景对话",examples=[f"""
{json_schema_to_example(DialogueLine)}
"""])
空间上,和时间上的合理性
接下来我们根据生成内容生成动画脚本,脚本会运行在事先准备好的虚拟环境,这是 pygame 搭建的场景,主要是检查时间上,空间上合理性,运行脚本(script)输出日志(log)交给模型进行复核然后给出意见给到生成模型进行挑战和修改。