ノウハウ

Microsoft GuidanceでのFunction Calling機能の使い方とその特徴

目次

はじめまして。調和技研 研究開発部の山形です。

社内の取り組みでLLM(Large Language Model:大規模言語モデル)を活用したアプリケーションを検討していたところ、Function Callingについて調べる必要性が出てきました。Function Callingについて詳しくは後述しますが、23年6月にChatGPTのAPIでも追加された機能です。この機能により、AIはユーザーからの指示を解析して、必要に応じて外部サービスやライブラリの処理を呼び出して返答に生かすなど、複雑なタスクを実行できるようになります。

私は時系列分析や組み合わせ最適化など、基本的にはテーブルデータと格闘することが多く、普段はなかなかLLM周辺の技術と関わる機会がなかったため、今回の調査はとても新鮮で楽しかったです。折角なので、ブログとして調査内容をまとめておこうと思います。

実現方法が複数考えられる中で、今回はMicrosoftがOSSとして公開したGuidanceについて調べてみました。Guidanceに着目したのは、GitHubのStarsの数がMicrosoftの内部ツールであるSemantic Kernelを超えていると耳にしたのがきっかけです(とはいえ、執筆時点だと微差ですが)。

この記事では、GuidanceでのFunction Calling機能の利用方法や、その特徴について少し掘り下げて紹介してみたいと思います。

Microsoft Guidanceの概要

Microsoft Guidanceは、LLMの活用を支援するためのフレームワークです。直感的に理解しやすいテンプレートベースのスクリプトを使用して、モデルの振る舞いをカスタマイズできます。

また、後に紹介する通りLLMとフレームワークがバックグラウンドで行っている対話を可視化することができます。LLMのアプリケーション開発では、プロンプトの試行錯誤が発生するので、このあたりのカスタマイズのしやすさや、解釈性の高さはポイントになりそうですね。

Function Callingの概要

Function Callingとは、LLMが事前に定義された関数の呼び出しの要否を判断して、コールする機能のことを指します。たとえば、LLMに「今日の天気を教えてください」と尋ねると、LLMは天気予報を取得する関数を呼び出して、結果をユーザーに返すことができます。以降でサンプルを提示しますが思っていた以上に簡単に実現できる印象でした。

また、Function CallingはOpenAIのAPIにも実装されていますが、現状は内部でのLLMとのやり取りが隠蔽されており、デバッグがしづらいという性質があると思います。その点、Guidanceはそれらを再公開し確認できるため便利です。後ほど例で示す通り、アシスタントロールの中で生のモデル出力を見ているかのように、応答や関数呼び出しの手順が確認できます。

必要なツールと設定の説明

以下で具体的な利用方法を説明していきます。

チュートリアルとコードスニペット

まずLLMが呼び出す関数を定義します。この例では、特定の場所の現在の天気を取得するget_current_weatherという関数を定義しました。ここでは固定値のオブジェクトを返していますが、外部APIをコールして結果を返却するなど柔軟に処理を記述できそうですね。

import json
def get_current_weather(location, unit="fahrenheit"):
    """ Get the current weather in a given location.

    Parameters
    ----------
    location : string
        The city and state, e.g. San Francisco, CA
    unit : "celsius" or "fahrenheit"
    """
    weather_info = {
        "location": location,
        "temperature": "31",
        "unit": unit,
        "forecast": ["sunny", "windy"],
    }
    return json.dumps(weather_info)

次にGuidanceのプログラムを作成します。guidance()内の文字列がGuidance固有の文法で書かれたプロンプトでパラメータや制御構造を埋め込むことができます。これを上から見ていくと、このプロンプトの動きが分かりやすいと思います。

{{~#system~}}…{{~/system~}}タグと{{~#user~}}…{{~/user~}}タグの中に、各ロールの指示文が記載されています。LLMはこれらの指示を解析して、回答にfunctions内の関数を利用するべきかどうかを判断します。

{{>tool_def functions=functions}}では、返答に利用できる関数の情報をオブジェクトで渡しています。functionsの中は後で具体的に見ていきますが、関数名やその説明など、呼び出しや結果返却に関する定義がされています。

{{gen 'answer' max_tokens=50 function_call="auto"}}は、指示に対する回答を生成する箇所です。

{{~#each range(10)~}}の中では、最大 10 回の連続した関数呼び出しを行っています。その中でモデルが関数呼び出しを含まないテキストを生成したら、その最終回答のテキストを回答変数に残しています。

import guidance
guidance.llm = guidance.llms.OpenAI("gpt-3.5-turbo-0613", caching=False)


program = guidance("""
{{~#system~}}
You are a helpful assistant.
{{>tool_def functions=functions}}
{{~/system~}}


{{~#user~}}
{{query}}
{{~/user~}}


{{~#each range(10)~}}
    {{~#assistant~}}
    {{gen 'answer' max_tokens=50 function_call="auto"}}
    {{~/assistant~}}


    {{#if not callable(answer)}}{{break}}{{/if}}

    {{~#function name=answer.__name__~}}
    {{answer()}}
    {{~/function~}}
{{~/each~}}""")

以下の記述では、実行のパラメータが定義されています。

“description”にget_current_weatherの説明文が自然言語で記載されており、LLMはuserから入力されたテキストを解析して、”description”で紹介されている関数を実行すべきかどうかを判断しているようです。

また”properties”では、LLMに生成させるパラメータ値について定義されています。後の出力結果と見比べると理解しやすいですが、LLMは”location”,”unit”にどのような値を設定するのが適切かを判断しています。

executed_program = program(functions=[
    {
        "name": "get_current_weather",
        "description": "Get the current weather in a given location",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "The city and state, e.g. San Francisco, CA",
                },
                "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
            },
            "required": ["location"],
        }
    }
], 
get_current_weather=get_current_weather,
query="Could you please tell me the current temperature in Sapporo today in Celsius?")

以下は実行結果です。ユーザーが天気を訪ねてから結果が出力されるまでの一連のLLMとの会話のやり取りが可視化されていますね。

”system”の記述を見ると、呼び出し候補となる関数名や、呼び出し判断に利用する説明、またその関数で返却されるオブジェクトの型など、上記の記述で定義した内容がLLMへの指示として渡されていることが確認できます。”user”には指示の内容が記載されていますね。

”assistant”ロールの出力結果は2つあることが確認できます。1つ目にはどのようなパラメータでどの関数を呼び出すべきとLLMが判断したのかを示すログが示されています。get_current_weatherを選んでいますし、”location”には”Sapporo”、”unit”には”celsius”と適切な文字列が設定されていますね。次の”function”ではそれらの関数・パラメータを使用して得た結果が出力されています。

また最後の”assistant”ロールの出力結果では、”function”の結果を踏まえて最終的な回答文が生成されているのが分かります。

このように通常Function Callingでは隠蔽されている内容も含めて、一連の会話の内容が出力から確認できました。

体験と学び

Guidanceを使用することで比較的容易に複雑な機能を実現できると感じました。ここまでに見てきた内容を実現するために、自分でプロンプトを作成して解析することもできますが、フレームワークを使うことでコアな処理の記述に集中できそうです。

また今回は試していませんが、Openなモデルでも統一的な記法でFunction Callingの仕組みを利用できるようです。最近はモデルもいろいろな選択肢が出てきているので、これも非常に便利ですね。

今後の進化に注目

本記事では、Microsoft GuidanceというLMMフレームワークでFunction Callingを試す方法を紹介しました。

Function Callingを利用することで、LLMが各種サービスと連携して、ユーザーの要求に応じた情報を提供することが可能になります。具体的には今回紹介したような天気予報の提供だけでなく、スケジュールの管理、データベースからの情報取得など、様々なタスクが想像されます。

個人的には、調和技研の最適化系のプロダクトと組み合わせていくつか面白そうなアプリケーションを作れそうなので、まずはプロトタイプレベルで動くものを作ってみようと思います。

またこのあたりは、まだ発展途上の新機能であり、今後の発展で機能がどのように進化していくのか、見守るのが楽しみです。



【参考文献】
https://github.com/microsoft/guidance/blob/main/notebooks/art_of_prompt_design/tool_use.ipynb


記事を書いた人
山形 聖志

普段は需要予測や数理最適化など、テーブルデータを扱うプロジェクトによく関わっています。北海道大学 調和系工学研究室を卒業(修士)、メーカーでIoT系の基盤構築・分析業務を経験し、2018年から「調和技研」の一員になりました。好きなものは、将棋、データ分析コンペ、HHKBなど。