⚒️

ChatGPT (gpt-3.5-turbo) で安定してタスクの失敗時にもJSON形式で返す手法

2023/04/05に公開

ChatGPTをシステムに組み込むさいに、結果をJSON形式にすることでシステム上で扱いやすくすることがあります。

system
userの入力した数式を計算して結果を回答をしてください。
回答は下記のJSONフォーマットで回答してください。
{
  "formula": "input",
  "result": number
}
user
184 + 739
answer
{
  "formula": "184 + 739",
  "result": 923
}

例えば上記のようなタスクが成功できる場合では、おおよそ100回実行すると60回ぐらいはJSONとしてパース可能な結果が返ってきます。

ではタスクが失敗するケースではどうなるでしょうか。

system
userの入力した数式を計算して結果を回答をしてください。
回答は下記のJSONフォーマットで回答してください。
{
  "formula": "input",
  "result": number or null
}
user
100 / 0
answer
{
  "formula": "100 / 0",
  "result": null
}

このケースでは100回実行しておおよそ01回ぐらいの成功になります。このあたりはプロンプト次第ですが、あまり高い結果にはならないと思います。
もちろん、このプロンプトの作りはかなり甘いため、作り込めばもう少し成功率を上げることはできますが、それでもそこまで高い数字にはなりません。

ではこのタスクに失敗するときのケースがどうなるかと言うと、おおよそ下記のようになります。

answer
計算エラー(ゼロ除算)が発生しているため、結果は存在しません。下記が回答のJSONフォーマットになります。

{
  "formula": "100 / 0",
  "result": null
}

JSON+文章の形ですね。これはチャットという性質上、失敗したさいには一言付け加えるという性質がChatGPTにはありそうです。

answer
0での割り算は定義されていないため、エラーになります。

他のケースとしては完全に指示に従わずに文章だけを返すと言ったケースもあります。
おおよそこの2パターンになる傾向があります。

失敗時にも安定させる方法

assistantで { を入れる

傾向としては、はじめに文章でなぜ失敗するのかと説明するのでassistant{を入れて、そこから回答を生成させるようにします。
長い回答のさいに「続きを」と言って、前の回答から続けて回答をしてもらったこともある人は多いのではないでしょうか。これはその応用な形ですね。

system
userの入力した数式を計算して結果を回答をしてください。
回答は下記のJSONフォーマットで回答してください。
{
  "formula": "input",
  "result": number or null
}
user
100 / 0
assistant
{
answer
  "formula": "100 / 0",
  "result": null
}

この方法の場合、100回実行したら47回成功するようになります。これは成功するタスクのケースでも成功率を格段に上げてくれる手法になります。(成功するタスクだと100回中99回の成功)

フィールドに自由入力させるフィールドを持たせる

タスクの失敗時にJSON外に何か文章を入れてしまうということは、逆に言えばJSONに文章を含めることが出来れば良いということです。
回答するJSONのフィールドに自由に文章を書けるフィールドを付け足します。

system
userの入力した数式を計算して結果を回答をしてください。
回答は下記のJSONフォーマットで回答してください。
{
  "additional_information": "理由や注意事項、補足、特記事項など言いたいことを書いてください",
  "formula": "input",
  "result": number or null
}
user
100 / 0
assistant
{
answer
  "additional_information": "0で割ることはできません。"
  "formula": "100 / 0",
  "result": null
}

この方法の場合、100回実行して100回成功するようになります。かれこれ500回ほど回して失敗がでませんでした。

assistantで {"additional_information": を入れる

今回のプロンプトでは先の手法でほぼ成功するようになりましたが、ケースによってはまだ失敗することもあると思います。
そういったケースではassistantに入れる値を変えることで成功率が上がることがあります。

system
userの入力した数式を計算して結果を回答をしてください。
回答は下記のJSONフォーマットで回答してください。
{
  "additional_information": "理由や注意事項、補足、特記事項など言いたいことを書いてください",
  "formula": "input",
  "result": number or null
}
user
100 / 0
assistant
{"additional_information":
answer
  "0で割ることはできません。"
  "formula": "100 / 0",
  "result": null
}

{"additional_information":まで入れることで必ずこのキーに対する値を回答させることで、"additional_information"を省略するケースを許さないようにすることができます。

確認したところでは{"additional_information"まで入れるケース、{"additional_information":まで入れるケース{"additional_information":"まで入れるケースで成功率が変わってきます。(誤差かもしれませんが)
が、先の手法で100%の成功率が出るとは思っていなかったので例を用意できませんでした。

おまけ

個人的に最近は回答のフォーマット指定をJSONスキーマにするのが気に入っています。
文章でどういった値にするかなどを書いてもいいのですが、型や説明を明示しやすいというので気に入っています。
また、JSONスキーマを使って結果が正しいか検証しやすいのも良いですね。

system
userの入力した数式を計算して結果を回答をしてください。
回答は下記のJSON Schemaに一致するJSON形式で回答してください:
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "formula": {
      "type": "string",
      "description": "入力された元の数式"
    },
    "result": {
      "type": ["null", "number"],
      "description": "入力された数式の計算結果。JSONのnumber型で表現できない結果はすべてnullになる"
    }
  },
  "required": ["formula", "result"]
}
user
184 + 739
answer
{
  "formula": "184 + 739",
  "result": 923
}

この方法では100回実行して95回成功します。
ただし、JSONスキーマを厳密に解釈するわけではないのでスキーマによっては、スキーマの検査が通らないというのは普通にあります。特にプロパティが多かったり、複雑なときは要注意です。
また、JSON形式で指定するのと比べ意図を入れやすいですが、その分token数も増加するので使用可能なtoken数が減ることにも注意してください。


余談ですが、タスクに失敗するとJSONは壊れやすいという性質を考えると、あえて壊すことでそのタスクは成功しないとみなす使い方もできそうな気がします。具体的なケースは思いつきませんが頭の片隅に入れておくと何かで使えるかもしれませんね。

Discussion