🤖

Vertexの認証でハマったお話

に公開

Google AI StudioとVertex AI Studioの関係

Googleって、Google AI StudioやGoogle Cloudなどあって、どれどうなんや?ってことなんですが...大枠は以下のようなイメージです。

Google AI Studio
- chat, stream, buildなど...

Google Cloud
- Vertex AI Studio, Vertex AI

ここでややこしいのが、Google AI StudioとVertex AI Studioの関係だ。

ほぼUIにているし、機能も似ているので、あれ?今自分どっちだっけ?とたまに混乱する。
ただ、Google AI Studioは独立したサービスしているものだし、Vertex AI StudioはGCPの中の1つのサービスとなっている。

当然、認証もGoogle AI Studioと、Vertex AI Studio(GCP)は別である。

Vertex AIでアプリを作った時にハマったこと

①google.genai.types.Part.from_uriの不具合?

Vertex AI を用いて、Cloud Storageの情報をRAGにして、Geminiを走らせるコードを作成

以下、Vertex AIから生成されたファイルを実行
※YOUR_BUCKET_NAME,YOUR_FILE_NAMEは各環境に置き換えてください。

main.py

from google import genai
from google.genai import types
import base64
import os

def generate():
  client = genai.Client(
      vertexai=True,
      api_key=os.environ.get("GOOGLE_CLOUD_API_KEY"),
  )

  document1 = types.Part.from_uri(
      file_uri="gs://{YOUR_BUCKET_NAME}/{YOUR_FILE_NAME}",
      mime_type="text/plain",
  )

  model = "gemini-2.5-flash-lite"
  contents = [
    types.Content(
      role="user",
      parts=[
        document1,
        types.Part(
             text="要約してください",
        )
      ]
    )
  ]

  generate_content_config = types.GenerateContentConfig(
    temperature = 1,
    top_p = 0.95,
    max_output_tokens = 65535,
    safety_settings = [types.SafetySetting(
      category="HARM_CATEGORY_HATE_SPEECH",
      threshold="OFF"
    ),types.SafetySetting(
      category="HARM_CATEGORY_DANGEROUS_CONTENT",
      threshold="OFF"
    ),types.SafetySetting(
      category="HARM_CATEGORY_SEXUALLY_EXPLICIT",
      threshold="OFF"
    ),types.SafetySetting(
      category="HARM_CATEGORY_HARASSMENT",
      threshold="OFF"
    )],
    thinking_config=types.ThinkingConfig(
      thinking_budget=0,
    ),
  )

  for chunk in client.models.generate_content_stream(
    model = model,
    contents = contents,
    config = generate_content_config,
    ):
    print(chunk.text, end="")

generate()

しかし、以下の関数で400エラーが出る。

  document1 = types.Part.from_uri(
      file_uri="gs://{YOUR_BUCKET_NAME}/{YOUR_FILE_NAME}",
      mime_type="text/plain",
  )

どうやら、この関数がうまくいかないようだ。
Googleの人にも聞いたが、なぜか使えないとのこと...
他の方法で試すように案内される。
結果的に断念。

②google.genaiの認証

上記のコードでうまくいかなかったので、しょうがないので、google cloudを別の方法で取得し、それをRAGでtextで埋め込むように以下のように修正

※GOOGLE_APPLICATION_CREDENTIALSの環境変数を設定

main.py

from google import genai
from google.genai import types
from google.cloud import storage


def read_gcs_file(bucket_name, source_blob_name):
    """Google Cloud Storageのファイルの内容を読み込みます。"""
    storage_client = storage.Client()

    # バケットを取得
    bucket = storage_client.bucket(bucket_name)

    # blob(ファイル)を取得
    blob = bucket.blob(source_blob_name)

    # blobの内容を文字列としてダウンロード
    contents = blob.download_as_text()

    return contents


def generate():
    client = genai.Client(
          vertexai=True,
          api_key=os.environ.get("GOOGLE_CLOUD_API_KEY"),
    )

    model = "gemini-2.5-flash"
    file_contents = read_gcs_file(bucket_name, source_blob_name)

    contents = [
        types.Content(
            role="user",
            parts=[
                types.Part(text=file_contents),
                types.Part(
                     text="要約してください",
                )
            ],
        ),
    ]

    generate_content_config = types.GenerateContentConfig(
        temperature=1,
        top_p=0.95,
        max_output_tokens=8192,
        safety_settings=[
            types.SafetySetting(
                category="HARM_CATEGORY_HATE_SPEECH", threshold="BLOCK_NONE"
            ),
            types.SafetySetting(
                category="HARM_CATEGORY_DANGEROUS_CONTENT", threshold="BLOCK_NONE"
            ),
            types.SafetySetting(
                category="HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold="BLOCK_NONE"
            ),
            types.SafetySetting(
                category="HARM_CATEGORY_HARASSMENT", threshold="BLOCK_NONE"
            ),
        ],
    )
 
    for chunk in client.models.generate_content_stream(
        model = model,
        contents = contents,
        config = generate_content_config,
        ):
        print(chunk.text, end="")

generate()

ここで、401の認証エラーが起きた。
ここで、押さえておきたいポイント!

google.genaiの立ち位置だ!
google.cloudは言わずもがなGCPなのですが、google.genaiが厄介でした。

    client = genai.Client(
          vertexai=True,
          api_key=os.environ.get("GOOGLE_CLOUD_API_KEY"),
    )

こちら、vertexai=Trueのパラメータによって、認証が変わる?ようなのだ。(正しい言い方ではないです)

vertexaiがTrueだとgoogle cloudの認証情報でやる必要があるが、そうでない場合は、google ai studioのapi keyでできちゃうとのこと。

③google.genaiとgoogle.cloudの認証

google cloudのvertexaiとcooud storageの権限をつけたサービスアカウントで認証すれば良いでしょう?ということで、GOOGLE_APPLICATION_CREDENTIALSを設定しました。

しかし、それでも認証エラーが出てしまった。
cloud storageの中身は取れるが、genaiの認証がおかしい。。。

gemini cliでデバッグして最終的に出た答えがこれだ。

※GCP_PROJECT_ID, YOUR_REGIONは置き換えてください。

main.py

from google import genai
from google.genai import types
from google.cloud import storage


def read_gcs_file(bucket_name, source_blob_name):
    """Google Cloud Storageのファイルの内容を読み込みます。"""
    storage_client = storage.Client()

    # バケットを取得
    bucket = storage_client.bucket(bucket_name)

    # blob(ファイル)を取得
    blob = bucket.blob(source_blob_name)

    # blobの内容を文字列としてダウンロード
    contents = blob.download_as_text()

    return contents


def generate():
    client = genai.Client(
          vertexai=True,
          project="{GCP_PROJECT_ID}",
          location="{YOUR_REGION}",
    )

    model = "gemini-2.5-flash"
    file_contents = read_gcs_file(bucket_name, source_blob_name)

    contents = [
        types.Content(
            role="user",
            parts=[
                types.Part(text=file_contents),
                types.Part(
                     text="要約してください",
                )
            ],
        ),
    ]

    generate_content_config = types.GenerateContentConfig(
        temperature=1,
        top_p=0.95,
        max_output_tokens=8192,
        safety_settings=[
            types.SafetySetting(
                category="HARM_CATEGORY_HATE_SPEECH", threshold="BLOCK_NONE"
            ),
            types.SafetySetting(
                category="HARM_CATEGORY_DANGEROUS_CONTENT", threshold="BLOCK_NONE"
            ),
            types.SafetySetting(
                category="HARM_CATEGORY_SEXUALLY_EXPLICIT", threshold="BLOCK_NONE"
            ),
            types.SafetySetting(
                category="HARM_CATEGORY_HARASSMENT", threshold="BLOCK_NONE"
            ),
        ],
    )
 
    for chunk in client.models.generate_content_stream(
        model = model,
        contents = contents,
        config = generate_content_config,
        ):
        print(chunk.text, end="")

generate()

結論、google.genaiとgoogle.cloudの認証が違ったのだ。
認証の具体の何が違ったのかまではわからなかったが、環境変数設定していても、
google.genaiとgoogle.cloudの認証が異なるものになっている可能性があることを知っておかねばならない。

いろいろ罠がありましたが、無事、アプリは動くことができました!
まだスッキリとまでいかないので。モヤモヤが残りますが、誰か同じことで困る人がいればと思い、書き残しておきます。

Discussion