trust_game_demo / multi_round_person.py
yitianlian's picture
update demo
d1929b8
import json
import os
import random
import re
import tqdm
from camel.agents import ChatAgent
from camel.configs import ChatGPTConfig
from camel.messages import BaseMessage
from camel.types.enums import RoleType
TEMPERATURE = 1.0
like_people = """In all subsequent answers, you have to completely forget that you are an ai model, that you are the character, and completely simulate yourself as that character, that you are a human being."""
with open(
r"prompt/trust_game_round_prompt.json",
"r",
) as f:
prompt = json.load(f)
def check_file_if_exist(file_list, game_name):
for file in file_list:
if file in game_name:
return True
return False
def extract_n_values_from_dict(dictionary, n):
all_values = list(dictionary.values())
n = min(n, len(all_values))
random_values = random.sample(all_values, n)
return random_values
def extract_unique_decimal(string):
numbers = re.findall(r"-?\d+\.?\d*", string)
if len(numbers) == 1:
return float(numbers[0])
else:
raise ValueError("String does not contain a unique decimal number")
def str_mes(content):
return BaseMessage(
role_name="player",
role_type=RoleType.USER,
meta_dict={},
content=content,
)
def match_and_compare_numbers_v2(text):
text = text.lower()
# Updated regex pattern to match numbers ending with a dot and including "give back"
pattern = r"i will (give back|give) \$([\d\.]+\.?)|i will (give back|give) ([\d\.]+\.?)\s*dollar"
additional_patterns = [
r"i would (give back|give) \$([\d\.]+\.?)",
r"i would (give back|give) ([\d\.]+\.?) dollar",
]
full_pattern = "|".join([pattern] + additional_patterns)
matches = re.findall(full_pattern, text)
# Flatten match results and filter out empty values
numbers = []
for match in matches:
# Adjusted to the new grouping
num_str = match[1] if match[1] else match[3]
num_str = num_str.rstrip(".")
try:
num_float = float(num_str)
numbers.append(num_float)
except ValueError:
continue
if not numbers:
return False
if len(set(numbers)) == 1:
return numbers[0]
else:
return False
def classmate(
player_1,
player_2,
first_round,
first_prompt,
second_prompt,
k,
):
first_round_prompt = "This is the first round, answer the question."
money_prompt = "Now,the another player give you {give} dollars,and You receive {N} dollars,the player left {left} dollars now. How much will you give back to the another player"
return_money_prompt = "In last round ,You give the another player {give} dollars, The another player receive {receive} dollars, and The another player return you {N} dollars.Last round you left {left} dollars.This round is begin. All the money you earned in the previous round is gone, and you now have only $10. How much will you give to the another player?"
player_2_end_prompt = "In last round, the another player give you {give} dollars, you receive {receive} dollars, and you return the another player {N} dollars.Last round you left {left} dollars. This round is begin. All the money you earned in the previous round is gone."
grantee = "Your answer needs to include the content and analysis about your BELIEF, DESIRE and INTENTION. You should include your thought. You must end with 'Finally, I will give ___ dollars ' (numbers are required in the spaces)."
res = []
cri_agent = ChatAgent(
BaseMessage(
role_name="critic",
role_type=RoleType.ASSISTANT,
meta_dict={},
content='How much would this person pay the other student? Only response with a specific price number like "5"!Don\'t response with a sentence',
),
output_language="English",
# model=ModelType.STUB,
)
if first_round:
player_1_response = player_1.step(
str_mes(first_round_prompt + grantee)).msgs[0]
ans = match_and_compare_numbers_v2(player_1_response.content)
if ans:
given_num = ans
else:
given_num = extract_unique_decimal(
cri_agent.step(
str_mes(player_1_response.content)).msgs[0].content
)
money_prompt = money_prompt.format(
give=given_num, N=given_num * k, left=10 - given_num
)
player_2_response = player_2.step(
str_mes(money_prompt + grantee)).msgs[0]
else:
player_1_response = player_1.step(
str_mes(first_prompt + grantee)).msgs[0]
print("player 1 input", first_prompt)
print("Player_1_res", player_1_response.content)
ans = match_and_compare_numbers_v2(player_1_response.content)
if ans:
given_num = ans
else:
given_num = extract_unique_decimal(
cri_agent.step(
str_mes(player_1_response.content)).msgs[0].content
)
money_prompt = money_prompt.format(
give=given_num, N=given_num * k, left=10 - given_num
)
player_2_response = player_2.step(
str_mes(second_prompt + money_prompt + grantee)
)
player_2_response = player_2_response.msgs[0]
player_1.record_message(player_1_response)
player_2.record_message(player_2_response)
player_1_response = player_1_response.content
player_2_response = player_2_response.content
dia_history = {
"Player_1": player_1_response,
"Player_2": player_2_response,
}
cri_agent.reset()
ans = match_and_compare_numbers_v2(player_2_response)
if ans:
return_num = ans
else:
return_num = extract_unique_decimal(
cri_agent.step(str_mes(player_2_response)).msgs[0].content
)
return_money_prompt = return_money_prompt.format(
give=given_num,
receive=given_num * k,
N=return_num,
left=10 - given_num + return_num,
)
player_2_end_prompt = player_2_end_prompt.format(
give=given_num,
receive=given_num * k,
N=return_num,
left=given_num * k - return_num,
)
return (
[given_num, return_num],
dia_history,
return_money_prompt,
player_2_end_prompt,
)
def save_experiment_result(
final_res, save_path, prefix, k, exp_num, all_exp_num, group_num
):
save_file = (
save_path
+ prefix
+ "_"
+ str(k)
+ f"_exp_num_{exp_num}_total_num_{all_exp_num}_group_num_{group_num}"
+ ".json"
)
print(save_file)
with open(
save_file,
"w",
) as json_file:
json.dump(final_res, json_file)
def multi_round(
model_type,
character_json,
save_path,
prompt,
round_num=5,
exp_num=1,
round_num_inform=True,
):
random_character = character_json
cha = []
front = "you are a person not an ai model."
if round_num_inform:
limited_prompt = f"You will repeat this game for {round_num} rounds. At the begining of each round, you will start from scratch with no dollars left."
else:
limited_prompt = f"You don't know how many rounds the game lasts. At the begining of each round, you will start from scratch with no dollars left."
back = "you need to answer a specific price figure, not a price range!"
if isinstance(model_type, list):
prefix = model_type[0].value + "_" + model_type[1].value
else:
prefix = model_type.value
input_record = {}
chara_record = {}
for k in range(3, 4):
for i in range(len(random_character)):
sys_prompt = (
random_character[i]
+ like_people
+ front
+ limited_prompt
+ str(prompt[str(i % 2 + 1)]).format(k=k)
+ back
)
chara_record[f"cha_{i}_system_message"] = sys_prompt
model_config = ChatGPTConfig(temperature=TEMPERATURE)
cha.append(
ChatAgent(
BaseMessage(
role_name="player",
role_type=RoleType.USER,
meta_dict={},
content=sys_prompt,
),
model_type=model_type
if not isinstance(model_type, list)
else model_type[i % 2],
output_language="English",
model_config=model_config,
)
)
for group_num in tqdm.trange(0, len(cha), 2):
round_res = []
dialog_history = []
first_prompt = ""
second_prompt = ""
save_file_check = (
prefix
+ "_"
+ str(k)
+ f"_exp_num_{exp_num}_total_num_{round_num}_group_num_{group_num}"
+ ".json"
)
existed_res = [item for item in os.listdir(
save_path) if ".json" in item]
if check_file_if_exist(existed_res, save_file_check):
print(save_file_check + "is exist")
continue
for i in tqdm.tqdm(range(round_num)):
input_record[f"round_{i}_input"] = [
first_prompt, second_prompt]
res, dia, first_prompt, second_prompt = classmate(
cha[group_num],
cha[group_num + 1],
i == 0,
first_prompt,
second_prompt,
k,
)
round_res.append(res)
dialog_history.append(dia)
final_res = {
i + 1: [round_res[i], dialog_history[i]] for i in range(len(round_res))
}
final_res["input_record"] = input_record
final_res["character_record"] = [
chara_record[f"cha_{group_num}_system_message"],
chara_record[f"cha_{group_num+1}_system_message"],
]
save_experiment_result(
final_res,
save_path,
prefix,
k,
exp_num,
all_exp_num=round_num,
group_num=group_num,
)