見出し画像

Function Calling と LangChainを比較してみる

こんにちは。斉藤です。

今回は、前回の記事で紹介したChatGPTFunction Callingについて掘り下げていきます。また、新たな要素として、LangChainとの比較も行っていきます。

Function Callingとは

Function Callingは、OpenAIが提供しているChatGPTの機能のひとつです。これにより、事前に定義された関数をAIが呼び出すことができるようになります。通常のChatGPTなどのAIは、学習データに基づいて生成された回答しか返すことができませんが、Function Callingを利用することで、ユーザーが用意した関数を活用した回答を生成することが可能になります。

LangChainとは

LangChainは、ChatGPT(正確にはOpenAI APIのGPTモデル)をより高度に制御するためのライブラリです。LangChainを使用することで、ChatGPTのプラグインのように様々な機能を追加することができます。具体的には、AgentやToolと呼ばれるものを定義し、AIに対して特定のタスクや処理を実行させることができるようになります。

下記がLangChainの公式ページになります。

それでは、早速Function CallingとLangChainの比較について見ていきましょう。


デモ画面はこちら

まずは動くものを見ていきましょう。
入力した地域名によって、その土地の伝承などを返すように作られていますが、予めcsvファイルに定義された内容を元に文章を作成するようにしているため、定義されてないものは返してくれません。
企業やサービスのFAQにも活用できそうです。

画面はこんな感じです。

すべて同じ関数を呼んでいます

csvファイルはfolklore.csvは下記のように定義してあります。
内容に関してはChatGPTに適当にテストデータを作らせたのでまだファクトチェックはしてません。
そのうちちゃんとcsvファイルも整えようと思います。

location,folklore
箱根,箱根山の自然環境を生かした芸術として、箱根寄木細工は江戸時代から伝わる工芸品です。
日下部民藝館,日本各地の美しい民芸品が集められ、展示されている美術館です。ここでは地元の人々が代々受け継いできた伝統的な技術が生かされた作品を見ることができます。
..., ...

画面下部にあるExamplesから実行結果をすぐに見れるようにしておきました。
エンジニア向けの比較のため出力内容がjson形式なので非エンジニアの方には見づらいですが、ちょっと違いだけ見ていきましょう。

  • query_text: フリーテキストで地域を入力して送信すると、それぞれの処理結果を確認できます。

  • LangChainのレスポンス: 辞書型のレスポンスが返ってきます。実際にユーザに見せる出力はオレンジ色にハイライトの部分のみで、csvファイルに定義されたままの文章を使うことになります。json形式なのでlocationの「日下部民藝館」を切り出して再利用できます。

  • Function Callingのレスポンス: いつものChatGPTのような自然言語の文章で返ってきています。csvファイルに定義された文章を参考にした、ChatGPTの文章が返ってきます。「日下部民藝館」などの単語を切り出したりするのはこのままでは困難です。

  • Function Calling Agentのレスポンス: こちらは上記ふたつを組み合わせているので、json形式であり、自然言語の文章とcsvファイルに定義された内容の両方が返ってきます。固定の文章では味気ない、でも決まったことを話してほしいを実現できます。


LangChainの実行結果

以下のコードはLangChainを使って地域の伝承情報を取得する例を見てみましょう。

# LangChainエージェントからレスポンスを取得する関数
def get_response_from_lang_chain_agent(query_text):
    # ChatOpenAIを使用して言語モデルを初期化
    language_model = ChatOpenAI(model_name='gpt-3.5-turbo-0613')
    tools = [
        # 民間伝承を取得するToolを作成
        Tool(
            name="Folklore",
            func=fetch_folklore,
            description="伝承を知りたい施設や地名を入力。例: 箱根",
        )
    ]
    # エージェントを初期化してから応答を取得
    agent = initialize_agent(tools, language_model, agent="zero-shot-react-description",
                             verbose=True, return_intermediate_steps=True)
    response = agent({"input": query_text})
    print(type(response))
    response = json.dumps(response, default=serialize_agent_action, indent=2, ensure_ascii=False)

    return response

上記のコードでは、LangChainのAgentを初期化し、fetch_folkloreという関数を登録しています。fetch_folkloreは他の関数でも呼んでいるので、主処理は一緒です。箱根の伝承情報を取得するための対話を行い、csvに定義された内容が結果として返されます。
下記はCLIから実行した様子。

% pipenv run python app.py 日下部民藝館 langchain


> Entering new  chain...
I don't know what 日下部民藝館 is. I should try using the Folklore tool to find information about it.
Action: Folklore
Action Input: 日下部民藝館
Observation: 日本各地の美しい民芸品が集められ、展示されている美術館です。ここでは地元の人々が代々受け継いできた伝統的な技術が生かされた作品を見ることができます。
Thought:Based on the observation, 日下部民藝館 is a museum that showcases traditional crafts from various regions of Japan. It features artworks that highlight the traditional techniques passed down through generations by local people.
Final Answer: 日下部民藝館 is a museum that displays traditional crafts from different parts of Japan.

> Finished chain.
<class 'dict'>
{
  "input": "日下部民藝館",
  "output": "日下部民藝館 is a museum that displays traditional crafts from different parts of Japan.",
  "intermediate_steps": [
    [
      {
        "tool": "Folklore",
        "tool_input": "日下部民藝館",
        "log": "I don't know what 日下部民藝館 is. I should try using the Folklore tool to find information about it.\nAction: Folklore\nAction Input: 日下部民藝館"
      },
      "日本各地の美しい民芸品が集められ、展示されている美術館です。ここでは地元の人々が代々受け継いできた伝統的な技術が生かされた作品を見ることができます。"
    ]
  ]
}
% 

LangChainを使用することで、GPTに特定の機能を追加することができます。対話に関連する関数を定義し、エージェントに登録することで、伝承情報の取得などの機能を追加することができます。
ただ、このままだと定義された文章をが返すことになりますね。


Function Callingの実行結果

以下のようなコードを使用して、Function Callingを実行し地域の伝承を取得しています。

# Function Callingからレスポンスを取得する関数
def get_response_from_function_calling(query_text):
    function_definitions = [
        # 関数の定義を作成
        {
            "name": "fetchFolklore",
            "description": "伝承を調べる",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "伝承を知りたい施設や地名。例: 箱根",
                    },
                },
                "required": ["location"],
            },
        }
    ]
    messages = [HumanMessage(content=query_text)]
    language_model = ChatOpenAI(model_name='gpt-3.5-turbo-0613')
    # 言語モデルを使ってメッセージを予測
    message = language_model.predict_messages(
        messages, functions=function_definitions)

    if message.additional_kwargs:
        # 関数の名前と引数を取得
        function_name = message.additional_kwargs["function_call"]["name"]
        arguments = message.additional_kwargs["function_call"]["arguments"]

        # JSON 文字列を辞書に変換
        arguments = json.loads(arguments)

        # 関数を実行してレスポンスを取得
        function_response = fetch_folklore(location=arguments.get("location"))
        # 関数メッセージを作成
        function_message = FunctionMessage(
            name=function_name, content=function_response)
        # 関数のレスポンスをメッセージに追加して予測
        messages.append(function_message)
        second_response = language_model.predict_messages(
            messages=messages, functions=function_definitions)
        content = "AIの回答: " + second_response.content
    else:
        content = "AIの回答: " + message.content
    return content

このコードを実行すると、Function Callingでfetch_folklore関数を呼んでいますが、その結果をもとにGPTが文章を新しく生成してくれます。引数までうまいこと受け取った文章から定義してくれるのが素晴らしいです。
コマンドラインからの実行結果は下記の通り。

% pipenv run python app.py 日下部民藝館の伝承を教えて functioncalling
AIの回答: 日下部民藝館は、日本各地の美しい民芸品が集められ、展示されている美術館です。ここでは地元の人々が代々受け継いできた伝統的な技術が生かされた作品を見ることができます。具体的な伝承については、詳細がわかり次第お知らせいたします。

こちらの処理はデータの処理と返却する文章生成にGPTを使っているので、同じ処理をさせていても、返すメッセージが変わるところが興味深いです。


LangChain AgentのFunction Calling機能の実装例

agent=AgentType.OPENAI_FUNCTIONSを指定するだけでかなりスッキリなコード。リリースから10日程度で実装してしまうあたり、勢いの良さを実感できます。。。

# Function Call Agentからレスポンスを取得する関数
def get_response_from_function_calling_agent(query_text):
    language_model = ChatOpenAI(model_name='gpt-3.5-turbo-0613')
    tools = [
        # 民間伝承情報を提供するツールの追加
        Tool(
            name="Folklore",
            func=fetch_folklore,
            description="伝承を知りたい施設や地名を入力。例: 箱根"
        )
    ]
    # エージェントの初期化とレスポンスの取得
    agent = initialize_agent(tools, language_model, agent=AgentType.OPENAI_FUNCTIONS,
                             verbose=True, return_intermediate_steps=True)
    response = agent({"input": query_text})
    response = json.dumps(response, default=serialize_agent_action, indent=2, ensure_ascii=False)
    return response

こちらも`fetch_folklore`関数をFunction Callingで呼び出しています。コマンドラインの実行結果を見ると、LangChainで辞書型(json)に整えつつ、自然言語の返信文章まで生成しているのでいいとこ取りです。

 % pipenv run python app.py 日下部民藝館の伝承を教えて functioncallingagent


> Entering new  chain...

Invoking: `Folklore` with `日下部民藝館`


日本各地の美しい民芸品が集められ、展示されている美術館です。ここでは地元の人々が代々受け継いできた伝統的な技術が生かされた作品を見ることができます。日下部民藝館は日本各地の美しい民芸品が集められ、展示されている美術館です。ここでは地元の人々が代々受け継いできた伝統的な技術が生かされた作品を見ることができます。

> Finished chain.
{
  "input": "日下部民藝館の伝承を教えて",
  "output": "日下部民藝館は日本各地の美しい民芸品が集められ、展示されている美術館です。ここでは地元の人々が代々受け継いできた伝統的な技術が生かされた作品を見ることができます。",
  "intermediate_steps": [
    [
      {
        "tool": "Folklore",
        "tool_input": "日下部民藝館",
        "log": "\nInvoking: `Folklore` with `日下部民藝館`\n\n\n"
      },
      "日本各地の美しい民芸品が集められ、展示されている美術館です。ここでは地元の人々が代々受け継いできた伝統的な技術が生かされた作品を見ることができます。"
    ]
  ]
}



Function CallingとLangChainの比較とまとめ

ここまで、Function CallingとLangChainを使って地域の伝承情報を取得する例を紹介しました。それぞれの実行結果を見てみると、どちらもcsvに定義した同じ結果が得られるが、それぞれjson形式にしたり自然言語の文章が生成したりと特徴がありました。

LangChain Agentはサードパーティながらも更新速度も早く、Function Calling機能も少ないソースコードで実装できます。しかし時折location取得時にマッチングしないこともしばしばありました。これはLangChain側の問題なのかGPT側の問題なのかまだ切り分けできないですが、なんとなくGPT側の問題のような気がしています。

以前の記事も含め、どの方法を選ぶかは目的や要件によりますが、公式機能のFunction CallingもサードパーティのLangChainもどちらもシステムに組み込める実用レベルの機能だということがわかりました。

今後もChatGPTやOpenAI APIのGPTまわりの機能がさらに充実するかとも思いますが、LangChainなどのライブラリの併用も進化していくことが予想されます。

もしこの記事が参考になりましたら、記事購入やサポートをしていただければ飛んで喜びます!

参考

この記事をまとめるのに下記を参考にさせていただきました。
ありがとうございます!

もし興味があれば、下記も読んでみてください!

大規模言語モデルを活用したお仕事のご相談も承ります!
お問い合わせはこちらに。


この記事が気に入ったらサポートをしてみませんか?