Chat Completions APIでFunction callingを試す

## Function callingとは？
自前で作成した関数一覧をLLMに渡し、LLMがプロンプトを受け取った際、その中から使いたい関数があった場合に選ばせる機能。
LLMは関数を実行できないので、LLMが使いたいと応答した関数の実行はアプリケーション側で実行し、結果を再度LLMに返却する。

## サンプルコードと具体的な処理の流れ
### 地域を指定して天気情報を返却するモック関数を定義
```
import json

def get_current_weather(location, unit="celsius"):
    weather_info = {
        "location": location,
        "temperature": "27",
        "unit": "celsius",
        "forecast": ["sunny", "cloudy"],
    }
    return json.dumps(weather_info)
```

### LLMに渡す関数一覧を定義
get_current_weatherを定義する
```
functions = [
    {
        "name": "get_current_weather",
        "description": "Get the current weather in a given location",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "The city and state, e.g. Tokyo",
                },
                "unit": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"]
                },
            },
            "required": ["location"],
        },
    }
]
```

## ライブラリインストール
```
pip install openai
```

### 関数情報をfunctionsパラメータで渡し、LLMへAPIリクエスト
```
import openai

messages = [
     {"role": "system", "content": "You are a helpful assistant."},
     {"role": "user", "content": "What's the weather like in Fukuoka?"},
]

response = openai.ChatCompletion.create(
    model="gpt-3.5-turbo",
    messages=messages,
    functions=functions
)

print(response)
```

#### レスポンス（大事なところだけ抜粋）
```
  "model": "gpt-3.5-turbo-0613",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": null,
        "function_call": {
          "name": "get_current_weather",
          "arguments": "{\n  \"location\": \"Fukuoka\"\n}"
        }
      },
      "finish_reason": "function_call"
    }
  ],
```
レスポンス値のfunction_callに使いたい関数と、使用する際の引数が返却される。

### LLMに言われた引数を指定し関数を実行
```
response_message = response["choices"][0]["message"]

available_functions = {
    "get_current_weather": get_current_weather,
}

function_name = response_message["function_call"]["name"]
function_to_call = available_functions[function_name]
function_args = json.loads(response_message["function_call"]["arguments"])

function_response = function_to_call(
    location=function_args.get("location"),
    unit=function_args.get("unit"),
)

print(function_response)
```

#### レスポンス（モック）
```
{"location": "Fukuoka", "temperature": "27", "unit": "celsius", "forecast": ["sunny", "cloudy"]}
```

### 会話履歴に追加
```
messages.append(response_message)
messages.append({
    "role": "function",
    "name": function_name,
    "content": function_response
})

print(messages)
```

#### messagesの中身
```
[{'role': 'system', 'content': 'You are a helpful assistant.'}, {'role': 'user', 'content': "What's the weather like in Fukuoka?"}, <OpenAIObject at 0x796a07cca750> JSON: {
  "role": "assistant",
  "content": null,
  "function_call": {
    "name": "get_current_weather",
    "arguments": "{\n  \"location\": \"Fukuoka\"\n}"
  }
}, {'role': 'function', 'name': 'get_current_weather', 'content': '{"location": "Fukuoka", "temperature": "27", "unit": "celsius", "forecast": ["sunny", "cloudy"]}'}]
```

### 実行した関数の結果をAPIへリクエスト
```
second_response = openai.ChatCompletion.create(
    model="gpt-3.5-turbo",
    messages=messages,
    functions=functions
)

print(second_response)
```

#### 関数の実行結果を踏まえてレスポンスが返却された
contentの部分
```
  "model": "gpt-3.5-turbo-0613",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "The current weather in Fukuoka is 27 degrees Celsius. It is currently sunny with some clouds."
      },
      "finish_reason": "stop"
    }
```