BigQueryからGeminiとClaudeを利用する

## 概要

この記事では、BigQueryMLを使用してGeminiおよびClaudeのリモートモデルを呼び出す方法を解説します。

2024年9月時点では、Claudeはus-east5またはeurope-west1のリージョンでのみ使用可能なため、本記事ではus-east5を前提に進めます。

## 利用するデータ

それぞれのモデルを用いてBigQuery Public DatasetにあるIMDbのデータに対して翻訳を行います。今回のデータはusリージョンにあるため、まずusリージョン内のデータセットに複製し、次にCPコマンドを使用してus-east5リージョンにクロスリージョンコピーします。

データセットについての詳細は、以下のリンクをご確認ください。

https://cloud.google.com/bigquery/public-data?hl=ja

以下のクエリでは、IMDbのテーブルから「スターウォーズ」に関連するレビューを10件抽出しています。

```sql
CREATE OR REPLACE TABLE
  `projectName.dataset_us.reviews_starwars` AS (
  SELECT
    *
  FROM
    `bigquery-public-data.imdb.reviews`
  WHERE
    REGEXP_CONTAINS(title, "Star Wars")
  LIMIT
    10 )
```

なお、Cloud Consoleではクロスリージョンコピーができないため、bqコマンドを使用してusリージョンからus-east5リージョンへデータをコピーします。

```shell
bq cp dataset_us.reviews_starwars dataset_us_east5.reviews_starwars
```


## Vertex AIの有効化

Vertex AI関連のAPIをまだ利用していない場合は、VetexAIのTOPページの[すべての推奨APIを有効化]を押下して有効化します。

![d962ea03-f976-4cb7-a983-38003701e986.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147689/0e0a9777-9007-c6d9-72e9-4fb8a83180b3.png)

## Claudeの有効化

Claudeを利用するには、個別に有効化する必要があります。Vertex AIの「モデルガーデン」から有効化を行ってください。

![スクリーンショット 2024-09-07 22.18.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147689/05b4b60b-6c25-59be-27dd-eab7f18522b5.png)

## 外部接続の追加
BigQuery Studioのエクスプローラペインから「追加」を押し、「外部データソースへの接続」を選択します。

接続タイプを「Vertex AIリモートモデル」に設定し、任意の接続IDを設定します。リージョンはClaudeが対応しているリージョンであるus-east5（コロンバス）を選択します。

![スクリーンショット 2024-09-08 0.27.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147689/36b66477-f88b-d8a5-4834-5fa8b002e431.png)


## サービスアカウントに権限付与
外部接続を追加すると、サービスアカウントIDが払い出されます。このサービスアカウントに対して、IAMで「Vertex AIユーザー」の権限を付与します。

![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3134373638392f32306665333337352d656530392d356435662d316238342d3336613530306164613763362e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147689/d0035fec-7c56-b88d-80f4-b8e1644aace4.png)


## モデルの追加

外部接続を作成した後、CREATE OR REPLACE MODEL文を使ってモデルを作成できます。

### Gemini Pro

```sql
CREATE OR REPLACE MODEL
`projectName.dataset_us_east5.gemini_pro`
REMOTE WITH CONNECTION `projectName.us-east5.vertex-ai`
OPTIONS (ENDPOINT = 'gemini-pro');
```


### Claude 3.5 Sonnet

```sql
CREATE OR REPLACE MODEL
`projectName.dataset_us_east5.claude_3-5`
REMOTE WITH CONNECTION `projectName.us-east5.vertex-ai`
OPTIONS (ENDPOINT = 'claude-3-5-sonnet');
```

※なお、Claudeはモデルのバージョン指定が推奨されているようです。

> You can use an Anthropic SDK or curl commands to send requests to the Vertex AI endpoint using the following model names:
> 
> - For Claude 3.5 Sonnet, use claude-3-5-sonnet@20240620.
> - For Claude 3 Opus, use claude-3-opus@20240229.
> - For Claude 3 Haiku, use claude-3-haiku@20240307.
> - For Claude 3 Sonnet, use claude-3-sonnet@20240229.
>
> We recommend using the Anthropic Claude model versions that include a suffix that starts with an @ symbol (such as claude-3-5-sonnet@20240620 or claude-3-haiku@20240307) because of the possible differences between model versions. If you don't specify a model version, the latest version is always used, which can inadvertently affect your workflows when a model version changes.
>
> https://cloud.google.com/vertex-ai/generative-ai/docs/partner-models/use-claude?hl=en#claude-haiku



ここまで作業を進めるとエクスプローラペインは以下のような状態になります。外部接続にVertex AIとの接続が表示され、BigQueryで使用可能なモデルは「モデル」配下に`gemini_pro`と`claude_3-5`が配置されます。

![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3134373638392f66376236356266632d643862662d353936302d376630362d6635313562386666346163342e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147689/6effe431-81c9-39dd-7629-28ba6ae46175.png)


ここまで問題なく進んだら、`ML.GENERATE_TEXT`文を含んだクエリを実行します。今回はIMDbのレビューを翻訳するため、以下のクエリを使用します。

## Gemini Proで翻訳

```sql
DECLARE
  prompt STRING DEFAULT "";
SET
  prompt = "次の文章を日本語に翻訳してください。翻訳結果だけ出力してください。改行コードは除去してください。:";
SELECT
  review,
  STRING(_ml_generate_text_result_candidates.content.parts[0].text) AS translations
FROM
  ML.GENERATE_TEXT( MODEL `projectName.dataset_us_east5.gemini_pro`,
    (
    SELECT
      SUBSTR(review,1,400) as review,
      CONCAT(prompt,SUBSTR(review,1,400)) AS prompt
    FROM
      `projectName.dataset_us_east5.reviews_starwars`
    LIMIT
      3 ),
    STRUCT( 500 AS max_output_tokens) )
LEFT JOIN
  UNNEST(JSON_EXTRACT_ARRAY(ml_generate_text_result.candidates)) AS _ml_generate_text_result_candidates
```

### 返却結果
| review | translations |
| --- | --- |
| I couldn't have been more thrilled; Just eight years old back in 1983, I was going to see a Star Wars movie at the theater! The best day of my life was about to happen. To that time, my only Star Wars experience had been a few HBO showings of Star Wars. I hadn't even seen The Empire Strikes Back yet.And boy, did that day deliver for my less critical eyes. Jabba. Big Rebel spaceships. The Emporer.  | 1983年、8歳の私は、映画館でスターウォーズを観ることに興奮していました。人生最高の日はまさに訪れようとしていました。それまで、私のスターウォーズ体験は、HBOで何度か放送されたスターウォーズだけでした。帝国の逆襲さえまだ見ていませんでした。そして、その日は私の批判的な目には届きませんでした。ジャバ。大きな反乱軍の宇宙船。皇帝。 |
| I very nearly walked out, but I'd paid my money, and my nearly-as-disgusted friend wanted to hold out. After the endearing, wide-eyed innocence of "A New Hope" and the thrilling sophistication of "The Empire Strikes Back," I remember awaiting "Return of the Jedi" with almost aching anticipation. But from the opening scene of this insultingly commercial sewage, I was bitterly disappointed, and enra | 「私はほとんど席を立とうとしましたが、お金を払っていたので、ほとんど同じように嫌悪感を抱いていた友人は我慢したいと言いました。愛らしく、目を大きく見開いた「新たなる希望」と、スリリングな洗練された「帝国の逆襲」の後、「ジェダイの帰還」をほとんど痛むほどの期待感を持って待っていたことを覚えています。しかし、この侮辱的な商業下水道のオープニングシーンから、私はひどく失望し、そして」 |
| Well, I finally saw it. I didn't go when it first came out because, well, frankly, I was afraid. Afraid of how bad it might be, or how disappointing. While not as bad as Menace, and better than Clones, it wasn't particularly memorable, or satisfying.I was 11 years old when I saw Star Wars. I still remember sitting in the theater. From the opening crawl to the final credits it was a movie experienc | スターウォーズを初めて観たのは11歳の時でした。映画館で観た時のことは今でも覚えています。オープニングクロールからエンドクレジットまで、映画体験そのものでした。 |


## Claude 3.5 Sonnetで翻訳

```sql
DECLARE
  prompt STRING DEFAULT "";
SET
  prompt = "次の文章を日本語に翻訳してください。翻訳結果だけ出力してください。改行コードは除去してください。:";
SELECT
  review,
  ml_generate_text_result.content[0].text AS translations
FROM
  ML.GENERATE_TEXT( MODEL `projectName.dataset_us_east5.claude_3-5`,
    (
    SELECT
      SUBSTR(review,1,400) as review,
      CONCAT(prompt,SUBSTR(review,1,400)) AS prompt
    FROM
      `projectName.dataset_us_east5.reviews_starwars`
    LIMIT
      3 ),
    STRUCT( 500 AS max_output_tokens) )
```

※Claudeでは稀にエラーが発生することがあり、まだ安定していないかもしれません。

### 返却結果

| review | translations |
| --- | --- |
| I couldn't have been more thrilled; Just eight years old back in 1983, I was going to see a Star Wars movie at the theater! The best day of my life was about to happen. To that time, my only Star Wars experience had been a few HBO showings of Star Wars. I hadn't even seen The Empire Strikes Back yet.And boy, did that day deliver for my less critical eyes. Jabba. Big Rebel spaceships. The Emporer.  | "これ以上ないほど興奮していました。1983年当時8歳だった私は、映画館でスター・ウォーズを観ることになったのです！人生最高の日が訪れようとしていました。それまでの私のスター・ウォーズ体験は、HBOで数回放送された「スター・ウォーズ」を観ただけでした。「帝国の逆襲」さえまだ観ていませんでした。そして、その日は私のあまり批判的でない目には素晴らしいものでした。ジャバ。大きな反乱軍の宇宙船。皇帝。" |
| I very nearly walked out, but I'd paid my money, and my nearly-as-disgusted friend wanted to hold out. After the endearing, wide-eyed innocence of "A New Hope" and the thrilling sophistication of "The Empire Strikes Back," I remember awaiting "Return of the Jedi" with almost aching anticipation. But from the opening scene of this insultingly commercial sewage, I was bitterly disappointed, and enra | "ほとんど途中で退出しそうになりましたが、お金を払っていたし、私とほぼ同じくらい嫌気がさしていた友人が最後まで見たがっていました。「新たなる希望」の愛らしい、無邪気な純真さと、「帝国の逆襲」のスリリングな洗練さの後、私は「ジェダイの帰還」を胸を躍らせて待ち望んでいました。しかし、この侮辱的なほど商業主義的なクズ映画の冒頭シーンから、私は深く失望し、怒り" |
| Well, I finally saw it. I didn't go when it first came out because, well, frankly, I was afraid. Afraid of how bad it might be, or how disappointing. While not as bad as Menace, and better than Clones, it wasn't particularly memorable, or satisfying.I was 11 years old when I saw Star Wars. I still remember sitting in the theater. From the opening crawl to the final credits it was a movie experienc | "まあ、ついに見ました。最初に公開されたときは行きませんでした。正直言って、怖かったからです。どれほどひどいものか、あるいはどれほど失望させられるかが怖かったのです。メナスほどひどくはなく、クローンズよりは良かったものの、特に記憶に残るものでも、満足できるものでもありませんでした。スター・ウォーズを見たとき、私は11歳でした。劇場で座っていたことを今でも覚えています。オープニングのクロールから最後のクレジットまで、それは映画体験でした。" |

## エラーメッセージ

一連の作業で発生し得るエラーメッセージを以下にまとめておきます。

### サービスアカウントの権限不足

> bqcx-xxxxxxx@gcp-sa-bigquery-condel.iam.gserviceaccount.com does not have the permission to access or use the endpoint. Please grant the Vertex AI user role to the service account following https://cloud.google.com/bigquery/docs/generate-text-tutorial#grant-permissions . If issue persists, contact bqml-feedback@google.com for help.

→サービスアカウントに「Vertex AIユーザー」のロールを追加してください。

### ClaudeのAPIが無効

> Project `xxxxxxxxx` is not allowed to use Publisher Model `projects/axxxxxx/locations/us-central1/publishers/anthropic/models/claude-3-5-sonnet`. Please reach out to Vertex AI to allowlist your project.

→Vertex AI側でClaudeを有効にしてください。

### BQMLが利用できない

> Resource Using BQML related functionalities is disallowed in STANDARD edition. See https://cloud.google.com/bigquery/docs/editions-intro for more information. did not meet condition at [5:1]

→ Editions Standardを利用しているため、BQMLが利用できない状態。Editions Enterpriseに切り替えるか、Editionを使用していないGCPプロジェクトで実行してください。

## おわりに

BigQueryMLを使ってGeminiやClaudeのリモートモデルを呼び出す方法をざっくり紹介しました。

外部接続のVertexAIを経由することでBigQueryテーブルに対してもSQLで生成AIが利用できようになっているので、積極的に活用していきたいですね。

## 追記
- 費用について
  - モデルごとに費用が大きく異なるため、必ず利用するモデルの費用を確認・シミュレーションの上 ご利用ください。
    - 2024年9月現在、テキスト入力においてGemini 1.5 ProはGemini 1.5 Flashの66倍の料金となっています。
- 処理能力について
  - 10,000レコードのテーブルのカラム（すべて40文字以内のSTRING）に対してGemini-Flashで推論を行いましたが、6時間かかり、BigQueryの実行時間上限に到達してしまいました（処理がキャンセルされました）。まだパフォーマンス面で課題がありそうです。
  - なぜかnullで返ってくるレコードも多くあります。