function callingで、ChatGPTにQiitaのトレンド記事を表示させる

こんばんは、すきにーです。

ChatGPTの新機能であるfunction callingを使って、
ChatGPTにQiitaのトレンド記事を表示させたいと思います。
https://qiita.com/trend

現在のトレンドのトップ3はこんな感じです
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3116479/414270b1-b8b4-307d-3f5c-e65040edad1a.png)


# function callingとは
イメージとしては、GPT4のプラグイン機能をAPIで実装できるようになったという感じです。

Function callingは大きく3つのステップに分かれ、
>① ユーザの質問からどの関数を呼ぶかをChatGPTに決めてもらう
>② chatGPTが決めた関数を呼び出して処理実行
>③ 関数の実行結果と最初の質問をChatGPTに渡して回答を生成する

という流れになります。

function callingの説明は[こちらの記事](https://qiita.com/windows222/items/bc2c25b3e7526d8926ec)が分かりやすかったです。


# トレンド記事を表示させるコードを実装
まずライブラリをインポートします。
```py
import openai
import os
import json

# OpenAI APIキーの準備
openai.api_key = "APIキー"
```

次にqiitaのトレンドを取得する関数を定義します。記事の取得にはQiitaのAPIを使用しました。
引数にタグを入れることで、タグに関するトレンドを表示させます。
（何もないときはトレンドTOP5が表示されます）
```py
def get_qiita_trend(tag=None):
    url = "https://qiita-api.vercel.app/api/trend"
    response = requests.get(url)

    # ResponseオブジェクトからJSONデータを取得
    data = response.json()

    # 'node'の値を取得
    node_values = [item['node'] for item in data]

    # DataFrameを作成
    df = pd.DataFrame(node_values)
    
    # 'tags'列の中のJSONデータの'name'キーの値を表示
    df['tags'] = df['tags'].apply(lambda x: [tag['name'] for tag in x])
    
    if tag:
      df = df[df[['title', 'tags']].apply(lambda x: tag in x)] 

    # 指定された行数を表示
    qiita_trend = df[['title', 'tags', 'linkUrl']].head()
    
    return qiita_trend.head().to_json(orient='records')
```
functionの定義
```py
functions=[
    {
        "name": "get_qiita_trend",
        "description": "qiitaのトレンド記事を取得",
        "parameters": {
            "type": "object",
            "properties": {
                "tag": {
                    "type": "string",
                    "description": "表示させたいトレンドのタグ",
                },
            },
        },
    }
]
```
入力文から 「呼び出す関数」 「引数」 をChatGPTに決めてもらう
```py
model_name = "gpt-3.5-turbo-0613"

question = "Qiitaのトレンドを教えて"

response = openai.ChatCompletion.create(
    model=model_name,
    messages=[
        {"role": "user", "content": question},
    ],
    functions=functions,
    function_call="auto",
)
message = response["choices"][0]["message"]
```
messageの中身はこんな感じです。
今回は「Qiitaのトレンドを教えて」という入力だったので、引数であるargumentsの中身は空です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3116479/f5043ac7-4e0e-83d9-b1b8-74486abc34cc.png)
もし入力文が「JavaScriptのトレンドを教えて」であった場合、argumentsのtagはJavaScriptになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3116479/542c9bd8-f0ac-5f64-adf5-c3fb7c699bcc.png)


get_qiita_trend関数の実行
```py
if message.get("function_call"):

    function_name = message["function_call"]["name"]

    arguments = json.loads(message["function_call"]["arguments"])
    function_response = get_qiita_trend(
        tag=arguments.get("tag"),
    )
```
関数を実行した結果をもとに、ChatGPTが回答を生成する
```py
    second_response = openai.ChatCompletion.create(
        model=model_name,
        messages=[
            {"role": "user", "content": question},
            message,
            {
                "role": "function",
                "name": function_name,
                "content": function_response,
            },
        ],
    )
    
    print(second_response.choices[0]["message"]["content"].strip())
```
ChatGPTの出力
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3116479/b8ca6e52-d4db-6c55-73f5-dbe4cc5faf86.png)

入力文が「JavaScriptのトレンドを教えて」の場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3116479/fbea7058-fc45-4f76-05f8-49695bf51779.png)

# おわりに
以上がfunction callingでQiitaのトレンドを表示させる方法です。
function calling、使い方次第ではもっとすごいことができそうな気がしますが、私の中にはそれが浮かんでいないです...

お読みいただきありがとうございました！

# 参考記事
[毎朝Qiitaトレンド記事が届くLINE Botを開発してみた](https://taraoblog.com/%E6%AF%8E%E6%9C%9Dqiita%E3%83%88%E3%83%AC%E3%83%B3%E3%83%89%E8%A8%98%E4%BA%8B%E3%81%8C%E5%B1%8A%E3%81%8Fline-bot%E3%82%92%E9%96%8B%E7%99%BA%E3%81%97%E3%81%A6%E3%81%BF%E3%81%9F/)
[OpenAIがChatGPTを更にパワーアップ！「Function Calling」の使い方とは？](https://qiita.com/windows222/items/bc2c25b3e7526d8926ec)
[OpenAI Function callingで複雑なタスクを簡単に実現
](https://qiita.com/canadie/items/161aaa41a5d110470e8f)https://platform.openai.com/docs/guides/gpt/function-calling