OpenAIのPrompt engineeringガイドまとめ
業務にAIを実装するにあたってAIの出力が安定しないので、改めてOpenAIのガイドを読んでまとめてみる
出典 "Prompt engineering - OpenAI API"
よい結果を得るための 6 つの戦略
- 明確な指示を与える
- 参考文献を与え
- 複雑なタスクはシンプルなタスクに分ける
- 思考の過程を求める
- 外部ツールを利用する
- システマティックに変更をテストする
明確な指示を与える
LLM は人間の心が読めないので、モデルが推測する余地が少ないように記述すべき。ながければ短くし、アウトプットがシンプルすぎるのであれば専門的にし、フォーマットが好ましくないのであればフォーマットを与える。 忙しいときはついふわっとした指示を出しがち。普段から具体的な指示を出す訓練だと思って LLM を使うべし。 以下にいくつかピックアップして具体例を深堀り。
ペルソナを採用する
例えば具体的に誰向けのアウトプットなのかを指定する(内容は OpenAI のサンプルを翻訳)。ここでは「重要な注文を予定通りに配達できるようにしてくれた、短期間での納品を可能にしてくれた鋼鉄ボルトの業者に」。
System:何かを書くのを手伝ってほしいときには、各段落に少なくとも1つのジョークや遊び心のあるコメントを含む文書で返信してください。User:重要な注文を予定通りに配達できるようにしてくれた、短期間での納品を可能にしてくれた鋼鉄ボルトの業者に感謝の手紙を書いてください。
区切り文字を入れる
記号で記載する場合の具体例。シンプルなタスクではあまり効果を発揮しないが、複雑なタスクになればなるほど重要になる。
三重引用符で囲まれたテキストを俳句で要約してください。"""insert text here"""
例を与える
Assistant に例を与えることで、回答の形式をコピーしてくれる。"few-shot"プロンプトという。以下の例では Assistant の回答フォーマットをコピーして別の質問に答えている。
System:一貫したスタイルで答えてください。User:忍耐について教えてください。Assistant:最も深い谷を刻む川は控えめな泉から流れ出し、最も壮大な交響曲は一つの音から始まり、最も複雑なタペストリーは一本の糸から始まります。User:海について教えてください。Assistant:無尽蔵なる力を持つ大海は、微細な塩粒から生まれ、最も奥深い深海には、一粒の砂から広がる生命の宝庫があると言えます。
参考文献を与える
特に難しいトピックや URL についての場合、LLM は自信満々にウソを付く。メモが学生のテストに役立つように、参考文献を与えるとウソを減らして回答してくれるようになる。参考文献を区切り文字で明確にするのと、わからない場合の答え方を指定するとよい。また、参考文献は Embeddings などを使ってより効果的な参考文献の渡し方もできる。
System:三重引用符で囲まれた提供された記事を使用して質問に答えてください。記事内で答えが見つからない場合は、「I could not find an answer.」と書いてください。User:"""<ここに提供すべき文章>"""質問: <ここに質問>
複雑なタスクはシンプルなサブタスクに分ける
ソフトウェア開発同じように、LLM への指示もシンプルなものに分割する。そもそも難しいタスクは初期のシンプルなタスクを入力としたワークフローとなっている。
意図分類を使用して指示を分岐する
異なるケースを処理したい場合、多くの独立した指示セットを準備する必要がある。そのためまずはクエリのタイプを分類しておき、意図を分類して必要な指示を決定する。上手く分解できれば必要な指示のみを含むタスクになるので、単一のクエリで実行する場合よりもエラー率が低くなる。また、会話の状態が変わるときは JSON などの特殊な文字列を出力するように指示してあるが、システムによる状態遷移を実現するためのもの。
具体例は以下。
System:カスタマーサービスの問い合わせが提供されます。各問い合わせを主カテゴリーと副カテゴリーに分類してください。出力は、キーとして "primary" と "secondary" を含むJSON形式で提供してください。主カテゴリー:Billing(請求)Technical Support(技術サポート)Account Management(アカウント管理)General Inquiry(一般的な問い合わせ)Billingの副カテゴリー:Unsubscribe or upgrade(解約またはアップグレード)Add a payment method(支払い方法の追加)Explanation for charge(請求の説明)Dispute a charge(請求の異議申し立て)Technical Supportの副カテゴリー:Troubleshooting(トラブルシューティング)Device compatibility(デバイス互換性)Software updates(ソフトウェアの更新)Account Managementの副カテゴリー:Password reset(パスワードリセット)Update personal information(個人情報の更新)Close account(アカウントの閉鎖)Account security(アカウントのセキュリティ)General Inquiryの副カテゴリー:Product information(製品情報)Pricing(価格)Feedback(フィードバック)Speak to a human(人間と話す)User:インターネットを再び使えるようにしたい。
分類を実行したら、以下のように個別のタスクを実行させる。
SYSTEM技術サポートの文脈でトラブルシューティングを必要とするカスタマーサービスの問い合わせが提供されます。ユーザーを次の手順で支援してください:すべてのケーブルがルーターに正しく接続されているか確認するように依頼します。時間が経つとケーブルが緩むことが一般的です。すべてのケーブルが接続されていて問題が解決しない場合、使用しているルーターモデルを尋ねます。次に、デバイスの再起動方法をアドバイスします:-- モデル番号がMTD-327Jの場合、赤いボタンを押し続けて5秒間保持し、接続をテストする前に5分間待つように指示します。-- モデル番号がMTD-327Sの場合、プラグを抜いて再度差し込み、接続をテストする前に5分間待つように指示します。デバイスを再起動して5分待っても問題が解決しない場合、{"IT support requested"}を出力してITサポートに接続します。ユーザーがこのトピックに関連しない質問を始めた場合、現在のトラブルシューティングに関するチャットを終了したいか確認し、次のスキームに従ってリクエストを分類します:<上記の主/副分類スキームを挿入>USERインターネットを再び使えるようにしたい。
長い会話が必要な場合、直前の会話をフィルタリングさせるか要約させる
フィルタリングや要約を実行することでコンテキスト長を節約する。 入力のサイズが指定のサイズに達した場合や、前の会話を非同期で要約したりして使う。
長文を部分ごとに要約し、再帰的に実行し全体の要約を生成する
コンテキスト長が限られているため、全体を一気に要約することは不可能。なので例えば各セクションを要約し、その要約を連結して要約を生成することで全体の要約を作成する手法。
思考の過程を求める
LLM は複雑な問題の答えを短時間で出すのは難しいが、解く前に思考の過程を考えさせることによってより正しい回答を行ってくれる。 たとえば学生の解答を確認する場合、以下のように解き方や原理原則を最初に考えさせる方法。
System:まず、自分で問題の解決策を考え出してください。その後、自分の解決策と学生の解答を比較し、学生の解答が正しいかどうかを評価してください。自分で問題を解くまで、学生の解答が正しいかどうかを決めないでください。
内的独白で文章の一部をユーザーに返す
思考の過程を求めてしまうとモデルがユーザーに答えを返してしまう。例えば学生の答えの添削などのように、答えそのものをユーザーに返さずヒントだけ返したい場合などに「内的独白」の形式でモデルが推測した内容を分けておく。 以下の例ではモデルが考えた内容を"""で囲んでいる。この出力からユーザーに返す際にレスポンスから除いたりするとよい。
System:ユーザーの問い合わせに答えるために、次のステップに従ってください。ステップ1 - まず、自分で問題の解決策を考え出してください。学生の解答に頼らないでください。学生の解答が間違っているかもしれないからです。このステップの作業はすべて三重引用符(""")で囲んでください。ステップ2 - 自分の解答と学生の解答を比較し、学生の解答が正しいかどうかを評価してください。このステップの作業はすべて三重引用符(""")で囲んでください。ステップ3 - 学生が間違えた場合、答えを明かさずに学生に与えることができるヒントを決定してください。このステップの作業はすべて三重引用符(""")で囲んでください。ステップ4 - 学生が間違えた場合、前のステップで決定したヒントを学生に提供してください(三重引用符の外で)。「ステップ4 - ...」と書く代わりに、「Hint:」と書いてください。User:問題文: さくらさんはりんごを17個持っています.あおいさんに9個あげると,さくらさんには何個残りますか.学生の解答: 7個Assistant:ステップ1 - まず、自分で問題の解決策を考え出してください。学生の解答に頼らないでください。学生の解答が間違っているかもしれないからです。このステップの作業はすべて三重引用符(""")で囲んでください。"""さくらさんがりんごを17個持っていて、あおいさんに9個あげると、さくらさんが持っているりんごの数は17 - 9 = 8個になります。"""ステップ2 - 自分の解答と学生の解答を比較し、学生の解答が正しいかどうかを評価してください。このステップの作業はすべて三重引用符(""")で囲んでください。"""学生の解答は7個で、これは私の解答と一致していません。したがって、学生の解答は間違っています。"""ステップ3 - 学生が間違えた場合、答えを明かさずに学生に与えることができるヒントを決定してください。このステップの作業はすべて三重引用符(""")で囲んでください。"""学生は17から9を引く計算を行う必要があります。計算が正しいか再度確認してみてください。"""ステップ4 - 学生が間違えた場合、前のステップで決定したヒントを学生に提供してください(三重引用符の外で)。「ステップ4 - ...」と書く代わりに、「Hint:」と書いてください。Hint: 17から9を引く計算をもう一度確認してみてください。
そもそも、あらかじめ解答を考えさせておいて最後の部分だけを返すように設計してより安全な設計にする事もできる。
見逃したものがないかをモデルに確認させる
例えば大きなドキュメントをまとめる場合などに、すべてを織り込んだ出力にならないことがある。このような場合にフォローアップのクエリとして見逃し内容を探すように指示することでより良いパフォーマンスを得られることがある。
外部ツールを利用する
LLM には弱点があるので、弱点を補うために外部ツールの出力をモデルに入力する。例えば RAG や Code Interpreter がある。 これは後日何かを実装するタイミングでインプットしたい。
システマティックに変更をテストする
もしパフォーマンスを計測できれば改善が容易になる。特に一部の例を改善した結果が全体に悪影響を及ぼす可能性があるので、包括的なテストを実行できる仕組みを設けるとよい。
OpenAI Evalsというのがあるらしい。
おわりに
まとめていてすぐに使えて特に面白いと思ったところは 3 の「複雑なタスクはシンプルなタスクに分ける」のところ。これを読んで、まだまだ LLM を使いこなせてないなと思った。
また、こうやって見てみると対人に対しての指示出しでもほぼ同じことが言えそう。人間が設計した AI はやはり人間に近いという事かもしれない。AI が作った AI とかがでてくるとどうなるかは楽しみ。