global_log = dict()
classCharacter:
initial_system_content_template = """
I am trying to write a story. You will assist by playing the role of a character in this story.
I will provide information about the character you are to portray as follows. From then on, please respond in character.
Character Name: {name}
Character Personality: {personality}
Character Goal: {goal}
Current Need: {current_need}
What have happened in this story: {log_summary}
"""
initial_system_content = ""
current_need = ""
name = ""
personality = ""
log = dict()
log_summary = ""
log_summary_pointer = 0
place = ""def__init__(self, name = "", personality = "" , goal = "", current_need = "", log = [], place = ""):
self.name = name
self.personality = personality
self.goal = goal
self.current_need = current_need
self.place = place
for i in range(len(log)):
self.log[i] = log[i]
self.refresh_initial_system_content()
def__str__(self):return self.name
defsummarize_log(self):if len(self.log) == 0:
self.log_summary = ""return
log_to_be_summarized = ""for i in range(self.log_summary_pointer, len(self.log)):
log_to_be_summarized += self.log[i] + "¥n"
self.log_summary_pointer = len(self.log)
system_content = f"""
You are given a summary of the events that have occurred in the story so far, called 'log_summary', and the most recent events of the story, called 'log_to_be_summarized'.
Please update the 'log_summary' by incorporating the information from 'log_to_be_summarized'.
"""
user_content = f"""
log_summary: {self.log_summary}
log_to_be_summarized: {log_to_be_summarized}
"""
response = gpt_response_from_lists(system_content_list= [self.initial_system_content ,system_content], user_content_list=[user_content], assistant_content_list=[])
self.log_summary = response.content
print(f"""{self.name} updated log_summary to:
{self.log_summary}""")
return response
defrefresh_initial_system_content(self):
self.summarize_log()
prompt_template = PromptTemplate(input_variables=["name", "personality", "goal", "current_need", "log_summary"], template=self.initial_system_content_template)
self.initial_system_content = prompt_template.format(name=self.name, personality=self.personality, goal=self.goal, current_need=self.current_need, log_summary=self.log_summary,)
defset_current_need(self, current_need):
self.current_need = current_need
self.refresh_initial_system_content()
defset_name(self, name):
self.name = name
self.refresh_initial_system_content()
defset_personality(self, personality):
self.personality = personality
self.refresh_initial_system_content()
defset_goal(self, goal):
self.goal = goal
self.refresh_initial_system_content()
defadd_to_logs(self, message, refresh_initial_system_content = True):global global_log
self.log[len(self.log)] = message
global_log[len(global_log)] = message
if refresh_initial_system_content:
self.refresh_initial_system_content()
defadd_to_self_log(self, message, refresh_initial_system_content = True):
self.log[len(self.log)] = message
if refresh_initial_system_content:
self.refresh_initial_system_content()
defcleanse_action_thought(self, action_object):
action = action_object.action
who_took_action = action_object.who_took_action
consequence = action_object.consequence
system_content= """
You will be given an action taken by characters in this story.
From the action, please remove any information you should not be able to know, and describe only the physical appearance of those actions.
A prominent example of information you cannot know would be the inner thoughts of a character who is not you.
The output should be JSON parsable string.
sample output: {"action": "{content of action}", "who_took_action": "{the name of who took action}", "consequence": "{content of consequence}"}
"""
user_content_example = """
Action: Watson suspected that the coffee cup might contain poison. Thinking that Holmes shouldn't drink it, Watson tasted the coffee to inspect whether it contained any poison.
Who took action: Watson
Consequence: The next moment, he collapsed due to the effect of the poison.
"""
assistant_content_example = """
{"action": "Watson tasted the coffee.", "who_took_action": "Watson", "consequence": "The next moment, he collapsed"}
"""
user_content = f"""
Action: {action}
Who took action: {who_took_action}
Consequence: {consequence}
"""
success = False
response = Nonewhilenot success:
response = gpt_response_from_lists(system_content_list= [self.initial_system_content, system_content], user_content_list=[user_content_example, user_content], assistant_content_list=[assistant_content_example])
if is_valid_json(response.content):
success = True
response_json = json.loads(response.content)
cleansed_action_object = Action_Object(action=response_json["action"], who_took_action=response_json["who_took_action"], consequence=response_json["consequence"])
return cleansed_action_object
defevaluate_action_and_reassess_need(self, action_object):
action_object = self.cleanse_action_thought(action_object)
action = action_object.action
who_took_action = action_object.who_took_action
consequence = action_object.consequence
# if action's type is not string, stringfy itif type(action) != str:
action = str(action)
user_content_template = """
Given the following infomation, please infer the purpose of the action.
Action: {action}
Who took action: {who_took_action}
Consequence: {consequence}
The output should be a string that describes the intent of the action from your perspective.
If name of who took action is same as your name, the action is taken by you and you already know the purpose of the action, so return the purpose of the action."
sample output: "The dog was beaten up by the police, and I assume that the police wanted to intimidate the dog."
"""
prompt_template = PromptTemplate(input_variables=["action", "who_took_action", "consequence"], template=user_content_template)
user_content = prompt_template.format(action=action, who_took_action=who_took_action, consequence=consequence,)
inferred_purpose = gpt_response_from_lists(system_content_list=[self.initial_system_content], user_content_list=[user_content]).content
self.add_to_self_log(f"{who_took_action}'s action: {action}.", refresh_initial_system_content=False)
self.add_to_self_log(f"Consequence of previous action: {consequence}.", refresh_initial_system_content=False)
self.add_to_logs(f"{self.name}'s thought: " + inferred_purpose, refresh_initial_system_content=False)
self.refresh_initial_system_content()
user_content_template = """
Given the following information, suggest what your immediate need might now be. It is acceptable for the immediate need to be the same as the current need.
Action: {action}
Who took action: {who_took_action}
Consequence: {consequence}
The output should be a string that describes your immediate need.
"""
prompt_template = PromptTemplate(input_variables=["action", "who_took_action", "consequence",], template=user_content_template)
user_content = prompt_template.format(action=action, who_took_action=who_took_action, consequence=consequence,)
new_need = gpt_response_from_lists(system_content_list=[self.initial_system_content], user_content_list=[user_content]).content
old_need = self.current_need
self.set_current_need(new_need)
self.add_to_logs(f"{self.name}'s need renewed: {new_need}")
user_content_template = """
Your current need is:
{new_need}
Your previous need was:
{old_need}
Tell me why you did (not) change your need.
"""
prompt_template = PromptTemplate(input_variables=["new_need", "old_need"], template=user_content_template)
user_content = prompt_template.format(new_need=new_need, old_need=old_need)
determination = gpt_response_from_lists(system_content_list=[self.initial_system_content], user_content_list=[user_content]).content
self.add_to_logs(f"{self.name}'s thought: " + determination)
response = {"inferred_purpose": inferred_purpose, "new_need": new_need, "determination": determination,}
return response
defconsider_next_actions(self, actants = []):
actions = dict()
if len(actants) == 0:
actants.append(self.name)
action_user_content_template = """
Consider your next move in the current scene where there is {actant} involved.
If this element aids you in achieving your goals or satisfies your needs, figure out a way to incorporate it into your action.
But, if it hinders your goal or needs, think about a strategy to eliminate or neutralize it.
The action must be narrated from the viewpoint of an outside observer, without incorporating any individual's internal thoughts.
The output should descrive and only describe your next action, and don't describe the consequence of the action.
The action should be taken in {place}.
output example: "Micky will go to the police station and ask for the release of Duffy."
"""
consequence_user_content_template = """
Consider the consequence of your action.
To determine the consequence must consistent with the log.
The consequence must be narrated from the viewpoint of an outside observer, without incorporating any individual's internal thoughts.
The consequence should be taken in {place}.
output example: "Micky went to the police station and ask for the release of Duffy, then Micky was arrested."
Action you took: {action}
"""for actant in actants:
# if actant is not string, stringfy itif type(actant) != str:
actant = str(actant)
# determine the action
prompt_template = PromptTemplate(input_variables=["actant", "place"], template=action_user_content_template)
user_content = prompt_template.format(actant = actant, place = self.place)
action = gpt_response_from_lists(system_content_list=[self.initial_system_content], user_content_list=[user_content]).content
# determine the consequence
prompt_template = PromptTemplate(input_variables=["action", "place"], template=consequence_user_content_template)
user_content = prompt_template.format(action = action, place = self.place)
consequence = gpt_response_from_lists(system_content_list=[self.initial_system_content], user_content_list=[user_content]).content
# store the action to the actions dictionary
actions[actant] = Action_Object(action=action, who_took_action=self.name, consequence=consequence)
return actions
defreconsider_next_actions(self, action_canditate, consequence):
user_content_template = """
You proposed performing `action`.
However, due to the demands of the narrative, it must lead to a result known as `consequence`.
Please modify `action` to ensure it results in `consequence`.
The action must be described from third person's perspective and must not include a person's inner voice.
The output should descrive and only describe your next action, and don't describe the consequence of the action.
The action should be taken in {place}.
action you suggested: {action}
required consequence: {consequence}
output: modified action
"""
prompt_template = PromptTemplate(input_variables=["action", "consequence", "place"], template=user_content_template)
user_content = prompt_template.format(action=action_canditate.action, consequence=consequence, place=self.place)
action = gpt_response_from_lists(system_content_list=[self.initial_system_content], user_content_list=[user_content]).content
reconsidered_action = Action_Object(action=action, who_took_action=self.name, consequence=consequence, score = 9)
return reconsidered_action
classGame_Master:def__init__(self, actants = [], schedule_stack = [], tolerance = 1):
self.actants = actants
self.schedule_stack = schedule_stack
self.tolerance = tolerance
self.deviated = 0defadd_to_global_log(self, message):global global_log
global_log[len(global_log)] = message
defmain(self, catalyst_action_object, max_iterations=10):global global_log
next_action = catalyst_action_object
iteration_count = 0while len(self.schedule_stack) > 0and iteration_count <= max_iterations:
iteration_count += 1# check if there is a character in the actants list
character_count = 0for actant in self.actants:
if type(actant) == Character:
character_count += 1if character_count == 0:
print("There is no character in the actants list.")
break
action_candidates = self.aggregate_action_candidates(next_action)
next_action = self.determine_next_action(action_candidates)["max_scored_action"]
self.add_to_global_log(str(next_action))
if iteration_count > max_iterations:
print(f"The iteration count exceeded the maximum iteration count: {max_iterations}")
break
print("finished")
defadd_actant(self, actant):
self.actants.append(actant)
defadd_schedule_stack(self, situation):
self.schedule_stack.append(situation)
defremove_actant(self, name):for i in range(len(self.actants)):
if str(self.actants[i]) == str(name):
self.actants.pop(i)
breakdefpop_schedule_stack(self, situation = None):if situation == None:
return self.schedule_stack.pop()
for i in range(len(self.schedule_stack)):
if str(self.schedule_stack[i]) == str(situation):
return self.schedule_stack.pop(i)
defaggregate_action_candidates(self, action_object):
action_candidates = []
for actant in self.actants:
if type(actant) == Character:
actant.evaluate_action_and_reassess_need(action_object)
action_candidates.append(actant.consider_next_actions(self.actants))
print(f"action_candidates: {action_candidates}")
return action_candidates
defdetermine_next_action(self, action_candidates):# Consequenseとsheduled_situationが最もマッチするものを選択する。# sheduled_situationを取得する。
sheduled_situation = self.schedule_stack[-1]
# 点数をつける。点数が付けられていない場合は、繰り返す。ここで、どのactionが取られるかが決定する。
max_score = 0
max_scored_action = None
print(f"action_candidates: {action_candidates}")
for item in action_candidates:
for key in list(item.keys()):
item[key] = self.calculate_score(sheduled_situation, item[key])
if item[key].score >= max_score:
max_score = item[key].score
max_scored_action = item[key]
if max_score < 9:
if self.deviated < self.tolerance:
self.deviated += 1
print(f"deviated: {self.deviated} times")
else:
who_took_action = max_scored_action.who_took_action
#find index of the actant who took the action
reconsiderer_index = 0for i in range(len(self.actants)):
if str(self.actants[i]) == str(who_took_action):
reconsiderer_index = i
break
max_scored_action = self.actants[reconsiderer_index].reconsider_next_actions(max_scored_action, sheduled_situation)
self.deviated = 0
max_score = 9# 点数が最も高いものが9点未満の場合、スタックはそのままにする。9点の場合、スタックからpopする。if max_score == 9:
self.pop_schedule_stack()
## max_scored_actionによって削除・退場するactantの処理を行う。#removed_actants = self.remove_actant_with_action_object(max_scored_action)#print(f"removed_actants: {removed_actants}")# consequenceによって追加されたactantを追加する。
added_actants = self.add_actant_with_action_object(max_scored_action)
print(f"added_actants: {added_actants}")
print(f"max_scored_action: {str(max_scored_action)}")
return {"max_scored_action": max_scored_action, "removed_actants": [], "added_actants": added_actants}
defremove_actant_with_action_object(self, action_object):
consequence = action_object.consequence
system_content = """
You will be presented with a list of elements of actants, and the next consequence of the scene.
Based on that information, list out people or things that have exited, been destroyed, died, or no longer function.
The listed actants should be separated by commas (,). Also, the output should only be a comma-separated string of actant names, do not output any other strings.
"""
user_content_example_1 = """
actants: Bocchi,Guitar,room
consequence: Bocchi awkwardly bowed and then quickly left the room.
"""
assistant_content_example_1 = """
Bocchi
"""
user_content_example_2 = """
actants: Frogman,cigarette
consequence: Surprisingly, Frogman ate the cigarette.
"""
assistant_content_example_2 = """
cigarette
"""
user_content_example_3 = """
actants: Dolton,Anna,village
consequence: A helicopter and a passenger plane crashed into the village. The village chief, Dolton, was caught in it and lost his life. Anna, the wife of the village chief, was terrified and fled.
"""
assistant_content_example_3 = """
Dolton,Anna
"""
actants_names = ",".join([str(actant) for actant in self.actants])
user_content = f"""
actants: {actants_names}
consequence: {consequence}
"""
system_contents = [system_content]
user_contents = [user_content_example_1, user_content_example_2, user_content_example_3, user_content]
assistant_contents = [assistant_content_example_1, assistant_content_example_2, assistant_content_example_3]
actants_to_remove = gpt_response_from_lists(system_content_list=system_contents, user_content_list=user_contents, assistant_content_list=assistant_contents).content.split(",")
for actant in actants_to_remove:
actant = actant.strip().strip("\"").strip("\n")
self.remove_actant(actant)
print(f"actants_to_remove: {actants_to_remove}")
# squash none
actants = []
for actant in self.actants:
if actant != None:
actants.append(actant)
self.actants = actants
return actants_to_remove
defadd_actant_with_action_object(self, action_object):
consequence = action_object.consequence
system_content = """
You will be presented with a list of elements of actants, and the next consequence of the scene.
Based on that information, list out people or things that have newly entered, been created, or have begun to function.
Then, determine the actant has will or not and list them in json format.
The actant that is already in the actants list may not be included in the list, but it is not bad to include it.
output example: {"Alice" : {"has_will": true}, "Bycycle" : {"has_will": false}, "cat" : {"has_will": true}, "Doraemon" : {"has_will": true}}
"""
user_content_example_1 = """
actants: Bocchi,room
consequence: Bocchi awkwardly bowed and then quickly left the room, and a guitar was left behind.
"""
assistant_content_example_1 = """
{"Bocchi's guitar" : {"has_will": false}}'
"""
user_content_example_2 = """
actants: Laboratory
consequence: Suddenly, Frogman advented and vomited Spiderman and a cigarette.
"""
assistant_content_example_2 = """
{"Frogman" : {"has_will": true}, "Spiderman" : {"has_will": true}, "cigarette" : {"has_will": false}}
"""
user_content_example_3 = """
actants: Dolton,Anna,village
consequence: A helicopter and a passenger plane crashed into the village. The village chief, Dolton, was caught in it and lost his life. Anna, the wife of the village chief, was terrified and fled.
"""
assistant_content_example_3 = """
{"helicopter" : {"has_will": false}, "passenger plane" : {"has_will": false}}
"""
actants_names = ",".join([str(actant) for actant in self.actants])
user_content = f"""
actants: {actants_names}
consequence: {consequence}
"""
system_contents = [system_content]
user_contents = [user_content_example_1, user_content_example_2, user_content_example_3, user_content]
assistant_contents = [assistant_content_example_1, assistant_content_example_2, assistant_content_example_3]
success = False
trial_count = 0whilenot success and trial_count < 5:
trial_count += 1
actants_to_add = gpt_response_from_lists(system_content_list=system_contents, user_content_list=user_contents, assistant_content_list=assistant_contents).content
if is_valid_json(actants_to_add):
success = Trueif trial_count >= 5:
print("Failed to get valid json from GPT-3.")
return
actants_to_add_json = json.loads(actants_to_add)
existing_actants_names = [str(actant) for actant in self.actants]
added_actants = []
for key in list(actants_to_add_json.keys()):
if key in existing_actants_names:
continueif actants_to_add_json[key]["has_will"] == False :
self.add_actant(key)
added_actants.append(key)
else:
new_character = self.create_character(name = key, first_log = consequence)
self.add_actant(new_character)
added_actants.append(new_character)
print(f"actants_to_add: {actants_to_add}")
return added_actants
defcreate_character(self, name, first_log = None):
system_content = """
You will be provided with a character's name (name), the record of the story so far (global_log), and the first record (first_log) that character holds in this story. The first_log also represents the most recent event for that character.
Based on this information, please determine the character's personality, goals, and current desires.
The output should be in JSON format.
Output example: {'name': 'Alice', 'personality': 'kind', 'goal': 'to become a better person', 'current_need': 'to feel loved'}
"""
user_content_example_1 = """
name: Alice
global_log: {"1": {"action": "Seiji saw a girl attempting suicide and falling into a river", "consequence": "the girl fell into the river"},
"2": {"action": "Alice was saved by Seiji.", "consequence": "Alice was saved by Seiji."},
"3": {"Seiji's thought": "What a beautiful girl she is! Why did she fall into the river?"}}
first_log: Alice was saved by Seiji.
"""
assistant_content_example_1 = """
{'name': 'Alice', 'personality': 'grateful', 'goal': 'to repay Seiji', 'current_need': 'to understand her own feelings'}
"""
user_content = f"""
name:{name}
global_log: {str(global_log)}
first_log: {first_log}
"""
system_contents = [system_content]
user_contents = [user_content_example_1, user_content]
assistant_contents = [assistant_content_example_1]
success = False
trial_count = 0
new_character = Nonewhilenot success and trial_count < 5:
trial_count += 1
character_content = gpt_response_from_lists(system_content_list=system_contents, user_content_list=user_contents, assistant_content_list=assistant_contents).content
if is_valid_json(character_content):
character_content_json = json.loads(character_content)
# check if character_content_json has all the keysif"name"in character_content_json and"personality"in character_content_json and"goal"in character_content_json and"current_need"in character_content_json:
new_character = Character(name = character_content_json["name"],
personality = character_content_json["personality"],
goal = character_content_json["goal"],
current_need = character_content_json["current_need"],
log = [first_log])
success = Trueif trial_count >= 5:
print("Failed to get valid json from GPT-3.")
return
print(f"new_character: {new_character}")
return new_character
defcalculate_score(self, scheduled_situation, action_candidate):
system_content = """
The information provided to you includes the scheduled_situation, action_candidate, and global_log.
scheduled_situation: This is the situation that should be accomplished next in the narrative. It is typically provided as a string.
action_candidate: This is a candidate for the next action to be taken in the narrative. It is given as a parsed string in JSON format.
An example of an action_candidate: {"action": "This item represents the action taken.", "who_took_action": "This represents who took the action.", "consequence": "This roughs out the result of the action."}
global_log: This is a record of the actions that have occurred in the narrative. It is given as a parsed string in JSON format.
You need to consider the combination of the scheduled_situation and action_candidates and provide a score. If the scheduled_situation has been achieved, give a score of 9. If there has been no progress towards achieving the scheduled_situation, give a score of 0. The output must always be parsable as JSON.
Output example: '{"matchness": "The scheduled_situation has been achieved. However, it is inconsistent with the global_log", "score": 5}'
Output example: '{"matchness": "It is consistent with the global_log. However, the scheduled_situation has not been achieved.", "score": 5}'
Output example: '{"matchness": "It is inconsistent with the global_log, and the scheduled_situation has not been achieved.", "score": 0}'
Output example: '{"matchness": "It is inconsistent with the global_log, and the scheduled_situation has not been achieved. However, it seems likely to approach a situation where the scheduled_situation can be achieved.", "score": 7}'
Output example: '{"matchness": "It is consistent with the global_log. In addition, the scheduled_situation has been achieved.", "score": 9}'
"""# 点数をつける。点数が付けられていない場合は、繰り返す。
system_contents = [system_content]
stringified_action_candidate = str(action_candidate)
user_content_template = """
sheduled_situation: {scheduled_situation}
action_candidate: {action_candidate}
global_log: {global_log}
"""
user_content = user_content_template.format(scheduled_situation = scheduled_situation, action_candidate = stringified_action_candidate, global_log = str(global_log))
has_result_valid_score = False
score = None
trial_count = 0whilenot has_result_valid_score and trial_count < 1:
trial_count += 1
print("trial_count: ", trial_count)
result = gpt_response_from_lists(system_contents, [user_content]).content
print("result: ", result)
if is_valid_json(result):
result = json.loads(result)
print("type(result): ", type(result))
#check if "score" is in resultif"score"notin result:
continue#check if result["score"] can be interpreted as a integertry:
score = int(result["score"])
has_result_valid_score = Trueexcept:
continue
action_candidate.score = score
return action_candidate
実行例
place = "White House's caffeteria, where President Tramp is having a lunch with Princess Aurora and the Dragon Lord."
DragonLord = Character("Dragon Lord", "Brutal", "Be the emperor of the entire world", "kill heroes of the world", ["I am the Dragon Lord. I am the emperor of the entire world. I am going to kill heroes of the world."], place = place)
PrincessAurora = Character("Princess Aurora", "beautiful, cunning and evil", "After Dragon Lord conquers the world, she will kill Dragon Lord and become the new emperor of the world.", "Help Dragon Lord to conquer the world", ["I am Princess Aurora. I am beautiful, cunning and evil. After Dragon Lord conquers the world, I will kill Dragon Lord and become the new emperor of the world."], place = place)
PresidentTramp = Character("President Tramp", "selfish", "To make America great again", "Save the world", ["I am President Tramp. I am selfish. I want to make America great again."], place = place)
action_object_instance = Action_Object("Princess Aurora courteously greeted President Trump and Dragon Lord.",
"Princess Aurora",
"Dragon Lord and President Trump returned Princess Aurora's greeting.")
game_master = Game_Master([DragonLord, PrincessAurora, PresidentTramp, "pizza"],
["a character show a responce to the recent event","The meeting devasted", "The entire people laughed"],
tolerance= 1,)
translated = []
for i in range(len(global_log)):
print(global_log[i])
content = gpt_response_from_lists(["translate English to Japanese"], assistant_content_list=[], user_content_list=[global_log[i]]).content
print(content)
translated.append(content)
for i in range(len(translated)):
print(f"■{i}")
print(translated[i])
print(global_log[i])
print("")
print("")