OpenAI APIに投げるプロンプトを改良してみた

# はじめに

OpenAI APIを用いてプロンプトの改良を行っていた際に、特に改善効果があった変更点をまとめてみました。

本記事のプロンプトは可読性を上げるため一部省略しています。
また、LLMは**GPT3.5-turbo**を使用しています。

## 改善①：命令を出来る限りシンプルに

出力してほしくない情報を明示する際、以下のような命令を行っていました。

```
# 命令
以下の「出力してほしくない情報」と類似している情報を結果に含めることは禁止します。

# 出力してほしくない情報
<800トークン程度の情報リスト>
```

しかし、実際の出力では明示した「出力してほしくない情報」をそのまま出力することがありました。

原因として考えられるのは、「出力してほしくない情報」の**トークン数の多さ**です。

基本的に**プロンプトは短ければ短いほど良い**ため、「出力してほしくない情報」を短く伝えることが出来ればハルシネーションを減らせるかもしれません。

そこで、以下のようなアプローチをとりました。

1. 「出力してほしくない情報」から特徴的な単語を抜き出し、「禁止ワード」として提示する
2. 「禁止ワード」を含む出力を禁止するよう命令

変更後のプロンプトは以下の通りです。

```
# 命令
「禁止ワード」に含まれるワード、類似する情報、単語を出力することを禁止します。

# 禁止ワード
<禁止ワードのリスト>
```

これにより、結果的に「出力してほしくない情報」を含む出力は大幅に減りました。

単語そのものを禁句とすることで、シンプルな命令に変換することが出来ました。

## 改善②：曖昧な数値表現を避ける

質問を出力させる際、以下のような命令をしていました。

```
# 命令
以下の情報を取得するための質問を5つ以上作成してください。

# 情報
<情報データ>
```

この命令で出力をすると、5つ以上質問を生成してくれる場合もありましたが、高頻度で**5個未満の質問**しか生成してくれないケースがありました。

原因として考えられるのは、「5つ以上」という**曖昧な命令**です。

曖昧な数値の表現は、「結局何個出せばいいんだろう」という推論を発生させるため、ハルシネーションが発生することがあります。

そこで、以下のように「5つ」の質問を作成するよう命令しました。

```
# 命令
以下の情報を取得するための質問を5つ作成してください。

# 情報
<情報データ>
```

これにより、8割程の確率で5つの質問を生成してくれるようになりました。

数値の指定は、「明確に」「多すぎず」行うことが重要だと考えます。


## 改善③：例を示す

理想的な出力結果が決まっている場合、出力例を示すことは効果的です。

いわゆるFew-Shotを行います。

```
# 回答例
1. リンゴは好きですか？<追加質問>他に好きな果物はありますか？
2. 犬は好きですか？<追加質問>他に好きな動物はいますか？
3. パンは好きですか？<追加質問>他に好きなパンは何ですか？
```

今回であれば、「○○は好きですか？」という質問の後に、「他に好きな○○は何ですか？」という追加質問を行う回答例を示しています。

しかし、この場合、例に引っ張られた回答になってしまうことがありました、
（例をそのまま使ったような、類似した内容の回答）

そのため、以下の制約条件を追加してみました。「そのまま使わないでください」ではなく「使うことを禁止します」としています。

```
# 制約条件
- 提供された「質問例」をそのまま使うことを禁止します。その内容を参考にして質問を考えてください。
```

これによって、例に引っ張られない質問を出してくれるようになりました。

回答例の量は多すぎず少なすぎない、ハルシネーションが起こらない程度の量が適切です。


## 改善④：出力形式を示す

ハルシネーションの1つに、「想定している出力をしてくれないことがある」事例がありました。
決まった出力形式がある場合は、出力形式を明示すべきです。

```
# 出力形式
- 質問：$1つ目の質問$
    - 追加質問：$1つ目の追加質問$
- 質問：$2つ目の質問$
    - 追加質問：$2つ目の追加質問$
- 質問：$3つ目の質問$
    - 追加質問：$3つ目の追加質問$
- 質問：$4つ目の質問$
    - 追加質問：$4つ目の追加質問$
- 質問：$5つ目の質問$
    - 追加質問：$5つ目の追加質問$
```

出力形式で意識した点は以下の通りです

- ラベルの明記
  - 「質問」「追加質問」のようにラベルを明記しました。
- 生成した回答を入れてほしい部分の明示
  - 「＄1つ目の質問＄」のように$マークや`{}`で囲むことで、生成した回答を入れる変数的な部分であることを明示します。

出力形式は以下のように省略することも可能ですが、明示していない分ハルシネーションを発生させる要因にもなります。実際の出力結果やハルシネーションの頻度を鑑みて、省略するかどうか決めると良いと思います。

```
# 出力形式
- 質問：$1つ目の質問$
    - 追加質問：$1つ目の追加質問$
//以下、4つの質問が続く
```

## 改善⑤：タスクを1プロンプトに詰め込まない

「質問文を作成させ、指定したJSON形式に変換させて出力する」というタスクを以下のプロンプトで実装しました。

```
# 命令
以下の情報を取得するための質問を5つ作成してください。
出力形式は以下のようにしてください。

# 情報
<情報データ>

# 出力形式
<出力形式テキスト>

最終的な出力はJSONで行ってください。
<JSONスキーマ>
```

この場合、最終的な出力はJSONで行ってくれますが、生成した質問の質が落ちてしまうことが多くありました。

原因として考えられるのは以下の2点です。

- プロンプトの量
  - JSONスキーマをそのままプロンプトに載せているため、プロンプトのトークン数が増えている
- タスクの複雑化
  - 「質問の生成」→「出力形式に合わせる」→「JSON変換」のようにタスクが複雑化
  - 「質問生成」の時点でハルシネーションが発生し、質の悪い質問のままJSON変換されている

これらを解決するために、タスクの分散を行います。

「質問の生成」→「出力形式に合わせる」→「JSON変換」から、「JSON変換」を別プロンプトで実行するようにします。

1つ目のプロンプトでは「質問の生成」→「出力形式に合わせる」まで行い、その結果を2つ目のプロンプトに持ち越し「JSON変換」を行います。

分けた結果は以下の通りです。

1つ目のプロンプト
```
# 命令
以下の情報を取得するための質問を5つ作成してください。
出力形式は以下のようにしてください。

# 情報
<情報データ>

# 出力形式
<出力形式テキスト>
```

2つ目のプロンプト
```
以下のテキストをJSONに変換してください。

<1つ目のプロンプトの出力データ>

<JSONスキーマ>
```

最終的にJSON形式で出力したい場合にハルシネーションに悩んでいる際は、上記のようにタスクを分けてみると良いかもしれません。


# まとめ

今回の場合では、「シンプルに」「曖昧にせず」「例を示し」「出力形式を示し」「タスクを詰め込まない」ことが、ハルシネーションを防ぐ上で重要でした。

生成AIの応答精度を気にする場合、ハルシネーションを出来るだけ減らすことはなにより重要です。
そのためにはプロンプトの細かな見直しを行う必要があります。

本記事の改善点の多くはプロンプトエンジニアリングガイドに記載されています。
タスクを詰め込みすぎて応答精度が悪いと感じたときは、プロンプトエンジニアリングガイドを見直して細かな改善を行ったり、命令内で分けられるタスクは無いか考えてみると改善に繋がるかもしれません。

### プロンプトエンジニアリングガイド

https://www.promptingguide.ai/jp/introduction/tips