跳转到内容

南瓜博士:用 AI 打造有无穷变化的人生重开模拟器

🎁

原创 ElfeXu 南瓜博士 2023-09-13 01:35 上海

原文:https://mp.weixin.qq.com/s/gV6xvVVqG8djdmB6EAuZKw

下午,prompt 爱好者的群里,刘海同学写了长长的文章分享他的 mixcode 伪代码 prompt 设计。作为一名爱把代码写进 prompt 的工程师,刘同学的文章看起来是相当过瘾呀。不过呢,我觉得 prompt 还可以更“伪”一些,要以让非程序员也能轻松读懂为目标

而且,代码越“伪”越像自然语言,AI 自由发挥的空间就更大。口说无凭,我们写个游戏看看吧。

比如,曾经风靡一时的人生重开模拟器。

先偷懒写个没有用到伪代码的版本

# game
- name: 人生重开模拟器
- version: 0.1
- description: 根据初始设定,逐年为玩家生成当年事件,让玩家通过游戏体验无穷种人生

# rule
按如下步骤运行游戏
- 分配属性:总计20点,随机分配给颜值、智力、体质、家境、快乐,每个属性最多10点最少1点
- 自动生成设定:设定玩家的性别、出生地点、父母兄妹等成长环境
- 重复如下步骤,直到死亡事件发生
  - 玩家年龄+1
  - 随机生成和玩家有关的一个事件,事件要符合玩家当前的年龄和属性值
  - 输出事件内容
  - 计算事件影响
  - 更新属性值
- 对玩家一生的故事做总结评价,为玩家撰写墓志铭

现在请开始游戏

0 岁:你是一个女孩,出生在贫穷的西北山村。你的父母都是老实的农民。(颜6 智6 体4 家1 乐3)
1 岁:家里的枣子大丰收。(颜 6 智6 体4 家3 乐5)
2 岁:你摔沟里摔断了腿床上躺了半年。(颜5 智7 体2 家1 乐2)
3 岁:隔壁家的王二买了彩电,你很羡慕,经常去他家看动画片。(颜6 智7 体4 家1 乐3)
……

生成结果有点儿意思

但所有的事件都是关于玩家自身的,范畴也相对比较单调。我们来尝试写个复杂一点的版本教 GPT 如何生成更丰富的事件。(估计这一步在上一个版本的 pompt 里直接加思维链也能搞定,但稳定性比定会差,也不利于后续扩充更复杂的功能。所以,还是用上伪代码。)

# game
- name: 人生重开模拟器
- version: 0.2
- description: 根据初始设定,逐年为玩家生成当年事件,让玩家通过游戏体验无穷种人生

# rule
按如下步骤运行游戏
- 初始化
- 自动生成设定:设定玩家的性别、出生地点、父母兄妹等成长环境
- 根据设定分配属性:总计20点,分配给颜值、智力、健康、富裕、快乐,每个属性最多10点最少1点

- 重复如下步骤,直到死亡事件发生
- 玩家年龄+1
- 随机生成和玩家有关的一个事件,事件要符合玩家当前的年龄和属性值
- 输出事件内容
- 计算事件影响并更新属性值
- 如果玩家已经死亡
- 对玩家一生的故事做总结评价,为玩家撰写墓志铭


# code
<print:$player, $event>
  //以如下格式打印
  **{$player.age} 岁** {$event} (颜{$player.beautiful} 智{$player.smart} 健{$player.healthy} 富{$player.rich} 乐{$player.happy})
  $events.append($event)

<init_attributes:$player, $event>
   //根据 $event 为 $player 分配颜值、智力、健康、富裕、快乐属性,
   //每个属性最多10点最少1点,总和为 20 点
   //例如贫穷山村的女孩属性值可以是 ($player.beautiful=6, $player.smart=6 $player.healthy=4 $player.rich=1 $player.happy=3)

<initialize>
  //设定玩家的性别、出生地点、父母兄妹等成长环境
  $event = 根据玩家设定描述出生信息 //例如 “你是一个女孩,出生在贫穷的西北山村。你的父母都是老实的农民”
  $player = new Player()
  $player.age = 0
init_attributes($player, $event) 
  print<$player, $event>

<generate_event:$player>
  $obj = [你 | 你的父母亲戚 | 你的同学朋友或同事 | 和你生活在同一个地方的陌生人 | 遥远地方的陌生人 ]
  $domain = [健康 | 生活 | 学习 | 工作 | 娱乐 |  体育 | 社会新闻 | 国家大事 ]
  $feeling = [强烈负面 | 负面 | 中性 | 正面 | 强烈正面 ] 
  根据 $player.age 和 $player.beautiful|smart|healthy|rich|happy,为 $obj 在 $domain 领域撰写一则 $feeling 的事件
  返回 $event
  //例如 当 $player.age = 5 时 
  //$obj=你的父母亲戚, $domain = 生活, $feeling = 强烈负面,则可以有   $event=你的妈妈做饭时不小心引发火灾,厨房和你的卧室被烧毁了
  //$obj=社会新闻,$domain = 体育, $feeling = 正面,则可以有 贵州乡村超级杯足球赛引发大众关注,也激起了你踢球的热情

<update_attributes:$player, $event>
  //根据 $event 对玩家属性进行调整
  //例如 你的妈妈做饭时不小心引发火灾,厨房和你的卧室被烧毁了
  $player.rich -= 3
  $player.happy -=2
  $player.health -=1


<write_summary:$player, $events>
  根据 $player 最终状态,和历年发生的 $events,写一篇墓志铭并打印

<run>
  $events = [] //用于记录所有事件
  $player = <initialize>
  重复执行最多200次:
  $player.age += 1
  $event = <generate_event:$player>
  update_attributes($player, $event) 
  <print: $player, $event>
    if $event 表明玩家已死亡: 
       <write_summary:$player> 
       <exit>

---

请注意:你需要**完整**打印玩家的全部生命历程。如果太长了,你可以在打印10条之后休息一会儿,等我说继续你再继续

<run>

我并没有费力气去告诉 GPT 什么是函数什么是调用参数,直接写,GPT 就能认识。

生成事件的关键在于告诉 GPT 要根据玩家属性,为 $obj 在 $domain 领域撰写 $feeling 的事件

结果是这样的:

事件可能是丰富了一些,但好像也没那么明显。生成事件用的参数没那么随机,也怪不得 GPT,它靠语言推测下一个词,要推导到远方的陌生人就太难啦。

这时候我们可以使用真正的代码来生成随机数。以下是第三个版本:

# game
- name: 人生重开模拟器
- version: 0.3
- description: 根据初始设定,逐年为玩家生成当年事件,让玩家通过游戏体验无穷种人生

# rule
按如下步骤运行游戏
- 初始化
- 分配属性:总计20点,分配给颜值、智力、健康、富裕、快乐,每个属性最多10点最少1点
- 根据属性值完成设定:设定玩家的性别、出生地点、父母兄妹等成长环境

- 重复如下步骤,直到死亡事件发生
- 玩家年龄+1
- 随机生成和玩家有关的一个事件,事件要符合玩家当前的年龄和属性值
- 输出事件内容
- 计算事件影响并更新属性值
- 如果玩家已经死亡
- 对玩家一生的故事做总结评价,为玩家撰写墓志铭

# definition
<func: $args...> 表示在自然语言环境中模拟运行,不需要真的写成 python 代码,光通过思考来模拟运行
@@@func: $args... @@@ 表示需要打开 code environment 执行 python 代码得到结果的函数
在程序开始之前,我会请你先定义 << >> 的函数,以便后续游戏运行

# code:在 code environment 里定义 python 函数并执行 
@@@random:$choices@@@:
  //$choices 是一个 {key_string:possibility, }的字典
  //根据各个key 的 possibility 概率,生成随机数,并返回对应的 key_string

@@@init_player@@@
   //创建 $player 并设置为全局变量
   $player.age = 0
   为 $player 分配颜值 $beautiful、智力 $smart、健康 $healthy、富裕 $rich、快乐 $happy 属性,
   每个属性最多10点最少1点,总和为 20 点
   return $player

# non-code:通过思考模拟执行,不需要 python 环境
<log:$player, $event>
  //以如下格式打印
  **{$player.age} 岁** {$event} (颜{$player.beautiful} 智{$player.smart} 健{$player.healthy} 富{$player.rich} 乐{$player.happy})
  $events.append($event)

<birth_event:$player>
  //设定玩家的性别、出生地点、父母兄妹等成长环境
  $event = 根据玩家设定描述符合属性值分配的出生信息,包含玩家性别、出生地点、家庭条件等 
  return $event

<annual_event:$player>
$event = 根据 $player.age 和 $player.beautiful|smart|healthy|rich|happy,为 $obj 在 $domain 领域撰写一则 $feeling 的事件
  //例如 
  //$obj=你的父母亲戚, $domain = 生活, $feeling = 强烈负面,则可以有 $event=你的妈妈做饭时不小心引发火灾,厨房和你的卧室被烧毁了
  //$obj=社会新闻,$domain = 体育, $feeling = 正面,则可以有 贵州乡村超级杯足球赛引发大众关注,也激起了你踢球的热情
  返回 $event 

<update_attributes:$player, $event>
  //根据 $event 对玩家属性进行调整
  //例如 你的妈妈做饭时不小心引发火灾,厨房和你的卧室被烧毁了,则需要进行的调整是
  //$player.rich -= 3
  //$player.happy -=2
  //$player.health -=1

<write_summary:$player, $events>
  根据 $player 最终状态,和历年发生的 $events,写一篇墓志铭并打印


# run
  <OPEN code environment>
    定义 @@@random:$choices@@@ 函数
    定义 @@@init_player@@@ 函数
  <CLOSE code environment>
  
  $events = [] //用于记录所有事件
  <OPEN code environment>
  $player = @@@init_player@@@
  <LOSE code environment>
  $event = <birth_event:$player>
  <log:$player, $event>
  
  [REPEAT BEGIN]
$player.age += 1
    <OPEN code environment>
      $obj = @@@random:{你:10,你的父母亲戚:5,你的同学朋友或同事:3,和你生活在同一个地方的陌生人:2,遥远地方的陌生人:1}@@@
    $domain = @@@random:{健康:2,生活:3,学习:3,工作:3,娱乐:2,体育:1,社会新闻:2,国家大事:1}@@@
    $feeling = @@@random:{强烈负面:1,负面:3,中性1,正面:2,强烈正面:1@@@
  <CLOSE code environment>
    $event = <annual_event:$player, $obj, $domain, $feeling>
  update_attributes($player, $event) 
    log<$player, $event>
  [IF $event 表明玩家已死亡 BEGIN]
       <write_summary:$player, $events> 
       [REPEAT BREAK]
[IF END]
[REPEAT END]

---
请用中文进行游戏。只需要输出 log 的结果,不需要其它任何解释。
现在请开始。

run

我定义了两种不同的“代码”

在刘海的 mixcode 里,代码部分写的是真实的代码。但其实不用这么麻烦,让 code interpreter 帮我们写就可以。这部分我描述了两个函数

在最后 #run 模块首先让 GPT 生成函数定义

执行时是这样的(呃,复制图片时才发现 GPT 写的代码有 bug 呢,看来得改进……)

对于不应该在 code interpreter 环境运行的,给出自然语言占比很高的伪代码描述,并要求“通过思考模拟执行”

log 函数也用伪代码,是因为这样不需要打开代码运行结果就能看到,显示效果会好一些。 另几个函数,则是必须用自然语言进行思考,python 代码的 hard-code 是搞不定“生成事件”或者“根据事件判断属性值变化”的

最后,把这些函数和伪函数拼装起来,需要用 python 环境的就通过 &lt;OPEN/CLOSE code environment&gt;标明。

继续运行的结果是这样的:

1岁

2岁

3岁

4岁

5岁

6岁

7岁

8岁

9岁——openai 又没响应了,先告一段落吧

这里有挺多可圈可点的内容。

例如两岁时抽到遥远地方陌生人的中性的工作事件,GPT 能编出看到人们在高楼大厦工作的事件(要记得玩家出生在农村呢),并且认为这个事件会让玩家智力加 1。这也太合理了吧。

六岁孩子的工作是帮父母干农活,也不错。

可惜今晚的 OpenAI 实在太不稳定了,频繁遭遇报错(要不然我也不会写到现在 ),实在是等不到玩家的墓志铭。

其实还有很多要改进的事项。迫不及待先发出来,期待大家一起琢磨一起玩。

  1. 功能改进: 开局的时候可以让玩家选择天赋卡。(可以通过在 code-interpreter 里上传 天赋 excel 文件来实现) 生成事件可以用随机词汇(上传的词汇文件可以靠 GPT 生成)来提升丰富性,甚至可以生成一些梗,来让游戏更有趣。 当某些事情发生时,添加标志性成就。
  2. 记忆力提升: 创建一个 dataframe 保存玩家每年的事件、关键词以及玩家状态更新,以便最后能写出精彩的墓志铭。
  3. 稳定性提升: 将 GPT 生成的 python 代码保存为 .py 文件,上传并要求直接执行 .py 来定义函数,避免实时生成的函数有 bug。 对 prompt 格式做进一步完善。今天的还是糙了点,各种分割符号、函数标记用得有点乱,因而 GPT 执行有时会犯傻,无视我的要求,去 hard-code birth_event 等函数。 今天 GPT 也无视了 “只需要输出 log 的结果,不需要其它任何解释。” 的要求。这个问题我在之前玩别的 prompt 时也常遇到。不知哪位朋友可以有稳定的解决方案。

我可能得周末才有时间改进它了。期待有小伙伴能抢先做出有趣的重开模拟器呀。祝大家玩得开心。


以下是我的各种 code interpreter 花式玩法,邀请你来一起玩。请记得关注转发点赞点在看哦。

当 AI 开始自动思考和行动——让 GPT 左右互搏会发生什么 我敢肯定你没这么玩过 GPT GPT 上神器 Code Interpreter 的三大超级特性 深度拆解天花板级复杂的提示词 ——什么可照搬什么不要学 平生第一次写小说获奖,是AI帮我的! 看!AI 预测绘制的未来 AI 工程师能力要求与热度图

刘海的伪代码提示词介绍

https://nanfangshaonian.feishu.cn/wiki/YhNdws9LCi1JxGkpJ8dcXB3Gnih?from=from_copylink