クラスでVertex AIのファインチューニング済みコードチャットモデルにリクエストする

## 背景

最近、Vertex AIのモデルをファインチューニングする機会がありました。
回答精度を上げるために、条件を変えて学習させた複数のモデルを比較していました。
Vertex AIでは、各モデルにリクエストするためのPythonサンプルコードが提供されていますが、このコードは抽象化されておらず、各モデルの管理が難しいと感じました。

そこで、デプロイしたモデルごとにモデル情報を管理するクラスを作成すれば、モデルの比較が楽になると考えました。
今回は、Vertex AIのサンプルコードを参考にして、Pythonでクラスを作成してみました

## 前提条件
ファインチューニングしたモデルがデプロイされていることが前提です
また以下を実行し、`~/.config/gcloud/application_default_credentials.json`を生成する必要があります
```sh
gcloud auth application-default login
```
## リクエスト先のモデルを管理するクラス

```python:requester.py
import vertexai
from vertexai.language_models import CodeChatModel

class VertexAIRequester:

  # プロパティの初期化
  def __init__(self, project_id, location, base_model, model_id):
    self.project_id = project_id         # プロジェクトID
    self.location   = location           # リージョン名
    self.base_model = base_model         # ファインチューニング元のモデル名
    self.model_id   = model_id           # モデル固有のID

  # プロパティ内で指定されたモデルにAIにリクエストを送信する
  def request_to_ai(self, prompt, max_output_tokens=1000, temperature=0.9):
    # 関数内でリクエストに使用するプロパティが設定されていない場合はエラーを返す
    if (
         self.project_id == ""
      or self.location   == ""
      or self.base_model == ""
      or self.model_id   == ""
    ): return {"message": "not set propaty"}
    
    # モデルの初期化をし、チャットモデルを取得する
    vertexai.init(project=self.project_id, location=self.location)
    tuned_model_name = "projects/" + self.project_id + "/locations/" + self.location + "/models/" + self.model_id
    chat_model       = CodeChatModel.from_pretrained(self.base_model)
    chat_model       = chat_model.get_tuned_model(tuned_model_name)

    # 回答生成のパラメータを設定し、リクエストを送信
    parameters = {
      "max_output_tokens": max_output_tokens,
      "temperature": temperature
    }

    chat     = chat_model.start_chat()
    response = chat.send_message(prompt, **parameters)

    return response
```
コンストラクタとAIに問い合わせる関数のみで作られています
AIに問い合わせる関数はほとんどサンプルコードの流用ですが、リクエストを操作するのに最低限必要な値を変数として抜き出しています

## 使い方
### インスタンス化
事前に準備が必要な情報は以下の4つで、これらを使ってインスタンス化します
- project_id
  - GCPのプロジェクトのID
- base_model
  - ファインチューニングしたベースとなるモデル名
- location
  - `Model Registry >  使いたいモデル > バージョンの詳細` から`リージョン`の値をコピーする
- model_id
  - `Model Registry >  使いたいモデル > バージョンの詳細` にある`モデル ID`の値

### AIにリクエストを送る
`request_to_AI()`を使い、リクエストを送ることができます
AIにリクエストしたいプロンプト(文字列)を引数に入れて実行します
```python
インスタンス名.request_to_ai("hello")
```
AIの最大出力トークン数、温度はデフォルト値が設定してありますが、変えたい時は以下の引数を指定します

- max_output_tokens
  - 最大出力トークン数
  - デフォルトでは1000トークン
- temperature
  - 温度   
  - デフォルトでは0.9
  - 高いほど創造的な回答を生成する

## 使用例
```python
import os
from dotenv import load_dotenv
from requester import VertexAIRequester

# envファイルから環境変数を読み込む
def load_env():
  load_dotenv()
  aiconfig = {}
  aiconfig["PROJECT_ID"] = os.getenv("PROJECT_ID")
  aiconfig["LOCATION"]   = os.getenv("LOCATION")
  aiconfig["BASE_MODEL"] = os.getenv("BASE_MODEL")
  aiconfig["MODEL_ID"]   = os.getenv("MODEL_ID")

  return aiconfig

config = load_env()

# 先ほどのクラスをインスタンス化location, base_model, model_id
requester = VertexAIRequester(project_id = config["PROJECT_ID"], 
                              location   = config["LOCATION"], 
                              base_model = config["BASE_MODEL"], 
                              model_id   = config["MODEL_ID"])

# AIにリクエストを送信
prompt   = "hello"
response = requester.request_to_ai(prompt)

print(response.text)

```
VertexAIRequesterクラスのインスタンス化に必要な値を、`.env`ファイルから取ってくるようにしています
最大出力トークン数や温度の設定がしたい場合は、`requester.request_to_AI(prompt)`を以下のように呼び出してください
```python
# 最大出力トークン数や温度を指定する場合は、以下のように記述
response = requester.request_to_AI(prompt, max_output_tokens=400, temperature=0.8)
```
これを実行すると以下のように出力され、AIからプロンプトに対する回答を得ることができます
```
 Hi there! How can I help you today?
```

## 終わりに
今回はデプロイされたコードチャットモデルに対するリクエストができるクラスを作りました
コードチャットモデル、コード補完モデル、コード生成モデルでそれぞれ別に関数が用意されているので、限られたモデルへの実装になってしまいました
他のモデルを使う機会があれば、そちらでも作ってみようと思います