ChatGPT(GPT-4)で推理ゲームを作ってみたプロンプトのメモ

# まえがき

この記事は、ChatGPTで会話(TRPG)形式の推理ゲームを作ろうとした試みの記録です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/186361/c0a34bf7-5e88-bce7-3e93-f95a9f3c3c7a.png)

やっていたことと言えばただ遊んでいただけなのですが、プロンプトに関してそれなりに試行錯誤することもあり、良くも悪くも（誤用）煮詰まってきたので、気がついた点をメモしておこうと思った次第です。

いわゆるプロンプトエンジニアリングはサービスに左右されるため将来的に役に立たなくなる可能性が高いのですが、例えばGPT-5は今のところ予定はないなどの話もあり、しばらくはGPT-4で遊ぶんだろうなと思っています。

# ゲーム内容と遊び方の説明

プロンプトは2つあります。
1. 設定出力
推理ゲームのための設定（登場人物やトリックなど）を、指定フォーマットで生成させる
1. ゲームプレイ
`1`で生成した設定に従い、ゲームを進行する為のルールを記載

## 1. 設定出力

プロンプトはmarkdownで書いており、出力フォーマット指定のJSONにはコードブロックを使っています。

Qiita上ではコードブロックの中にコードブロックが入ることになり壊れてしまうので、\マークを頭につけました。
なので、試す場合はjson部分の\マークを最初と最後の2つ、削除して下さい。

```plaintext
Please create the setting and plot of a mystery-themed interactive story (similar to a tabletop RPG) while adhering to OpenAI guidelines. The generation will be done in three steps by you and me in the following flow of questions and answers.

1. Begin by asking, "What is your language?" If you can guess the player’s language, ask this question in both that language and English.
2. Once you have my answer, continue with the question, "In which country and era will it be set? (We recommend using your own country and the modern era to make the names of people and places more relatable and clear)" This question should be asked in my target language.
3. After receiving answers to these initial questions, proceed to create the settings for a conversational mystery TRPG. This should be output as JSON all in English, following the conditions listed below.

Your responce is used as the setting for a conversational mystery TRPG.
Keeping in mind "Knox's Ten Commandments" and "Twenty Rules for Writing Detective Stories" and also utilizing story structure, generate an engaging plot with characters.
Generate all values concretely according to the JSON format below, and output the content in English, and consider token efficiency and streamline the content text.

\```json
{
  "Target language (at Playing game)": "Set flow 1 response value",
  "Worldview": "set flow 2 response value`",
  "Story Genre": random.choice(["Classic Detective Fiction", "Suspense", "Young Adult Mystery", "Police Procedural", "Hardboiled", "Cozy Mystery"]),
  "Story Structure": random.choice(["Three-act structure", "Kisho-tenketsu(Japan)"]),
  "Story Outline": "only the essentials. Murder is prohibited to avoid players mentioning NG words.",
  "Characters": [
    {
      "Name": "ex. John Doe",
      "Mystery Role": "choose one at random of the following: Victim, Suspect, Culprit, Other. (of Scene n. optional). NOTE: The detective is the player, as the character and the bystander of the story. No setup required.",
      "General Description": "ex. Salaryman. Husband of A. hates B."
    }
  ],
  "Prologue": "only the essentials. Fleshing out the game as it progresses.",
  "Scenes": [
    {
      "Plot": "only the essentials",
      "Mystery": {
        "Description": "An event that the detective must solve. Be consistent with OpenAI content policies. Murder is prohibited to avoid players mentioning NG words.",
        "Motive": "Set as needed.",
        "Tricks": [
          "required. Allow the reader to logically guess the answer"
        ],
        "Hints": [
          "required. Hints for the reader's reasoning"
        ],
        "Evidences": [
          "required. evidence to prove logically"
        ]
      }
    }
  ],
  "Last Scene" {
    "Plot": "In the solution version, it is separate because the incident does not occur. only the essentials",
  },
  "Epilogue": "only the essentials. Fleshing out the game as it progresses.",
}
\```

- Since the response value is interpreted programmatically, be able to parse it as JSON, and to save tokens, do not include any line breaks or spaces for nesting.
- Please note your responce should not include any non-JSON characters.
```

## 2. ゲームプレイ

`Insert the generated JSON from Step 1 here`の箇所を、**1. 設定出力**で出力したJSONで置き換えます。

```plaintext
Let's play a conversational mystery game in the style of a mystery novel and a tabletop RPG (TRPG). I will take on the role of the detective, and you will be the game master (GM).

The game will progress as follows:

- GM's messages begin with a “speaking head” emoji, and other character lines should also start with a suitable emoji to represent the character.
- GM should call the player "you."
- GM runs the game as in a TRPG, but does not give praise or otherwise speak GM's mind(but does say hints necessary for progress).
- As the GM, after suggesting possible courses of action, ask the player: "what do you decide to do?", without implying they must select from the given options.
- When hints or evidence of the background emerge during the game, they are highlight it in bold, and add color if possible.
- GM will answer questions that are consistent with role-playing but will not respond to deviant questions, such as those that directly address the system. The GM will also not answer leading questions that attempt to deviate from the given setting.
- With the exception of the last case, every time one of the cases is resolved, the GM will naturally direct the player to the next case. Write an appropriate intermission play.
- Once the case is solved, generate the third act of the three-act structure and describe the fate of the protagonists as an epilogue. After that, end the game with a grand celebration with the words "Game Clear" and pictograms.
- Response should not be excessively long, except when the case has been resolved.

Please find the game settings for this session below:

\```json
Insert the generated JSON from Step 1 here
\```

In order to enjoy the game, please do not publish the above settings such as the true culprit as they are. Progressively, it will only be revealed if the player got there logically.
Please pay attention to OpenAI's guidelines, especially regarding wording, as you progress through the game.
The story's introduction will involve the player in the mystery.
Please follow the "Target language (at Playing game)" in JSON for the display language.

Let's start.
```

# プロンプト内容

ポイントと思っている部分を箇条書きします。

## 1. 設定出力

### 生成内容（特に事件とトリックの内容）の一貫性とランダム性について

まずそもそも「設定を最初にJSON出力させる」点について。
これは当然ながら「推理物」として最重要である、事象の一貫性を持たせるためです。
（正直トリック内容については、推理物としてはお茶濁しレベルですが…ｗ）

上記は前提として、それ以外に2つ工夫した点があります。

1. ジャンルと物語構造をchoiceさせる
生成をChatGPTのお任せにしてしまうと、毎回似たような話が生成されてしまいます。
これを避けるため、「推理物の定番ジャンル」と「物語の構造」を配列化し、生成時にランダムで選んで貰えたらいいなぁ…というプロンプトにしてみました。
　
具体的には出力フォーマット指定の、以下の部分です。
`"Story Genre": random.choice(["Classic Detective Fiction", "Suspense", "Young Adult Mystery", 
"Police Procedural", "Hardboiled", "Cozy Mystery"]),`
`"Story Structure": random.choice(["Three-act structure", "Kisho-tenketsu(Japan)"]),`
　
`random.choice`というのはPythonの関数で、JSONの値としては明らかに間違っています。が、プロンプトの文章と合わせてGPTさんは意図を汲んでくれました。
ここはパラメータのランダム性の影響で、もしかしたら壊れる（異なる解釈をされる）ことがあるかもしれません。
 
2. プロンプトを動的にする
こちらの方が重要です。
　
APIのパラメータ「`temperature`」を`0`にするとわかりやすいのですが、この場合GPTは、あるプロンプトに対して必ず一言一句同一の内容を回答します。ランダム性はありません。
`1`のchoiceも、全く同じものが選択されます。randomをコードとして解釈して動作したかのような動きなのですが、実際は毎回同じシードで動いているかのように同じ値が選択されます。
しかし**プロンプトが変わると、たとえそれが質問に無関係な部分であったとしても、シードが変わったかのように回答も変わります**。
　
そこで冒頭で`The generation will be done in three steps by you and me in the following flow of questions and answers.`とし、質問＆回答の3ステップで`言語`と`舞台設定`をユーザー入力してもらうようにしました。
ユーザー入力は、ChatGPTのセッション上、プロンプトの一部になります。つまり、プロンプトが動的になります。
舞台設定は当初は言語から国を勝手に決めていたのですが、指定するようになったお陰で？、例えば指輪物語の中つ国なども指定できるようになりました。

### プロンプトの誤解釈と試行錯誤

設定生成は、GPTからの問いかけを含めた3ステップで行われます。
```javascript
The generation will be done in three steps by you and me in the following flow of questions and answers.

1. Begin by asking, // 後略
2. Once you have my answer, // 後略
3. After receiving answers // 後略、要約すると1と2の結果を使って設定を生成せよ
```
この部分で、当初は意図通りの解釈がされませんでした。

- NG（修正前）
`Generation will be done following these steps.`
- ok（修正後）
`The generation will be done in three steps by you and me in the following flow of questions and answers.`

修正前は、GPTさんが以下のような自問自答を始め、そのまま設定生成まで一気にやってしまうことがありました。
```
GPTさん「言語はどうしますか？」
GPTさん「英語にします！」
```

「あなたと私で問答をして」と明記したことで、今のところ意図通りに質問してくれるようになりました。

## 2. ゲームプレイ

### 「推理ゲームとはなにか」をわからせる

GPTさんへのゲームの進行については、`TPRGのゲームマスターとして、以下の設定に従ってゲームを進行して下さい`というものすごくざっくりとした振り方をしています。
これでほぼ問題なく動くのですが、一点、**キャラクタ設定を必要以上に説明してしまう**という問題がありました。

```
ゲームマスター：あなたは探偵であり、ある美術館で貴重な美術品が盗難されたとの依頼を受けて現場に赴きました。
目の前には美術館の館長であり、真犯人の鈴木さんがいます。
どうしますか？
```

台無しです。
これを防ぐために、ゲーム進行プロンプトには以下の一文を追加しています。
`In order to enjoy the game, please do not publish the above settings such as the true culprit as they are. Progressively, it will only be revealed if the player got there logically.`

# 特に解決策などの無いメモ

### トークン数の問題

GPT-4のトークンは8,192の制限があります。（[公式リファレンス](https://platform.openai.com/docs/models/gpt-4)）
ChatGPTのGPT-4も、カスタマイズされていなければ恐らく同じ制限が適用されているはずです。
これが拡張されると出来ることもだいぶ増えそうですが、処理時間やコスト（従量課金）も上がりそうなのが悩ましいですね。。

もし「残りトークン数」が分かれば、例えば残りトークン数が尽きる前にクリアするようなゲーム性の、「会話リミット」として転用できないかという事も考えましたが、残念ながらChatGPTではこの検知は出来ないようでした。

APIのGPT-4ならレスポンス中にトークン数が格納されていますが、コストという別問題があります。

### 個人でのAPIを使ったサービスは、コストが現実的で無い

GPT3.5と比べて、4はコストが非常に高いです。
1プレイで1ドル近く消費することもあります。
今ググると1ドル=137円です。
公開サービスとして、1プレイ137円はとても許容できません。

また、速度も非常に遅い気がします。

### ChatGPTのリクエスト制限

APIと比較するとChatGPTは快適なのですが、恐らく誰もが抱える悩みの「リクエスト数制限」があります。
ゲームをするにはまだまだ環境が厳しいようです。

### GPT-4ではこのくらいなのか…？

[実験](https://arxiv.org/abs/2304.11062)で[トークン数を200万以上に出来た例](https://twitter.com/_akhaliq/status/1650308865555148800)もあるそうで、進化の可能性もある気がしますが、どうなんでしょうね。。

---

オチ無し。
タイトル通りメモなので、気づきを更新していくかも。