
もっとguidanceらしいプロンプトを書いてみる
次のLangChainもくもく会では、LangChainと言いつつguidanceを中心に扱ってみようということにしているので、できるだけguidanceを使った実装を試みている今日この頃です。
ちなみに次のLangChainもくもく会は6/7(水)20:00からを予定していますので、ご予定合う方はぜひご参加ください。
さて、guidanceらしいプロンプトって何?って感じなのですが、以前こんなことをツイートしていました。
チャットモデルはLangChainのConversationみたいにユーザーメッセージに何でもかんでも突っ込むような使い方だと全然上手く動かないんだけど、自然な対話の流れで推論してもらう分にはパフォーマンスが高くなる体感がある。guidanceが優れているのは、この流れを簡潔に作り出せるからだと思う。
— mah_lab / Masahiro Nishimi (@mah_lab) May 24, 2023
LangChainの例で言うと、例えばConversationChainを使う場合、内部では以下のようなプロンプトが使用されています。
The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.
Current conversation:
{history}
Human: {input}
AI:
ConversationBufferMemoryなどといった会話履歴を保持する仕組みから「history」の中身が代入され、ユーザーの入力内容が「input」に入るようなイメージです。代入後のイメージは以下のようなプロンプトになります。
The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.
Current conversation:
Human: hoge
AI: fuga
Human: foo!
AI: bar!
Human: fuga?
AI:
ユーザープロンプトにこれまでの会話履歴も何もかもを設定する感じです。このプロンプトはText Completionなら上手く動作しますが、体感ではChat Modelだと不自然な動きになることが多いように思えます。
一方でguidanceの場合は一つのプロンプトでユーザーとアシスタントの対話としてプロンプトを表現できるので、出力も自然な感じになります。
{{#system~}}
You are a helpful assistant.
{{~/system}}
{{#each conversations}}
{{#user~}}{{this.user}}{{/user}}
{{#assistant~}}{{this.assistant}}{{/assistant}}
{{/each}}
{{#user~}}{{user_input}}{{/user}}
{{#assistant~}}
{{gen 'reply' max_tokens=1000}}
{{~/assistant}}
チャットモデルらしい対話の流れでもう少しプロンプトを組んでみるとします。例えば、一回何らかの検討を踏まえた上で出力して欲しい場合はどうでしょうか。ユーザープロンプトに手順を全て突っ込もうとすると、以下のようなプロンプトになりそうです。
Run #Command
#冷蔵庫の中身: """
- xxxxx
- xxxxx
- xxxxx
"""
#Command: """
- タンパク質が多く含まれ、日本国内で比較的簡単に購入できる食材を元に10個献立を提案してください。
- #冷蔵庫の中身を踏まえ、上記の献立から最も本日の献立にふさわしいものを選択してください。
- 本日の献立に具体的なレシピを示して下さい。
"""
これでも上手くいく感じはしますが、guidanceであれば以下のように会話の流れとして書き下せます。
{{#system~}}
You are a helpful assistant.
{{~/system}}
{{#user~}}
本日の夜ご飯について検討しています。
タンパク質が多く含まれ、日本国内で比較的簡単に購入できる食材を元に10個献立を提案してください。
なお、冷蔵庫には以下の食材が入っていることを考慮に入れて下さい。
{{#each foods}}
- {{this.name}} {{this.amount}}{{this.unit}}
{{/each}}
{{/user}}
{{#assistant~}}
{{gen 'planning' max_tokens=1000 temperature=1.0}}
{{~/assistant}}
{{#user~}}
冷蔵庫の中身を踏まえ、上記の献立から最も本日の献立にふさわしいものを選択し、
レシピを提案して下さい。
{{/user}}
{{#assistant~}}
{{gen 'recipe' max_tokens=1000 temperature=0.7}}
{{~/assistant}}
いったん献立を10個ほど実際に提案してもらってから、その中から選んでもらうという会話の流れを表現できています。上記のプロンプトの例では「recipe」で出力された部分のみ取り出してあげれば、自動的に最適な献立を考えてくれるスクリプトのできあがり、ということになります。
ChatGPTを使っていても、一度に大量のプロンプト(呪文)を処理させようとするよりも、会話の流れで少しずつ調整していった方が、結果として上手くいくことが多いのではないでしょうか。恐らくチャットモデル自体がそのような使い方をするようチューニングされているために、対話形式のやりとりで深めていくようなプロンプト設計にする方が上手くいきやすいのだと思われます。
何回か出力を挟む分、パフォーマンスが落ちてしまうのは難点ですが、guidanceにはキャッシュ機構もついているので、適切にキャッシュできればそれなりのパフォーマンスでレスポンスすることはできるのかなーと思っています。実際、キャッシュが効くように工夫したプロンプトでは、そこそこのパフォーマンスでレスポンスを返せているようです。
いやー、guidanceは面白いですね。
現場からは以上です。