なんか、流行ってますね、 ChatGPTのプロンプトエンジニアリング(プロンプト芸)。ChatGPTなどのLLMを活用する技術として流行しているのがプロンプトエンジニアリングですが、日本語より英語の方が有利だったり、one-shotで終わらなかったりで結構大変だったりします。そういう訳で、翻訳したり、連続して LLM 呼び出ししたりする Python の class を作りました。
正直、ChatGPT PlusになるとGPT-4の性能が高すぎて小手先のプロンプトエンジニアリングなんか必要ないんじゃないか? と思いますが、25回50回制限/3hがあったり、Custom Instructionsがちょっと使いづらかったりと不満があることも事実です。
かといって、GPT-4 API を叩くとしても、さすがにtoken単価が高いという問題があります(円安つらい)。また、速度面でも充分に早いとは言えません。
それからLlama2なんかのローカルで動く LLM に乗り換えるかもしれないという将来的な需要があります。ということで、プロンプトエンジニアリングを加速するクラスを作りました。ちょっと長めのコードなのと自分でもメンテナンスしていくつもりなので、珍しくGitHubで公開です。
プロンプトエンジニアリングをやりやすくするWaterfallChain
https://github.com/kske/Waterfallchain
とりあえずの使い方
- Python をインストールする
- pip install langchain する
- 上のリポジトリからCloneするか、なんならraw でコピーする
- import して使う
# 翻訳に使う LLM
translate_llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0.5)
# job を処理する LLM
job_llm1 = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0.9)
# job のシステムテンプレート
job_template = """* Instructions *
- Title: Help implementing systems thinking.
- Your Client: Manager working to solve problems
- Your Role: Consultants with expertise in systems thinking.
- Objectives: Help the manager understand the problem and how to solve it with Systemthinking.
* End Instructions *
Analyze the following issues using the systems archetype and provide solutions according to the results of your analysis.
"""
# 人間からの入力(翻訳されたもの)はそのまま入力する
human_template = "{text}"
job_prompt = ChatPromptTemplate.from_messages([SystemMessagePromptTemplate.from_template(job_template), HumanMessagePromptTemplate.from_template(human_template)])
# LLM Chain にする
job_chain_instance = LLMChain(llm=job_llm, prompt=job_prompt)
# 翻訳に利用する LLM, jobのリストを使って実体化
wfc = WaterfallChain(llm=translate_llm, job_chains=[job_chain_instance1, job_chain_instance2])
# 日本語で動かしてみる
result = wfc.run("カスタマーサポートの、一番社歴が長いメンバーに仕事が集中してしまいます。どうしたらいいでしょうか?")
print(result)
WaterfallChainのやること
Langchain の LLM Chainを受け取って、それを順番に実行する。前回のLLMの出力結果を受け取って、次のLLMの出力に繋げるというシンプルな動作です。
というか、現時点でのメインの機能は、
- 入力の英訳をLLMで実行
- 翻訳された入力を、設定されたプロンプトで実行
- LLMの実行結果を和訳して返す
というものになっています。何故このような動作になっているかというと、
- 日本語だとTokenが嵩みやすく、費用が高額になる。また長いコンテキストが扱いづらく、必然的にプロンプトに制約がかかる
- LLMの構築には英語が使われていることが多く、日本語より英語の方が性能が高い
- だからといっていちいち英訳してから LLM に入力するのはなんか違う感じがするし、エンドユーザーにそれを求めたところで無駄
以上のような課題に対処するためです。幸いなことに GPT-3.5-turbo であっても翻訳の精度は十分に高く、速度も期待できますのでこれを翻訳に活用します。
本命である LLM に処理させたいタスクや質問、相談事項については、 GPT-3.5-turbo や LLaMa2 などを利用しても日本語のプロンプトを直接入力するよりは高い性能を期待できます。一方で、 GPT-4 のような最新の(日本語でも充分な性能を期待できる) LLM API を利用した場合の利点としては Token を節約して費用を抑制できます。
欠点としては、
- LLM を複数回呼び出すので相応に時間がかかる
- 直接英語の Prompt で叩くよりは費用が当然3倍以上になる
という点が挙げられます。費用面については、 GPT-4 を叩くのであれば許容出来ると思います。しかし、GPT-3.5-turboが高速であるとはいえ、シリアルで動作する関係上、Streamingも使いづらく、出力が得られるまでに非常に時間がかかるという印象を受けると思います。この点についてはユーザーの性質にもよってしまいますが、費用面には目をつぶって、 GPT-4 を直接叩いてしまった方がいいケースも多いでしょう。
ただ冒頭に述べた通り、翻訳を前後に実行するだけでなく、Chain of Thought のような考え方や Self Critic なプロンプトを利用するケースも想定し、 LLM に実際に与えるタスク、 Jobはリストで受け取り順次実行するようにしています。
意図としては、例えば「作成した文章を批評し、さらに批評に基づき修正を行う」といった再帰的な修正を行うプロンプト(Recursively Criticizes and Improves とかいうらしい)は、プロンプト内で批評→修正を繰り返させるのと、出力に対し、何度も批評と修正を指示するのでは結果が大きく異なることが経験則で理解されます(論文とかじゃないんで体感です)。この繰り返し処理を自動化するという点があります。
# 批評と修正を繰り返す waterfall な jobの設定
# critic_chainには批評を行うシステムプロンプトを、improve_chainには修正を行うシステムプロンプトを入力する
# ただし、現時点ではメモリの実装がないため、critic_chainには原文を繰り返させるといったprompt上の工夫が必要
wfc = WaterfallChain(llm=translate_llm, job_chains=[critic_chain, improve_chain, critic_chain, improve_chain])
また、再帰的な批評と修正では単一の目的・立場での入力が基本になりますが、全く別の Prompt Template を持つ LLM を順に実行させて結果を得ることもできます。例えば、ビジネス目的を入力とします。
- それを戦略コンサルタントの役割を与えた LLM に整理させ、
- 部門長の観点から部門目標を設定させ
- 現場リーダーの視点から現場の課題を設定し
- メンバーの観点から自己の目標を設定する
といった立場の違う動作を実行する目的があります(ただこれを実行する場合、メモリの実装をどうすべきか? という問題がでてくるのですが……)。
Callback で進行状況を確認する
def my_callback(source="NONE", result="NONE"):
print(source)
print(f"Last result: {result}")
wfc = WaterfallChain(llm=translate_llm, job_chains=[job_chain_instance], callback=my_callback)
callbackにコールバック関数を入れることで、各 LLM の実行状況を監視することができます。途中経過とか、今どの辺りかなとか。
作って GitHub に公開した目的
大した Class ではなく、技術的には Langchainもくもく会を聞きながら適当に作成できる程度のものです。ついでにいえば、アイディア自体も API を叩いたことがある人なら10回くらいは思いついた程度のものだと思います。もっと素晴らしい実装を持っている人も多いだろうと思います
が、敢えて公開した理由としては、「Python と Langchain インストールできれば、プロンプトエンジニアリングの幅は大きく広がるよ」と言いたいがためです。
ChatGPT の GUI はフロントエンドとして非常に優れていると思いますが、あれが LLM の全てだと考えて全リソース(下手をすると個人レベルではなく法人レベル)をつぎ込もうとしている事例や、それを推奨するような言動を見かけるにつけ、「それはちょっと気が早いから違うオモチャも触って見よう」という感じです。実際に叩くのは大体の場合、 GPT-3.5-turbo や GPT-4 になるので、 ChatGPT と知見を共有できますからね。
今後
とりあえずメモリを扱えるようにしないとプロンプトエンジニアリングの知見を生かした実装を行うのは難しいと思うので、 Langchain 標準のメモリを扱うか、独自でメモリ機能を実装するかしたいと思います。
また、翻訳用のprompt template も、入力を翻訳する方はともかく、出力の翻訳については直訳過ぎて少し不自然な気がするので、修正が必要かなと思います。また、当然他言語への対応を考えるのであれば、翻訳用の prompt template 自体を設定できるようにする必要があると思います。
カバー画像:UnsplashのAnders Ipsenが撮影した写真