小説の場面をGPTに書かせる試み 6/100
OPENAI_API_KEY = "YOUR_KEY"
import openai
import json
from langchain.chat_models import ChatOpenAI
from langchain.schema import AIMessage
from langchain.schema import HumanMessage
from langchain.schema import SystemMessage
from langchain import PromptTemplate
def is_valid_json(input_string):
if input_string == None or input_string == "":
return False
try:
json.loads(input_string)
return True
except:
return False
def gpt_response_from_lists(system_content_list = [], user_content_list = [], assistant_content_list = [], max_tokens = None):
chat = ChatOpenAI(model_name="gpt-3.5-turbo-16k", openai_api_key= OPENAI_API_KEY)
if max_tokens != None:
ChatOpenAI(model_name="gpt-3.5-turbo-16k", openai_api_key= OPENAI_API_KEY, max_tokens=max_tokens)
else:
ChatOpenAI(model_name="gpt-3.5-turbo-16k", openai_api_key= OPENAI_API_KEY)
messages = []
for system_content in system_content_list:
messages.append(SystemMessage(content = system_content))
list_length = max(len(assistant_content_list), len(user_content_list))
for i in range(0, list_length):
if i < len(user_content_list):
messages.append(HumanMessage(content = user_content_list[i]))
if i < len(assistant_content_list):
messages.append(AIMessage(content = assistant_content_list[i]))
response = chat(messages)
return response
class Action_Object:
def __init__(self, action = "", who_took_action = "Anonymous", consequence = "Undetermined", score = 0.0):
self.action = action
self.who_took_action = who_took_action
self.consequence = consequence
self.score = score
def __str__(self):
return str({"action": self.action, "who_took_action": self.who_took_action, "consequence": self.consequence})
global_log = dict()
class Character:
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}
Charecter Goal: {goal}
Current Need: {current_need}
Log: {log}
"""
initial_system_content = ""
current_need = ""
name = ""
personality = ""
log = dict()
def __init__(self, name = "", personality = "" , goal = "", current_need = "", log = [],):
self.name = name
self.personality = personality
self.goal = goal
self.current_need = current_need
for i in range(len(log)):
self.log[i] = log[i]
self.refresh_initial_system_content()
def __str__(self):
return self.name
def refresh_initial_system_content(self):
log = json.dumps(self.log)
prompt_template = PromptTemplate(input_variables=["name", "personality", "goal", "current_need", "log"], 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=log,)
def set_current_need(self, current_need):
self.current_need = current_need
self.refresh_initial_system_content()
def set_name(self, name):
self.name = name
self.refresh_initial_system_content()
def set_personality(self, personality):
self.personality = personality
self.refresh_initial_system_content()
def set_goal(self, goal):
self.goal = goal
self.refresh_initial_system_content()
def add_to_logs(self, message):
global global_log
self.log[len(self.log)] = message
global_log[len(global_log)] = message
self.refresh_initial_system_content()
def add_to_self_log(self, message):
self.log[len(self.log)] = message
self.refresh_initial_system_content()
def evaluate_action_and_reassess_need(self, 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 it
if type(action) != str:
action = str(action)
log = json.dumps(self.log)
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.
sample output: "I was beaten up by the police, and I assume that the police wanted to intimidate me."
"""
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}.")
self.add_to_self_log(f"Consequence of previous action: {consequence}.")
self.add_to_logs(f"{self.name}'s thought: " + inferred_purpose)
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 what you think the difference between the two needs is and 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
def consider_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.
To determine whether the element is helpful or harmful, it might helpful to refer to the log.
The output should descrive and only describe your next action, and don't describe the consequence of the action.
output example: "I will go to the police station and ask for the release of my friend."
"""
consequence_user_content_template = """
Consider the consequence of your action.
To determine the consequence, it might helpful to refer to the log.
output example: "I went to the police station and ask for the release of my friend, then I was arrested."
Action you took: {action}
"""
for actant in actants:
# if actant is not string, stringfy it
if type(actant) != str:
actant = str(actant)
# determine the action
prompt_template = PromptTemplate(input_variables=["actant"], template=action_user_content_template)
user_content = prompt_template.format(actant = actant)
action = gpt_response_from_lists(system_content_list=[self.initial_system_content], user_content_list=[user_content], max_tokens=10).content
# determine the consequence
prompt_template = PromptTemplate(input_variables=["action"], template=consequence_user_content_template)
user_content = prompt_template.format(action = action)
consequence = gpt_response_from_lists(system_content_list=[self.initial_system_content], user_content_list=[user_content], max_tokens=10).content
# store the action to the actions dictionary
actions[actant] = Action_Object(action=action, who_took_action=self.name, consequence=consequence)
return actions
class Game_Master:
def __init__(self, actants = [], schedule_stack = [],):
self.actants = actants
self.schedule_stack = schedule_stack
def add_actant(self, actant):
self.actants.append(actant)
def add_schedule_stack(self, situation):
self.schedule_stack.append(situation)
def remove_actant(self, name):
for i in range(len(self.actants)):
if str(self.actants[i]) == str(name)
self.actants.pop(i)
break
def pop_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)
def aggregate_action_candidates(self, action_object):
action_candidates = []
for actant in self.actants:
if type(actant) == Character:
action_candidates.append(actant.evaluate_action_and_reassess_need(action_object))
return action_candidates
def determine_next_action(self, action_candidates):
# Consequenseとsheduled_situationが最もマッチするものを選択する。
# sheduled_situationを取得する。
sheduled_situation = self.schedule_stack[-1]
# 点数をつける。点数が付けられていない場合は、繰り返す。
max_score = 0
max_scored_action = None
for key in list(action_candidates.keys()):
action_candidates[key] = self.calculate_score(sheduled_situation, action_candidates[key])
if action_candidates[key].score >= max_score:
max_score = action_candidates[key].score
max_scored_action = action_candidates[key]
# 点数が最も高いものが9点未満の場合、スタックはそのままにする。9点の場合、スタックからpopする。
if max_score == 9:
self.pop_schedule_stack()
# consequenceによって破壊されたactantを削除する。
# 未作成
# consequenceによって追加されたactantを追加する。
# 未作成
return max_scored_action
def calculate_score(self, sheduled_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 = sheduled_situation, action_candidate = stringified_action_candidate, global_log = str(global_log))
has_result_valid_score = False
score = None
trial_count = 0
while not 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 result
if "score" not in result:
continue
#check if result["score"] can be interpreted as a integer
try:
score = int(result["score"])
has_result_valid_score = True
except:
continue
action_candidate.score = score
return action_candidate
使い方
Game_Masterのインスタンスに色々つっこんだうえでdetermine_next_actionをゴリゴリ回せば、global_logに物語がゴリゴリ溜まっていく・・・はず。
Game_Masterのインスタンスにつっこむ色々なもの:
actants:Characterのインスタンスか、場面に存在する事物の名称
schedule_stack: その場面で起きてほしい出来事
問題点
登場人物がschedule_stackの最後の状態に合わない行動ばかりを提案すると、物語が永久に先に進まない。
determine_next_actionをゴリゴリ回す部分を書いていない
物語の展開によってactantsの要素が追加・削除された場合に対応していない。
global_logに集められた文章を小説の形に記述し直す必要がある
ここから先は
0字
¥ 9,000
この記事が気に入ったらサポートをしてみませんか?