Function Callingで遊戯王APIを呼び出す 2(rapidfuzzによる名寄せ機能追加)

Function Callingで遊戯王APIを呼び出す 2(rapidfuzzによる名寄せ機能追加)

OpenAI APIを活用した遊戯王APIからの情報取得システムがパワーアップ

前回作成したFunction Callingから遊戯王APIにアクセスするシステムは英語のカード名にしか対応しない問題があった。

そこで、

  1. 日本語への対応
  2. あいまい入力への対応

を行った。

1. 実験結果

まずは実験結果から見せる。

  • 実験1
    1
    2
    [SYSTEM MESSAGE]テキストを入力してください: バスターブレイダーーの攻撃力は?
    [AI]バスターブレイダーーの攻撃力は2600です。ただし、相手のフィールドや墓地にいるドラゴン族モンスターが1体につき攻撃力が500アップします

「バスターブレイダーー」という間違った入力に対して答えていることが分かる。
また、普通のChatGPTと比較すると遊戯王APIから取得した正確な情報を返しているところが優れている。

  • 実験2
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    [SYSTEM MESSAGE]テキストを入力してください: オシリスの天空流について表にまとめて!
    [AI]オシリスの天空流についての表は以下の通りです。
    ------------------------------
    | 名前 | オシリスの天空竜 |
    ------------------------------
    | レベル | 10 |
    ------------------------------
    | 属性 | 神 |
    | 種族 | 神獣 |
    ------------------------------
    | 効果 | モンスターがフィールドに召喚された場合、そのモンスターの攻撃力を2000ポイント下げる。もし下げた結果、攻撃力が0になった場合、そ
    のモンスターは破壊される。手札のカード1枚につき、このカードの攻撃力と守備力は1000ポイントアップする。このカードの召喚は、通常召喚時に3
    体のモンスターをリリースしなければならない。この効果の召喚は無効化されない。このカードの召喚時、カードの効果は発動できない。1ターンに1
    度、このカードが特殊召喚された場合、エンドフェイズ時に墓地に送る。 |

    これがオシリスの天空流のカード情報です。

参照元が英語テキストなので本来の日本語テキストとは若干異なるが正しい答えが返ってきている。
あえて「オシリスの天空流」というこちらの誤字を使って回答しているのはユーザーへの配慮なのか。

  • 実験3
    1
    2
    3
    4
    [SYSTEM MESSAGE]テキストを入力してください: サイコショッカーの攻撃力は?
    [DEBUG][('人造人間-サイコ・ショッカー', 78.75, 12), ('脅威の人造人間-サイコ・ショッカー', 78.75, 3785), ('ダッカー', 77.14285714285715, 2498), ('サイコトラッカー', 75.0, 2730), ('ブロッカー', 67.5, 6870)]
    [AI]「サイコショッカー」というカード名の詳細情報は存在しません。ただし、人造人間-サイコ・ショッカーや脅威の人造人間-サイコ・ショッカ
    ーなど、関連するカードが存在することがわかりました。これらのカードについての攻撃力については詳細情報が必要です。

今回のシステムは、

  1. 最も類似度が高いカード名の類似度を取得
  2. 類似度が80以上であれば:そのカードの情報を返す
  3. 類似度が80未満であれば:類似度上位5件のカード名を返す

という処理にしている。
「サイコショッカー」と「人造人間-サイコ・ショッカー」の類似度は78.75と、80未満だったので正解の候補を返す。

この場合の処理についても良い感じのメッセージを生成してくれている。

2. 前回からの修正点

前回からの修正点を記す。
コードは最後に載せる。

2.1. システムフロー



この図の「関数が選ばれた場合の処理」の中身が追加、変更されている。

2.2. 日本語への対応

たまたま持っていた遊戯王の「日本語カード名」と「カードID」の紐づいた表を使用。
下記のような形式。

  • cardinfo.csv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    rank,name_jp,card_id,atk,def
    1,ブラック・マジシャン,46986414,2500,2100
    2,青眼の白龍,89631139,3000,2500
    3,ブラック・マジシャン・ガール,38033121,2000,1700
    4,マスター・オブ・OZ,27134689,4200,3700
    5,サイバー・ドラゴン,70095154,2100,1600
    .
    .
    .

これは以前の実験で得た副産物。

2.3. あいまい入力への対応

rapidfuzzを使用して名寄せを行う関数search_correct_nameを作成。
入力文字列と最も類似度の高い文字列が正式名称だと判断して処理を行う。

3. コード

今回の実験で使用したコード。
python3.11
rapidfuzz, pandas, openaiを別途インストール

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
from rapidfuzz.process import extract
import pandas as pd
import openai
import os
import json
import requests

# APIキーを取得
openai.api_key = os.environ["OPENAI_API_KEY"]

# 定数
MODEL = "gpt-3.5-turbo-0613"

# カード情報の読み込み
CARD_INFO_DF = pd.read_csv("cardinfo.csv", encoding="utf-8")
CARD_NAME_LIST = CARD_INFO_DF["name_jp"].tolist()


def search_correct_name(input_name):
result = extract(input_name, CARD_NAME_LIST)
print(f"[DEBUG]{result}")
if result[0][1] > 80: # 類似度のトップが80%以上の場合はそのカードのidを返す
card_id = CARD_INFO_DF.iloc[result[0][2]]["card_id"]
return (True, card_id)
else: # 類似度のトップが80%未満の場合は上位5件のカード名と類似度を返す
return (False, result)


# YU-GI-OH APIにカード名を渡してカード情報を取得する関数
def get_card_info_by_name(arguments):
card_name = arguments.get("cardname")

result = search_correct_name(card_name)
if(result[0] == False): # 類似度のトップが80%未満の場合
return f"カード名が完全一致しませんでした。\nカード名類似度上位5件から該当のカードを探してください。\n{result[1]}"

else: # 類似度のトップが80%以上の場合
url = f"https://db.ygoprodeck.com/api/v7/cardinfo.php?id={result[1]}"
response = requests.get(url)
if response.status_code == 200:
# APIから取得した情報をJSON→辞書→文字列に変換
data = response.json()
# 'data'キーの最初の要素から指定されたキーの情報を取得
card_info = data["data"][0]
keys_to_extract = ["id", "name", "type", "frameType", "desc", "atk", "def", "level", "race", "attribute", "archetype"]
extracted_data = {key: card_info.get(key, None) for key in keys_to_extract} # keyが存在しない項目はNoneを返す(魔法罠のatkなど)
# 辞書を文字列に変換
result_string = ", ".join([f"'{key}': {value!r}" for key, value in extracted_data.items()])
return result_string
else:
return "カードが見つかりませんでした"

# Functions Callingで使用する関数
FUNCTIONS = [
{
"name": "get_card_info_by_name",
"description": "YU-Gi-OH APIによってカード名からカード情報を取得",
"parameters": {
"type": "object",
"properties": {
"cardname": {
"type": "string",
"description": "カード名のみを文字列で入力"
}
},
"required": ["cardname"]
}
},
]

# Functions Callingで使用する関数の辞書
FUNCTIONS_DICT = {
"get_card_info_by_name": get_card_info_by_name,
}


# メインの会話処理
def conversation(user_message):
# 【ステップ1】: OpenAI APIにユーザークエリと利用可能な関数を送信して、使う関数を選んでもらう
message_01 = {"role": "user",
"content": user_message}

response_01 = openai.ChatCompletion.create(
model=MODEL,
messages=[
message_01, # ユーザークエリ
],
functions=FUNCTIONS, # 使用可能な関数
function_call="auto" # auto:APIに関数の選択を任せる
)
response_message = response_01["choices"][0]["message"]

if response_message.get("function_call") is None: # 関数が呼び出されなかった場合
print(f"[SYSTEM MESSAGE]関数は呼び出されませんでした。")

else: # 関数が呼び出された場合
# 【ステップ2】: OpenAI APIの選んだ関数をOpenAI APIの選んだ引数で実行
function_name = response_message["function_call"]["name"]
arguments = json.loads(response_message["function_call"]["arguments"])
function_to_call = FUNCTIONS_DICT[function_name]
function_result = function_to_call(arguments)

# 【ステップ3】: 関数の実行結果をOpenAI APIに送信
message_02 = {
"role": "function",
"name": function_name,
"content": function_result,
}
response_02 = openai.ChatCompletion.create(
model=MODEL,
messages=[
message_01, # ユーザークエリ
message_02, # 関数の実行結果
],
)
response_message_02 = response_02["choices"][0]["message"]
print(f"[AI]{response_message_02['content']}")

# メッセージ入力
user_message = input(f"[SYSTEM MESSAGE]テキストを入力してください: ")
conversation(user_message)

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×