GPT Assistantsに日本語フォントを使わせる OpenAI API Code Interpreter コードインタープリター

# 新APIで登場したGPTのAssistants
2023年11月にOpenAIのAPIがリニューアルされAssistantsという機能が追加されました。

今まではGPTのAPIを利用する際にはモデル名（GPT-3.5など）、インストラクション（「英語教師になってください」など）を指定する必要があったのですが、これらをまとめて**Assistant**として登録できるようになり、そのアシスタントのIDがあればいちいちこのような指定が不要となりました。

![Snapshot_23.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/d4c630ba-b60a-ee77-7161-0c0f7cdddf45.png)
※OpenAIマイページ"Assistants"画面

まさに **「GPTはあなたのアシスタントですよ」** とOpenAIは言わせたいようです。

:::note warn
Assistants APIはβ版です。今後記述方法は変更がありそうです。
:::

# Assistantにファイルを渡す
Assistantにはファイルを持たせることができます。

最初は「なんでファイルを？」と思ったのですが、よく使うコード表であったり利用する可能性高いデータを持たせておけるので、いちいち毎回アップして渡す必要がなくなります。

**「秘書に資料を持たせる。」** みたいなイメージで使うことができるようです。
<br>
渡し方は2つあり、1つは上記Assistants画面のFilesから追加

![Snapshot_24.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/940dc715-3138-607b-f02e-af850c904ad1.png)

またはAPIを利用してアップ（※後述）

<br>
アップできるファイル形式はtxtなどはもちろんdocxなども渡すことができます。
https://platform.openai.com/docs/assistants/tools/supported-files



# Assistantにフォントファイルを渡す
で、今回の目的は日本語フォントファイル（拡張子.ttf）を使わせることです。上記の方法で渡せばよいのですが、現在サポート対象外で.ttfのファイルは渡すことができません。

そもそも今更ですが、なぜフォントファイルを渡したいかというとコードインタープリターを使ってグラフを描いてもらうときタイトルと軸が日本語の際に文字化けしてしまうからです。

![a8ea56f6-b398-46c6-91ec-0664428f14cc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/62e4be22-f970-f953-f386-eb72a8156850.png)
<br>
これはChatGPT Plus（月20ドルの有料版）でも起こっていた問題で、日本語のフォントファイルがGPTのPython環境にないことが原因のようです。なので正しいフォントファイルさえあればこの文字化けが防げるようです。（GPTがPythonのmatplotlibでグラフを描く際に日本語フォントが使えればよい）
<br>
で、戻りましてどうしても.ttfのファイルが渡したいので**ZIPにして渡してあげます。**（ZIPで渡せるなら.ttfでも渡せるようにしろ！って思っちゃいますが現在の仕様なので仕方ありません）

フォントはなんでもよさそうですが今回はGoogleのフォントNoto Sans JPを使います。（ダウンロードは[Download family]から）
https://fonts.google.com/noto/specimen/Noto+Sans+JP

このうちのなんでもいいのですがNotoSansJP-Boldを渡してみます。NotoSansJP-Bold.ttfをzip化しNotoSansJP-Bold.zipとしてアップ。APIを利用してみます。
<br>
```:OpenAIインストール
!pip install openai
```
```Python:OpenAI接続
from openai import OpenAI
import os

# APKIキーのセットとクライアント接続
os.environ["OPENAI_API_KEY"] = 'sk-*********************'

client = OpenAI()
```
```Python:ファイルのアップ
file_response = client.files.create(
    purpose="assistants",
    file=open("NotoSansJP-Bold.zip","rb"),
)

# IDの取得
file_id = file_response.id

print(file_id)
```
```
file-eTKaOgE2EY3xPSq9L1Cswzgo
```

<br>
次にこのファイルをアシスタントに渡します。アシスタントは新しく作成した方が早いですが今回は既存のアシスタントに渡すことにします。

※新規のアシスタント作成方法はこちらから
https://qiita.com/nekoniii3/items/7128e1d0cec06e3ff6a4

```Python:アシスタントへファイルを渡す
assistant = client.beta.assistants.update(
    assistant_id="asst_KLliLgmfXBEVigIvYV65bDnD"  # 既存のアシスタントID
    ,file_ids=[file_id]  # 先ほどのファイルID "file-eTKaOgE2EY3xPSq9L1Cswzgo"
)
```

# アシスタントへフォントの指示
あとはアシスタントにフォントファイルを使ってもらうだけです。

ただグラフなどのメインの指示とフォントの指示を同時に行うと、失敗する可能性が高いので、まずフォントの解凍の指示のみ行うことをおすすめします。
※回答取得までは[前々回の記事](https://qiita.com/nekoniii3/items/7128e1d0cec06e3ff6a4)と同じです

```Python:①スレッドの作成
thread = client.beta.threads.create()
```

```Python:②message作成（フォントの取得）
prompt = "まずNotoSansJP-Bold.zipを解凍してNotoSansJP-Bold.ttfを取得して下さい。"

message = client.beta.threads.messages.create(
    thread_id=thread.id,
    role="user",
    content=prompt
)
```

```Python:③RUN作成
assistant_id = "asst_KLliLgmfXBEVigIvYV65bDnD"

run = client.beta.threads.runs.create(
  thread_id=thread.id,
  assistant_id=assistant_id
)
```

```Python:④RUNの実行状態を受け取る
run = client.beta.threads.runs.retrieve(
  thread_id=thread.id,
  run_id=run.id
)

print(run.status)
```

```Python:⑤message受け取り
messages = client.beta.threads.messages.list(
  thread_id=thread.id,
  order = "asc"
)
```

```Python:⑥message表示
for msg in messages:
    print(msg.role + "：" + msg.content[0].text.value)
```
```
user：まずNotoSansJP-Bold.zipを解凍してNotoSansJP-Bold.ttfを取得して下さい。
assistant：NotoSansJP-Bold.zipファイルからNotoSansJP-Bold.ttfファイルを取得しました。ファイルの準備ができましたので、ご質問があればお知らせください。
```

正常に解凍してフォントファイルの取得ができたようです。
あとはグラフを描いてもらう際にNotoSansJP-Bold.ttfを使ってもらうだけです。
<br>

```Python:データファイルのアップ
file_response = client.files.create(
    purpose="assistants",
    file=open("HIKAKIN再生回数推移.csv","rb"),
)

file_id = file_response.id
```
```Python:message作成（グラフ作成の指示）
prompt = "次のファイルはある人気YouTuberの総再生回数のデータです。こちらを棒グラフにして下さい。\
タイトル・各軸名はNotoSansJP-Bold.ttfを使って日本語にして下さい。縦軸の単位は万にしてください。"

message = client.beta.threads.messages.create(
    thread_id=thread.id,
    role="user",
    content=prompt,
    file_ids=[file_id]
)
```
③RUN作成～⑤message受け取りはフォントの指示の時と同じコードを実行。

```Python:messages表示（image_fileのIDも取得）
for msg in messages:

    print(msg.role + "：")

    if msg.role == "assistant":

        for content in msg.content:

            image_fileid = ""
            assist_msg = ""

            cont_dict = content.model_dump()  # 辞書型に変換

            if cont_dict.get("image_file") is not None:

                image_fileid = cont_dict.get("image_file").get("file_id")

                print(f"ファイルID:{image_fileid}")

            if cont_dict.get("text") is not None:

                assist_msg = cont_dict.get("text").get("value")

                print(assist_msg)

    else:

        print(msg.content[0].text.value)

```
```
user：
次のファイルはある人気YouTuberの年別の総再生回数のデータです。こちらを棒グラフにして下さい。タイトル・各軸名はNotoSansJP-Bold.ttfを使って日本語にして下さい。縦軸の単位は万にしてください。
assistant：
ファイルID:file-ufUPttZASrhmPITuXKrjZ3HF
上記が、年別の総再生回数を示す棒グラフです。また、縦軸の単位は「万」となっております。他にも何かお手伝いできることがありましたら、遠慮なくお知らせください。
```
<br>
では、ファイルIDからグラフの画像を取得してみます。

```Python:ファイルの取得
image_fileid = "file-ufUPttZASrhmPITuXKrjZ3HF"

retrieve_file = client.files.with_raw_response.retrieve_content(image_fileid)

content = retrieve_file.content

# ファイル名は適当に付けて下さい
with open(image_fileid + ".png", 'wb') as f:
    f.write(content)
```

取得した画像です。

![file-ufUPttZASrhmPITuXKrjZ3HF.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3549938/10938456-5dbc-922b-5fc9-03bf3368bfc2.png)

<br>
タイトル・各項目を日本語で表示させることができました！

# おわりに

長々書きましたが、要は日本語フォントをZIPにして渡してあげるだけです。
今のところβ版なので、今後はもっと簡単になることに期待します！

# 宣伝
コードインタープリターが使えるチャットアプリを作ったのでよかったら試してみてください！ ※テスト中はAPIキーなしで利用できます。

https://huggingface.co/spaces/nekoniii3/GPT_Chat_Code_Interpreter

[ソースはこちら](https://github.com/nekoniii3/GPT_Code_Interpreter/tree/main)（現在Colabファイルのみ）

動画でのAssistants APIの紹介もあります！

https://www.youtube.com/watch?v=tFmedAM1FM8