catch-img

[OpenAI]Function Callingとは?実例で仕組みを解説

生成AIの重要機能の一つ「Function Calling(ファンクション・コーリング)」(※ClaudeではTool Useと呼びます)は、生成AIの活用において非常に重要な機能です。

画期的で驚くべき機能なのですが仕組みが複雑であり、理解して慣れて、使いこなすまでには時間がかかります。

この記事では、Function Callingの仕組みの説明、実装方法、ノーコードLLMツールDifyとの関係などを中心に、初学者の方にも概要をつかめるよう、なるべく分かりやすく解説していきたいと思います。

目次[非表示]

  1. Function Callingとは
    1. 関数とは
    2. Function Callingは千手観音、RAGはカンニングペーパー
    3. APIとは
  2. Function Callingの実行プロセス
    1. X APIでFunction Callingを実装
    2. STEP1:使う関数を事前に定義
    3. STEP2:プロンプトを設定
    4. STEP3:toolsを設定する
    5. STEP4:1回目の呼び出し
      1. レスポンスデータ
    6. STEP5:関数の実行
    7. STEP6:2回目の呼び出し
      1. レスポンスデータ
  3. GPTsもエージェントもFunction Calling
    1. Open APIとは
  4. Function Callingの実務活用
    1. ノーコードLLM構築ツール「Dify」
    2. 生成AI連携おすすめAPI
      1. SERP API
      2. ahrefs.API
      3. DS.API
      4. Keyword Tool API
      5. Google Books API
      6. NHK番組表API
      7. 国会図書館API
    3. ないAPIは自分で作る
  5. ポイントは業務のタスク化
  6. まとめ

Function Callingとは

Function Callingの例を探すと、天気を調べるコードなどが出てきて全く面白くないので、実際の一例を元に解説します。

例えば「イーロンマスクのXのアカウントと投稿内容を分析して」とAIに伝えるとします。

普通のChatGPTであれば、このような返答が返ってきます。

イーロン・マスク氏のX(旧Twitter)アカウントは、彼の直接的な影響力や意見を反映しており、特に自身のビジネスやテクノロジーに関連する話題が多いです。彼の投稿には以下のような特徴があります。
フォロワー数の動向: 彼のアカウントは非常に多くのフォロワー(約1億5300万人)を持っていますが、その中には非アクティブなアカウントも多く含まれているとのことです。
経済的な影響: Xの収益はマスク氏が経営を引き継いでから約40%減少していると報じられています。これは、広告収入の減少やユーザー数のほとんどの伸びが見られないことに起因している可能性があります。
コンテンツの多様性: マスク氏は、コミュニティ・ノート機能の導入や、特定のテーマに対する投稿を通じて、自身の意見や周囲の反応を促進しています。また、彼のアカウントでは、時に物議を醸す内容や、極端な意見が多く見られることもあります。
社会的・政治的発言: マスク氏の投稿は、時に政治的な意見や社会問題についても強い意見を示すことが多く、彼のフォロワーや一般市民の間にさまざまな反応を引き起こしています。
ユーザーの関与: 彼の投稿はしばしば話題を呼び、フォロワーとの対話を促進しますが、同時に誤情報や不適切なコンテンツの拡散という懸念も抱えています。
全体として、彼のアカウントは個人的な意見をシェアする場であり、その影響力は大きいものの、同時にそれに伴うリスクや反響も大きいと言えるでしょう。

抽象的で分析とは言い難いです。

次にFunction Calling を実装し、同じく「イーロンマスクのXのアカウントと投稿内容を分析して」と聞いてみます。

すると以下のような分析結果が返ってきます。

以下がイーロンマスクの情報です。

  • プロフィール概要

  • ユーザー名: @elonmusk
  • 名前: Elon Musk
  • フォロワー数: 192,961,418
  • フォロー数: 686
  • ツイート数: 48,661
  • リスト数: 150,809
  • いいね数: 65,040
  • 作成日: 2009年6月2日
  • プロフィール画像: Elon Musk Profile Image
    Elon Muskは、Twitterを通じて彼のビジネスや技術に関する情報を発信しています。また、彼のフォロワー数は非常に多く、影響力のある人物であることが伺えます。


    最近の投稿分析

    以下は、Elon Muskが最近行った10件のツイートです。
  • Starlinkの展開
       - 内容: "Starlink now available in 102 countries!" 
       - リンク: ツイートリンク
  • エンジニアリングに関する会話
       - 内容: "@ajtourville The fundamental architecture is now right, but we will still make thousands of improvements". 
  • 製造技術の革新
       - 内容: "@ajtourville Many of them are 3D metal printed into the wall of the part".
  • 興味深い情報への反応
       - 内容: "Interesting", リンク付き。
       - リンク: ツイートリンク
  • 熱防護についての言及
       - 内容: "@Erdayastronaut And it no longer requires a heat shield. Everything is regeneratively cooled."
  • 同じく熱防護に関する確認
       - 内容: "@Erdayastronaut It really is".
  • Raptorエンジンの改善
       - 内容: "The amount of work required to simplify the Raptor engine...".
       - 長文で詳細な説明あり。
  • Raptor 3の画像
       - 内容: "Raptor 3, SN1"
       - リンク: ツイートリンク
  • YouTubeに関連するリプライ
       - 内容: "@GadSaad @YouTube @TeamYouTube @TRobinsonNewEra Wow".
  • 詳細な説明の要求
       - 内容: "@hoovlet Detailed explanation".


    分析まとめ

    Elon Muskは主に彼の企業(例: スペースX、テスラ)の技術的進展について情報を発信しています。特に、Starlinkのグローバルな拡張や、Raptorエンジンの設計革新についての言及が目立ちます。また、彼はフォロワーとのインタラクションも多く、自身の技術やビジョンを広めることに力を入れていることが分かります。
    彼のツイートは技術的な内容が豊富で、専門的な知識を持つフォロワーに向けた情報提供が中心となっています。

大きな違いは、実数値や実投稿などが含まれていることです。

これはFunction Calling で事前設定したプロセスによりXのAPIから情報を取得して、その結果を元にAIが返答しているためこのような回答になっているのです。

Function Calling はこのように、生成AI単体では回答が難しい外部データの取得や操作を実行することができる機能です。

関数とは

関数とは、特定の入力に対して特定の出力を返す処理や計算のことです。この関数は、プログラミングコードなどで自由に作ることが可能です。

この関数の中には、APIを入れることもできますし、プログラミングコードで実現できることであればどのようなものでも入れることができます。

Function Calling(関数)を使えば、生成AIを通して、

  • 天気を調べる
  • カレンダーに予定を追加する
  • ニュースを調べる
  • 検索トレンドを調べる
  • インフルエンサーを見つける
  • 部屋の電化製品をAI経由で操作する
  • ahrefsをAIで操作してSEOの分析をする
  • Xの投稿文を自動的に作って投稿までさせる

といった複雑な操作も可能となります。Function Callingは 簡単に言えば、外部ツール(いわゆるAPIや設定した関数)を生成AIに操作させる機能、ということです。

このとき混乱しがちなのが、生成AIで実現できることと関数で実現できることをごっちゃにしてしまうケースです。

テキストなどの生成は生成AI単体で実行可能です。APIは生成AI単体では実行できません。この2つの業務分担をしっかりと分けて理解しておくと理解も深まるのではないかと思います。


※Perplexityなどの検索機能を持つLLMは、内部で検索ツールを外部利用しているイメージです。

こうした関数は事前に準備しておくことが必要となります。

Function Callingは生成AIがユーザーとの会話の中で、

  • 保有ツールの確認
  • どのツールを使うとそのタスクが解決できるのかを判断
  • いつ使うのか
  • どうやって使うのか(オプション<引数>に何をいれるか)

などを判断して、実行と結果の確認まで自分でやってしまう優れものです。

Function Callingは千手観音、RAGはカンニングペーパー

Function Callingはいうなれば、様々なツールを自在に扱えるドラえもんのような存在であり、様々なツールを手に持つ千手観音のようなものとイメージすると良いかもしれません。

 
一方で事前知識として活用できる「RAG」は以下でも記載しましたが「カンニングペーパー」であると考えると理解しやすいかと思います。

  生成AIの独自データ活用(RAG)と著作権のまとめ 生成AIに重要となるRAGの仕組みと利点、著作権との関係性について、なるべく分かりやすく解説します。 広報・PR支援の株式会社ガーオン

APIとは

Function Callingでは各種API(Application Programming Interface) を、生成AIを通して利用することができます。

API とは簡単に言ってしまえば、Webサービスなどをプログラミング上で活用する方法です。

例えば今回のX APIで説明すると、Web上(ブラウザだけ)でイーロン・マスクのプロフィールと投稿内容を取得するには、検索欄に「イーロン・マスク」と入れる、もしくはユーザー名で「(from:elonmusk) 」と入れるとユーザーが見つかります。

ブラウザでユーザー名をクリックするとプロフィールが表示されます。そしてそこからタイムラインを追っておっていけば、実際の投稿文を確認することができます。

このプロセスはデータの取得だけを考えると非常に面倒くさい行為です。これが10人100人となっていけば、もう人間の手で実行するのは限界があります。

これらのデータをエクセルにまとめるとしたら、コピペを永遠に繰り返さなくてはなりません。

こうしたプロセスもAPIを使えば、あっという間にデータ格納していくことができるわけです。

APIは通常プログラミングコードで実行しますが、今回のFunction Callingでは、このAPIの操作自体もAIが実行してしまう、という内容になります。

Function Callingの実行プロセス

ここからは実際のFunction Callingの流れについて解説していきます。

Function Callingは生成AIが大きく以下3つの要素を、判断・実行して進めます。

  • 保有関数の確認
  • 関数の使用判断
  • 関数の実行

流れは図で確認すると分かりやすいかと思います。以下はGPTでFunction Callingを行った場合の挙動プロセスです。

Function Callingの仕組み

①ユーザーがプロンプトを入力

ここは通常の会話プロセスと変わりません。

②AIモデルが関数呼び出しの必要性を判断(GPT呼び出し1回目)

Function Callingが設定されている場合は、ここから通常のLLMの動きと異なる動きをします。

初回のGPT呼び出しで、どのような関数(ツール)を持っているかを確認し、ユーザーが入力したプロンプトから、それら関数が必要であるか否かを判断します。

③AIモデルが関数名とパラメーターを指定

どの関数を使うか、引数はどうするかの指示をアプリケーションに送ります。

④アプリケーションが指定された関数を実行

AIモデルから渡された引数を入れた関数で、アプリケーションが関数を実行します。

⑤APIが実行結果を出力

APIが実行結果をアプリケーションに戻します。

⑥アプリケーションが実行結果をAIモデルに渡す

アプリケーションが実行結果をAIモデルに戻します。

⑦AIモデルが結果を元に最終結果を出力(GPT呼び出し2回目)

APIから戻ってきた内容を元に、AIモデルが最終出力を行います。

⑧AIモデルがユーザーに結果を出力

出力した結果をユーザーに返します。


流れとしては以上です。

ポイントは関数実行そのものにはAI(GPT)は携わっていないというところです。

ここが最大の混乱ポイントなので、後ほど改めて説明します。


X APIでFunction Callingを実装

ここからはGPTモデルを使って、実際にコードでFunction Callingを実行する方法を解説します。

コードは詳細を載せておきますが、必要無い方はこの項目は読み飛ばしてください。


まず先にXのプロフィールデータとタイムラインを取得する関数を設定しておきます。

このあたりはClaude3.5であれば、「X APIのプロフィールを取得するAPIコードを作って」と入れれば、大体1発で作ってくれます。

STEP1:使う関数を事前に定義

# プロフィールを取得する関数

def get_user_profile(username, bearer_token):

    bearer_token= userdata.get('X_API_KEY')

    # APIエンドポイント
    url = f"https://api.twitter.com/2/users/by/username/{username}"

    # クエリパラメータ
    params = {
        "user.fields": "created_at,description,entities,id,location,name,pinned_tweet_id,profile_image_url,protected,url,username,verified,withheld,most_recent_tweet_id,public_metrics,verified_type",
        "expansions": "pinned_tweet_id",
        "tweet.fields": "attachments,author_id,conversation_id,created_at,entities,geo,id,in_reply_to_user_id,lang,possibly_sensitive,referenced_tweets,source,text,withheld"
    }

    # ヘッダー
    headers = {
        "Authorization": f"Bearer {bearer_token}"
    }

    # リクエストを送信
    response = requests.get(url, params=params, headers=headers)

    # レスポンスをチェック
    if response.status_code == 200:
        return response.json()
    else:
        return f"エラー: {response.status_code}, {response.text}"


# 投稿(タイムライン)を取得する関数

def get_user_tweets(user_id, bearer_token, max_results=100):

    bearer_token= userdata.get('X_API_KEY')

    # APIエンドポイント
    url = f"https://api.twitter.com/2/users/{user_id}/tweets"

    # クエリパラメータ
    params = {
        "max_results": max_results
    }

    # ヘッダー
    headers = {
        "Authorization": f"Bearer {bearer_token}"
    }

    # リクエストを送信
    response = requests.get(url, params=params, headers=headers)

    # レスポンスをチェック
    if response.status_code == 200:
        return response.json()
    else:
        return f"エラー: {response.status_code}, {response.text}"

続いて、Open AIのAPIコードを設定していきます。

今回は例として、イーロン・マスクのプロフィールと投稿を取得します。

STEP2:プロンプトを設定

bearer_token= userdata.get('X_API_KEY')

client = OpenAI(api_key = userdata.get('OPENAI_API_KEY'))

system_prompt = "あなたは有能な分析官です。"

prompt = "@elonmusk 44196397 のプロフィールと投稿を分析して"


messages = [
    {"role": "system", "content": system_prompt},
    {"role": "user", "content": prompt}
    ]

プロンプトは「@elonmusk 44196397 のプロフィールと投稿を分析して 」とget_user_tweets用に 事前に調べた user_id を入れていますが、user_id 自体も関数で調べてしまうこともできます。

X APIなどのAPIキーが必要なものは、別途bearer_tokenなどを事前に設定しておく必要があります。

GPTモデルのためのAPIキーも合わせて設定します。

STEP3:toolsを設定する

ここからがFunction Calling独自の設定となります。

まずtoolsに使う関数を設定します。

これが生成AIにツールを手に持たせているようなイメージです。

先ほど定義した、get_user_profileとget_user_tweets を記載します。

この辺はあまり深く考えず、「この関数をFunction Calling用に tools = [ から始まるリストに入れて 」とClaudeに出させてしまうのがおすすめです。

ポイントはnameやdesctiptionに機能や使い方をしっかりと自然言語で記載することです。生成AIはこの説明を読んでツールを使うか判断します。

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_user_profile",
            "description": "指定されたユーザー名のXユーザープロフィールを取得します。",
            "parameters": {
                "type": "object",
                "properties": {
                    "username": {
                        "type": "string",
                        "description": "プロフィールを取得するユーザーの名前"
                    }
                },
                "required": ["username"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "get_user_tweets",
            "description": "指定されたユーザーIDのXユーザーのツイートを取得します。",
            "parameters": {
                "type": "object",
                "properties": {
                    "user_id": {
                        "type": "string",
                        "description": "ツイートを取得するユーザーのID"
                    },
                    "max_results": {
                        "type": "integer",
                        "description": "取得するツイートの最大数",
                        "default": 10
                    }
                },
                "required": ["user_id"]
            }
        }
    }
)

STEP4:1回目の呼び出し

ここで1回目の会話を投げかけ、モデルからの返答をresponseに代入します。

response = client.chat.completions.create(
    model= "gpt-4o-mini",
    messages=messages,
    tools=tools,
    tool_choice="auto",
)

ここで実際のレスポンスデータを見てみます。

レスポンスデータ

まずFunction Callingを設定していない場合の返答です。通常の会話機能しかないので、場合によってはプロフィールは答えられないと返ってきます。

#Function Callingなし

ChatCompletion(id='chatcmpl-9s25ioB8jkjlBY0QlomqLNEEFlEgR', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='申し訳ありませんが、特定のユーザーのプロフィールや投稿を直接分析することはできません。しかし、一般的にエロン・マスク(@elonmusk)に関する情報や彼の投稿スタイルについての分析を提供することはできます。エロン・マスクは、テスラやスペースXなどの企業のCEOとして知られ、Twitterでは革新的なアイデアや最新のプロジェクトについて頻繁に発信しています。彼の投稿はしばしば注目を集め、時には論争を引き起こすこともあります。\n\n具体的な分析が必要な場合、彼の投稿内容や傾向、影響力についての一般的な点をお知らせいただければ、できる限りお手伝いします。', role='assistant', function_call=None, tool_calls=None))], created=1722664778, model='gpt-4o-mini-2024-07-18', object='chat.completion', service_tier=None, system_fingerprint='fp_0f03d4f0ee', usage=CompletionUsage(completion_tokens=175, prompt_tokens=35, total_tokens=210))

次に下記がFunction Callingを設定している場合のレスポンスです。

 tool_choice="auto"でFunction Callingを使うかどうかを自動判断、tool_choice: "required"で強制利用になります。

# Function Callingを使うと判断された場合

ChatCompletion(id='chatcmpl-9s20caIXcldYLgg4ZwrlazpxGJUS2', choices=[Choice(finish_reason='tool_calls', index=0, logprobs=None, message=ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_6JwUixjjcm7ImhogdTBxurwB', function=Function(arguments='{"username": "elonmusk"}', name='get_user_profile'), type='function'), ChatCompletionMessageToolCall(id='call_zMf60XB54hJOFwuwsyknep7X', function=Function(arguments='{"user_id": "44196397"}', name='get_user_tweets'), type='function')]))], created=1722664462, model='gpt-4o-mini-2024-07-18', object='chat.completion', service_tier=None, system_fingerprint='fp_0f03d4f0ee', usage=CompletionUsage(completion_tokens=52, prompt_tokens=160, total_tokens=212))

テキストでの返信ではなく、データが返ってきていることが分かります。

この中のtool_callsを見ていくと以下の内容が記載されていることが分かります。

[ChatCompletionMessageToolCall(id='call_nHLI3xla0MGoleAkZpzMU2Iz', function=Function(arguments='{"username": "elonmusk"}', name='get_user_profile'), type='function'),

 ChatCompletionMessageToolCall(id='call_rq8NlZLUISsUH9QreOM4azeh', function=Function(arguments='{"user_id": "44196397"}', name='get_user_tweets'), type='function')]

nameとarguments(引数)を見ると、以下のように記載されていることが分かります。

arguments='{"username": "elonmusk"}'
name='get_user_profile'

arguments='{"user_id": "44196397"}'
, name='get_user_tweets'

これが生成AI側が、ユーザーが入力したプロンプト「@elonmusk 44196397 のプロフィールと投稿を分析して」を受けて、Function Callingを利用すると判断し、その「関数名と引数」を返している部分のデータとなります。

この2つのデータは、
・get_user_profileという関数をelonmuskを引数(設定オプション)にして使え
・get_user_tweetsという関数を44196397を引数にして使え

という2個の司令を意味しています。

STEP5:関数の実行

AIが使う関数と引数を決定しましたので、次は実際に関数を使うプロセスです。

ここからが混乱しやすい箇所ですが、この関数を実行する主体は実はAIではありません。

Pythonで実行しているのであればPythonのプログラムが、Difyで実行しているのであればDify(の裏側で走っているプログラム)が実行します。

つまりAIは使う関数は選べても実行はできないのです。

Function Callingを触りはじめた多くの方がここを誤解すると思います。

AIはテキスト出力はできても関数実行はできないので、プログラム上でこの選ばれた関数と引数を実行する必要があります。以下がそのコードです。

まずは全文記載します。

response_message = response.choices[0].message

tool_calls = response_message.tool_calls

if tool_calls:
    available_functions = {
        "get_user_profile": get_user_profile,
        "get_user_tweets": get_user_tweets,
    }
    messages.append(response_message)

    for tool_call in tool_calls:
        function_name = tool_call.function.name
        function_to_call = available_functions[function_name]
        function_args = json.loads(tool_call.function.arguments)

        if function_name == "get_user_profile":

            function_response = function_to_call(
                username=function_args.get("username"),
                bearer_token=function_args.get(bearer_token)
                )
        elif function_name == "get_user_tweets":
            function_response = function_to_call(
                user_id=function_args.get("user_id"),
                max_results=function_args.get("max_results", 10),
                bearer_token=function_args.get(bearer_token)
                )
        messages.append(
            {
                "tool_call_id": tool_call.id,
                "role": "tool",
                "name": function_name,
                "content": json.dumps(function_response)
            }
        )

    second_response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=messages,
        temperature=1
    )


    print("second_response:", second_response.choices[0].message.content)

一つ一つブロックで見ていきます。

tool_calls = response.choices[0].message.tool_calls

if tool_calls:

    # STEP1で設定した2つの関数をavailable_functionsの中に入れる
    # available_functionsは複数の関数を入れておく入れ物
    available_functions = {
        "get_user_profile": get_user_profile,
        "get_user_tweets": get_user_tweets,
    }
    messages.append(response_message)

AIがFunction Callingを使うと決めた際、resposeデータにはtool_callsというデータが入っているのでした。

このtool_callsをresponseデータから抜き出し、tool_callsという変数に入れておきます。

if tool_calls: という文は、tool_callsが入っていたらという意味になります。今回tool_callsには2つのデータが入っているので、その次の行の処理に進みます。

available_functionsにSTEP1で作成しておいた関数プログラムを同じ名前で代入します。

messagesは生成AIでは、会話履歴として使われます。Difyではトレースデータの一部として表示されます。

messages.append(response_message) は、会話履歴に最新の返答(response_message)を追加する処理になります。

この時点でmessagesの中身は以下のような状況になっています。

[{'role': 'system', : 'あなたは有能な分析官です。'},

 {'role': 'user', 'content': '@elonmusk 44196397 のプロフィールと投稿を分析して'},

 ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_3DREd51cbvZKbUDVdRIFJFhX', function=Function(arguments='{"username": "elonmusk"}', name='get_user_profile'), type='function'), ChatCompletionMessageToolCall(id='call_wYPvxARFnb2qWJHlYsf47nCZ', function=Function(arguments='{"user_id": "44196397"}', name='get_user_tweets'), type='function')])]

続いてfor文の箇所です。

    for tool_call in tool_calls:

        # tool_callから関数の名前を代入
        function_name = tool_call.function.name

        # available_functionsから関数を一つ設定(これが関数プログラムそのものになる)
        function_to_call = available_functions[function_name]

        # tool_callから関数に使う引数を代入
        function_args = json.loads(tool_call.function.arguments)

この部分は関数の個数文だけそれぞれ処理を実行するというものです。今回は2つの関数が含まれているので、2回それぞれ繰り返すことになります。

もう一度先程のtool_callの中身を確認すると、functionとnameなどがイメージつきやすいかと思います。

[ChatCompletionMessageToolCall(id='call_nHLI3xla0MGoleAkZpzMU2Iz', function=Function(arguments='{"username": "elonmusk"}', name='get_user_profile'), type='function'),

 ChatCompletionMessageToolCall(id='call_rq8NlZLUISsUH9QreOM4azeh', function=Function(arguments='{"user_id": "44196397"}', name='get_user_tweets'), type='function')]


次のif文が実際に関数を実行する箇所です。今回引数が異なる関数を入れているのでif文で分けています。

それぞれの関数の戻り値を会話履歴であるmessagesに記録させます。

        if function_name == "get_user_profile":
            # 以下のfunction_to_callで関数を実行し、function_responseに戻り値を入れる
            function_response = function_to_call(
                username=function_args.get("username"),
                bearer_token=function_args.get(bearer_token)
                )
            
        elif function_name == "get_user_tweets":
            # 以下のfunction_to_callで関数を実行し、function_responseに戻り値を入れる
            function_response = function_to_call(
                user_id=function_args.get("user_id"),
                max_results=function_args.get("max_results", 10),
                bearer_token=function_args.get(bearer_token)
                )

        # 会話履歴messagesに関数の戻り値を追加します
        messages.append(
            {
                "tool_call_id": tool_call.id,
                "role": "tool",
                "name": function_name,
                #辞書型の関数戻り値をjson文字列に変換 ※messagesには辞書型は入れられない
                "content": json.dumps(function_response),

            }
        )

この段階でmessagesを見てみます。

以下5つの要素で構成されていることがわかります。

# STEP2で設定したシステムプロンプト
[{'role': 'system', 'content': 'あなたは有能な分析官です。'},

# STEP2で設定したユーザープロンプト
 {'role': 'user', 'content': '@elonmusk 44196397 のプロフィールと投稿を分析して'},

# STEP4でGPTから取得した戻り値(tool_callsなど)
 ChatCompletionMessage(content=None, role='assistant', function_call=None, tool_calls=[ChatCompletionMessageToolCall(id='call_jxqLnqpy4ovFHgat7ED7qcO9', function=Function(arguments='{"username": "elonmusk"}', name='get_user_profile'), type='function'), ChatCompletionMessageToolCall(id='call_Af8FtZ8MfgtNTZCtuiNZzSKL', function=Function(arguments='{"user_id": "44196397", "max_results": 10}', name='get_user_tweets'), type='function')]),

# STEP5で実行した関数の戻り値①(プロフィールデータ)
 {'tool_call_id': 'call_jxqLnqpy4ovFHgat7ED7qcO9',
  'role': 'tool',
  'name': 'get_user_profile',
  'content': '{"data": {"username": "elonmusk", "verified_type": "blue", "name": "Elon Musk", "verified": true, "protected": false, "public_metrics": {"followers_count": 192952776, "following_count": 686, "tweet_count": 48661, "listed_count": 150811, "like_count": 65040}, "most_recent_tweet_id": "1819613543081103747", "id": "44196397", "description": "", "profile_image_url": "https://pbs.twimg.com/profile_images/1815749056821346304/jS8I28PL_normal.jpg", "created_at": "2009-06-02T20:12:29.000Z"}}'},

# STEP5で実行した関数の戻り値②(投稿データ)
 {'tool_call_id': 'call_Af8FtZ8MfgtNTZCtuiNZzSKL',
  'role': 'tool',
  'name': 'get_user_tweets',
  'content': '{"data": [{"id": "1819613543081103747", "text": "Starlink now available in 102 countries! https://t.co/enkiN79Uma", "edit_history_tweet_ids": ["1819613543081103747"]}, {"id": "1819612640311742617", "text": "@ajtourville The fundamental architecture is now right, but we will still make thousands of improvements", "edit_history_tweet_ids": ["1819612640311742617"]}, {"id": "1819612317358735834", "text": "@ajtourville Many of them are 3D metal printed into the wall of the part", "edit_history_tweet_ids": ["1819612317358735834"]}, {"id": "1819607747203105044", "text": "Interesting https://t.co/skmuBMDkgy", "edit_history_tweet_ids": ["1819607747203105044"]}, {"id": "1819605322132074659", "text": "@Erdayastronaut And it no longer requires a heat shield. Everything is regeneratively cooled.", "edit_history_tweet_ids": ["1819605322132074659"]}, {"id": "1819605125427609718", "text": "@Erdayastronaut It really is", "edit_history_tweet_ids": ["1819605125427609718"]}, {"id": "1819597689283121225", "text": "The amount of work required to simplify the Raptor engine, internalize secondary flow paths and add regenerative cooling for exposed components was staggering. \\n\\nAs a result Raptor 3 doesn\\u2019t require any heat shield, eliminating heat shield mass & complexity, as well as the fire\\u2026 https://t.co/IyLI7QbPSX", "edit_history_tweet_ids": ["1819597689283121225"]}, {"id": "1819551225504768286", "text": "Raptor 3, SN1 https://t.co/gV1NemIyXU", "edit_history_tweet_ids": ["1819551225504768286"]}, {"id": "1819550004073177369", "text": "@GadSaad @YouTube @TeamYouTube @TRobinsonNewEra Wow", "edit_history_tweet_ids": ["1819550004073177369"]}, {"id": "1819548856910012724", "text": "@hoovlet Detailed explanation", "edit_history_tweet_ids": ["1819548856910012724"]}], "meta": {"next_token": "7140dibdnow9c7btw4b0awdm21xla9tyfaxq8rb84tnw4", "result_count": 10, "newest_id": "1819613543081103747", "oldest_id": "1819548856910012724"}}'}]

この状態で、
・プロンプト
・GPTのFunction Callingの指示
・関数の戻り値(プロフィールデータと投稿データ)
が揃いました。

これでようやく最後の段階です。

STEP6:2回目の呼び出し

最後は簡単で、これまでの流れを記録したmessagesを入れてGPTを呼び出すだけです。

つまりFunction CallingはGPT呼び出しを2回することになります。

    second_response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=messages,
        temperature=1
    )


    print(second_response.choices[0].message.content)

レスポンスデータ

ChatCompletion(id='chatcmpl-9s6uyAjV1KXiIdX3Ip2JLQE9B9loG', choices=[Choice(finish_reason='stop', index=0, logprobs=None, message=ChatCompletionMessage(content='### プロフィール概要\n- **ユーザー名**: [@elonmusk](https://twitter.com/elonmusk)\n- **名前**: Elon Musk\n- **フォロワー数**: 192,961,418\n- **フォロー数**: 686\n- **ツイート数**: 48,661\n- **リスト数**: 150,809\n- **いいね数**: 65,040\n- **作成日**: 2009年6月2日\n- **プロフィール画像**: ![Elon Musk Profile Image](https://pbs.twimg.com/profile_images/1815749056821346304/jS8I28PL_normal.jpg)\n\nElon Muskは、Twitterを通じて彼のビジネスや技術に関する情報を発信しています。また、彼のフォロワー数は非常に多く、影響力のある人物であることが伺えます。\n\n### 最近の投稿分析\n以下は、Elon Muskが最近行った10件のツイートです。\n\n1. **Starlinkの展開**\n   - **内容**: "Starlink now available in 102 countries!" \n   - **リンク**: [ツイートリンク](https://t.co/enkiN79Uma)\n\n2. **エンジニアリングに関する会話**\n   - **内容**: "@ajtourville The fundamental architecture is now right, but we will still make thousands of improvements". \n\n3. **製造技術の革新**\n   - **内容**: "@ajtourville Many of them are 3D metal printed into the wall of the part".\n\n4. **興味深い情報への反応**\n   - **内容**: "Interesting", リンク付き。\n   - **リンク**: [ツイートリンク](https://t.co/skmuBMDkgy)\n\n5. **熱防護についての言及**\n   - **内容**: "@Erdayastronaut And it no longer requires a heat shield. Everything is regeneratively cooled."\n\n6. **同じく熱防護に関する確認**\n   - **内容**: "@Erdayastronaut It really is".\n\n7. **Raptorエンジンの改善**\n   - **内容**: "The amount of work required to simplify the Raptor engine...".\n   - 長文で詳細な説明あり。\n\n8. **Raptor 3の画像**\n   - **内容**: "Raptor 3, SN1"\n   - **リンク**: [ツイートリンク](https://t.co/gV1NemIyXU)\n\n9. **YouTubeに関連するリプライ**\n   - **内容**: "@GadSaad @YouTube @TeamYouTube @TRobinsonNewEra Wow".\n\n10. **詳細な説明の要求**\n   - **内容**: "@hoovlet Detailed explanation".\n\n### 分析まとめ\nElon Muskは主に彼の企業(例: スペースX、テスラ)の技術的進展について情報を発信しています。特に、Starlinkのグローバルな拡張や、Raptorエンジンの設計革新についての言及が目立ちます。また、彼はフォロワーとのインタラクションも多く、自身の技術やビジョンを広めることに力を入れていることが分かります。\n\n彼のツイートは技術的な内容が豊富で、専門的な知識を持つフォロワーに向けた情報提供が中心となっています。', role='assistant', function_call=None, tool_calls=None))], created=1722683332, model='gpt-4o-mini-2024-07-18', object='chat.completion', service_tier=None, system_fingerprint='fp_9b0abffe81', usage=CompletionUsage(completion_tokens=781, prompt_tokens=1737, total_tokens=2518))



ここでもう一度流れをおさらいしておきます。

ポイントは以下の2点です。

1.GPTの呼び出しは2回あること
2.関数を実行するのは生成AIではなくPythonなどのアプリケーションであること


GPTsもエージェントもFunction Calling

こうしてみるとGPTsやDifyのエージェント機能も、Function Callingを利用しているということが分かります。

Function Callingは複雑ですが、仕組みを理解することは、GPTやDifyのプロセス活用にとても重要であることが分かります。

こうしたFunction Callingの一つ一つの関数設計や設定は、Open APIという形式で行われます。

Open APIとは

OpenAPIとは、APIを記述、設計、構築するための標準的な仕様です。通常YAMLやJSON形式で記載されており、APIの説明書のような位置づけです。

GPTsやDifyなどのノーコードLLM構築ツールには、一つ一つの関数やAPIをこうしたOpen API方式で記述しておかないと使うことができません。

しかし生成AI自体にOpen APIを作らせてしまえば作成も簡単で、特にコードやAPIのクエリURLがあれば、それと一緒にオーダーするだけでほぼ1発で出力してくれます。

今回のユーザープロフィールを取得するget_user_profile関数も、Open API形式だと以下のようになります。

openapi: 3.0.0
info:
  title: Twitter User Profile API
  version: 1.0.0
  description: Twitter User Profileを取得するためのAPI

servers:
  - url: https://api.twitter.com/2

paths:
  /users/by/username/{username}:
    get:
      summary: ユーザープロフィールの取得
      description: 指定されたユーザー名に基づいてTwitterユーザーのプロフィール情報を取得します。
      parameters:
        - name: username
          in: path
          required: true
          schema:
            type: string
          description: 取得したいユーザーのTwitterユーザー名
        - name: user.fields
          in: query
          schema:
            type: string
          description: 返すユーザーフィールド
        - name: expansions
          in: query
          schema:
            type: string
          description: 展開するフィールド
        - name: tweet.fields
          in: query
          schema:
            type: string
          description: 返すツイートフィールド
      security:
        - BearerAuth: []
      responses:
        '200':
          description: 成功レスポンス
          content:
            application/json:
              schema:
                type: object
        '400':
          description: 不正なリクエスト
        '401':
          description: 認証エラー
        '404':
          description: ユーザーが見つかりません
        '429':
          description: レート制限超過

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer

Function Callingの実務活用

最後に一番重要なのが、この機能を実務にどう活かすかということです。

Function Callingの強みは「APIを生成AIで管理できる」という点です。

つまりAPIで提供されているサービスであれば、生成AIをアシスタントにしてそのAPIをフル活用できるのです。

ノーコードLLM構築ツール「Dify」

Dify

ここまでFunction CallingのPython実装をメインに解説してきましたが、これをノーコードで実行できるのが今話題のDifyです。

Difyにはチャットフロー、エージェント、ワークフローと大きく3つの機能があり、この中のエージェント機能を使えば、ノーコードでFunction Callingを実行することが可能です。Difyの機能一覧

▼Dify関連記事

  生成AI(Dify)を活用した検索・情報収集のポイント Difyの様々な検索・情報収集ツールを実際に使い、精度を比較しながら細かく解説します。 広報・PR支援の株式会社ガーオン
  Difyにプレスリリースを自動作成させる方法 ノーコードLLM構築ツールDifyでプレスリリース作成業務を自動化する方法を解説します。 広報・PR支援の株式会社ガーオン
  Difyの使い方(メモリ—機能とは) Difyでつまづきそうなメモリ—機能の使い方を解説します。 広報・PR支援の株式会社ガーオン


生成AI連携おすすめAPI

実際に生成AIで操作すると、生産性が爆上がりしそうなおすすめAPIをご紹介します。

X API

今回紹介したXの投稿データやプロフィールデータを取得するAPIです。

  X API | Products Use the X API to analyze, learn from, and interact with posts, Direct Messages, and users. Scale your access to grow, experiment, and innovate. https://developer.x.com/en/products/twitter-api

イーロン・マスクの経営になってから、APIの機能はどんどん縮小していますが、まだまだ使える機能は残されています。

有料プラン:月$100ドル~


SERP API

検索エンジンAPIの代表格SerpApiです。

  SerpApi: Google Search API SerpApi is a real-time API to access Google search results. We handle proxies, solve captchas, and parse all rich structured data for you. SerpApi

グーグルトレンドのデータや、マップやYouTubeなど様々なGoogleの情報を取得することができます。

有料プラン: 月$75~



ahrefs.API

SEOツールの代表格ahrefsもAPIを提供しています。

  Introduction | API This documentation is for the latest version of our API (v3). Read about our legacy API v2. https://docs.ahrefs.com/docs/api/reference/introduction

エンタープライズプランのみとなりかなり高額な部類ですが、ahrefsの膨大な機能を生成AIで使えるのであれば価格に見合った機能かもしれません。

無料でもいくつかの限定エンドポイントが公開されています。

例えば「ahrefsのキーワードボリュームを調べて」と入力すれば、以下のように結果を解説してくれます。

Ahrefsは、SEO(検索エンジン最適化)とデジタルマーケティングの分野で広く使用されている強力なツールです。以下にAhrefsに関する重要な情報をお伝えします:


人気度:
Ahrefsは非常に人気のあるツールで、月間検索ボリュームが49,000回に達しています。これは多くのユーザーがこのツールに関心を持っていることを示しています。


トラフィックポテンシャル:
Ahrefsに関連するキーワードは、月間58,000回のトラフィックポテンシャルを持っています。これは、Ahrefsに関連するコンテンツが多くの人々に閲覧される可能性が高いことを示しています。


難易度:
Ahrefsに関連するキーワードの難易度は48となっています。これは中程度の難易度を示し、適切なSEO戦略を立てることで上位表示を狙える可能性があることを意味します。
Ahrefsは、バックリンク分析、キーワードリサーチ、競合分析、サイト監査など、多様なSEOツールを提供しています。多くのデジタルマーケターやSEO専門家が、ウェブサイトのパフォーマンス向上やオンライン戦略の立案にAhrefsを活用しています。


この情報から、Ahrefsは SEO業界で重要な位置を占めており、多くのユーザーに利用されていることがわかります。Ahrefsに関する情報やその使用方法についてのコンテンツを作成することは、潜在的に多くの読者を引き付ける可能性があります。

有料プラン:年230万円~


DS.API

ヤフーの検索情報サービス DS.INSIGHTのAPIです。

  DS.LAB https://datasolution.yahoo-net.jp/view/knowledgebase/post/4026

既に本サービスにもGPTが組み込まれていますが、生成AIで操作するとより強力になりそうです。

有料プラン:月額10万円+従量課金


Keyword Tool API

SEOツールKeyword ToolのAPIです。

  Keyword Tool API: Google Ads API Alternative #1 For Search Volume Get Google search volume data for your keywords or generate up to 1,120,000 keywords / day from Google, YouTube, Bing, Amazon, eBay or App Store! https://keywordtool.io/api

検索ボリュームなどを調べられます。

有料プラン:月$299~


Google Books API

Googleの書籍検索APIです。

  Google Books APIs  |  Google for Developers Google Books API を使用して、アプリでテキストの検索や書籍情報の取得を行えるようにする方法を学びます。 Google for Developers

無料


NHK番組表API

NHKが公開している番組データAPIです。

  https://api-portal.nhk.or.jp/ https://api-portal.nhk.or.jp/

これから放映予定の番組データが調べられます。

無料


国会図書館API

国会図書館のAPIです。

  国立国会図書館サーチ(NDLサーチ) 国立国会図書館サーチ(NDLサーチ)

無料

ないAPIは自分で作る

実はAPI自体も個人でも簡単に作れてWebで公開可能です。

様々なサービスが提供されていますが、Amazon API Gatewayを使う方法などがおすすめです。

  Amazon API Gateway(規模に応じた API の作成、維持、保護)| AWS Amazon API Gateway は、数回クリックするだけで、簡単に API の作成、配布、保守、監視、保護が行えるフルマネージドなサービスです。どのようなスケールにも対応するパフォーマンスを低コストでご利用いただけます。 Amazon Web Services, Inc.

一例としてはAWS Lambdaで関数コードを実行し、Amazon API GatewayでAPIとして公開することでネット経由でAPIを活用できます。

例えばPythonには、Xの文字数を確認し投稿可能かどうか判断する「parse_tweet」というライブラリがあります。

このライブラリを使った関数を作って、APIとして公開しFunction Callingとして使えば、「X文字数チェックAPI」が作れてしまいます。

これをDifyのエージェントなどで使わせれば、文字数調整を自動で行うことができます。

ライブラリとして出ているのであれば、Difyのコードで実行すればよいじゃないかと思われるかもしれませんが、現バージョンのDifyはpip install がjinja2、httpx、requestsの3種しか対応していません。

なので、pip installが必要なものは現状のDifyでは実装できない状況です。ということもあり一旦APIにあげてしまい、そこから使おうという試みです。

この具体的な設定方法はまた別の機会で解説してみたいと思います。

ポイントは業務のタスク化

ここまでFunction Callingを見てきましたが、Difyが登場したことでノーコードで簡単にFunction Callingの機能(エージェント機能)を使えるようになりました。

これができるようになると、生成AIの活用範囲が大幅に広がり、様々な業務プロセスを大幅に自動化・効率化できる可能性があります。

その際重要となるのは、一つ一つの抽象的な業務を、いかに細かな具体的なタスクに落とし込むかということです。

そして細分化されたタスクの中から、定型的・データ処理的業務はAIに割り振り、人間はより 創造性、柔軟な判断力、深い洞察が必要なタスクなどに注力すべきでしょう。

生成AI時代は、Function CallingやDifyのようなツールを活用しながら、継続的に業務プロセスを最適化していくことが重要となってくると考えられます。

まとめ

  • 生成AIはプロンプトエンジニアリングだけでなくFunction Callingの活用も重要
  • Function CallingはAPIや関数を生成AIが自在に操る仕組み
  • タスクをプログラムに置き換えるプログラミング思考が重要



関連記事

広報・PRやプレスリリースまわりでお困りのことはございませんか?
多数の企業サポートを行ってきた当社が解決いたします。
お気軽にお問合せください。