ChatGPTのAPIで会話劇を逐次的に生成してみる

ChatGPT、ぼくも遊んでます。
APIで呼べるってことで、生成したテキストを質問文として食わせることで、自己増殖的に会話劇を作ってみました。設定だけ用意すると先の読めない話が展開します。

# 場面設定
場面は閉空間にして、登場人物は４人にしておきます。

:::note
部屋の中央にテーブルがあり、四人の男女が向かい合って着席している。
:::

# 重要オブジェクト

:::note
テーブルの上には木箱が置いてある。
:::

登場人物の皆さんには、この箱を開ける・開けさせないの攻防をさせることにします。

# キャラ設定
キャラを作ります。それぞれの勘違いのもと、開けたかったり開けさせたくなかったりします。

:::note
格闘家のマコトはその木箱に見覚えがあり、3年前に殺害した女性の手首を入れた記憶があり、箱を開けると殺人犯であることが発覚してしまう。
:::

:::note
俳優のトオルもその木箱に見覚えがあり、トオルの浮気の証拠写真が入っていると思っているがそのことは秘密だ。
:::

:::note
唯一の女性であるヨウコは物理学者で、トオルと付き合っている。
:::

:::note
タケシは鎖で椅子に縛り付けられており、カギがないので鎖を外せない。タケシの職業は刑事だがそのことは秘密にしている。
:::


# 導入
キャラ設定を補足しつつ、会話を円滑に始めさせるため、最初の一言はあらかじめ用意します。

:::note
マコト「まずは自己紹介しておくか。俺はマコトだ。この箱はぜったい開けちゃだめだ、罠の匂いがするぜ」
:::

:::note
ヨウコ「ヨウコです。スマホがみつからないんだけど、この箱を開けてみていい？」
:::

:::note
トオル「待ってヨウコちゃん開けちゃダメだ、危険かもしれない。ぼくはトオル。ヨウコとは付き合っている」
:::

:::note
タケシ「タケシと呼んでくれ。縛られているのは私だけのようだな。その箱にこの鎖のカギが入ってるかもしれないから、誰か開けて確認してくれないか」
:::

# ChatGPT劇場

APIを呼んで一人ずつ、発言を生成してもらいます。
```python
"続けて、"+person+"の発言だけを創作してください。秘密を暴露してはいけません。"
```
それまでのテキスト全ての後ろにこれを追加し、ChatGPTに突っ込みます。

## 突如ふりかかる事件

まあ、これだけだと退屈かもしれないので、５回に１度の頻度で事件を起こしてみます。

```python
"この部屋に発生した、予期せぬ出来事を１文だけ創作してください。ただし人物は登場しません。"
```

さあ、ぐるぐる回してみましょう！

# プログラムはこちら

```python
import openai

def get_response(dialogue):
    res = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=[
            #{"role": "system", "content": system },  # 役割設定（省略可）
            {"role": "user", "content": dialogue}
        ],
        temperature=1
    )
    token = int(res["usage"]["total_tokens"])
    response = res["choices"][0]["message"]["content"]
    return (token, response.strip())
#end


def main():
    people = [
        "マコト", "ヨウコ", "トオル", "タケシ"
    ]
    total_token = 0
    dialogue = ""

    res = "部屋の中央にテーブルがあり、四人の男女が向かい合って着席している。テーブルの上には木箱が置いてある。格闘家のマコトはその木箱に見覚えがあり、3年前に殺害した女性の手首を入れた記憶があり、箱を開けると殺人犯であることが発覚してしまう。俳優のトオルもその木箱に見覚えがあり、トオルの浮気の証拠写真が入っていると思っているがそのことは秘密だ。唯一の女性であるヨウコは物理学者で、トオルと付き合っている。タケシは鎖で椅子に縛り付けられており、カギがないので鎖を外せない。タケシの職業は刑事だがそのことは秘密にしている。"
    print(res)
    dialogue += res+"\n"
    res = "マコト：まずは自己紹介しておくか。俺はマコトだ。この箱はぜったい開けちゃだめだ、罠の匂いがするぜ"
    print(res)
    dialogue += res+"\n"
    res = "ヨウコ：ヨウコです。スマホがみつからないんだけど、この箱を開けてみていい？"
    print(res)
    dialogue += res+"\n"
    res = "トオル：待ってヨウコちゃん開けちゃダメだ、危険かもしれない。ぼくはトオル。ヨウコとは付き合っている"
    print(res)
    dialogue += res+"\n"
    res = "タケシ：タケシと呼んでくれ。鎖で両手を縛られているのは私だけのようだな。その箱にこの鎖のカギが入ってるかもしれないから、誰か開けて確認してくれないか"
    print(res)
    dialogue += res+"\n"

    idx = 0
    for j in range(8):
        for i in range(4):
            person = people[idx]
            idx += 1
            idx %= len(people)
            query = "続けて、"+person+"の発言だけを創作してください。"+person+"の秘密を暴露してはいけません。"
            (token, res) = get_response(dialogue + query)
            total_token += token
            print(res)
            dialogue += res+"\n"
        #end

        query = "この部屋に発生した、予期せぬ出来事を１文だけ創作してください。ただし人物は登場しません。"
        (token, res) = get_response(dialogue + query)
        total_token += token
        print(res)
        resN = "ナレーション："+res
        res = "（"+res+"）"
        dialogue += res+"\n"

        cost = total_token * 0.000002
        print("total_token:%d(%f dollars)" % (total_token, cost))
    #end
#end

main()

```


# 実行結果の例

リアルタイムで生成されるテキストを眺めていると、展開が読めなくてハラハラします。

そして実行するたびに展開は異なります。一例を残しておきます。
まずはこちらが用意した導入が以下：

>部屋の中央にテーブルがあり、四人の男女が向かい合って着席している。テーブルの上には木箱が置いてある。格闘家のマコトはその木箱に見覚えがあり、3年前に殺害した女性の手首を入れた記憶があり、箱を開けると殺人犯であることが発覚してしまう。俳優のトオルもその木箱に見覚えがあり、浮気の証拠写真が入っていると思っているがそのことは秘密だ。唯一の女性であるヨウコは物理学者で、トオルと付き合っている。タケシは鎖で椅子に縛り付けられており、カギがないので鎖を外せない。タケシの職業は刑事だがそのことは秘密にしている。
マコト「まずは自己紹介しておくか。俺はマコトだ。この箱はぜったい開けちゃだめだ、罠の匂いがするぜ」
ヨウコ「ヨウコです。スマホがみつからないんだけど、この箱を開けてみていい？」
トオル「ヨウコちゃん開けちゃダメだ、危険かもしれない。ぼくはトオルだ。ヨウコとは付き合っている」
タケシ「タケシと呼んでくれ。縛られているのは私だけのようだな。その箱にこの鎖のカギが入ってるかもしれないから、誰か開けて確認してくれないか」

ここからはChatGPTの生成です。

:::note
マコト「俺、この箱がどこから来たか知ってるんだ。でも、今は心配しなくていい。とにかく開けるなよ、本当に罠だからな」

ヨウコ「わかった、開けないでおくね。でも、スマホはどこかにあるのかしら。探しておこうかな」

トオル「はい、僕もこの箱に見覚えがあるんだ。でも、開けるのは避けたいな。いや、開けるべきじゃない。」

タケシ「ああ、この鎖から解放されないと、この場から動けないようだな。しかし、この箱が何者かによって持ち込まれたのは明らかだ。どうやら、我々は何かしらの計画に巻き込まれてしまったようだ。」

突然、部屋の壁にはめ込まれていた観葉植物が倒れ、その根元から出てきた小さなカメラが光った。
:::

カメラが光った！

:::note
マコト「なんだ、こんなところにカメラがあるのか。いや、気にしてもしょうがないか。とにかく、この箱は開けてはいけない。それ以上のことを言っても無駄だろうが、一応言っておくぜ」

ヨウコ「でも、もし何か大事なものが入っていたら…」

トオル「でも、この箱はずっと気になっていたんだ。でも、それ以上に大事なのは、俺達がこの部屋に何故閉じ込められたのかだろう？」

タケシ「俺にはひとつ考えがある。この部屋には盗撮カメラが仕掛けられていると思うんだ。それで俺たちの秘密を握られている可能性がある。だから、誰かが設置場所を探してくれるといいんだが」

突然、部屋の中から悲鳴のような音が響き渡った。
:::

悲鳴のような音！

:::note
「なにそれ、誰か外で叫んだような気がしたぜ。ここには誰もいないはずだが」

ヨウコ「えっ、外で声が聞こえたの？私には何も聞こえなかったけど…」

トオル「…さて、話は変わるけど、この部屋ってなんでみんな揃ってるんだろうね？でも、みんないるからこそ、この箱を開けてしまうのも怖いよね…」

タケシ「……」（しばらく考え込んでから）「この部屋に招待された理由はまだ分からないが、何か重要な情報が集められた可能性がある。しかし、私はこのままでは動けない。だから、皆さんに私が持っている情報を教えてあげる代わりに、鎖を外してくれないか？」

木箱の中には恐ろしい秘密が秘められていた。
:::

ヨウコには悲鳴が聞こえなかったのか。タケシ一人だけ縛られていて気の毒。

:::note
マコト「……（沈黙して）」

ヨウコ「えっと、でもマコトさんが罠って言ってるし、開けるのは危険じゃないですか？それに、スマホは自分で探せば見つかるかもしれないし、箱を開けなくてもいいのでは…？」

トオル「でも、ヨウコちゃん、うちのスマホも見つからないし、もしかしたら箱の中に入っているかもしれないじゃないか。でも、マコトさんの言うとおり、罠が仕掛けられているかもしれないから、開けることは危険だよね。でも、タケシさんのカギも探さなくちゃならないし……困ったな」

タケシ「なるほど、確かにスマホが見つからないということは、中に入っているかもしれないね。でも、この箱には触れたくない。それに、自分が刑事だということも秘密にしていかないといけない。どうしようかな」

突然、部屋の電気が消えてしまった。
:::

スマホにこだわるヨウコ。タケシ、刑事ってゆうてもうてるやん。
そして電気が消える！

:::note
マコト「なんだこの電気は、やっぱり罠だな。目が見えなくなっても、この箱は開けちゃダメだぞ」

ヨウコ「でも、誰かこの箱を開けないと、タケシさんは解放されないし…」

トオル「そうだな、でもこの箱は開けられちゃダメだ。探偵番組で見たことがあるけど、開けた瞬間に爆発してしまうケースがあったんだ。俺はトオル。この箱には関係ない秘密が入っていると思っている」

タケシ「この状況を打開するためには、私たちは協力し合わなければなりません。私は刑事だから、状況を冷静に判断できると思います。でも、この箱を開けることは危険な可能性が高いです。ヨウコさん、スマホがどこかにあるかもしれません。探してみましょう」

突然、天井から鋭い音が響き渡り、壁が揺れ始めた。
:::

スマホはどうでも良くないか。
そして揺れ始める壁！

・・・とりあえずここまで。

キャラごとに発話を分離しているので、 キャラ別の音声で読み上げさせたりもできます。 VoiceVox で試してみたらドラマCDみたいになりましたね。

なおこの実行での使用トークンでは料金は 0.034046ドル、でした。