スクエニ ITエンジニア ブログ

実行する AI

もはや最近は一日家で過ごしていると、人間よりも AI と会話しているほうが多い日もあるかもしれません。外に出るにも危険な日差しで、精神の健康を保つのに工夫が必要な日々と感じます 😓

さてかなり以前から映画などでは、人間をサポートする AI が膨大な知識と優れた判断力を使って素早く解決案を提示し、主人公を助けるというシーンをよく見ます。そこで主人公はその提案に対して「よし、それで頼む」とさらっと言うわけですが、AI が実行までしちゃうと主人公がいなくてもよくなってしまうので、映画では何かしら問題が起きて人間たちが活躍し、「やっぱり手動がいちばんや!」となるオチが多いです。
ともあれもし AI に実行までしてもらえるんなら、特に現実世界では、こんな楽なことは無いです。

日々会話している AI。会話で応答を得るだけでなく、実際に行動をする仕組みを追加することができます。今回はそれを試して映画の世界に一歩近づきましょう。

Vertex AI Function Calling

Vertex AI には、AI の判断に基づいて (AI model の) 外部の機能を呼び出す機能があります。
Generative AI on Vertex AI > Function calling

こちらに沿って試してみます。

事前準備

いつもながら Visual Studio CodeDev Container 機能を使用します。他の環境をご利用の方はお手数ですが、以下の構築手順を参考にして環境構築してください。

このブログでは他にも Dev Container に関する記事 があります。Dev Container って何? と思われたかたにもご覧いただけたら幸いです。

host 側

Vertex AI の利用に必要な Google Cloud 認証は、host 側の application default authentictation を使用します。host 側で以下を行っておきます。(host 側に Cloud SDK の install が必要です)

gcloud auth application-default login

Dev Container 側

VS Code を開いて .devcontainer/devcontainer.json を作成します。

{
	"name": "python",
	"image": "mcr.microsoft.com/devcontainers/python:3.12-bookworm",
	"mounts": [
		"source=${localEnv:HOME}/.config/gcloud/application_default_credentials.json,target=/home/vscode/.gcloud/application_default_credentials.json,type=bind,consistency=cached"
	],
	"containerEnv": {
		"GOOGLE_APPLICATION_CREDENTIALS": "/home/vscode/.gcloud/application_default_credentials.json"
	}
}

今回は container image として mcr.microsoft.com/devcontainers/python:3.12-bookworm を使用しました。記事作成時点では Python 3.12.4 / Debian bookworm (12.6) でした。

host 側で Google Cloud 認証をした結果生成された ~/.config/gcloud/application_default_credentials.json を container 内に mount してその path を環境変数 GOOGLE_APPLICATION_CREDENTIALS に設定し、Python program から使用できるようにしています。

Dev Container を起動したら、container 内の Python に vertexai を install しておきます。

pip install vertexai

動かしてみる

冒頭に示したページにある Chat examples の Python 版を実行してみます。 お手元で、 PROJECT_ID 部分をご自身の project ID に書き換えておいてください。

ということで実行してみます。

$ python main.py
WARNING: All log messages before absl::InitializeLog() is called are written to STDERR
I0000 00:00:1722573305.570986    3444 check_gcp_environment.cc:61] BIOS data file does not exist or cannot be opened.
name: "get_product_sku"
args {
  fields {
    key: "product_name"
    value {
      string_value: "Pixel 8 Pro"
    }
  }
}

Yes, we have the Pixel 8 Pro in stock. 

name: "get_store_location"
args {
  fields {
    key: "location"
    value {
      string_value: "Mountain View, CA"
    }
  }
}

Yes, there is a store at 2000 N Shoreline Blvd, Mountain View, CA 94043, US. 

正常に動作したようです。コード内容を見るとわかりますが、二度ある chat のやり取りの過程で、機能 (function) の呼び出しを行っており、その結果を用いて回答を生成しています。

先頭に WARNING が出てしまっていますが、原因などについては後述します。

見てみる

具体的にどのように呼び出す対象の機能を実装しているかを見てみます。以下はサンプルコードからの抜粋です。

# Check the function name that the model responded with, and make an API call to an external system
if function_call.name == get_product_sku:
    # Extract the arguments to use in your API call
    product_name = function_call.args["product_name"]  # noqa: F841

    # Here you can use your preferred method to make an API request to retrieve the product SKU, as in:
    # api_response = requests.post(product_api_url, data={"product_name": product_name})

    # In this example, we'll use synthetic data to simulate a response payload from an external API
    api_response = {"sku": "GA04834-US", "in_stock": "yes"}

get_product_sku という function は、結果として {"sku": "GA04834-US", "in_stock": "yes"} という response を返します。実際は database などを検索して回答を生成すべきですが、ここではサンプルのため固定の response になっています。

AI は chat の input によってこの function を呼び出すかを判断します。その際、おなじくコーディングされている FunctionDeclaration の内容を判断基準としています。賢いですね。

というわけで、結局この function 内にやりたいことを書いてしまえば、AI の判断によって何かをすることができそうです。今回は在庫を確認する機能と定義しているのでそれ以上のことを実装するのはおかしな (意図に反した) ことになってしまいますが、単純にはベルを鳴らすとか部屋の明かりをつけるといった例が考えられます。

試行錯誤

ここからはサンプルコードを すこし単純化したもの を使います。
主な変更点は、元は二度あった chat のやりとりをひとつにしたくらいです。
コードのライセンスは 元のサイト 末尾の記述に従い Apache 2.0 License を継承します。

いくらか option・parameter の類があるので、すこしいじって結果の違いを見てみましょう。

ToolConfig.FunctionCallingConfig.Mode を設定してみる

GenerativeModel の生成で ToolConfig の記述を追加してみます。 Mode.NONE を指定し、function calling を禁止します。

model = GenerativeModel(
    model_name="gemini-1.5-flash-001",
    generation_config=GenerationConfig(temperature=0.0),
    tools=[retail_tool],
    tool_config=ToolConfig(
        function_calling_config=ToolConfig.FunctionCallingConfig(
            mode=ToolConfig.FunctionCallingConfig.Mode.NONE,
    ))
)
  • default では AUTO (AI が判断) の動作をする
  • NONE (function call を行わない) にすると response に function_calls が入ってこない。(以下参照)
  • ANY (function call を強制する) には Gemini 1.5 Pro が必要。(Flash では利用不可)
    • このとき allowed_function_names が指定できるようになる

変更前のサンプルコードでは、function call が行われる前提になっているため、以下のようなエラーになります。

Traceback (most recent call last):
  File "/workspaces/vxai-fcalling/main.py", line 69, in <module>
    function_call = response.candidates[0].function_calls[0]
                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^
IndexError: list index out of range

上記した対応版を使うと、どのような返答があったかを確認することができます。

[text: “I am an AI and do not have access to real-time information, including product inventory. To check if the Pixel 8 Pro is in stock, I recommend visiting the Google Store website or contacting a Google Store representative. \n” ]

賢い返事…!

用意した function と関係ない質問をしてみる

在庫検索の機能 get_product_sku しか無いのに What is the weather like in Boston? と天気を聞いてみます。

結果は function call を禁止したときとおなじで、(AI が function call をしないことを選択し、) response.candidates[0].function_calls が empty になります。これを判定してエラー反応するようにしておくと、その時の返答が見えるようになりました。

[text: “I am sorry, I do not have access to real-time information, including weather data. \n” ]

ちょっとかわいそう。

temperature parameter を変えてみる

元のサンプルでは deterministic (毎回同じ結果) な動作となるように 0.0 を指定しています。

Generative AI on Vertex AI > Documentation > API reference > Generate content with the Gemini API より

Range for gemini-1.5-flash: 0.0 - 2.0 (default: 1.0)

最大値は 2.0 のようなので、それに変更してみましょう。

何度か在庫を聞いてみます。

Yes, we do. The SKU is GA04834-US. 
Yes, we have the Pixel 8 Pro in stock. 

天気 (機能と無関係) を聞いてみます。

I am sorry, I do not have access to real-time information, including weather.
I am sorry, I do not have access to real-time information, including weather data.

結果が微妙に変わることがわかります。今回は確認できていませんが、微妙な質問をすると function call をする・しないの結果も変わるのでしょう。( mode=ToolConfig.FunctionCallingConfig.Mode.AUTO の場合)

日本語で聞いてみる

response = chat.send_message("Pixel 8 の在庫はありますか?")
はい、Pixel 8 は在庫があります。

問題ないようです。賢い 🤗

費用

費用について確認しておきます。

Vertex AI Pricing

Function Calling に対応している model の選択肢の中では Gemini 1.5 Flash がいちばん安いようです。性能が不足するなら Gemini 1.5 Pro を選択することになりますが、text input で比較すると 66.6 倍、output で 50 倍費用が高くなります。

入出力をそれぞれざっくり 1k characters として、Gemini 1.5 Flash で試算してみます。input $0.00001875 / 1k characters、output $0.000075 / 1k characters で、一度の実行で仮に in/out 計 2k chars とすると、一回あたり ((0.00001875 + 0.000075) * 2 * .5) = 0.00009375 USD であり、$/円 160 とすると 0.015 円。100 回やって 1.5円。なかなか安いのではないでしょうか。

10 回程度実行して、数日後 billing report から Vertex AI の料金を確認してみました。

SKU Usage Cost
Gemini 1.5 Flash Text Output - Predictions 1,357 Characters $0.00
Gemini 1.5 Flash Text Input - Predictions 3,285 Characters $0.00

これくらいだとタダですね。参考にならなくてごめんなさい。
使い方によっては気にしなくてもいいくらいということがわかりましたが、今後料金体系変更等の可能性などもありますので、ご利用の際はしっかりご確認ください。

感想

あらかじめ実行する function を実装・定義しておく必要はありますが、それを AI の反応として実行できるのは応用範囲が広く、夢が広がる機能です。想像力が貧困なので、「電気つけてー」と言って電気をつけるくらいの応用しか思いつかないのが悲しい限りです 😢 とはいえ何か危険な機能を実装したときに、天気を聞いただけで間違って実行されてしまってもこわいので皆様もお気をつけください。

参考

当 blog では似た記事として以下もありますのでぜひご参照ください。

実行時の警告について確認しておく

実行時に二点、警告・情報が出ていました。内容を確認しておきます。

  • 一点目
WARNING: All log messages before absl::InitializeLog() is called are written to STDERR

GitHub issue | google-gemini/generative-ai-python | WARNING: All log messages before absl::InitializeLog() is called are written to STDERR #486 によると、grpcio の問題であり、1.60.1 にしたらなおったとのこと。

pip install grpcio==1.60.1

これでたしかに出なくなりました。grpcio 側でそのうち対応が入るのかもしれません。いずれにせよ動作には問題が無さそうなのであまり気にしないことにします。

  • 二点目
I0000 00:00:1722573305.570986    3444 check_gcp_environment.cc:61] BIOS data file does not exist or cannot be opened.

GitHub issue | googleapis/google-cloud-cpp | check_gcp_environment.cc:60] BIOS data file cannot be opened. #6300 によると、GCE などの Google Cloud 環境での実行のときに credential を自動取得するためにそれを自動判定しようとする段階で特定の file を開くのに失敗した。ということのようです。

このあたり を見ると、 /sys/class/dmi/id/product_nameGoogle Compute Engine と入っているか等を確認する処理と思われます。

今回は Docker での実行でありそれらの判定は利用できないのでこの出力がありますが、credential は $GOOGLE_APPLICATION_CREDENTIALS で与えることで問題なく動作しますのでやはり気にしないことにします。

この記事を書いた人

記事一覧
SQUARE ENIXでは一緒に働く仲間を募集しています!
興味をお持ちいただけたら、ぜひ採用情報ページもご覧下さい!