LLMの出力解析:関数呼び出し対言語チェーン
LLMの出力解析
Open AI APIとLangChain関数呼び出しを使用してLLMsからの出力を一貫して解析する方法:方法の利点と欠点を評価する
LLMsと一緒にツールを作成するには、ベクトルデータベース、チェーン、エージェント、ドキュメントスプリッターなど、多くのコンポーネントが必要です。
ただし、最も重要なコンポーネントの1つはLLMの出力解析です。LLMから構造化された応答を受け取れない場合、生成物を扱うことが困難になります。特に、LLMへの単一の呼び出しで複数の情報を出力する場合には、これがさらに明白になります。
仮想のシナリオで問題を説明しましょう:
LLMに単一の呼び出しで特定のレシピの材料と手順を出力してもらいたいとします。ただし、これらのアイテムを別々に使用するために、システムの2つの異なる部分で使用したい場合です。
- 「生成タスクを分類タスクに変換する」
- 「ユナイテッド航空がコスト効率の高い光学文字認識アクティブラーニングパイプラインを構築した方法」
- 『NVIDIAのCEO、ジェンソン・ファング氏がテルアビブで開催されるAIサミットの主演を務めます』
import openairecipe = 'Fish and chips'query = f"""{recipe}のレシピは何ですか?材料リストと手順を別々に返します。"""response = openai.ChatCompletion.create( model="gpt-3.5-turbo-0613", messages=[{"role": "user", "content": query}])response_message = response["choices"][0]["message"]print(response_message['content'])
これにより、次の結果が返されます:
フィッシュアンドチップスの材料:- 1ポンドの白身魚の切り身(コダラやハドックなど)- 1カップの中力粉- 小さじ1のベーキングパウダー- 小さじ1の塩- 小さじ1/2の黒コショウ- 冷たいビール1カップ- 揚げ油- 大きなラセットポテト4個- 塩(お好みで)フィッシュアンドチップスの作り方:1. オーブンを200°C(400°F)に予熱する。2. ポテトをむき、厚くて均一な細切りにする。余分なでんぷんを除去するために、冷たい水でポテトを洗い、清潔なキッチンタオルで水気を取る。3. 大きな鍋または深いフライヤーで、揚げ油を175°C(350°F)に熱する。ポテトと魚を完全に浸すのに十分な量の油があることを確認する。4. ボウルに中力粉、ベーキングパウダー、塩、黒コショウを混ぜ合わせる。冷たいビールを少しずつ加えながら、なめらかな生地ができるまでよく混ぜる。生地を横に置いておく。5. 乾燥させたポテトのストリップを5〜6分、または金茶色になるまで揚げる。ヘラでフライを取り出し、余分な油を吸い取るために紙タオル敷きの皿に置く。予熱したオーブンで暖かく保つ。6. 準備した生地に魚の切り身を浸して、十分にコーティングする。余分な生地を垂らす前に、熱い油に慎重にフィレを置く。7. 魚の切り身を両面で4〜5分、または金茶色になり、カリッとするまで揚げる。ヘラで油をきり、紙タオル敷きの皿に置いて余分な油を吸い取る。8. 熱いうちにフィッシュアンドチップスに塩をかける。9. タルタルソース、マルトビネガー、またはケチャップを添えて、熱々のフィッシュアンドチップスをお楽しみください!
これは非常に長い文字列であり、パースするのは困難です。なぜなら、LLMはわずかに異なる構造を返すことがあり、書いたコードを壊すからです。プロンプトで常に「材料:」と「手順:」を返すように要求すれば解決できると主張できるかもしれませんが、それは間違っていません。これは機能するかもしれませんが、文字列を手動で処理する必要があり、いつでも変化や幻覚を受け入れる必要があります。
解決策
この問題を解決する方法はいくつかあります。上記で言及された方法の1つはありますが、より良い方法としていくつかのテスト済みの方法があります。この記事では、2つのオプションを紹介します:
- Open AI関数呼び出し;
- LangChain出力パーサー。
Open AI関数呼び出し
これは私が試している方法で、最も一貫した結果が得られます。Open AI APIの関数呼び出し機能を使用して、モデルが構造化されたJSONとして応答を返すようにします。
この機能は、LLMが外部の関数を呼び出す能力を提供することを目的としています。入力をJSONとして提供します。モデルは、与えられた関数を使用する必要がある場合に適切に調整されています。例えば、現在の天気のための関数があります。GPTに現在の天気を尋ねると、答えることはできませんが、それを行う関数を提供し、GPTに渡すことで、入力に基づいてアクセスできることを認識させることができます。
この機能について詳しく知りたい場合は、Open AIの発表をご覧ください。また、こちらの素晴らしい記事も参考になります。
では、手元の問題を考えながら、コードを見てみましょう。
functions = [ { "name": "return_recipe", "description": "要求されたレシピを返す", "parameters": { "type": "object", "properties": { "ingredients": { "type": "string", "description": "材料のリスト" }, "steps": { "type": "string", "description": "レシピの手順" }, }, }, "required": ["ingredients","steps"], }]
まず、LLMが使用できる関数を宣言する必要があります。関数の名前と説明を与える必要があります。これにより、モデルが関数を使用するタイミングを理解できます。ここでは、この関数が要求されたレシピを返すために使用されることを伝えています。
次に、パラメータに入ります。まず、オブジェクトのタイプであることを宣言し、使用できるプロパティはingredientsとstepsです。これらのプロパティには、出力のための説明とタイプも含まれています。最後に、関数を呼び出すために必要なプロパティを指定します(これは、LLMがそれらを使用するかどうかを判断するためにオプションのフィールドを持つことができることを意味します)。
それでは、LLMへの呼び出しでそれを使用しましょう。
import openairecipe = '魚とチップス'query = f"{recipe}のレシピは何ですか?材料リストと手順を別々に返してください。"response = openai.ChatCompletion.create( model="gpt-3.5-turbo-0613", messages=[{"role": "user", "content": query}], functions=functions, function_call={'name':'return_recipe'})response_message = response["choices"][0]["message"]print(response_message)print(response_message['function_call']['arguments'])
ここでは、変数入力(recipe)を使用してAPIへのクエリを作成します。次に、”gpt-3.5-turbo-0613″を使用してAPI呼び出しを宣言し、messages引数にクエリを渡し、そしてfunctionsを渡します。
関数に関しては、2つの引数があります。最初の引数では、モデルがアクセスできる関数を上記のフォーマットでリストとして渡します。そして、2番目の引数である”function_call”では、モデルがこれらの関数をどのように使用するかを指定します。3つのオプションがあります:
- 「Auto」 -> モデルがユーザーの応答または関数の呼び出しを自動的に判断します。
- 「none」 -> モデルが関数を呼び出さず、ユーザーの応答を返します。
- {「name」:「my_function_name」} -> 関数名を指定することで、モデルに使用させることができます。
公式のドキュメントはこちらからご覧いただけます。
私たちの場合、出力の解析に使用するために後者を使用しました:
function_call={'name':'return_recipe'}
それでは、レスポンスを見てみましょう。このフィルター[「choices」][0][「message」]の後に得られるレスポンスは以下の通りです:
{ "role": "assistant", "content": null, "function_call": { "name": "return_recipe", "arguments": "{\n \"ingredients\": \"魚には以下が必要です:\\n- 1ポンドの白身の魚フィレ\\n- 全粒粉1カップ\\n- ベーキングパウダー1茶匙\\n- 塩1茶匙\\n- 黒胡椒1/2茶匙\\n- 冷たい水1カップ\\n- 揚げ物用の植物油\\nチップスには以下が必要です:\\n- 大きなジャガイモ4個\\n- 揚げ物用の植物油\\n- 塩(好みで)\",\n \"steps\": \"1. 魚の準備から始めましょう。浅い容器に、小麦粉、ベーキングパウダー、塩、黒胡椒を混ぜます。\\n2. 冷たい水を少しずつ加え、生地が滑らかになるまで徐々に混ぜます。\\n3. 大きなフライパンまたはフライヤーで植物油を熱します。\\n4. 魚のフィレを生地に浸し、均等にコーティングします。\\n5. コーティングされたフィレを熱い油に優しく入れ、両面で4〜5分、または金色になるまで揚げます。\\n6. 揚げた魚を油から取り出し、余分な油を切りたい紙タオルが敷かれたお皿に置きます。\\n7. チップスのために、ジャガイモの皮を剥き、厚いチップスに切ります。\\n8. フライヤーまたは大きなフライパンで植物油を熱します。\\n9. チップスを揚げ、金色になるまでバッチごとに揚げます。\\n10. チップスを油から取り出し、余分な油を切りたい紙タオルが敷かれたお皿に置きます。\\n11. チップスに塩をかけます。\\n12. 魚とチップスを一緒に提供し、お楽しみください!」\n}" }}
さらに「function_call」にパースすると、意図した構造化された応答が見えます:
{ "ingredients": "魚の材料:\n- 1ポンドの白身魚のフィレ\n- 1カップの多目的粉\n- 1茶匙のベーキングパウダー\n- 1茶匙の塩\n- 1/2茶匙の黒胡椒\n- 1カップの冷水\n- 揚げ油\nチップスの材料:\n- 大きなポテト4個\n- 揚げ油\n- 塩、味付けに", "steps": "1. 魚の準備から始めましょう。浅い皿に、粉、ベーキングパウダー、塩、黒胡椒を混ぜ合わせます。\n2. 冷水を徐々に加え、生地が滑らかになるまで混ぜます。\n3. 大きなフライパンまたはフライヤーで揚げ油を熱します。\n4. 魚のフィレを生地にくぐらせ、均等にコーティングします。\n5. コーティングされたフィレを熱い油にそっと入れ、両面を4-5分揚げます。または、金色になりサクサクになるまで揚げます。\n6. 揚げた魚を油から取り出し、余分な油を切るために紙タオルが敷かれた皿に置きます。\n7. チップスのために、ポテトを剥き、厚いチップスに切ります。\n8. フライヤーや大きなフライパンで揚げ油を熱します。\n9. チップスをバッチごとに金色になるまで揚げます。\n10. チップスを油から取り出し、余分な油を切るために紙タオルが敷かれた皿に置きます。\n11. チップスに塩をかけます。\n12. 魚とチップスを一緒に提供し、お楽しみください!"}
関数呼び出しの結論
Open AI APIから直接関数呼び出しの機能を使用することが可能です。これにより、LLMが呼び出されるたびに同じキーを持つ辞書形式の応答を得ることができます。
使用方法は非常にシンプルで、関数オブジェクトを宣言し、タスクに焦点を当てた名前、説明、およびプロパティを指定しますが(説明で)これがモデルの応答であることを明示します。また、APIを呼び出す際にモデルに強制的に関数を使用することもでき、さらに一貫性を持たせることができます。
この方法の主な欠点は、すべてのLLMモデルとAPIでサポートされていないことです。したがって、Google PaLM APIを使用したい場合は別の方法を使用する必要があります。
LangChain出力パーサ
モデルに依存しない代替手段として、LangChainを使用する方法があります。
まず、LangChainとは何かということから始めましょう。
LangChainは、言語モデルによって動作するアプリケーションを開発するためのフレームワークです。
これがLangChainの公式の定義です。このフレームワークは最近作成され、既にLLMによって動作するツールの業界標準として使用されています。
このフレームワークには、私たちのユースケースに非常に適した「出力パーサ」という機能があります。このモジュールでは、LLMの呼び出しからさまざまな形式の返り値を返し、パースするために作成された複数のオブジェクトがあります。
以下ではコードを詳しく見ていきましょう:
from langchain.prompts import ChatPromptTemplatefrom langchain.output_parsers import ResponseSchema, StructuredOutputParserfrom langchain.llms import GooglePalm, OpenAIingredients = ResponseSchema( name="ingredients", description="料理の材料を一意の文字列として返します。", )steps = ResponseSchema( name="steps", description="料理の準備手順を一意の文字列として返します。", )output_parser = StructuredOutputParser.from_response_schemas( [ingredients, steps])response_format = output_parser.get_format_instructions()print(response_format)prompt = ChatPromptTemplate.from_template("「{recipe}のレシピは何ですか?材料リストと手順を別々に返してください。\n{format_instructions}」という形式で入力してください。")
ここで最初に行うことは、パーサの入力となるResponse Schemaを作成することです。材料と手順の2つを作成し、各々辞書のキーとなる名前と、LLMへの応答に関するガイドとなる説明を含めます。
次に、その応答スキーマからStructuredOutputParserを作成します。これには異なるスタイルのパーサが複数ありますので、詳細についてはこちらをご覧ください。
最後に、フォーマットの指示を取得し、レシピ名とフォーマットの指示を入力とするプロンプトを定義します。フォーマットの指示は以下の通りです:
"""出力は以下のスキーマに従ったマークダウン形式のコードスニペットである必要があります。先頭と末尾に "```json" および "```" を含めます:```json{ "ingredients": string // レシピからの材料、一意の文字列として。 "steps": string // レシピの準備手順、一意の文字列として。} """
残りはAPIを呼び出すだけです。ここでは、Open AI APIとGoogle PaLM APIの両方をデモンストレーションします。
llm_openai = OpenAI()llm_palm = GooglePalm()recipe = '魚とチップス'formated_prompt = prompt.format(**{"recipe":recipe, "format_instructions":output_parser.get_format_instructions()})response_palm = llm_palm(formated_prompt)response_openai = llm_openai(formated_prompt)print("PaLM:")print(response_palm)print(output_parser.parse(response_palm))print("Open AI:")print(response_openai)print(output_parser.parse(response_openai))
見ての通り、モデル間の切り替えは非常に簡単です。LangChainでサポートされている任意のモデルに対して、前に定義された構造全体を同じ方法で使用することができます。また、両方のモデルに同じパーサーも使用しました。
以下の出力が生成されました:
# PaLM:{'ingredients': '''- 1カップの小麦粉\n- 1茶匙のベーキングパウダー\n- 1/2茶匙の塩\n- 1/2カップの冷水\n- 1卵\n- 1ポンドの白身魚フィレ(コダラまたはハドックなど)\n- 揚げ物用の植物油\n- 1カップのタルタルソース\n- 1/2カップのモルト酢\n- レモンくし'''、'steps': '''1. 大きなボウルに小麦粉、ベーキングパウダー、塩を混ぜ合わせます。\n2. 別のボウルに卵と水を混ぜ合わせます。\n3. 魚フィレを卵の混合物に漬け、小麦粉の混合物でコーティングします。\n4. 深いフライヤーまたは大きなフライパンで油を375°F(190°C)に加熱します。\n5. 魚フィレを片面につき3〜5分、または金色になり、完全に調理されるまで揚げます。\n6. 魚フィレをキッチンペーパーに油を切ります。\n7. タルタルソース、モルト酢、レモンくしと一緒に魚フィレを即座に提供します。'''}# Open AI{'ingredients': '1 ½ポンドのコダフィレ、4つに切った、小麦粉2カップ、ベーキングパウダー2茶匙、塩1茶匙、新鮮に挽いた黒コショウ1茶匙、ガーリックパウダー1/2茶匙、ビール(または水)1カップ、揚げ物用植物油、タルタルソース、供するため', 'steps': '1. オーブンを400°F(200°C)に予熱し、ベーキングシートにパーチメントペーパーを敷きます。2. VoAGIボウルに小麦粉、ベーキングパウダー、塩、コショウ、およびガーリックパウダーを混ぜ合わせます。3. ビールを注ぎ、厚い生地が形成されるまでよく混ぜます。4. コダを生地に漬けて全面にコーティングします。5. 大きな鍋またはフライパンに約2インチ(5 cm)の油をVoAGI-highの熱で加熱します。6. コダを片面につき3〜4分、または金色になるまで揚げます。7. コダを用意したベーキングシートに移し、5〜7分焼きます。8. タルタルソースと一緒に温かく供します。'}
結論: LangChainの出力の解析
この方法も非常に優れており、柔軟性を持っています。レスポンススキーマ、出力パーサー、およびプロンプトテンプレートなどの構造を容易に組み合わせて、さまざまなモデルで使用できます。また、複数の出力形式に対応しているという利点もあります。
主な欠点は、フォーマット指示をプロンプト経由で渡すことから生じます。これにより、ランダムなエラーや幻覚が発生する可能性があります。具体的な例として、この特定のケースでは、レスポンススキーマの説明で「一意の文字列」として指定する必要がありました。これを指定しないと、モデルは手順と指示のリストを返し、これが出力パーサーの解析エラーを引き起こしました。
結論
LLMを使用したアプリケーションで出力パーサーを使用する方法は複数あります。ただし、選択肢は問題によって変わる可能性があります。私自身の場合、次の考え方に従います:
LLMからの出力が1つしかない場合でも、常に出力パーサーを使用します。これにより、出力を制御し指定することができます。Open AIと一緒に作業している場合、関数呼び出しが選択肢です。これは最も制御が可能であり、本番アプリケーションでのランダムなエラーを回避することができます。ただし、異なるLLMを使用する場合や、異なる出力形式が必要な場合は、LangChainが選択肢ですが、出力に対してテストを行い、最もミスの少ないプロンプトを作成するために注意が必要です。
読んでくれてありがとう。
フルのコードはこちらで見つけることができます。
もしコンテンツが気に入ってサポートしたい場合は、私にコーヒーを買っていただけます:
Gabriel Cassimiroは、コミュニティに無料のコンテンツを提供するデータサイエンティストです。
私はクリエイターをサポートするのが好きです!
www.buymeacoffee.com
以下に興味があるかもしれない他の記事があります:
Langchainを使用したチェーンの非同期呼び出し
Langchainのチェーンを非同期呼び出しで動作させる方法、シーケンシャルな処理時間を短縮する方法について
towardsdatascience.com
ディープ強化学習によるUnity環境の解決
ディープ強化学習エージェントのPyTorch実装とコードを使用したエンドツーエンドプロジェクト。
towardsdatascience.com
We will continue to update VoAGI; if you have any questions or suggestions, please contact us!
Was this article helpful?
93 out of 132 found this helpful
Related articles