見出し画像

OpenAI function callingの機能比較を実装-2 複数関数をcall(ChatCompletionとAssistants)

先回に続いて、複数関数をcallする仕様にしました。
function 1 : 誕生日から今日までの日数を算出する
function 2: 名言を取得する

名言をランダムに取得するAPIは、以下を使わせていただきました。
ありがとうございます。

先回の記事

コードと実行結果(途中経過付き)は、google colabのnotebookで共有します。(このように共有するの初めてなので、おかしな点があれば、コメントで教えていただけると大変ありがたいです)

他の人に説明する仕様になっていませんこと、お断りしておきます。

それぞれのコードと実行結果だけ、以下に記載します。
(import類とapi keyの設定は省略)

ChatCompletion

def get_meigen(num):
    try:
        # APIのエンドポイント
        url = f"https://meigen.doodlenote.net/api/json.php?c={num}"
        # APIリクエストを送信
        response = requests.get(url)
        # レスポンスの内容をJSON形式で取得
        data = response.json()
        return data
    except ValueError:
        return "名言は取得できませんでした"


def get_meigen(num):
    try:
        # APIのエンドポイント
        url = f"https://meigen.doodlenote.net/api/json.php?c={num}"
        # APIリクエストを送信
        response = requests.get(url)
        # レスポンスの内容をJSON形式で取得
        data = response.json()
        # 名言リストを文字列に変換
        meigen_list = []
        for item in data:
            meigen_list.append(f"「{item['meigen']}」 - {item['auther']}")
        # リストを改行区切りの文字列に変換して返す
        return "\n".join(meigen_list)
    except ValueError:
        return "名言は取得できませんでした"


# 上記の2つの関数の説明を変数toolsに入れる
tools = [
    {
        "type": "function",
        "function": {
            "name": "cal_you_lived",
            "description": "ユーザーが入力した誕生日から数えて今日は何日目かを計算して、日数を返す",
            "parameters": {
                "type": "object",
                "properties": {
                    "birthday ": {
                        "type": "string",
                        "description": "ユーザーの生年月日。format:yyyy-mm-dd.  e.g. 2001-11-10 ",
                    },
                },
                "required": ["birthday"],
            },
        },
    },
    {
        "type": "function",
        "function": {
            "name": "get_meigen",
            "description": "api経由で名言をnumの数だけ取得する",
            "parameters": {
                "type": "object",
                "properties": {
                    "num": {
                        "type": "string",
                        "description": "名言の数 最大数は10。 e.g. 3 ",
                    },
                },
                "required": ["num"],
            },
        },
    }
]


# messageを保存するlistの初期化
messages = []
# gptに問い合わせる。二つの関数がcallされる
message = "私の誕生日は、昭和41年10月10日です。"

messages = [
    {'role':'system', 'content':
     "ユーザが入力した誕生日から数えて、今日が何日目かを計算してください ¥n\
     同時に、api経由で名言3つ取得してください ¥n"},
    {"role": "user", "content": message}
    ]

response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=messages,
    tools=tools,
    tool_choice="auto",
    )

# 1回目の問い合わせ回答を変数に入れる
response_message = response.choices[0].message
# 問い合わせ回答をmessagesに追加
messages.append(response_message)

# callする関数を変数に入れて実行
tool_calls = response_message.tool_calls
if tool_calls:
    available_functions = {
        "cal_you_lived": cal_you_lived,
        "get_meigen": get_meigen,
    }
    for tool_call in tool_calls:
        # tool_callsにあるfunctionを実行する準備
        function_name = tool_call.function.name
        function_to_call = available_functions[function_name]
        function_args = json.loads(tool_call.function.arguments)

        # 関数を引数とともに実行
        if function_name == "cal_you_lived":
            function_response = function_to_call(
                birthday=function_args.get("birthday"),
            )
        elif function_name == "get_meigen":
            function_response = function_to_call(
                num=function_args.get("num"),
            )

        # 呼び出して実行した関数の情報と引数をmessagesに追加
        messages.append(
            {
                "tool_call_id": tool_call.id,
                "role": "tool",
                "name": function_name,
                "content": function_response,
            }
        )

# 2回目の問い合わせでお祝いしてもらう
message = {'role':'user','content':'あなたは、ユーザーが今日生まれてから何日目であることを、ぴったりな名言を1つ引用してお祝い文を作って、祝福してください'}
messages.append(message)

second_response = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=messages,
)

# 問い合わせの結果の印字
print(second_response.choices[0].message.content)

おめでとうございます!今日であなたは誕生から21130日目を迎えました。 「私は生きているときに、死以外のあらゆるものに対して備えをしていた。今、私は死なねばならぬ。それでも、まだなんの備えもない。」 - チャールズ・ボールドウィン この名言は、人生を充実させることや、時間を大切にすることの重要性を教えてくれます。これからも素晴らしい日々をお過ごしください!

assistans(関数の定義類は省略)

# assistantsの作成
assistant = client.beta.assistants.create(
    name="birthday_calculator",
    instructions="古今東西の名言を引用して祝福するプロのコピーライダーです。ユーザーを幸せな気持ちにします",
    model="gpt-4o-mini",
    # ツールの定義
    tools = tools
    )

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

# messageの作成
message = client.beta.threads.messages.create(
  thread_id=thread.id,
  role="assistant",
  content="ユーザが入力した誕生日から数えて、今日が何日目かを計算してください。同時に、api経由で名言3つ取得してください。"
)

message = client.beta.threads.messages.create(
  thread_id=thread.id,
  role="user",
  content="私の誕生日は、昭和41年11月11日です。3つの名言の中からお祝いにピッタリな明言を1つ引用して、今日まで生きてきた私を祝福してください。"
)


# assistantを実行する
run = client.beta.threads.runs.create_and_poll(
  thread_id=thread.id,
  assistant_id=assistant.id,
)

if run.status == 'completed':
  messages = client.beta.threads.messages.list(
    thread_id=thread.id
  )
  print(messages)
else:
    print("\n")
#   print(run.status)

# 関数実行結果のリスト
tool_outputs = []

available_functions = {
    "cal_you_lived": cal_you_lived,
    "get_meigen": get_meigen,
}

for tool in run.required_action.submit_tool_outputs.tool_calls:
    # tool_callsにあるfunctionを実行する準備
    function_name = tool.function.name
    function_to_call = available_functions[function_name]
    function_args = json.loads(tool.function.arguments)
    # 関数を引数とともに実行
    if function_name == "cal_you_lived":
        function_response = function_to_call(
            birthday=function_args.get("birthday"),
        )
    elif function_name == "get_meigen":
        function_response = function_to_call(
            num=function_args.get("num"),
        )
    tool_outputs.append({
    "tool_call_id": tool.id,
    "output": function_response,
    })

# tool_outputsを使って2回目のrunを実行し、問い合わせ結果を印字
if tool_outputs:
  try:
    run = client.beta.threads.runs.submit_tool_outputs_and_poll(
      thread_id=thread.id,
      run_id=run.id,
      tool_outputs=tool_outputs
    )
    # print("Tool outputs submitted successfully.")
  except Exception as e:
    print("Failed to submit tool outputs:", e)
else:
  print("No tool outputs to submit.")

if run.status == 'completed':
  messages = client.beta.threads.messages.list(
    thread_id=thread.id
  )
  print(messages.data[0].content[0].text.value)
else:
  print(run.status)

お誕生日おめでとうございます!今日であなたは**21,098日**生きてこられましたね。 その人生を祝福するにふさわしい名言を一つ、引用します。 「日本人は働くことを美化しすぎている気がしますね。」 - 西村博之(ひろゆき) この言葉は、ただ働くことが美徳ではなく、人生のさまざまな側面を楽しむことこそが重要だと教えてくれます。あなたのこれまでの道のりも、多くの喜びや発見があったことでしょう。これからの人生も、ぜひ素晴らしい瞬間で満たされますように!




最後に
これ、いい感じのデザインにして、音声つけたり、お祝いテキストを元にAIで作曲した音楽を後ろで流したりするスマホアプリにしたら、面白いと思います。(私は毎日使いたい)
今の私には、これを実装まで持っていく力がないので、興味のある方は、ぜひ実装してみてください。
実装したら、教えてくださいね。


いいなと思ったら応援しよう!