見出し画像

GPT APIを組み込んだ業務システムを開発するときの虎の巻

はじめに

幸運なことに、今年は多くのGPTを組み込んだシステムのPoCなどを担当させていただいた。
自身の備忘録も兼ねて、それらで得られたコツのようなものをまとめてみる。
ここで解説するテクニックは、OpenAI公式で解説されている基本的な情報は含んでいないため、未読の方はOpenAI公式のドキュメントも読了することを強くおすすめする。

プロンプトはできる限り単純に、単一タスクの指示にする

まず、一番大事なことはこれである。
プロンプトエンジニアリングと検索してよくヒットする様々なテクニックは、「ChatGPTを用いて複雑なタスクを一つのプロンプトで実行する」ためのものである。 そのため、それらのテクニックに沿ってプロンプトを作成すると、大抵の場合様々な処理を一つにまとめた長文になることが多い。

しかしこれはGPT APIをシステムに組み込む場合にはアンチパターンとなる。
なぜならプロンプトが複雑になればなるほど、GPTの出力は不安定になり、システムに組み込むことが難しくなるからである。

そのため、GPT APIをシステムに組み込む場合は、タスクを細分化した複数のプロンプトを作成し、それらをプログラムで組み合わせて使用する方が良い。
例えば、入力内容に応じてWeb検索を行い、結果をレポートにまとめるタスクをChatGPTでさせる場合、プロンプトは以下のようになる。

あなたは優秀なリサーチャーです。
私の指示に従ってネット検索を行い、結果をレポートにまとめて出力してください。

==フォーマット==
# {レポートタイトル}
## 概要
{200文字くらいの概要}

## 詳細
{1000文字以下}

## 参考資料
{[タイトル](<https://example.com>)}

これをGPT APIを用いてシステムに組み込む場合は、以下のように複数のプロンプトとスクレイピング処理をプログラムで組み合わせて使う事が望ましい。

  1. 【プロンプト】調査依頼が入力されるので、Google検索を行うための検索文を考えてください

  2. 【スクレイピング】Google検索 & ページの内容を取得

  3. 【プロンプト】ページの内容を調査依頼の内容を踏まえて要約してください

  4. 【プロンプト】ページの内容をレポートにまとめてください

こうすることにより、一つ一つのタスクを高精度かつ安定的に実行できるようになり、結果として全体の高精度化・安定化が実現できる。

GPT3.5-turboとGPT4を使い分けて、処理の高速化・コスト削減を行う

業務システムの場合、そのほとんどはGPT4並の精度が必要な事が多いが、前述したようにタスクを複数のプロンプトに分割した場合、その一つ一つは必ずしもGPT4を必要とするわけでは無い場合がある。

例えば前述した検索・レポート作成のシステムの場合、要約・レポート作成のプロンプトはGPT4が必要だが、検索文の作成だけはGPT3.5-turboでも可能そうである。

このような場合は、検索文の作成だけはGPT3.5-turboを用いて処理の高速化・コスト削減を行うと良い。

また、GPT4でないと実行できないタスクを高速化したい場合も、単一タスクであれば、GPT3.5-turboをファインチューニングする事で達成可能な場合がある。
これらのことを実現するためにも、プロンプトを単一タスクに分割する事が重要になる。

タスクを整理し、GPTにはGPTにしか出来ないところだけを任せる

GPTは汎用性の高い技術ではあるが、コストはかかるし、出力が安定しないことも多い。
そのため、GPTにさせることは可能な限り限定し、できる限りプログラムや他のAIで解決させるようにした方が良い。
例えば前述した検索・レポート作成システムにおいては、以下のようにする。

  • 検索を複数回実行する場合、「検索文を五つ考えろ」とプロンプトで命令するのではなく、一つ作成するタスクを五回プログラムで実行させる

  • Webページから取得した内容をそのまま要約させるのではなく、既存のスクレイピング技術を使ってできる限り必要な部分だけを抽出してから要約させる

GPTにしかできない事とは、例えば次のような事である。

  • 論理的に考えて結論を出す

  • 文章を理解する

  • 文章を作成する

  • ユーザーと会話する

これら以外の、計算する、繰り返す、置換処理、その他自然言語系AIで代替可能な処理はGPT以外の機能で解決するようにした方が良い。

function_callingやJSONモードを使用し、GPTの動作を安定化させる

プロンプトを複数に分けてシステムを構築する場合、それぞれの出力は安定している事が求められる。
そのために使える機能が、function_callingや’gpt-4-1106-preview’から実装されたJSONモードである。
例えば、業務システムに組み込む上でのアンチパターンとして、以下のようなプロンプトがある。

入力された文章から購入者の名前と商品名を抽出してください。
購入者の名前と商品名が抽出できた場合は、以下のフォーマットで出力してください。
```
購入者 : {氏名} 
商品名 : {購入したい商品名}
```


抽出できなかった場合は、以下の文章を出力してください。
```
申し訳ありません。購入者名と商品名を抽出できませんでした。
```

このようなプロンプトを組んだ場合、出力をパースして処理を分岐・画面出力させる事になるが、出力が意図した通りになるとは限らず、予期せぬエラーが発生しやすい。

また、抽出できなかった場合、定型分を出力するだけなのに、トークンを消費してしまうことになり、速度・コスト的に効率が悪い。

この場合、ベストな方法はfunction_callingを使うことである。
function_callingを使うと、プロンプトは以下のようになる。

入力された文章から購入者の名前と商品名を抽出してください。
購入者の名前と商品名が抽出できた場合は、register関数を呼び出してください。
抽出できなかった場合は、not_found関数を呼び出してください

そしてfunctionsを以下のように定義する。

functions = [
      {
          "name": "register",
          "description": "Register the name of the buyer and the name of the product",
          "parameters": {"type": "object", "properties": {"buyer_name": {"type": "string"}, "product_name": {"type": "string"}}},
          "required": ["buyer_name", "product_name"],
      },
      {
          "name": "not_found",
          "description": "If the buyer's name or product name is not found, this function is called",
          "parameters": {"type": "object", "properties": {}},
          "required": [],
      },
  ]

こうすることによって出力は安定し、not_found関数が呼び出された場合は、定型分を出力するようにプログラムすることで、トークンを消費せずに定型分を出力することができる。(定型分がどんなに長くても安定・高速で出力できる)

バッチ処理によって、リアルタイムに行わないといけない処理を簡単にする

前述したプロンプトを複数に分けて単純化する方法は、精度と安定性を向上させるが、処理速度やコストが犠牲になってしまう場合がある。
そんな時は、複数に分けたプロンプトの中に、バッチ処理として事前に処理しておける部分がないか考える。
例えば私が作成したGPTsである20 Questions Botは、20回のYes/Noで答えられる質問で隠されたお題を当てるゲームができるBotだが、ここで使われているお題は、事前にGPTによって200個ほど作成しておいたものである。
詳しくはこちらで作り方含めて解説しているので参考にしてほしい。
このようにGPTを使う処理もいくつか事前に済ませておくことで、処理精度/速度の向上・コスト削減ができる場合がある。

チャットを活用する

GPTを使うからにはぜひチャットを活用したシステムを作ってみてほしい。
つまり、一回の入力ではなく、複数回会話をしながら目的を達成するようなシステムである。
普通のシステムでは、一つのフォームに必要な全ての情報を入力してから処理を開始するが、チャットの場合は以下のようにして、ユーザーにストレスを与える事なく目的を達成する事ができる。

  • 必要に応じてBotが聞き返す

  • Botが足りない部分を予測し、ユーザーが選択・訂正する

例えば、私が作成したビジネスで使える画像生成GPTsは以下のようにしてユーザーが気軽に画像を生成できるようにしている。

  1. ユーザーが作成したい画像の内容を簡潔に入力(例 : 釣りをしている人)

  2. GPTsが以下のように生成する内容を詳細な文章にして三つ提案

    1. ボートの上で釣りをしている男性

    2. 橋の上から釣りをしている女性

    3. 父親と一緒に釣りをしている子供

  3. ユーザーが訂正・選択する

  4. GPTsが画像を生成

こちらも別の記事で作り方を解説しているので参考にしてほしい。
このようにチャットを活用することで、今までになかったユーザー体験を設計する事ができる。

ファインチューニングを活用する

OpenAI公式では、「ファインチューニングの前にプロンプト調整などを試すべき」と書かれていることもあり、あまり実践でファインチューニングを使う例は見ない。

しかし、私はファインチューニングは実践でこそガンガン使うべきだと思っている。
最後の手段と捉えるのではなく、プロンプトエンジニアリングと同等に扱うべきである。

ファインチューニングを活用することによって、以下のようなモデルを作成できる可能性がある。

  • 応答速度はgpt-3.5-turbo並

  • 精度は特定のタスクに対してのみgpt-4並(場合によってはそれ以上)

  • 価格はgpt-4-turboの1/3~1/5以下

    • Fewshotなどが要らなくなることで桁違いのコスト削減が可能な場合も

極端な例ではあるが、私のプロジェクトではgpt4を使ったシステムがファインチューニングによって以下のように改善できている。

  • 精度は同じ(特定条件においてはそれ以上)

  • 応答速度5倍

  • コスト500分の1

詳しくは以下の記事で解説しています。

まとめ

GPT APIを用いてシステムを開発する方法としてまとめたが、基本的にはこGeminiシリーズ等にも応用が効くテクニックであろうと思う。

今後時間が出来次第、RAG構成やマルチモーダルLLMの応用方法に関してもまとめていきたい。

おまけ

色々GPTs作っているので、よかったら使ってみてください


この記事が気に入ったらサポートをしてみませんか?