Spaces:
Runtime error
Runtime error
ver1.2
#2
by
sim-so
- opened
- .gitignore +3 -0
- README.md +1 -1
- __pycache__/app.cpython-310.pyc +0 -0
- __pycache__/app_test.cpython-310.pyc +0 -0
- app.py +86 -57
- data/rule.md +10 -0
- src/__pycache__/functions.cpython-310.pyc +0 -0
- src/__pycache__/semantle.cpython-310.pyc +0 -0
- src/__pycache__/utils.cpython-310.pyc +0 -0
- src/functions.py +26 -29
- src/semantle.py +16 -7
- src/utils.py +0 -15
.gitignore
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
# cache files
|
2 |
+
__pycache__
|
3 |
+
.DS_store
|
README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
---
|
2 |
title: Play Semantle with GPT (Japanese)
|
3 |
-
emoji:
|
4 |
colorFrom: pink
|
5 |
colorTo: purple
|
6 |
sdk: gradio
|
|
|
1 |
---
|
2 |
title: Play Semantle with GPT (Japanese)
|
3 |
+
emoji: 🍡💬
|
4 |
colorFrom: pink
|
5 |
colorTo: purple
|
6 |
sdk: gradio
|
__pycache__/app.cpython-310.pyc
DELETED
Binary file (5.95 kB)
|
|
__pycache__/app_test.cpython-310.pyc
DELETED
Binary file (3.66 kB)
|
|
app.py
CHANGED
@@ -5,75 +5,87 @@ import pandas as pd
|
|
5 |
import gradio as gr
|
6 |
import openai
|
7 |
|
8 |
-
from src.semantle import get_guess, get_secret
|
9 |
from src.functions import get_functions
|
10 |
-
from src.utils import add_guess
|
11 |
|
12 |
GPT_MODEL = "gpt-3.5-turbo"
|
13 |
-
TITLE = "やりとり
|
14 |
|
15 |
-
|
16 |
-
|
17 |
|
18 |
-
def
|
19 |
-
|
20 |
-
chat_messages = [{"role": "user", "content": user_input}]
|
21 |
-
response = openai.ChatCompletion.create(
|
22 |
-
model=GPT_MODEL,
|
23 |
-
messages=system_message+chat_messages,
|
24 |
-
functions=get_functions()
|
25 |
-
)
|
26 |
-
response_message = response.choices[0].message
|
27 |
|
28 |
-
|
29 |
-
if response_message.get("function_call"):
|
30 |
# Step 3: call the function
|
31 |
# Note: the JSON response may not always be valid; be sure to handle errors
|
32 |
available_functions = {
|
33 |
-
"
|
34 |
-
"
|
|
|
|
|
35 |
}
|
36 |
-
function_name =
|
37 |
function_to_call = available_functions[function_name]
|
38 |
-
function_args = json.loads(
|
39 |
function_response = function_to_call(
|
40 |
-
|
41 |
-
puzzle_num=puzzle_num
|
42 |
)
|
43 |
-
|
44 |
-
|
|
|
45 |
# Step 4: send the info on the function call and function response to GPT
|
46 |
-
chat_messages.append(response_message.to_dict()) # extend conversation with assistant's reply
|
47 |
chat_messages.append(
|
48 |
{"role": "function",
|
49 |
"name": function_name,
|
50 |
-
"content":
|
51 |
) # extend conversation with function response
|
52 |
-
|
53 |
model=GPT_MODEL,
|
54 |
-
messages=
|
|
|
|
|
55 |
) # get a new response from GPT where it can se the function response
|
56 |
-
chat_messages.append(
|
57 |
-
|
58 |
-
return chat_messages[-1]
|
59 |
-
|
60 |
-
chat_messages.append(response_message.to_dict())
|
61 |
-
chat_history = chat_history[-8:] + chat_messages
|
62 |
-
return chat_messages[-1]
|
63 |
|
64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
66 |
with gr.Row():
|
67 |
gr.Markdown(
|
68 |
"""
|
69 |
-
# やりとり
|
70 |
-
[semantle日本語版](https://semantoru.com/)
|
71 |
## ゲームのやり方
|
72 |
- 正解は一つの単語で、これを答えるとゲームの勝利になります。
|
73 |
- 推測した単語が正解じゃない場合、類似度スコアと順位が表示されます。それは正解を推測する大事なヒントになります。
|
74 |
-
##
|
75 |
- 単語のスコアとランク以外に他のヒントがもらえます。
|
76 |
- ゲームに関して困っている時、何か質問してみてください。
|
|
|
|
|
77 |
"""
|
78 |
)
|
79 |
|
@@ -83,10 +95,11 @@ with gr.Blocks() as demo:
|
|
83 |
idx = gr.State(value=0)
|
84 |
guessed = gr.State(value=set())
|
85 |
guesses = gr.State(value=list())
|
86 |
-
cur_guess = gr.
|
|
|
87 |
guesses_table = gr.DataFrame(
|
88 |
-
value=pd.DataFrame(columns=["
|
89 |
-
headers=["
|
90 |
datatype=["number", "str", "number", "str"],
|
91 |
elem_id="guesses-table",
|
92 |
interactive=False
|
@@ -102,27 +115,42 @@ with gr.Blocks() as demo:
|
|
102 |
|
103 |
def unfreeze():
|
104 |
return msg.update(interactive=True, placeholder="正解と思う言葉を答えてください。")
|
105 |
-
def
|
106 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
107 |
|
108 |
-
def respond(
|
109 |
-
|
110 |
-
|
|
|
|
|
|
|
|
|
|
|
111 |
time.sleep(2)
|
112 |
-
return
|
113 |
|
114 |
def update_guesses(cur, i, guessed_words, guesses_df):
|
115 |
-
if cur[
|
116 |
-
guessed_words.add(cur[
|
117 |
-
|
|
|
|
|
118 |
i += 1
|
119 |
-
guesses_df = guesses_df.sort_values(by=["
|
120 |
return i, guessed_words, guesses_df
|
121 |
|
122 |
-
api_key.change(unfreeze, [], [msg]).then(
|
123 |
-
msg.submit(respond, [
|
124 |
-
|
125 |
-
)
|
126 |
|
127 |
gr.Examples(
|
128 |
[
|
@@ -137,4 +165,5 @@ with gr.Blocks() as demo:
|
|
137 |
)
|
138 |
|
139 |
if __name__ == "__main__":
|
140 |
-
demo.queue(concurrency_count=20).launch()
|
|
|
|
5 |
import gradio as gr
|
6 |
import openai
|
7 |
|
8 |
+
from src.semantle import get_guess, get_secret, get_puzzle_num
|
9 |
from src.functions import get_functions
|
|
|
10 |
|
11 |
GPT_MODEL = "gpt-3.5-turbo"
|
12 |
+
TITLE = "やりとりxイミトル"
|
13 |
|
14 |
+
with open("data/rule.md", "r", encoding="utf-8") as f:
|
15 |
+
RULEBOOK = "\n".join(f.readlines())
|
16 |
|
17 |
+
def get_rulebook():
|
18 |
+
return RULEBOOK
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
|
20 |
+
def _execute_function(function_call, chat_messages, guess_result):
|
|
|
21 |
# Step 3: call the function
|
22 |
# Note: the JSON response may not always be valid; be sure to handle errors
|
23 |
available_functions = {
|
24 |
+
"guess_word": get_guess,
|
25 |
+
"lookup_answer": get_secret,
|
26 |
+
"retrieve_puzzle_num": get_puzzle_num,
|
27 |
+
"read_rule": get_rulebook,
|
28 |
}
|
29 |
+
function_name = function_call["name"]
|
30 |
function_to_call = available_functions[function_name]
|
31 |
+
function_args = json.loads(function_call["arguments"])
|
32 |
function_response = function_to_call(
|
33 |
+
**function_args
|
|
|
34 |
)
|
35 |
+
if function_call["name"] == "guess_word":
|
36 |
+
guess_result = function_response
|
37 |
+
print(function_response)
|
38 |
# Step 4: send the info on the function call and function response to GPT
|
|
|
39 |
chat_messages.append(
|
40 |
{"role": "function",
|
41 |
"name": function_name,
|
42 |
+
"content": f"{function_response}"}
|
43 |
) # extend conversation with function response
|
44 |
+
next_response = openai.ChatCompletion.create(
|
45 |
model=GPT_MODEL,
|
46 |
+
messages=chat_messages,
|
47 |
+
functions=get_functions(),
|
48 |
+
temperature=0
|
49 |
) # get a new response from GPT where it can se the function response
|
50 |
+
chat_messages.append(next_response.choices[0].message.to_dict())
|
51 |
+
return next_response.choices[0].message.to_dict(), chat_messages, guess_result
|
|
|
|
|
|
|
|
|
|
|
52 |
|
53 |
+
def create_chat(system_input, user_input, guess_result=dict()):
|
54 |
+
chat_messages = []
|
55 |
+
for s in system_input:
|
56 |
+
chat_messages.append({"role": "system", "content": s})
|
57 |
+
chat_messages.append({"role": "user", "content": user_input})
|
58 |
+
response = openai.ChatCompletion.create(
|
59 |
+
model=GPT_MODEL,
|
60 |
+
messages=chat_messages,
|
61 |
+
functions=get_functions(),
|
62 |
+
temperature=0
|
63 |
+
)
|
64 |
+
response_message = response.choices[0].message.to_dict()
|
65 |
+
chat_messages.append(response_message) # extend conversation with assistant's reply
|
66 |
|
67 |
+
# Step 2: check if CPT wanted to call a function
|
68 |
+
while response_message.get("function_call"):
|
69 |
+
# Step 3: call the function
|
70 |
+
# Note: the JSON response may not always be valid; be sure to handle errors
|
71 |
+
response_message, chat_messages, guess_result = _execute_function(response_message["function_call"], chat_messages, guess_result)
|
72 |
+
print(chat_messages)
|
73 |
+
return response_message, chat_messages, guess_result
|
74 |
+
|
75 |
+
with gr.Blocks() as demo:
|
76 |
with gr.Row():
|
77 |
gr.Markdown(
|
78 |
"""
|
79 |
+
# やりとりxイミトル
|
80 |
+
「イミトル」は[semantle日本語版](https://semantoru.com/)の名前で、こちらはイミトルをassistantと楽しめるspaceです。
|
81 |
## ゲームのやり方
|
82 |
- 正解は一つの単語で、これを答えるとゲームの勝利になります。
|
83 |
- 推測した単語が正解じゃない場合、類似度スコアと順位が表示されます。それは正解を推測する大事なヒントになります。
|
84 |
+
## assistantの仕事
|
85 |
- 単語のスコアとランク以外に他のヒントがもらえます。
|
86 |
- ゲームに関して困っている時、何か質問してみてください。
|
87 |
+
## ご了承のお願い
|
88 |
+
- ゲームをするため、openaiのapiが必要です。答えによって少々tokenを使うようになります。
|
89 |
"""
|
90 |
)
|
91 |
|
|
|
95 |
idx = gr.State(value=0)
|
96 |
guessed = gr.State(value=set())
|
97 |
guesses = gr.State(value=list())
|
98 |
+
cur_guess = gr.JSON(visible=False)
|
99 |
+
history = gr.State(list())
|
100 |
guesses_table = gr.DataFrame(
|
101 |
+
value=pd.DataFrame(columns=["i", "guess", "sim", "rank"]),
|
102 |
+
headers=["i", "guess", "score", "rank"],
|
103 |
datatype=["number", "str", "number", "str"],
|
104 |
elem_id="guesses-table",
|
105 |
interactive=False
|
|
|
115 |
|
116 |
def unfreeze():
|
117 |
return msg.update(interactive=True, placeholder="正解と思う言葉を答えてください。")
|
118 |
+
def reset_history():
|
119 |
+
return list()
|
120 |
+
def greet(key, gradio_messages):
|
121 |
+
openai.api_key = key
|
122 |
+
system_input = [get_rulebook(), "あなたは「イミトル」というゲームの進行役です。ユーザーが答えをするところです。よろしくお願いします。"]
|
123 |
+
user_input = ""
|
124 |
+
reply, _, _ = create_chat(system_input, user_input, guess_result=dict())
|
125 |
+
gradio_messages.append(("", reply["content"]))
|
126 |
+
# gradio_messages.append(("", f"今日のゲームのパズル番号は{get_puzzle_num()}です。それでは、始めましょう!言葉を当ててみてください。"))
|
127 |
+
time.sleep(2)
|
128 |
+
return gradio_messages
|
129 |
|
130 |
+
def respond(user_input, gradio_messages, guess_result=dict()):
|
131 |
+
system_input = [get_rulebook(), """あなたは「イミトル」というゲームの進行役です。
|
132 |
+
普段、ユーザーは答えをしますが、たまにゲームに関するヒントをリクエストをします。短くても丁寧に答えてください。
|
133 |
+
ヒントを教える場合、正解を必ず隠してください。絶対、正解を言わないでください。"""]
|
134 |
+
|
135 |
+
_user_input = "答え:" + user_input
|
136 |
+
reply, messages, guess_result = create_chat(system_input, _user_input, guess_result=dict())
|
137 |
+
gradio_messages.append((user_input, reply["content"]))
|
138 |
time.sleep(2)
|
139 |
+
return gradio_messages, guess_result
|
140 |
|
141 |
def update_guesses(cur, i, guessed_words, guesses_df):
|
142 |
+
if cur.get('guess') and cur['guess'] not in guessed_words:
|
143 |
+
guessed_words.add(cur['guess'])
|
144 |
+
cur['sim'] = round(cur['sim'], 2)
|
145 |
+
cur['i'] = i
|
146 |
+
guesses_df.loc[i] = cur
|
147 |
i += 1
|
148 |
+
guesses_df = guesses_df.sort_values(by=["sim"], ascending=False)
|
149 |
return i, guessed_words, guesses_df
|
150 |
|
151 |
+
api_key.change(unfreeze, [], [msg]).then(reset_history, [], [history]).then(greet, [api_key, chatbot], [chatbot])
|
152 |
+
msg.submit(respond, [msg, chatbot, cur_guess], [chatbot, cur_guess])
|
153 |
+
cur_guess.change(update_guesses, [cur_guess, idx, guessed, guesses_table], [idx, guessed, guesses_table])
|
|
|
154 |
|
155 |
gr.Examples(
|
156 |
[
|
|
|
165 |
)
|
166 |
|
167 |
if __name__ == "__main__":
|
168 |
+
demo.queue(concurrency_count=20).launch()
|
169 |
+
# demo.queue(concurrency_count=20).launch()
|
data/rule.md
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# イミトル
|
2 |
+
|
3 |
+
## ゲームの遊び方
|
4 |
+
今日の言葉を当てるゲームです。 正解と推測される単語を答えると、正解かどうかがわかります。正解でない場合、正解に近い度合いと順位がわかります。正解に近い程度とは単語の意味や文脈、単語の言語的特性などによって決まります。 推測した単語の近い度合いと順位を参考にして、答えを当て続けてみてください。
|
5 |
+
|
6 |
+
## ヒントのについて
|
7 |
+
もし正解が難しかったり、ゲームのルールがわかりにくい場合は、ヒントを求めることができます。正解単語を使う例文、正解単語とある単語の意味や文法的な共通点など正解に関する様々な情報を聞くことができます。正し、ヒントに正解単語は`<mask>`などで隠すようになっています。
|
8 |
+
|
9 |
+
## 「近い度合いと順位」について
|
10 |
+
「近い度合い」は正解単語との類似度で、-100から+100までの数字で表します。「順位」はデータベースの中、近い度合いの相対的な位置で、1位から1000位まで表します。「近い度合い('sim')」が100の単語が正解単語です。
|
src/__pycache__/functions.cpython-310.pyc
DELETED
Binary file (764 Bytes)
|
|
src/__pycache__/semantle.cpython-310.pyc
DELETED
Binary file (801 Bytes)
|
|
src/__pycache__/utils.cpython-310.pyc
DELETED
Binary file (986 Bytes)
|
|
src/functions.py
CHANGED
@@ -7,42 +7,29 @@ guess_word = {"name": "guess_word",
|
|
7 |
"type": "string",
|
8 |
"description": "A single Japanese word to guess, which is can be a noun, verb, adverb or adjective. e.g. 空, 近い, 行く, etc."
|
9 |
},
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
},
|
15 |
-
"required": ["word"
|
16 |
}}
|
17 |
|
18 |
-
|
19 |
-
"description": "Use this function to
|
20 |
-
"parameters": {
|
21 |
-
"type": "object",
|
22 |
-
"properties": {
|
23 |
-
"puzzle_num": {
|
24 |
-
"type": "interger",
|
25 |
-
"description": "An index for today's puzzle set."
|
26 |
-
}
|
27 |
-
},
|
28 |
-
"required": ["puzzle_num"]
|
29 |
-
}}
|
30 |
-
|
31 |
-
get_secret = {"name": "get_secret",
|
32 |
-
"description": "You can check what the correct answer of today's puzzle is by this function.",
|
33 |
"parameters": {
|
34 |
"type": "object",
|
35 |
"properties": {
|
36 |
-
"puzzle_num": {
|
37 |
-
|
38 |
-
|
39 |
}
|
40 |
-
},
|
41 |
-
"required": ["puzzle_num"]
|
42 |
}}
|
43 |
|
44 |
-
|
45 |
-
"description": "Use this function to
|
46 |
"parameters": {
|
47 |
"type": "object",
|
48 |
"properties": {}
|
@@ -60,13 +47,23 @@ update_history = {"name": "update_history",
|
|
60 |
},
|
61 |
"guess_history": {
|
62 |
"type": "object",
|
63 |
-
"description": "A dataframe containing the guessed words and its
|
64 |
}
|
65 |
},
|
66 |
"required": ["current_guess", "guess_history"]
|
67 |
}}
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
|
69 |
|
70 |
def get_functions():
|
71 |
-
functions = [guess_word
|
|
|
|
|
|
|
|
|
72 |
return functions
|
|
|
7 |
"type": "string",
|
8 |
"description": "A single Japanese word to guess, which is can be a noun, verb, adverb or adjective. e.g. 空, 近い, 行く, etc."
|
9 |
},
|
10 |
+
# "puzzle_num": {
|
11 |
+
# "type": "integer",
|
12 |
+
# "description": "An index indicating today's puzzle."
|
13 |
+
# }
|
14 |
},
|
15 |
+
"required": ["word"]
|
16 |
}}
|
17 |
|
18 |
+
lookup_answer = {"name": "lookup_answer",
|
19 |
+
"description": "Use this function to check the correct answer of today's puzzle.",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
"parameters": {
|
21 |
"type": "object",
|
22 |
"properties": {
|
23 |
+
# "puzzle_num": {
|
24 |
+
# "type": "integer",
|
25 |
+
# "description": "An index indicating today's puzzle."
|
26 |
}
|
27 |
+
# },
|
28 |
+
# "required": ["puzzle_num"]
|
29 |
}}
|
30 |
|
31 |
+
retrieve_puzzle_num = {"name": "retrieve_puzzle_num",
|
32 |
+
"description": "Use this function to retrieve today's puzzle number.",
|
33 |
"parameters": {
|
34 |
"type": "object",
|
35 |
"properties": {}
|
|
|
47 |
},
|
48 |
"guess_history": {
|
49 |
"type": "object",
|
50 |
+
"description": "A dataframe containing the guessed words and its score and rank in a row."
|
51 |
}
|
52 |
},
|
53 |
"required": ["current_guess", "guess_history"]
|
54 |
}}
|
55 |
+
read_rule = {"name": "read_rule",
|
56 |
+
"description": "Use this function to read the game rule for clarification of your response.",
|
57 |
+
"parameters": {
|
58 |
+
"type": "object",
|
59 |
+
"properties": {},
|
60 |
+
}}
|
61 |
|
62 |
|
63 |
def get_functions():
|
64 |
+
functions = [guess_word,
|
65 |
+
lookup_answer,
|
66 |
+
# retrieve_puzzle_num,
|
67 |
+
# update_history,
|
68 |
+
read_rule]
|
69 |
return functions
|
src/semantle.py
CHANGED
@@ -2,25 +2,34 @@ from datetime import date, datetime
|
|
2 |
from pytz import utc, timezone
|
3 |
import requests
|
4 |
|
5 |
-
def get_secret(
|
|
|
6 |
request_url = f"https://semantoru.com/yesterday/{puzzle_num+1}"
|
7 |
-
response = requests.get(request_url)
|
8 |
if response.status_code == 200:
|
9 |
return response.content
|
10 |
else:
|
11 |
return "Not found error."
|
12 |
|
13 |
-
def get_guess(word: str
|
|
|
14 |
request_url = f"https://semantoru.com/guess/{puzzle_num}/{word}"
|
15 |
-
|
|
|
16 |
print(response.status_code)
|
17 |
if response.status_code == 200:
|
18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
else:
|
20 |
return {"guess": word,
|
21 |
"sim": None,
|
22 |
"rank": None}
|
23 |
|
24 |
def get_puzzle_num():
|
25 |
-
|
26 |
-
return (utc.localize(datetime.utcnow()).astimezone(timezone('Asia/Tokyo')).date() -
|
|
|
2 |
from pytz import utc, timezone
|
3 |
import requests
|
4 |
|
5 |
+
def get_secret():
|
6 |
+
puzzle_num = get_puzzle_num()
|
7 |
request_url = f"https://semantoru.com/yesterday/{puzzle_num+1}"
|
8 |
+
response = requests.get(request_url, timeout=5)
|
9 |
if response.status_code == 200:
|
10 |
return response.content
|
11 |
else:
|
12 |
return "Not found error."
|
13 |
|
14 |
+
def get_guess(word: str):
|
15 |
+
puzzle_num = get_puzzle_num()
|
16 |
request_url = f"https://semantoru.com/guess/{puzzle_num}/{word}"
|
17 |
+
print(request_url)
|
18 |
+
response = requests.get(request_url, timeout=5)
|
19 |
print(response.status_code)
|
20 |
if response.status_code == 200:
|
21 |
+
rtn = response.json()
|
22 |
+
print(rtn)
|
23 |
+
if rtn['rank'] == '正解!':
|
24 |
+
return rtn
|
25 |
+
elif rtn['rank'] > 1000:
|
26 |
+
rtn['rank'] = '?'
|
27 |
+
return rtn
|
28 |
else:
|
29 |
return {"guess": word,
|
30 |
"sim": None,
|
31 |
"rank": None}
|
32 |
|
33 |
def get_puzzle_num():
|
34 |
+
fisrt_day = date(2023, 4, 2)
|
35 |
+
return (utc.localize(datetime.utcnow()).astimezone(timezone('Asia/Tokyo')).date() - fisrt_day).days
|
src/utils.py
CHANGED
@@ -1,18 +1,3 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
def add_guess(guess_result, guessed, guesses):
|
4 |
-
word, sim, rank = guess_result
|
5 |
-
if sim:
|
6 |
-
if word not in guessed:
|
7 |
-
sim = round(sim, 2)
|
8 |
-
rank = "情報なし" if rank == 1001 else rank
|
9 |
-
guesses.loc[len(guessed)] = ([len(guessed), word, sim, rank])
|
10 |
-
guessed.add(word)
|
11 |
-
cur_result = format_result(word, sim, rank)
|
12 |
-
else:
|
13 |
-
cur_result = "不正解: 正しくない単語"
|
14 |
-
return "\n".join([cur_result, "最高スコア:", format_table(guesses)]), guessed, guesses
|
15 |
-
|
16 |
def format_result(word, sim, rank):
|
17 |
return f"{word}: スコア {sim}, ランク {rank}"
|
18 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
def format_result(word, sim, rank):
|
2 |
return f"{word}: スコア {sim}, ランク {rank}"
|
3 |
|