Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

Agents for Amazon Bedrock の作成がより簡単になった!

こんにちは、機械学習チーム YAMALEX の駿です。
YAMALEX は Acroquest 社内で発足した、会社の未来の技術を創る、機械学習がメインテーマのデータサイエンスチームです。 (詳細はリンク先をご覧ください。)

先日Amazon Bedrock(以下、 Bedrock)の新機能が複数発表されましたが、今回は「Agents for Amazon Bedrock」(以下、エージェント)作成に追加されたアップデートを確認します。

以前、当社のブログでエージェントを扱った記事があったので、同じことをやってみて、どれくらい変わったのか確認してみます。

acro-engineer.hatenablog.com

概要

エージェントは生成AIを活用したアプリケーションの作成を助ける仕組みです。
ユーザーは、自然言語を用いてAIアプリで実施したいタスクを入力します。

エージェントは上記のタスクを推論モデルを用いて細分化、複数ステップのタスクに自動で分割し、それぞれの実行結果をまとめ、 最終的なタスクの実行結果をユーザーに返すAIアプリケーションを生成します。

今回のアップデートでエージェントを作成し設定するフローが簡易化され、今まで以上に誰でも簡単にAIアプリケーションを作れるようになりました。

aws.amazon.com

以下、以前の記事と同じアプリケーションを作成していきます。

横に並べて一緒にスクロールしながら読むと、特にLambda関数の定義が簡単になっていることがよくわかると思います。

Bedrock エージェントでEC2インスタンスを作成する

ユーザの入力に基づいてEC2インスタンスを起動するエージェントを作成します。

以下のような構成になります。


構成図

エージェントは次の順番に処理を行います。

  1. [エージェント] ユーザの入力を受け取る(①)
  2. [エージェント] 関数呼び出しが必要か判定(②)
  3. [エージェント] 関数呼び出しに使う引数を抽出(②)
  4. [エージェント] 関数呼び出し(③)
  5. [Lambda] EC2インスタンスを起動(④)
  6. [Lambda] 結果を返却(⑤)
  7. [エージェント] 返り値を元に返答を生成(⑥)
  8. [エージェント] ユーザに返答する(⑥)

us-west-2リージョンでBedrockのサイドメニューから、オーケストレーション > エージェントをクリックし、続けて「エージェントを作成」をクリックします。

エージェント

エージェントを作成

エージェント名入力

まずエージェント名と説明を求めるダイアログが出るので、入力します。

作成の一番最初で細かいところを気にせずに進められるのが良いですね。

エージェント名と説明を入力

エージェントの詳細入力

続いてエージェントの詳細を入力します。

ここでは利用するモデルやエージェント向けの指示(いわゆるシステムプロンプト)を設定できます。

「Additional settings」を開くと細かいエージェントの動きを設定できます。

詳細入力

モデル選択のプルダウンを開くと、先日Bedrockに追加されたClaude 3のモデルも利用可能になっていることが分かります。

今回は迷わず Claude 3 Haiku を選択しました。 性能が高い、早い、安いの三拍子がそろっています。


使えるモデルが増えた

アクショングループの追加

続いてアクショングループとしてLambda関数を指定します。

ここではEC2インスタンスを起動するLambdaを作成し、実行に必要な引数の情報などをエージェントに入力します。

Action group typeは「Define with function details」を選択します。

「Define with API schemas」を選択すると今までと同じくOpenAPIのYAML定義をS3に置いて読み込む従来の指定方法になります。

アクショングループの追加

Define with function details

functionの設定

Action group invocation はお勧めされている「Quick create a new Lambda function」を選択しました。

上記のParameters部などが今までOpenAPI形式でYAMLを記述してS3に保存しないといけなかったところです。
画面をポチポチするだけで設定が完了しました。

すぐに使い始めたい、複雑なことをする予定はないので簡単に始めたいユースケースにマッチします。

(ナレッジベースは今回使わないためスキップします。)

Lambda 作成

Lambdaのコンソールにアクセスします。

アクショングループを作成する際に新しいLambda関数を作る設定にしたため、自動でひな型が作られています。

アクセス権限が設定済みなので、ポリシーステートメントなど難しいことに煩わされることはありません。

Lambdaのひな型が自動で作成されます

アクセス権限も設定済み

ひな型に合わせて、EC2インスタンスを作成するコードを少し修正しています。

import json

import boto3

ec2 = boto3.client("ec2")


def lambda_handler(event, context):
    agent = event["agent"]
    actionGroup = event["actionGroup"]
    function = event["function"]
    parameters = event.get("parameters", [])

    # Execute your business logic here. For more information,
    # refer to: https://docs.aws.amazon.com/bedrock/latest/userguide/agents-lambda.html

    instance_type = next(item for item in parameters if item["name"] == "instance_type")["value"]
    ami_id = next(item for item in param if item["name"]  == "ami_id")["value"]
    
    instances = ec2.run_instances(
        ImageId=ami_id,
        InstanceType=instance_type,
        MinCount=1,
        MaxCount=1,
    )
    
    responseBody =  {
        "TEXT": {
            "body": f"Instance Created: {str(instances)}"
        }
    }

    action_response = {
        "actionGroup": actionGroup,
        "function": function,
        "functionResponse": {
            "responseBody": responseBody
        },
    }

    function_response = {"response": action_response, "messageVersion": event["messageVersion"]}
    print(f"Response: {function_response}")

    return function_response

※ ec2:RunInstances の権限をLambdaの実行ロールに付与する必要はあります。

以上でエージェントの作成が完了です。

作成したエージェントを試してみる

早速作成したエージェントにamazonlinux:2023ベースのインスタンスを作ってもらいましょう。

入力

"ami-0c0d141edc4f470cc" のAMIをベースに、インスタンスタイプを”t2.micro"として、EC2インスタンスを作成してください。

出力

EC2 instance created successfully with the following details:
- AMI ID: ami-0663b059c6536cac8
- Instance Type: t2.micro
- Instance ID: i-xxxx
- Private IP Address: xxx.xxx.xxx.xxx

作成できました

指示通りのAMIとインスタンスタイプで作成してくれました。

EC2のコンソールを確認すると、実際にLambda関数が実行されインスタンスが起動していることが確認できます。

実際に作られたインスタンス

「トレースを表示」を押下してステップの中身を確認することで、下記のようにAMI IDとinstance typeを適切に抽出できていることを確認できます。

(前略)
  "rationale": {
    "text": "To create the EC2 instance based on the user's request, I will need to invoke the CreateEC2::create-ec2 function with the provided AMI ID and instance type.",
    "traceId": "abdfe555-eca2-4670-8ca4-77a7344ff365-0"
  },
  "invocationInput": {
    "actionGroupInvocationInput": {
      "actionGroupName": "CreateEC2",
      "function": "create-ec2",
      "parameters": [
        {
          "name": "ami_id",
          "type": "string",
          "value": "ami-0663b059c6536cac8"
        },
        {
          "name": "instance_type",
          "type": "string",
          "value": "t2.micro"
        }
      ]
    },
    "invocationType": "ACTION_GROUP",
    "traceId": "abdfe555-eca2-4670-8ca4-77a7344ff365-0"
  },
(後略)

まとめ

エージェントの作成がどのように変わったのかを、以前作成したエージェントと同等のものを作成することで確認してきました。

関数定義の指定とLambdaの権限設定が簡単になっている部分が個人的には推しポイントです。

自分でシステムを作ることなく高度なAIアプリケーションを構築できる、エージェントの利点がさらに補強され、より気軽に利用できますね。

Acroquest Technologyでは、キャリア採用を行っています。
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長
  少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。 www.wantedly.com

Azure OpenAI Service の Assistants API でデータ分析

こんにちは、igaです。
最近は気温の上下が大きいので、服装選びが大変ですね。


今回は、Azure OpenAI Servce Assistants APIを使ってみました。
Azure OpenAI Servce Assistants API横浜市の人口データを投入して、人口の増減がどう推移しているのか自動で分析させてみました。

Azure OpenAI Servce Assistants API

Azure OpenAI Servce Assistants APIとは

Azure OpenAI Servce Assistants APIは、2024年4月現在パブリックプレビューとして利用できる機能です。

learn.microsoft.com

Azure OpenAI Servce Assistants API(以降、Assistantsと表記します)により、Azure OpenAI Servceに独自データを投入して、投入したデータに対してユーザーからの問い合わせの回答、コードインタープリターによる分析、Function callingによる独自処理を実施することができます。

Azure OpenAI Servce Assistants APIのポイント

Azure OpenAI Servce Assistants APIを利用するには、以下の条件があります。

項目
利用可能なリージョン オーストラリア東部、
米国東部2、
スウェーデン中部
利用可能なモデル gpt-35-turbo(0613)
gpt-35-turbo(1106) ※1
gpt-4(0613)、gpt-4(1106)

※1 米国東部2リージョンでは利用することができません。
※2024年4月15日現在の条件です。

learn.microsoft.com

Assistantsで利用可能なデータファイルの拡張子は、以下のページを参照してください。
learn.microsoft.com

利用手順

Assistantsを準備する

Assistantsを利用するため、 Azureポータル からAzure OpenAI Service環境を作成します。
Azure OpenAI Service環境の利用方法については、 過去の記事 を参照してください。

Assistantsの利用手順は、こちらを参照してください。
learn.microsoft.com


Azure OpenAI Studio にアクセスして、「アシスタント(プレビュー)」メニューから、Assistantsの設定画面に移動します。

「アシスタントのセットアップ」に必要な内容を入力します。

入力する項目のポイントは以下の通りです。

入力項目
関数 Function callingの関数定義をJSON形式で指定します
コードインタープリタ 「ファイル」に独自データを指定する場合は、ONにする必要があります

今回、ファイルには横浜市の人口動態のCSVファイルを使用しました。

www.city.yokohama.lg.jp


関数については、 過去の記事 でも解説した通り、呼び出す関数とその引数を特定するために必要な情報をJSON形式で指定します。
関数自体の定義を指定するのではない点を注意してください。

必要事項を入力したら、Saveボタンを押して入力情報を保存します。

Assisntantsの動作を確認する

Azure OpenAI Studioのチャット画面から、今回定義したAssistantsとチャットを行います。まずは与えた人口動態データから平均人口増加数を分析させてみました。
Assistantsに渡したCSVファイルにどのような列が入っているか、こちらで説明する必要はありません。すべて勝手に読み取って解析までやってくれます。


「10年単位の人口増加数の平均値を求めて」というユーザーの入力に対して、内部でコードインタープリターが動作して、「10年単位の人口増加数の平均値」を回答してくれます。2020年代は2020年から2023年までの人口増加数が減少しているため、マイナスの値になっています。
Assistantsからの回答に対して、グラフ化してほしい、という追加の要望を出します。

グラフを生成するのと合わせて、ダウンロード可能な形式にしてくれました。
ダウンロードしたファイルは以下のようになっています。


AssistantsでFunction callingを利用する

関数定義を入力する

Assistantsに、関数の定義を追加します。
「関数の追加」をクリックすると、関数定義の入力ダイアログが開くので、関数定義のJSONを入力します。
関数定義の入力ダイアログで「保存」ボタンをおしてダイアログが閉じた後、忘れずにアシスタントのセットアップで「Save」ボタンによりAssistantsに関数定義を反映させます。

今回の定義内容は以下の通りです。
こちらの関数では、前年と今年の人口の増減比率により、「HIGH」「NORMAL」「LOW」というラベルを決定します。

{
  "name": "diff_label",
  "description": "The label for the population increase is 'HIGH' if the population increase is 10% or more compared to the previous year, 'LOW' if the population increase is -10% or less, and 'NORMAL' for any other cases.",
  "parameters": {
    "type": "object",
    "properties": {
      "prev_population_increase": {
        "type": "number",
        "description": "Number of population increases in the previous year."
      },
      "current_population_increase": {
        "type": "number",
        "description": "Number of population increases this year."
      }
    },
    "required": [
      "prev_population_increase",
      "current_population_increase"
    ]
  }
}
Function callingを確認する

チャット画面から、上で定義したFunction callingが必要となるように、「2000年の人口増加数に対してラベルをつけて」というメッセージを入力します。
Assistantsが呼び出す関数と引数を特定してくれます。

関数を呼び出した結果を、Assistantsの応答に対して入力します。
そのままでは回答を返してくれなかったので、回答をお願いしたところFunction callingの結果を含めて回答してくれました。


APIによるAssistantsの操作

Azure OpenAI Studioのプレイグラウンドではなく、APIを使ってAssisntantsに要求を出して結果を受け取る方法を検証します。

APIの使い方については、以下の内容を参考にしました。
learn.microsoft.com

APIを操作するPythonのプログラムは以下のようになります。

Assistantsへのリクエストの送信や、応答の解析などの処理については、以下のプログラムを参考にしました。
Function callingで指定する関数については、JSONで定義した挙動になるように実装しました。

github.com

import json
import os
import time
from pathlib import Path
from typing import Optional

from openai import AzureOpenAI


def create_message(client, thread_id, role, content, message_id=None):
    """Assistantsのメッセージを作成する

    Args:
        client (AzureOpenAI): Azure OpenAIのクライアント
        thread_id (str): スレッドID
        role (str): メッセージのロール
        content (str): メッセージ内容
        message_id (str): メッセージID
    """
    if client is None:
        print("Client is required.")
        return None

    if thread_id is None:
        print("ThreadID is required.")
        return None

    try:
        if message_id is not None:
            return client.beta.threads.messages.retrieve(thread_id=thread_id, message_id=message_id)

        return client.beta.threads.messages.create(thread_id=thread_id, role=role, content=content)
    except Exception as ex:
        print(ex)
        return None


def wait_run_finish(client, thread_id, run_id):
    """実行結果の終了を待機する

    Args:
        client (AzureOpenAI): Azure OpenAIのクライアント
        thread_id (str): スレッドID
        run_id (str): 実行ID
    """
    if (client is None and thread_id is None) or run_id is None:
        print("Client, Thread ID and Run ID are required.")
        return

    print("wait run finish.")

    wait = 30  # 待機時間(秒)
    try:
        # 一定回数、状態確認を行う
        for cnt in range(20):
            run = client.beta.threads.runs.retrieve(
                thread_id=thread_id, run_id=run_id)

            print(f"Poll {cnt}: {run.status}")

            if run.status == "requires_action":
                tool_responses = []
                if (
                    run.required_action.type == "submit_tool_outputs"
                    and run.required_action.submit_tool_outputs.tool_calls is not None
                ):
                    tool_calls = run.required_action.submit_tool_outputs.tool_calls

                    for call in tool_calls:
                        # Function callingが必要とAssisntantsが判断したので、指定された関数を実行する
                        if call.type == "function":
                            if call.function.name not in available_functions:
                                raise Exception(
                                    "Function requested by the model does not exist")
                            function_to_call = available_functions[call.function.name]
                            tool_response = function_to_call(
                                **json.loads(call.function.arguments))
                            tool_responses.append(
                                {"tool_call_id": call.id, "output": tool_response})

                run = client.beta.threads.runs.submit_tool_outputs(
                    thread_id=thread_id, run_id=run.id, tool_outputs=tool_responses
                )
            if run.status == "failed":
                print("Run failed.")
                break
            if run.status == "completed":
                break
            time.sleep(wait)
    except Exception as ex:
        print(ex)


def retrieve_and_print_messages(client, thread_id, verbose, out_dir=None):
    """スレッド内のメッセージリストを取得して、結果を出力する

    Args:
        client (AzureOpenAI): Azure OpenAIのクライアント
        thread_is (str): スレッドID
        verbose (bool): 詳細表示の要否
        out_dir (str): 画像ファイルの出力先フォルダ
    Returns
        list: メッセージのリスト
    """

    if client is None and thread_id is None:
        print("Client and Thread ID are required.")
        return None
    try:
        messages = client.beta.threads.messages.list(thread_id=thread_id)
        display_role = {"user": "User query",
                        "assistant": "Assistant response"}

        prev_role = None

        if verbose:
            print("\n\nCONVERSATION:")
        for message_data in reversed(messages.data):
            if prev_role == "assistant" and message_data.role == "user" and verbose:
                print("------ \n")

            for message_content in message_data.content:
                # Check if valid text field is present in the mc object
                if message_content.type == "text":
                    txt_val = message_content.text.value
                # Check if valid image field is present in the mc object
                elif message_content.type == "image_file":
                    image_data = client.files.content(
                        message_content.image_file.file_id)
                    if out_dir is not None:
                        out_dir_path = Path(out_dir)
                        if out_dir_path.exists():
                            image_path = out_dir_path / \
                                (message_content.image_file.file_id + ".png")
                            with image_path.open("wb") as f:
                                f.write(image_data.read())

                if verbose:
                    if prev_role == message_data.role:
                        print(txt_val)
                    else:
                        print(f"{display_role[message_data.role]}:\n{txt_val}")
            prev_role = message_data.role
        return messages
    except Exception as e:
        print(e)
        return None


def get_label(prev_population_increase, current_population_increase):
    """前年からの増減の比率により、ラベルを返す

    Args:
        prev_population_increase (int): 前年の数値
        current_population_increase (int): 今年の数値

    Returns:
        dict: 今年の数値が前年比+10%以上ならば"HIGH"、今年の数値が前年比-10%以下ならば"LOW"、それ以外は"NORMAL"
    """
    if current_population_increase >= prev_population_increase * 1.1:
        label = "HIGH"
    elif current_population_increase <= prev_population_increase * 0.9:
        label = "LOW"
    else:
        label = "NORMAL"

    return json.dumps({"label": label})


available_functions = {"diff_label": get_label}

client = AzureOpenAI(
    api_key=os.getenv("AZURE_OPENAI_API_KEY"),
    api_version="2024-02-15-preview",
    azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT")
)

assistant_id = os.getenv("AZURE_OPENAI_ASSISTANT_ID")

assistant_list = client.beta.assistants.list()

# スレッドの作成
thread = client.beta.threads.create()

# メッセージの作成
first_message = create_message(
    client, thread.id, "user", "2000年の人口増加数について、前年との比較のラベルをつけてください。")

# メッセージをAzure OpenAIに送信する
run = client.beta.threads.runs.create(
    thread_id=thread.id, assistant_id=assistant_id)

# Azure OpenAIからの応答を待つ
wait_run_finish(client, thread.id, run.id)

# 応答内容を出力する
retrieve_and_print_messages(client, thread.id, True)

送信するメッセージは、Function callingが必要になる内容を送信します。
このプログラムを実行した結果は以下のようになります。プレイグラウンドでやった時と同じように、Function callingを使ったラベル付けした結果を得られています。

wait run finish.
Poll 0: in_progress
Poll 1: in_progress
Poll 2: requires_action
Poll 3: completed


CONVERSATION:
User query:
2000年の人口増加数について、前年との比較のラベルをつけてください。
Assistant response:
2000年の人口増加数は前年比で高い増加(10%以上の増加)となっており、ラベルは「HIGH」となります。

実行結果に 「ラベルは「HIGH」となります」というメッセージが出力されているので、Function callingが必要とAssistantsが判断してプログラムで実行した結果を使って、Assistantsが応答を返していることが確認できます。

APIによるファイルのダウンロード

APIを使ってAssistantsに要求するメッセージで、ファイルのダウンロードを確認します。

先ほど提示したPythonのプログラムで、作成するメッセージ内容を以下のように修正します。

# メッセージの作成
first_message = create_message(
    client, thread.id, "user", "10年単位での、平均人口増加数をグラフにして、画像ファイルにしてください。")

このプログラムを実行すると、以下のように画像ファイルがダウンロード可能な形式の結果が返ってきます。
プログラム中の`retrieve_and_print_messages()`で、ファイル情報を保存しています。

CONVERSATION:
User query:
10年単位での、平均人口増加数をグラフにして、画像ファイルにしてください。
Assistant response:
データには和暦の年と西暦の年、そして3種類の人口増加数が含まれています。ここでは「人口増加数[人]」の列を使用して、10年 単位での平均人口増加数を計算し、グラフにして画像ファイルとして保存します。まずは各10年ごとの期間に分けて平均を計算しましょう。
It seems there was an issue with plotting the data. Let me try again to calculate the average population increase per decade and create the graph.
It seems there was an issue with plotting the data. Let me try again to calculate the average population increase per decade and create the graph.
I have successfully calculated the average population increase per decade and created a bar graph. The graph has been saved as an image file. You can download the image using the link below:

[Download the image of the average population increase per decade](sandbox:/mnt/data/average_population_increase_per_decade.png)

ダウンロードした画像は以下の通りです。
10年単位の平均人口増加数が棒グラフになっています。


まとめ

今回は、Assistantsの利用方法について確認しました。
過去の記事で、Function callingや独自データの利用方法を検証しましたが、その時にはデータの準備など手順を踏む必要がありました。
Assistantsでは、データ準備の手順が簡略化されているため、簡単なチャットシステムを用意することができるようになりました。



Acroquest Technologyでは、キャリア採用を行っています。
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

www.wantedly.com

OpenAIのBatch APIを使ってお得にプロンプトを一括処理してみる

はじめに

こんにちは。データサイエンスチームYAMALEXのSsk1029Takashiです。
最近はOpenAIに日本支社が出来て、日本語対応が加速するというニュースにわくわくしています。
今回はそんなOpenAIから発表されたBatch APIという機能が便利、かつお得な機能だったのでどのように使えるのか試してみます。


このBatch APIという機能はその名の通りAPIから利用できる機能で、複数のプロンプトをファイルにしてアップロードすることで一括処理できるという機能になります。
チャットのようにインタラクティブに結果を得られない代わりに、通常のAPIよりも50%も料金が安くなるというかなりうれしい機能になります。

Batch APIの特徴

Batch APIは以下の特徴を持っています。

  1. トークンあたりの料金が通常のAPIより50%安い。
  2. 入力・出力はJSONLファイルになる。
  3. 最大処理時間は24時間で、24時間以上かかる処理については途中まで実行して残りはキャンセルされる。

注意するべきは処理の最大時間が24時間で、24時間以上かかるタスクについては途中まで実施してそれ以降の処理はキャンセルされます。
なので、もし24時間以上かかるBatchを実行したい場合は、複数回に分けて実行する必要があります。

Batch APIを使ってみる

ファイルを用意する

まずはBatch APIの入力にするプロンプトをまとめたファイルを作ります。
フォーマットが決められているので、以下の形のJSONを一行にしたJSONLファイルを用意します。

{
    "custom_id": "request-1", # ファイルの中で一意になるID
    "method": "POST", # 固定
    "url": "/v1/chat/completions", # 固定
    "body": {
        "model": "gpt-3.5-turbo", # 使うモデルを選択
        "messages": [ # ChatCompletionの入力形式
            {
                "role": "system",
                "content": "You are helpful assistant."
            },
            {
                "role": "user",
                "content": "What`s OpenAI?"
            }
        ]
    }
}

重要なのはbodyの中に、ChatCompletion APIの入力にするときのmessagesの形式でプロンプトを用意することです。
ここでシステムプロンプトとユーザーの入力を定義して入力できます。

今回はサンプルの実行なので、以下のようにGPT-4 Turboを使うプロンプトを10件用意します。

{"custom_id": "request-1", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-4-turbo", "messages": [{"role": "system", "content": "You are helpful assistant."}, {"role": "user", "content": "What`s OpenAI?"}]}}
{"custom_id": "request-2", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-4-turbo", "messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What`s GPT?"}]}}
{"custom_id": "request-3", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-4-turbo", "messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Please explain about Transformer."}]}}
{"custom_id": "request-4", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-4-turbo", "messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Please explain about LLM."}]}}
{"custom_id": "request-5", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-4-turbo", "messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Please explain about Azure."}]}}
{"custom_id": "request-6", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-4-turbo", "messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Please explain about Python."}]}}
{"custom_id": "request-7", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-4-turbo", "messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "How do I get started with machine learning?"}]}}
{"custom_id": "request-8", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-4-turbo", "messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "Please give me sample code for running the OpenAI API in Python."}]}}
{"custom_id": "request-9", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-4-turbo", "messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What are your strengths?"}]}}
{"custom_id": "request-10", "method": "POST", "url": "/v1/chat/completions", "body": {"model": "gpt-4-turbo", "messages": [{"role": "system", "content": "You are a helpful assistant."}, {"role": "user", "content": "What are your weaknesses?"}]}}

これをinstructions.jsonlというファイルで保存します。

ファイルをアップロードする

ファイルを作成したら、OpenAI APIから一度ファイルをOpenAIのStorageにアップロードします。
今のところOpenAIのコンソールからのアップロードには上記フォーマットは対応していないので、curlなどを使って直接アップロードしましょう。

curl https://api.openai.com/v1/files \
  -H "Authorization: Bearer $OPANAI_KEY" \
  -F purpose="batch" \
  -F file="@instructions.jsonl"

ここで重要なのは、purposeをbatchに設定することです。
こうすることで、Batch用のフォーマットということを認識させてアップロードできます。

リクエストを実行して以下のようなレスポンスを得られればアップロード成功です。

{
  "object": "file",
  "id": "file-otymKIlaJNJxVx8LaM1bAEQ2",
  "purpose": "batch",
  "filename": "instructions.jsonl",
  "bytes": 2501,
  "created_at": 1713285032,
  "status": "processed",
  "status_details": null
}

OpenAIのコンソールからもアップロードしたファイルが確認できるようになります。
この中で、File IDの値はBatch実行時に使用するので控えておきましょう。

Batchを実行する

ファイルをアップロードしたらBatchを実行します。
今のところ公式ドキュメントで紹介されているのはAPI経由の実行のみなので、こちらでもcurlなどを使用して実行しましょう。

curl https://api.openai.com/v1/batches \
  -H "Authorization: Bearer $OPANAI_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input_file_id": "file-otymKIlaJNJxVx8LaM1bAEQ2",
    "endpoint": "/v1/chat/completions",
    "completion_window": "24h"
  }'

この時リクエストボディに設定するパラメータは以下になります。

パラメータ名 設定する値
input_file_id ファイルアップロード時に取得したファイルのOpenAI上でのID
endpoint 固定で/v1/chat/completionsを設定
completion_window Batchの最大実行時間指定。今は24hしか指定できない。

無事に実行できると以下のようなレスポンスを取得できます。

{
  "id": "batch_9E41M4O8YeY78GcarOnzwaB0",
  "object": "batch",
  "endpoint": "/v1/chat/completions",
  "errors": null,
  "input_file_id": "file-otymKIlaJNJxVx8LaM1bAEQ2",
  "completion_window": "24h",
  "status": "in_progress",
  "output_file_id": null,
  "error_file_id": null,
  "created_at": 1713285571,
  "in_progress_at": 1713285573,
  "expires_at": 1713371971,
  "finalizing_at": null,
  "completed_at": null,
  "failed_at": null,
  "expired_at": null,
  "cancelling_at": null,
  "cancelled_at": null,
  "request_counts": {
    "total": 10,
    "completed": 0,
    "failed": 0
  },
  "metadata": null
}

また、一度実行したBatchは今の状態をAPIから確認できます。

curl https://api.openai.com/v1/batches/batch_9E41M4O8YeY78GcarOnzwaB0 \
  -H "Authorization: Bearer $OPANAI_KEY" \
  -H "Content-Type: application/json"

パスパラメータにはBatch作成時のレスポンスの中からidの値、今回でいえば「batch_9E41M4O8YeY78GcarOnzwaB0」を入れます。
その値が実行したBatchを表すIDになっており、以下のような結果を得られます。

{
  "id": "batch_9E41M4O8YeY78GcarOnzwaB0",
  "object": "batch",
  "endpoint": "/v1/chat/completions",
  "errors": null,
  "input_file_id": "file-otymKIlaJNJxVx8LaM1bAEQ2",
  "completion_window": "24h",
  "status": "in_progress",
  "output_file_id": null,
  "error_file_id": null,
  "created_at": 1713285571,
  "in_progress_at": 1713285573,
  "expires_at": 1713371971,
  "finalizing_at": null,
  "completed_at": null,
  "failed_at": null,
  "expired_at": null,
  "cancelling_at": null,
  "cancelled_at": null,
  "request_counts": {
    "total": 10,
    "completed": 0,
    "failed": 0
  },
  "metadata": null
}

レスポンスが長いですが、この中でstatusを見ればいまBatchがどのような状態なのか確認できます。
例えば、上記のレスポンスの場合だとin_progressになっているので、現在実行中ということになります。
このstatusが「completed」という値になればBatchが完了したことになります。

実行結果を確認する

Batchが完了すると以下のようにOpenAIのコンソールにBatch実行結果のファイルが追加されていることが分かります。
今回はGPT-4 Turboを使うとはいえ10行だけだったので、10分弱で処理が完了しました。

ダウンロードして中身を見ると以下のようにプロンプトを実行した結果の一覧を見ることができます。
ChatCompletionで得られるレスポンスは一通り記載されているので、usageなどの情報も見ることができます。
(長いので3行抜粋)

{"id": "batch_req_hZUECCsIMAT0NsrZ9taQC2S6", "custom_id": "request-7", "response": {"status_code": 200, "request_id": "req_735f5251489d353939dbda942ef6b37c", "body": {"id": "chatcmpl-9EgV0dWTMLrXNsPBSWA5Wj1xX2JRY", "object": "chat.completion", "created": 1713286986, "model": "gpt-4-turbo-2024-04-09", "choices": [{"index": 0, "message": {"role": "assistant", "content": "Getting started with machine learning (ML) can be an exciting journey into a field full of opportunities for innovation and impact across various industries like finance, healthcare, automotive, and more. Here\u2019s a structured way to begin learning about machine learning:\n\n### 1. Understand the Basics\nStart by gaining a foundational understanding of the concepts and terminology used in machine learning. Here are some key topics to cover:\n- **Basic Statistics & Mathematics:** Understand mean, median, mode, standard deviation, probability, and linear algebra.\n- **Types of Machine Learning:** Learn about supervised learning, unsupervised learning, reinforcement learning, and semi-supervised learning.\n- **Key Algorithms:** Get familiar with algorithms like linear regression, logistic regression, decision trees, SVM (Support Vector Machines), and neural networks.\n\n### 2. Online Courses\nThere are many online platforms offering free and paid courses that can provide structured learning and hands-on practice:\n- **Coursera:** Andrew Ng\u2019s Machine Learning course is highly recommended.\n- **edX:** Offers courses from MIT and Harvard.\n- **Udacity:** Known for its Nanodegree programs in Deep Learning and Machine Learning.\n- **Khan Academy:** Good for strengthening your math skills.\n\n### 3. Get Hands-On Experience\nAlongside theoretical learning, it\u2019s essential to practice:\n- **Kaggle:** Participate in competitions or explore datasets.\n- **GitHub:** Look at ML projects and try to replicate them. Start your own projects and share them.\n- **Python Libraries:** Get familiar with Scikit-learn, TensorFlow, PyTorch, and Pandas. Python is widely used in the machine learning community due to its simplicity and powerful libraries.\n\n### 4. Books\nSome good books to start with are:\n- **\u201cPattern Recognition and Machine Learning\u201d** by Christopher M. Bishop\n- **\u201cMachine Learning Yearning\u201d** by Andrew Ng (more strategy than technical details)\n- **\u201cHands-On Machine Learning with Scikit-Learn, Keras, and TensorFlow\u201d** by Aur\u00e9lien G\u00e9ron\n\n### 5. Work on Projects\nApplying what you\u2019ve learned to actual projects is crucial:\n- Begin with simple projects like predicting stock prices, or recognizing hand-written digits.\n- Move on to more complex problems as you grow comfortable.\n\n### 6. Follow Thought Leaders and Keep Updated\n- Follow key influencers on platforms like LinkedIn, Twitter, Medium.\n- Regularly read articles, research papers, and blogs to keep up with new trends and breakthroughs in ML technology.\n\n### 7. Engage with the Community\n- Participate in Meetups, Conferences, and Workshops.\n- Engage in forums like Stack Overflow, Reddit's r/MachineLearning, or Cross Validated on Stack Exchange.\n\n### 8. Understand Ethical Implications\nAs you delve deeper, understand the ethical considerations in deploying ML, including bias, fairness, and the environmental impact of training large models.\n\n### 9. Consider Specialization\nAs you advance, think about specializing in areas that are in high demand, such as deep learning, natural language processing, or computer vision, depending on what interests you the most.\n\nStarting with these steps will help you build a strong foundation and guide you through becoming proficient in machine learning. Always remember, consistent practice and continual learning are key components of success in this ever-evolving field."}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 26, "completion_tokens": 685, "total_tokens": 711}, "system_fingerprint": "fp_76f018034d"}}, "error": null}
{"id": "batch_req_DiZi0B5pBGwoESHn2VgSjrSz", "custom_id": "request-8", "response": {"status_code": 200, "request_id": "req_c3651f89bbb024df85d35a8d2f0c27b7", "body": {"id": "chatcmpl-9EgV0COWoixZMe7OszEThJlqb6xQU", "object": "chat.completion", "created": 1713286986, "model": "gpt-4-turbo-2024-04-09", "choices": [{"index": 0, "message": {"role": "assistant", "content": "Sure! To run the OpenAI API in Python, you'll primarily rely on the `openai` library. Let\u2019s use an example where we send a prompt to the GPT model and receive a text completion. First, you need to install the OpenAI Python library if you haven't yet. You can install it using pip:\n\n```bash\npip install openai\n```\n\n### Example Code for Using OpenAI GPT\n\nHere's a simple example of how you can use the API to interact with the GPT model. In this code snippet, I assume you have your API key ready. For security reasons, you should not hard-code your API Key in your source code. Instead, use environment variables or other secure methods to store your API Key.\n\n```python\nimport openai\n\ndef get_gpt_response(prompt_text):\n    openai.api_key = 'your-api-key-here'\n\n    try:\n        response = openai.Completion.create(\n            engine=\"text-davinci-003\",  # You can use different models like \"davinci\", \"curie\", etc.\n            prompt=prompt_text,\n            max_tokens=150  # You can adjust this value based on your needs\n        )\n        return response.choices[0].text.strip()\n    except Exception as e:\n        print(f\"An error occurred: {e}\")\n        return None\n\n# Example usage\nprompt_text = \"Explain the benefits of AI in education.\"\nresponse_text = get_gpt_response(prompt_text)\nprint(response_text)\n```\n\n### Notes:\n1. **API Key Security**: Do not hard-code your API keys in your code. Instead, use environment variables. You can set this up in your local environment or whatever deployment environment you are using.\n\n2. **API Usage Limits**: Be aware of the usage limits and potential costs associated with API requests. Make sure to review OpenAI's usage policies and pricing.\n\n3. **Error Handling**: The code includes basic error handling which is crucial for dealing with network issues or API limits.\n\n4. **Model Selection**: OpenAI provides various models with different capabilities and cost structures. Choose the appropriate model based on your needs.\n\n5. **Response Processing**: The response from the API contains multiple components. We are mostly interested in the textual completion, which is accessed via `response.choices[0].text`.\n\n6. **Environment**: This code is to be run in a standard Python environment where you have the necessary permissions and network access to reach OpenAI's API servers.\n\nThis should help you get started with using OpenAI's API in your Python applications!"}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 31, "completion_tokens": 526, "total_tokens": 557}, "system_fingerprint": "fp_76f018034d"}}, "error": null}
{"id": "batch_req_8nDALG7zrVMxiFk5o6nvrYbT", "custom_id": "request-5", "response": {"status_code": 200, "request_id": "req_0ef9ec2e8d633ebdf9f5000d9386136d", "body": {"id": "chatcmpl-9EgV0tJJ5JRL8SJxp7IvMlpfcPuYC", "object": "chat.completion", "created": 1713286986, "model": "gpt-4-turbo-2024-04-09", "choices": [{"index": 0, "message": {"role": "assistant", "content": "Microsoft Azure, commonly referred to simply as Azure, is a cloud computing service created by Microsoft for building, testing, deploying, and managing applications and services through Microsoft-managed data centers. It provides software as a service (SaaS), platform as a service (PaaS), and infrastructure as a service (IaaS) and supports many different programming languages, tools, and frameworks, including both Microsoft-specific and third-party software and systems.\n\n### Major Features of Azure:\n1. **Computing**: Azure provides virtual machines, containers, and batch processing, as well as remote application access.\n2. **Storage Solutions**: This includes Azure Blob storage, Queue Storage, File Storage, and Table Storage.\n3. **Networking**: This feature provides a variety of networking tools such as Virtual Networks, VPN Gateways, Application Gateways, and Content Delivery Networks.\n4. **Databases**: Azure offers managed SQL and NoSQL database services.\n5. **Mobile and Web Applications**: Azure facilitates the development, testing, and hosting of web and mobile applications.\n6. **AI and Machine Learning**: Tools and services designed to automate operations using AI and machine learning.\n7. **Internet of Things (IoT)**: Azure IoT Hub and other tools help collect, monitor, and analyze IoT data.\n8. **Developer Tools**: Provides support for DevOps, continuous integration, and testing environments.\n9. **Security**: Azure\u2019s security services protect workloads, applications, and data across cloud and hybrid environments.\n\n### Advantages of Azure:\n- **Scalability and Flexibility**: Azure can scale up or down based on demand, making it suitable for businesses of all sizes.\n- **Compliance and Security**: Azure has many compliance certifications and is built with security in mind, offering users peace of mind regarding their data.\n- **Hybrid Capabilities**: Azure provides a wide range of hybrid connections, including VPNs, caches, content delivery networks, and express routing connections to improve usability and performance.\n- **Integrated Environment**: Azure is deeply integrated with other Microsoft products, making it an ideal choice for organizations that rely on Microsoft tools and software.\n\n### Use Cases:\n- **Web Hosting**: Businesses can use Azure to host their websites and web applications.\n- **Data Storage and Backup**: It provides secure and scalable cloud storage solutions.\n- **Mobile App Development**: Azure supports backend services for mobile applications.\n- **Big Data and Analytics**: Offering powerful analytics services to handle large sets of data.\n- **Disaster Recovery**: With high availability and data recovery capabilities, Azure is an excellent option for disaster recovery.\n\nAzure continually evolves by expanding its services and capabilities to suit a broad spectrum of industries, including finance, health, government, manufacturing, and more, providing comprehensive cloud solutions for businesses globally."}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 22, "completion_tokens": 556, "total_tokens": 578}, "system_fingerprint": "fp_76f018034d"}}, "error": null}

以上でBatch APIを使う一通りの流れは完了になります。
実行自体はファイルアップロードしてAPIを実行するだけなので簡単でしたね。

ただし、簡単な注意事項としてリリースされたばかりだからか、日本語を結果で出力するときには下記のようにUnicodeがそのまま出力されてしまいます。

{"id": "batch_req_LFEtMhsQqZa4GU5qpXLTVmES", "custom_id": "request-1", "response": {"status_code": 200, "request_id": "req_5cdf7d1538045d5946d0dcaa0edf4c79", "body": {"id": "chatcmpl-9EWZ0wTGl6VRZm0AHZrenKMi6gdiE", "object": "chat.completion", "created": 1713248794, "model": "gpt-3.5-turbo-0125", "choices": [{"index": 0, "message": {"role": "assistant", "content": "\u65b0\u6a2a\u6d5c\u306b\u306f\u591a\u304f\u306e\u304a\u3044\u3057\u3044\u30e9\u30fc\u30e1\u30f3\u5c4b\u304c\u3042\u308a\u307e\u3059\u304c\u3001\u305d\u306e\u4e2d\u3067\u3082\u304a\u3059\u3059\u3081\u306e\u5e97\u8217\u3092\u3044\u304f\u3064\u304b\u7d39\u4ecb\u3057\u307e\u3059\u3002\n\n1. \u3089\u30fc\u3081\u3093\u4e8c\u90ce \u65b0\u6a2a\u6d5c\u5e97\n   - \u6fc3\u539a\u306a\u8c5a\u9aa8\u30b9\u30fc\u30d7\u3068\u5927\u76db\u308a\u306e\u5177\u304c\u7279\u5fb4\u306e\u4e8c\u90ce\u7cfb\u30e9\u30fc\u30e1\u30f3\u304c\u697d\u3057\u3081\u308b\u4eba\u6c17\u5e97\u3067\u3059\u3002\n\n2. \u3089\u30fc\u3081\u3093\u51e6 \u3068\u308f \u65b0\u6a2a\u6d5c\u5e97\n   - \u9d8f\u30ac\u30e9\u3084\u9b5a\u4ecb\u306e\u30b9\u30fc\u30d7\u304c\u30d9\u30fc\u30b9\u306e\u3042\u3063\u3055\u308a\u3068\u3057\u305f\u5473\u308f\u3044\u306e\u30e9\u30fc\u30e1\u30f3\u304c\u8a55\u5224\u3067\u3059\u3002\n\n3. \u3089\u30fc\u3081\u3093 \u5eb5 \u65b0\u6a2a\u6d5c\u672c\u5e97\n   - \u3053\u3063\u3066\u308a\u3068\u3057\u305f\u5473\u308f\u3044\u306e\u8c5a\u9aa8\u91a4\u6cb9\u30e9\u30fc\u30e1\u30f3\u304c\u4eba\u6c17\u3067\u3001\u5177\u6750\u3082\u8c4a\u5bcc\u3067\u6e80\u8db3\u611f\u304c\u3042\u308a\u307e\u3059\u3002\n\n\u3053\u308c\u3089\u306e\u304a\u5e97\u306f\u65b0\u6a2a\u6d5c\u99c5\u5468\u8fba\u306b\u4f4d\u7f6e\u3057\u3066\u304a\u308a\u3001\u7f8e\u5473\u3057\u3044\u30e9\u30fc\u30e1\u30f3\u3092\u697d\u3057\u3080\u3053\u3068\u304c\u3067\u304d\u307e\u3059\u3002\u305c\u3072\u8db3\u3092\u904b\u3093\u3067\u307f\u3066\u304f\u3060\u3055\u3044\u3002"}, "logprobs": null, "finish_reason": "stop"}], "usage": {"prompt_tokens": 51, "completion_tokens": 305, "total_tokens": 356}, "system_fingerprint": "fp_c2295e73ad"}}, "error": null}

時間が経てば解決する可能性がありますが、現状の注意点としては留意しておく必要はあります。

まとめ

この記事ではBatch APIを使って複数のプロンプトを一括処理する流れを試してみました。
APIを使うこと自体はとても簡単で入力のファイルさえ用意すれば簡単に実行できました。
リアルタイム性が必要なく、大量のテキストをまとめて要約するようなケースでは非常にありがたい機能になりそうです。
それではまた。

Acroquest Technologyでは、キャリア採用を行っています。


  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長
 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。


www.wantedly.com



ChatGPTの Assistants API でPDFを要約

こんにちは、安部です。 最近急に、暖かさを通り越して暑いぐらいになってきましたが、皆さまいかがお過ごしでしょうか。

季節外れかなとも思いつつ、もう半袖で過ごしたいくらいの気候ですね。

さて、今回は、OpenAIのAssistants APIの使い方を紹介していきます。 題材は「PDFを和訳して要約してもらう」としました。 これはWeb版のChatGPTでも単にPDFファイルを添付して依頼すればできますが、APIの使い方を示すサンプルとしてはちょうどよいと思います。

最新情報については以下の公式ドキュメントをご覧ください。

https://platform.openai.com/docs/assistants/overview

https://platform.openai.com/docs/api-reference/assistants

それでは早速、Assistants APIの使い方を見ていきましょう。

1. Assistants APIとは

Assistants APIは、開発者が独自にAIアシスタントを組み込んだアプリケーションを開発できるようOpenAIから提供されているAPIです。 AIアシスタントとは、要はCopilotやChatGPTみたいなもののことです。

旧来提供されていたChat Completions APIはステートレスでしたが、Assistants APIはステートフルになったため、メッセージ履歴の管理やモデルの最大トークンに合わせたメッセージの切り捨てなどの処理を抽象化してくれます。

2. Assistantsには何ができるのか

AssistantsはChatGPTでできるような会話はもちろんのこと、Code Interpreter・Knowledge Retrieval・Function Calling という3つの機能(ツール)を使用できます。各機能に関する詳細な解説は割愛しますが、それぞれ概ね以下のような機能です。

  1. Code Interpreter

    サンドボックス化された実行環境でPythonコードを記述し、実行することができます。このツールは、多様なデータやフォーマットのファイルを処理し、データやグラフの画像を含むファイルを生成できます。

  2. Knowledge Retrieval

    ユーザーから提供されたドキュメントなど、アシスタントのモデル外からの知識でアシスタントを補強します。 これにより、自社製品のマニュアルなど固有の情報に基づいた回答をアシスタントにしてもらうことができるようになります。

  3. Function calling

    文字通り、アシスタントに関数呼び出しをさせることができます。これにより、特定の情報は外部APIを使って取得し、その情報を含んだ自然な応答をアシスタントに返させたり、外部アプリを操作させたりすることができます。

3. Assistants APIを実際に使ってみる

それでは、実際にAssistants APIを使っていきましょう。冒頭で述べたように、PDFファイル("Attention is All You Need"という有名論文)を渡してその要約などをしてもらいます。

以下の順にやっていきます。

  1. 準備

  2. Assistantの作成

  3. Threadを作成し、Messageを追加

  4. Threadの実行

実際のユースケースでは、Webアプリケーションなどに組み込むことで、ユーザからの入力に応じてAssistantへのメッセージ送信などを行うことが多いかと思います。

今回はあくまでAPIの使い方を解説する目的なので、フロント側の処理などは省略しています。

3-1. 準備

本記事では、OpenAI Python API libraryを使ってAssistants APIの使い方を見ていきます。

紹介するサンプルコードは、ローカル環境やJupyter Notebookなどで動かすことを想定しています。 OpenAIのAPIキーを取得し、OPENAI_API_KEYという環境変数に登録しておいてください。

なお、Assistantsの動きを試したいだけであれば、OpenAIサイト内のPlayground > Assistantsで試すこともできます。

3-2. Assistantの作成

それでは、最初のステップとしてAssistantを作成していきましょう。

Assistant作成時に入力する項目のうち、重要なものは以下の通りです。

項目名 内容
model 使用するモデル
file_ids アシスタントに覚えてもらいたいファイル
name アシスタントの名前
instructions アシスタントの方向づけなどに使われる指示
tools アシスタントが利用できるツール

Pythonで実装すると、以下のようになります。

ここでは、モデルとしてgpt-4-turbo-previewを指定し、Knowledge Retrievalの機能を有効にしています。また、ローカルに置いてあるPDFファイルをアシスタントに渡すことで、これを知識ベースに加えてもらいます。

from openai import OpenAI

# 特に指定しなければ、環境変数OPENAI_API_KEYを参照してアカウントとの紐付けが行われる
client = OpenAI()

file = client.files.create(
  file=open("attention.pdf", "rb"),
  purpose='assistants'
)

assistant = client.beta.assistants.create(
  name="general-assistant",
  instructions="あなたはチャットサービスのアシスタントです。問い合わせに対し、可能な場合は知識ベースにある知識に基づいて回答してください。",
  model="gpt-4-turbo-preview",
  tools=[{"type": "retrieval"}],
  file_ids=[file.id]
)

これを実行し、Assistantが作成されたか確認してみます。OpenAIのサイト内のAssistantsを開いてみましょう。 ここでは、アカウントに紐づいたアシスタントの一覧をGUIで確認したり新規作成したりできます。

確かに、作成したAssistantが確認できていますね。添付したファイルも問題なくアップロードされているようです。

ついでに、Storageも確認してみます。

こちらにも、アップロードした画像が格納されています。

なお、今回のように1回限りのアシスタント作成であれば、最初から"Assistants"からGUIを使ってやってしまっても問題ありません。

Webアプリに組み込んでユーザごとにアシスタントを自動作成する場合などに、アシスタントの作成APIを利用することになるかと思います。

3-3. Threadを作成し、Messageを追加

無事にPDFファイルを保持したAssistantを作成できたので、Threadを作成してメッセージを追加していきましょう。

# Threadの作成
thread = client.beta.threads.create()

# メッセージをThreadに追加
message = client.beta.threads.messages.create(
    thread_id=thread.id,
    role="user",
    content="Attention is All You Needという論文の内容を、要約して教えてください"
)

Threadとは、アシスタントとユーザー間の会話セッションの単位です。 メッセージを保存したり、会話長をモデルのコンテキスト長に適合させるために自動切り捨て処理などを実行してくれます。

コードを見てもらってわかる通り、Threadは最初はアシスタントとは独立して定義します。 後ほどThreadを実行(Run)する際に、どのアシスタントに応答させるかを指定します。

ちなみにメッセージには、画像・テキストなどのファイルを含めることも可能です。

3-4. Threadの実行

それでは、Threadを実行し、回答を表示してみましょう。 Threadの実行(run)とは、Thread上でアシスタントを呼び出すことです。

Threadを実行することで、アシスタントが応答を返してくれます。

# 作成済みのAssistantを取得
assistant = client.beta.assistants.retrieve(assistant_id='asst_4TCRFV6BAULCpsCjXhAEuf3X')

# Threadの実行
run = client.beta.threads.runs.create(
  thread_id=thread.id,
  assistant_id=assistant.id
)

# アシスタントが回答のメッセージを返したら表示する
wait_response(client, thread.id, run.id)
print_thread_messages(client, thread.id)

回答待ちとメッセージの出力用関数は、以下のように作りました。

実際にサービスに組み込む際などは、返答を一気に表示するのではなく1文字ずつ出力するなどの工夫をするかと思いますが、ここでは単に回答を待ち、結果を全て出力するという処理にしています。

def wait_response(client, thread_id, run_id):
  """アシスタントが回答を返すまで待つ"""
  while True:
      time.sleep(5)
      # 実行ステータス取得
      run = client.beta.threads.runs.retrieve(
          thread_id=thread_id,
          run_id=run_id
      )
      status = run.status
      if status in ["completed", "cancelled", "expired", "failed"]:
          break

def print_thread_messages(client, thread_id):
  """メッセージを出力する"""
  msgs = client.beta.threads.messages.list(thread_id=thread_id)
  for message in msgs:
      print({"role": message.role, "message": message.content[0].text.value})

ちなみに、Runのライフサイクルは以下のようになっており、"completed"や"cancelled"など完了のステータスになったところで結果を出力するようにしております。

https://platform.openai.com/docs/assistants/how-it-works/run-lifecycle

こちらを実行した結果は、次のようになりました(メッセージ部分だけ載せています)。

論文「Attention Is All You Need」は、従来の系列変換モデルが複雑な再帰的(RNN)や畳み込みニューラルネットワーク(CNN)をベースにしており、
それらがエンコーダとデコーダを経由して接続される場合、その接続に注意機構が使われることが多いと指摘しています【7†source】。
これらのモデルの計算は基本的に連続して行われるため、トレーニング時の並列化が制限されるという問題がありました【8†source】。
この論文では、全体に渡って自己注意機構だけに依存する新しいネットワークアーキテクチャである「Transformer」を提案し、再帰や畳み込みを完全に排除しています。
これにより、モデルはより並列化可能で、トレーニング時間も大幅に短縮されると共に、品質も向上することを示しています【7†source】【8†source】。
Transformerモデルはエンコーダとデコーダから構成され、それぞれが自己注意機構と点ごとの全結合層のスタックで構成されています【10†source】。
この自己注意機構は、クエリと一連のキー・値のペアを出力にマッピングする関数として説明されており、出力は値の加重和として計算されます【11†source】。
2つの機械翻訳タスクでの実験により、このモデルは既存の最高の結果を上回り、英語からドイツ語への翻訳タスクにおいて28.4 BLEU、
英語からフランス語への翻訳タスクにおいては新しい単一モデルの最高スコアである41.8 BLEUを達成しました。
さらに、英文構文解析など他のタスクへの応用も成功しており、Transformerが幅広く汎用的に利用可能であることが確認されています【7†source】。

メッセージ内の【7†source】のような表記は、引用を表しています。引用された文章一覧は、message.content[0].text.annotationsで取得できます。

詳細は以下のドキュメントをご覧ください。

https://platform.openai.com/docs/assistants/how-it-works/message-annotations

4. 価格

Assistants APIの料金発生は、使用モデルに基づくトークンベースの課金と、Retrievalなどツールの使用にかかる課金とがあります。

https://openai.com/pricing

Retrievalの課金は、あくまでも保存しているファイルサイズとそれにアクセスしたアシスタントの数で決まり、Thread数などは関係ありません。 お試しでアシスタントをいくつも作成していると、いつの間にか保存しているファイルサイズが大きくなっていたということもあるので、不要なファイルはこまめに消すよう注意しましょう。

また、Thread上でアシスタントへの問い合わせをすると、過去の問い合わせの全てを含めた内容が送信される仕組み(だからこそ過去のやり取りの文脈を踏まえた回答をしてくれるわけですが)ですので、問い合わせを繰り返すたびに雪だるま式にコンテキストトークンが増えていってしまいます。そうするとトークンベースの課金が大きく膨らむことになるので、対策が必要です。

(ちなみに、Threadから過去のメッセージを削除するAPIは提供されていません。)

例えばWebアプリケーションに組み込んでいる場合などは、上限メッセージ数を定数としてシステムに持たせておき、会話のたびに過去のメッセージ一覧を取得して上限を超えていたら新しい方の一部メッセージのみを持った新たなThreadを作成し、古いThreadは削除する、というようなことをすれば回避できそうです。

まとめ

今回は、Assistants APIの基本的な使い方を見てきました。

APIで、ファイルに対する処理や、Function callingなどを実行できるようになり、できることが増えますね!

Acroquest Technologyでは、キャリア採用を行っています。
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長
  少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。 www.wantedly.com

Amazon Bedrock で Titan Image Generator を使って画像生成

今度、寝台列車で旅行に行きたいな、と思って先日予約に挑んでみたのですが、残念ながら予約競争に敗北してしまった、菅野です。 寝台列車は、今も人気が高いんですね。

ChatGPTでもDALL·E 3を用いることが出来るようになるなど、生成AIでも画像生成や読み込みなど一般的に用いられるようになってきました。
Amazonで利用できる生成AIプラットフォーム、Amazon Bedrokでも、以前から画像生成モデルとして世界的にも最も有名なStability AIが提供するStable DiffusionSDXLモデルを用いた画像生成が可能でしたが、 AmazonオリジナルのモデルTitanからも画像生成を行えるモデル、Titan Image Generatorも利用できるようになっています。
今回は、Titan Image Generatorを用いて実際にどのような画像生成ができるのかを見ていきましょう。

docs.aws.amazon.com

Titan Image Generatorとは

Amazonが提供する画像生成のモデルで以下のような処理を実施できます。

機能 説明
Text2Image テキスト入力から画像を生成する
インペインティング 画像の一部だけを書き換える
アウトペインティング 画像の外側を追加する
画像バリエーション 画像とプロンプトを入力し、入力画像のスタイルや背景を変更する

モデルの仕様は以下になっています。

項目 内容
モデルID amazon.titan-image-generator-v1
最大入力文字数 1024文字
最大入力画像サイズ 50MB
イン/アウトペインティングを使用する場合の最大画像サイズ 1024*1024ピクセル
画像バリエーションを利用する場合の最大画像サイズ 4096*4096ピクセル
言語 英語
出力タイプ 画像
サポートされている画像タイプ JPEG、JPG、PNG

まだ日本語は未対応みたいなのでプロンプトは英語で入力する必要がありそうです。

Titan Image Generatorの利用方法

それでは早速Titan Image Generatorを利用してみましょう。

モデルのアクティベート

Bedrokのモデルアクセスタブから開いた画面の、モデルアクセスを管理ボタンを押下します。

Titan Image Generator G1モデルにチェックを入れます。

同画面下部の変更を保存すると、モデルが利用できるようになります。

実際の利用

Amazon Bedrokでは利用できるモデルを簡単に試すことが出来るプレイグラウンド機能を提供してくれている為、そちらを利用して実際にTitan Image Generatorを利用してみましょう。
左側メニューのプレイグラウンド>イメージを押下して開いた画面のモデルを選択ボタンを押下します。

プロバイダをAmazon、モデルを Titan Image Generator G1に選択し適用を押下します。

これで Titan Image Generatorを利用する準備が整いました。

text2imageを試してみる

まずはシンプルにプロンプトから画像を生成してみましょう。

表示された、画面の下部からプロンプトを入力して実行ボタンを押下します。
今回はA modern architectual building with large glass windwos, situated on a cliff overlooking a serene ocean at sunset.(夕暮れ時の穏やかな海を望む崖の上に建つ、大きなガラス窓を備えたモダンな建築の建物。)と入力してみました。
およそ1分程度の時間がかかりますが、以下のように画像が生成されます。

デフォルトの設定では1024*1024ピクセルの画像が1枚生成されます。

入力した文字列を忠実に再現した画像が生成されました。
写実的な表現をするような命令文を入れていないにもかかわらず、本当に存在しそうな画像が出てきましたね。

インペインティングを試してみる

つづきまして、入力された画像の一部のみ変更するインペインティングを試してみます。
右側のメニューから、モードをEditに変更、変更したい写真を推論イメージからアップロードして変更したい箇所にマスクの矩形を移動し、プロンプトに変更内容を入力して、実行を押下します。
先ほど出力された画像に、雲を追加してみましょう。

プロンプトにはadd cloud(雲を追加)と入力しました。

夕焼けの印影に合わせて雲が追加されました。
指定した範囲外は元の画像のままになっています。

アウトペインティングを試してみる

お次は、入力画像の対象物体以外を置き換えるアウトペインティングです。
再度、text2imageで作った画像に加工を加えてみましょう。

Editモードのまま、変更したい写真を推論イメージからアップロード、マスクの矩形を画面全体に適応し、マスクプロンプトにsea(海)と入力します。
プロンプトにsea of trees(樹海)と入力して、実行ボタンを押下します。

海がうっそうと生い茂る森に代わりました。

画像バリエーションを試してみる

アップロードした画像とプロンプトをもとに、投入した画像のバリエーションを出力します。
モードをGenerateに戻し、推論イメージにバリエーションを作成したい画像をアップロードします。
text2imageで作った画像をアップロードし、同じ雰囲気の画像を作ってもらいましょう。

一分ほどで画像が生成されました。
配置自体は変わっていますが、建物の雰囲気や、海の中の岩礁など、同じような画像が生成されています。

推論イメージ無しで三枚出力したものと比較すると、より同じテイストになっていることがわかります。

まとめ

Amazonが作成して画像生成モデルTitan Image Generatorを実際に使ってどのような出力をすることが出来るか試してみました。
Stable DiffusionのSDXLとは異なり、いわゆる呪文のようなプロンプトなしで写真ライクな画像を簡単に生成できる点は強みであると感じました。
今後も生成AIでどういったことが出来るのか調査していこうと思います。

Acroquest Technologyでは、キャリア採用を行っています。
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長
  少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。 www.wantedly.com

旅行先を提案してくれる、『Travel Buddy』をGPT Storeに公開しました

こんにちは、igaです。

突然ですが、皆さんはどれくらい旅行に行くことがあるでしょうか?
私は昨年、あるアニメの舞台になった場所に、10回旅行しました。

このように、目的をもって旅行する場合もありますが、「どこかに旅行に行きたいな」と考えることがあると思います。
ただ、旅行の目的として景色やグルメ、などは思いついてもどこに行けばそれが楽しめるのか分からない人もいるのではないでしょうか。
そのような方にお勧めの『Travel Buddy』を今回は紹介します。

『Travel Buddy』とは?

概要

『Travel Buddy』は旅行ガイドのようなGPTになります。
こちらは、うちの社員随一の乗り鉄である、Sawaさんが作ったGPTです。

旅行の目的を入力すると、『Travel Buddy』が必要な情報を聞いてくるので、それに対して回答していきます。
すると、これまで入力した条件に合った目的地を提案してくれます。

chat.openai.com

使い方

Travel Buddyに対して、旅行の目的を入力します。

すると、Travel Buddyから出発地についての情報を入力するように言われるので、出発地を入力します。

続いて日程や利用する交通機関など、Travel Buddyからの質問に対して、回答を入力していきます。
最終的に、Travel Buddyから候補地が提案されました。

候補地から希望の場所を選択すると、モデルコースを提案してくれます。
今回は、金沢を選択してみました。

すると、移動方法や金沢での行先などを提案してくれました。
おすすめされた場所をGoogle Mapに書いてみると、次のようなルートになりました。
ルートを引いてみましたが、近いところなので歩いて回れそうです。


まとめ

今回は、旅行先の提案をしてくれる『Travel Buddy』を紹介しました。
連休などで旅行に行きたいと思った時に使ってもらえると嬉しいです。



Acroquest Technologyでは、キャリア採用を行っています。
  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSクラウドサービスを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

www.wantedly.com

AI王で準優勝を獲りました

皆さんこんにちは
機械学習チームYAMALEX@tereka114です。
YAMALEXは Acroquest 社内で発足した、会社の未来の技術を創る、機械学習がメインテーマのデータサイエンスチームです。
(詳細はリンク先をご覧ください。)

1月26日(金)に最終報告会が開催されたAI王の問題作成部門でYAMALEXチームが準優勝しました。
今回のブログでは、そのAI王について執筆します。先に一言感想ですが、最高に面白かったです。

問題作成部門準優勝賞状

AI王とは

「AI王」は、日本国内の質問応答研究を促進させることを目的とした、日本語のクイズ問題を題材とした質問応答システムのコンペティションです。
今年で4回目の開催となり、「早押し解答部門」と「問題作成部門」があります。
どちらも新設の部門であり、昨年までクイズに回答するだけでしたが回答にも早押し要素が追加されています。
また、LLMなどの流行もあるためか、クイズの作問をする「問題作成部門」も新設されました。

sites.google.com

私たちの取り組み

今年YAMALEXチームは問題作成部門に参加しました。
問題作成部門は、予選・本選・最終報告会のエキシビションとあります。

予選は足切り、本選の上位3チームが最終報告会のエキシビションに出場できます。
このエキシビションの結果に基づいて、審査員が更に評価し、本選のスコアに加算され最終的な結果が決まります。

問題作成のために色々と取り組み、なんと、本選では1位に!これは自分たちもうれしくなりましたね。

私たちのソリューションはGPTを利用して、問題の作成と評価を繰り返し、徐々に改善していくといったことに取り組みました。
取り組む上で、評価指標の数値の理解が難しく、チーム内でこれはなぜ7点なのかを検討するなど、活発に議論しました。
LLMの生成結果の評価が難しいといったことにコンペで直面し、色々と検討を重ね改善していきました。

YAMALEXチームのソリューション概要

最終報告会

今年の最終報告会はオンラインではなく現地開催により非常に面白かったです。
午前中は「早押し解答部門」と「問題作成部門」それぞれの上位チームからの解法紹介。
全体を見ても各参加者が異なるアプローチを取っており、多種多様な解法を聞くことができ、面白かったです。

そして、午後はエキシビションマッチが開催されます。今回のエキシビションマッチは2つあります。

1. 「早押し解答部門」:AIと人間が早押しクイズで対戦
2. 「問題作成部門」:AIが作成したクイズを人間が解く

今回特に盛り上がったのは早押し解答部門で、AIと人間のギリギリの戦いが見られます。
結果は動画を見てみると面白いと思います。動画はこちらです。
また、当チームの解法を1時間54分あたり〜10分少々説明しておりますので、こちらも興味があればぜひ御覧ください。

www.youtube.com

問題作成部門最終結

そして、最後のエキシビジョンでどうなるか?残念ながら、ここで逆転され、2位になってしまいました・・・。
1位になれないのは悔しくもありますが、納得の結果です。
本選までの採点は体裁が成り立っているかを重視されていたのでその点は抑えていたのですが、最終報告会はクイズとしての面白さが必要でした。
考慮しようとは考えましたが、クイズへの理解度が足りず、より面白いものを作るのは難しいと思いました。

なお、問題作成部門でAIが作問したクイズは、審査員によれば高校1年生の7月ぐらいの力量で作成したクイズの質程度とのことです。
まずは高校1年生の夏休み後にするのが、来年に向けての課題なのかなと痛感しています。

出場してよかったこと

プロンプトを使う上での学び

問題作成部門を実施するにあたり、GPT3.5とGPT4を使っていましたが、プロンプトを利用する上での学びが多かったです。
プロンプトでただ単純に問題を作成するといっても構造や文体の指定がなければ、正しく生成されないなど、試行錯誤が続きました。

作成したクイズをプロに解いてもらえる貴重な体験

また、エキシビションマッチで自分たちの問題がクイズのプロの方々にどんな感じで解かれるのか最終報告会前から非常に興味深いものでした。
こんな経験ができるAIエンジニアも滅多にないと思うので、自分の作ったものの良し悪しがどう評価されるのかやはりワクワクでした。

クイズに対する解像度が参加前後でかなり上がったと思いました。自分の想像していなかった要素も多分にあり、学びになりました。

最後に

AI王というイベントのリアルには初参加しました。
会の中でエキシビションマッチやクイズの奥深さを感じることができ、非常にワクワクです。
来年も開催されましたら、対戦よろしくお願いします。

Acroquest Technologyでは、キャリア採用を行っています。

  • ディープラーニング等を使った自然言語/画像/音声/動画解析の研究開発
  • Elasticsearch等を使ったデータ収集/分析/可視化
  • マイクロサービス、DevOps、最新のOSSを利用する開発プロジェクト
  • 書籍・雑誌等の執筆や、社内外での技術の発信・共有によるエンジニアとしての成長

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。

Kaggle Grandmasterと一緒に働きたエンジニアWanted! - Acroquest Technology株式会社のデータサイエンティストの採用 - Wantedlywww.wantedly.com