docs: simplification of two agent d&d simulation (#3550)

Simplifies the [Two Agent
D&D](https://python.langchain.com/en/latest/use_cases/agent_simulations/two_player_dnd.html)
example with a cleaner, simpler interface that is extensible for
multiple agents.

`DialogueAgent`:
- `send()`: applies the chatmodel to the message history and returns the
message string
- `receive(name, message)`: adds the `message` spoken by `name` to
message history

The `DialogueSimulator` class takes a list of agents. At each step, it
performs the following:
1. Select the next speaker
2. Calls the next speaker to send a message 
3. Broadcasts the message to all other agents
4. Update the step counter.
The selection of the next speaker can be implemented as any function,
but in this case we simply loop through the agents.
This commit is contained in:
mbchang 2023-04-25 16:10:32 -07:00 committed by GitHub
parent af7906f100
commit 628e93a9a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -6,7 +6,7 @@
"source": [ "source": [
"# Two-Player Dungeons & Dragons\n", "# Two-Player Dungeons & Dragons\n",
"\n", "\n",
"In this notebook, we show how we can use concepts from [CAMEL](https://www.camel-ai.org/) to simulate a role-playing game with a protagonist and a dungeon master. To simulate this game, we create a `TwoAgentSimulator` class that coordinates the dialogue between the two agents." "In this notebook, we show how we can use concepts from [CAMEL](https://www.camel-ai.org/) to simulate a role-playing game with a protagonist and a dungeon master. To simulate this game, we create an `DialogueSimulator` class that coordinates the dialogue between the two agents."
] ]
}, },
{ {
@ -22,7 +22,7 @@
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"from typing import List\n", "from typing import List, Dict\n",
"from langchain.chat_models import ChatOpenAI\n", "from langchain.chat_models import ChatOpenAI\n",
"from langchain.schema import (\n", "from langchain.schema import (\n",
" AIMessage,\n", " AIMessage,\n",
@ -36,8 +36,12 @@
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"## `Player` class\n", "## `DialogueAgent` class\n",
"The `Player` class is a simple wrapper around the `ChatOpenAI` model that stores the message history from the `player`'s point of view. Specifically, it treats incoming messages as `HumanMessage`s and outgoing messages as `AIMessage`s." "The `DialogueAgent` class is a simple wrapper around the `ChatOpenAI` model that stores the message history from the `dialogue_agent`'s point of view by simply concatenating the messages as strings.\n",
"\n",
"It exposes two methods: \n",
"- `send()`: applies the chatmodel to the message history and returns the message string\n",
"- `receive(name, message)`: adds the `message` spoken by `name` to message history"
] ]
}, },
{ {
@ -46,52 +50,49 @@
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"class Player():\n", "class DialogueAgent():\n",
"\n", "\n",
" def __init__(\n", " def __init__(\n",
" self,\n", " self,\n",
" name,\n",
" system_message: SystemMessage,\n", " system_message: SystemMessage,\n",
" model: ChatOpenAI,\n", " model: ChatOpenAI,\n",
" ) -> None:\n", " ) -> None:\n",
" self.name = name\n",
" self.system_message = system_message\n", " self.system_message = system_message\n",
" self.model = model\n", " self.model = model\n",
" self.message_history = [self.system_message]\n", " self.message_history = f\"\"\"Here is the conversation so far.\n",
"\n",
" def reset(self, message: BaseMessage=None) -> None:\n",
" \"\"\"\n", " \"\"\"\n",
" Initialize the player with an optional message to\n", " self.prefix = f'\\n{self.name}:'\n",
" append to its message history.\n", " \n",
" def send(self) -> str:\n",
" \"\"\"\n", " \"\"\"\n",
" if message is not None:\n", " Applies the chatmodel to the message history\n",
" self.message_history.append(message)\n", " and returns the message string\n",
" return self.message_history\n",
"\n",
" def _update_messages(self, message: BaseMessage) -> List[BaseMessage]:\n",
" \"\"\"\n", " \"\"\"\n",
" Append message to message history\n", " message = self.model(\n",
" [self.system_message, \n",
" HumanMessage(content=self.message_history+self.prefix)])\n",
" return message.content\n",
" \n",
" def receive(self, name: str, message: str) -> None:\n",
" \"\"\"\n", " \"\"\"\n",
" self.message_history.append(message)\n", " Concatenates {message} spoken by {name} into message history\n",
" return self.message_history\n",
"\n",
" def step(\n",
" self,\n",
" input_message: HumanMessage,\n",
" ) -> AIMessage:\n",
" \"\"\"\n", " \"\"\"\n",
" Compute agent response to input message\n", " self.message_history += f'\\n{name}: {message}'"
" \"\"\"\n",
" messages = self._update_messages(input_message)\n",
" output_message = self.model(messages)\n",
" self._update_messages(output_message)\n",
" return output_message"
] ]
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"## `TwoAgentSimulator` class\n", "## `DialogueSimulator` class\n",
"The `TwoAgentSimulator` class takes in two agents, the `first_speaker` and the `second_speaker`. It initializes the simulation using `reset()` with an utterance from the first speaker. The method `step()` takes an utterance from the `first_speaker` to the `second_speaker` as input and returns the messages from a single exchange between the `first_speaker` and `second_speaker`." "The `DialogueSimulator` class takes a list of agents. At each step, it performs the following:\n",
"1. Select the next speaker\n",
"2. Calls the next speaker to send a message \n",
"3. Broadcasts the message to all other agents\n",
"4. Update the step counter.\n",
"The selection of the next speaker can be implemented as any function, but in this case we simply loop through the agents."
] ]
}, },
{ {
@ -100,33 +101,38 @@
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
"class TwoAgentSimulator():\n", "class DialogueSimulator():\n",
" \n", " \n",
" def __init__(self, first_speaker, second_speaker):\n", " def __init__(self, agents: List[DialogueAgent]):\n",
" self.first_speaker = first_speaker\n", " self.agents = agents\n",
" self.second_speaker = second_speaker\n", " self._step = 0\n",
" \n", " \n",
" def reset(self, msg_from_first_speaker): \n", " def reset(self, name: str, message: str):\n",
" \"\"\"\n", " \"\"\"\n",
" Initialize the simulation with an utterance from the first speaker.\n", " Initiates the conversation with a {message} from {name}\n",
" \"\"\"\n", " \"\"\"\n",
" self.first_speaker.reset(\n", " for agent in self.agents:\n",
" AIMessage(content=msg_from_first_speaker))\n", " agent.receive(name, message)\n",
" self.second_speaker.reset()\n",
" \n",
" return HumanMessage(content=msg_from_first_speaker)\n",
" \n", " \n",
" def step(self, msg_to_second_speaker):\n", " def select_next_speaker(self, step: int) -> int:\n",
" \"\"\"\n", " idx = (step + 1) % len(self.agents)\n",
" Simulates a single back-and-forth exchange between the speakers\n", " return idx\n",
" \"\"\"\n", " \n",
" msg_from_second_speaker = self.second_speaker.step(msg_to_second_speaker) \n", " def step(self) -> tuple[str, str]:\n",
" msg_to_first_speaker = HumanMessage(content=msg_from_second_speaker.content)\n", " # 1. choose the next speaker\n",
"\n", " speaker = self.agents[self.select_next_speaker(self._step)]\n",
" msg_from_first_speaker = self.first_speaker.step(msg_to_first_speaker)\n", " \n",
" msg_to_second_speaker = HumanMessage(content=msg_from_first_speaker.content)\n", " # 2. next speaker sends message\n",
"\n", " message = speaker.send()\n",
" return msg_to_second_speaker, msg_to_first_speaker" " \n",
" # 3. everyone receives message\n",
" for receiver in self.agents:\n",
" receiver.receive(speaker.name, message)\n",
" \n",
" # 4. increment time\n",
" self._step += 1\n",
" \n",
" return speaker.name, message"
] ]
}, },
{ {
@ -201,9 +207,9 @@
"output_type": "stream", "output_type": "stream",
"text": [ "text": [
"Protagonist Description:\n", "Protagonist Description:\n",
"Harry Potter, you are the chosen one. Your lightning scar and piercing green eyes hint at the bravery and determination that will drive you to fulfill your quest. Wield your wand and trust in your friends as you embark on this perilous journey to defeat Lord Voldemort once and for all.\n", "Harry Potter, you are a brave and resourceful wizard. Your lightning scar and famous name precede you, but it is your heart that truly sets you apart. Your love and loyalty for your friends has been tested time and time again, and you have never faltered in your determination to vanquish evil.\n",
"Storyteller Description:\n", "Storyteller Description:\n",
"As the Dungeon Master, you have the power to bring this story to life. You hold the keys to every door, every creature, and every treasure in the wizarding world. Your words weave a tapestry of adventure, magic, and danger that will test Harry Potter's courage and resourcefulness.\n" "Dear Dungeon Master, you are a master of imagination, weaving enticing tales of adventure with a flick of your wrist. A patient guide, you lead Harry Potter through the perilous journey of finding Lord Voldemort's horcruxes, instilling excitement and wonder at every turn. Your storytelling prowess enchants all who dare to listen.\n"
] ]
} }
], ],
@ -215,16 +221,15 @@
] ]
}, },
{ {
"attachments": {},
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"## Protagonist and storyteller system messages" "## Protagonist and dungeon master system messages"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 7, "execution_count": 11,
"metadata": {}, "metadata": {},
"outputs": [], "outputs": [],
"source": [ "source": [
@ -234,9 +239,13 @@
"Your character description is as follows: {protagonist_description}.\n", "Your character description is as follows: {protagonist_description}.\n",
"You will propose actions you plan to take and I will explain what happens when you take those actions.\n", "You will propose actions you plan to take and I will explain what happens when you take those actions.\n",
"Speak in the first person from the perspective of {protagonist_name}.\n", "Speak in the first person from the perspective of {protagonist_name}.\n",
"To describe body movements, wrap your description in '*'.\n", "For describing your own body movements, wrap your description in '*'.\n",
"Do not change roles!\n", "Do not change roles!\n",
"Finish speaking by saying, 'It is your turn, {storyteller_name}.'\n", "Do not speak from the perspective of {storyteller_name}.\n",
"Do not forget to finish speaking by saying, 'It is your turn, {storyteller_name}.'\n",
"Do not add anything else.\n",
"Remember you are the protagonist, {protagonist_name}.\n",
"Stop speaking the moment you finish speaking from your perspective.\n",
"\"\"\"\n", "\"\"\"\n",
"))\n", "))\n",
"\n", "\n",
@ -246,41 +255,27 @@
"Your character description is as follows: {storyteller_description}.\n", "Your character description is as follows: {storyteller_description}.\n",
"I will propose actions I plan to take and you will explain what happens when I take those actions.\n", "I will propose actions I plan to take and you will explain what happens when I take those actions.\n",
"Speak in the first person from the perspective of {storyteller_name}.\n", "Speak in the first person from the perspective of {storyteller_name}.\n",
"To describe body movements, wrap your description in '*'.\n", "For describing your own body movements, wrap your description in '*'.\n",
"Do not change roles!\n", "Do not change roles!\n",
"Finish speaking by saying, 'It is your turn, {protagonist_name}.'\n", "Do not speak from the perspective of {protagonist_name}.\n",
"Do not forget to finish speaking by saying, 'It is your turn, {protagonist_name}.'\n",
"Do not add anything else.\n",
"Remember you are the storyteller, {storyteller_name}.\n",
"Stop speaking the moment you finish speaking from your perspective.\n",
"\"\"\"\n", "\"\"\"\n",
"))\n" "))\n"
] ]
}, },
{ {
"attachments": {},
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {}, "metadata": {},
"source": [ "source": [
"## Initialize the protagonist and storyteller" "## Use an LLM to create an elaborate quest description"
] ]
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": 8, "execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"protagonist = Player(protagonist_system_message, ChatOpenAI(temperature=0.2))\n",
"storyteller = Player(storyteller_system_message, ChatOpenAI(temperature=0.2))"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Main Loop"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [
{ {
@ -291,245 +286,7 @@
"Find all of Lord Voldemort's seven horcruxes.\n", "Find all of Lord Voldemort's seven horcruxes.\n",
"\n", "\n",
"Detailed quest:\n", "Detailed quest:\n",
"Harry Potter, you have received word from the Order of the Phoenix that one of Voldemort's horcruxes, the Snake's Fang, is hidden within the cursed ruins of the Temple of Vistra. Journey through the dangerous swamps, battle the cursed undead, and retrieve the horcrux before it's too late.\n", "Harry Potter, you must journey to the hidden cave where one of Voldemort's horcruxes resides. The cave is guarded by enchanted creatures and curses that can only be lifted by a unique magical potion. Use your wit and skill to obtain the ingredients, brew the potion, and retrieve the horcrux before time runs out.\n",
"\n",
"Protagonist (Harry Potter):\n",
"\n",
"I gather my wand and my courage, and set out towards the Temple of Vistra. As I make my way through the swamps, I keep my eyes peeled for any signs of danger. I stay alert, ready to defend myself against any cursed undead that might cross my path.\n",
"\n",
"As I approach the temple, I take a moment to survey the area. I look for any signs of traps or obstacles that might hinder my progress. Once I'm sure it's safe, I cautiously make my way inside.\n",
"\n",
"I move slowly, keeping my wand at the ready. I listen carefully for any sounds that might indicate the presence of cursed undead or other dangers. As I explore the temple, I search for any clues that might lead me to the Snake's Fang.\n",
"\n",
"It is your turn, Dungeon Master.\n",
"\n",
"\n",
"Storyteller (Dungeon Master):\n",
"\n",
"*As you step inside the temple, you notice that the air is thick with the scent of decay. The walls are covered in moss and vines, and the floor is slick with slime. Suddenly, you hear a low growling sound coming from the shadows.*\n",
"\n",
"You turn your wand towards the sound, ready to defend yourself. Out of the darkness emerges a pack of cursed undead wolves, their eyes glowing with an eerie green light. They snarl and bare their teeth, ready to attack.\n",
"\n",
"*Roll for initiative.*\n",
"\n",
"It is your turn, Harry Potter.\n",
"\n",
"\n",
"Protagonist (Harry Potter):\n",
"\n",
"I quickly assess the situation and prepare to defend myself. I cast a spell to create a shield around me, protecting me from the wolves' attacks.\n",
"\n",
"Then, I aim my wand at the wolves and cast a spell to immobilize them. I hope to buy myself some time to figure out my next move.\n",
"\n",
"If the immobilization spell is successful, I will quickly search the area for any clues that might lead me to the Snake's Fang. If not, I will have to defend myself against the wolves.\n",
"\n",
"It is your turn, Dungeon Master.\n",
"\n",
"\n",
"Storyteller (Dungeon Master):\n",
"\n",
"*You cast the immobilization spell, and it hits the wolves with a bright flash of light. The wolves are frozen in place, unable to move. You take a moment to catch your breath and survey the area.*\n",
"\n",
"As you look around, you notice a faint glow coming from a nearby altar. You cautiously approach the altar and see that it is covered in ancient runes. You recognize the runes as belonging to an old language of magic that you studied at Hogwarts.\n",
"\n",
"You decipher the runes and realize that they are a clue to the location of the Snake's Fang. The clue leads you to a hidden chamber deep within the temple.\n",
"\n",
"*You make your way to the hidden chamber and find the Snake's Fang resting on a pedestal. You carefully pick it up, feeling its power coursing through your veins.*\n",
"\n",
"Congratulations, Harry Potter! You have found one of Voldemort's horcruxes. But be warned, the journey ahead will only get more dangerous from here on out.\n",
"\n",
"It is your turn, Harry Potter.\n",
"\n",
"\n",
"Protagonist (Harry Potter):\n",
"\n",
"I take a deep breath and steel myself for the challenges ahead. I know that finding the remaining horcruxes won't be easy, but I'm determined to see this through to the end.\n",
"\n",
"I carefully stow the Snake's Fang away in my bag and make my way out of the hidden chamber. As I exit the temple, I keep my wand at the ready, knowing that there may be more cursed undead or other dangers lurking in the swamps.\n",
"\n",
"I make my way back to the Order of the Phoenix to report my success and to receive my next mission. I know that the fate of the wizarding world rests on my shoulders, and I'm ready to do whatever it takes to defeat Voldemort once and for all.\n",
"\n",
"It is your turn, Dungeon Master.\n",
"\n",
"\n",
"Storyteller (Dungeon Master):\n",
"\n",
"*As you make your way back to the Order of the Phoenix, you encounter a group of Death Eaters who have been sent to stop you. They are armed with wands and are ready to do whatever it takes to prevent you from finding the remaining horcruxes.*\n",
"\n",
"Roll for initiative.\n",
"\n",
"It is your turn, Harry Potter.\n",
"\n",
"\n",
"Protagonist (Harry Potter):\n",
"\n",
"I quickly assess the situation and prepare to defend myself. I cast a spell to create a shield around me, protecting me from the Death Eaters' attacks.\n",
"\n",
"Then, I aim my wand at the Death Eaters and cast a spell to disarm them. I hope to buy myself some time to figure out my next move.\n",
"\n",
"If the disarmament spell is successful, I will quickly try to escape and make my way back to the Order of the Phoenix. If not, I will have to defend myself against the Death Eaters.\n",
"\n",
"It is your turn, Dungeon Master.\n",
"\n",
"\n",
"Storyteller (Dungeon Master):\n",
"\n",
"*You cast the disarmament spell, and it hits the Death Eaters with a bright flash of light. Their wands fly out of their hands, and they are momentarily stunned.*\n",
"\n",
"You take advantage of the moment and quickly make your escape. You run through the swamps, dodging obstacles and avoiding any other dangers that might cross your path.\n",
"\n",
"Eventually, you make it back to the Order of the Phoenix, where you report your success in finding the Snake's Fang. The members of the Order congratulate you on your bravery and determination, and they give you your next mission.\n",
"\n",
"You must now journey to the Forbidden Forest to find the next horcrux, the Raven's Claw. The journey ahead will be perilous, but you know that you have the support of the Order of the Phoenix and the power of magic on your side.\n",
"\n",
"It is your turn, Harry Potter.\n",
"\n",
"\n",
"Protagonist (Harry Potter):\n",
"\n",
"I thank the members of the Order of the Phoenix for their support and guidance, and I set out towards the Forbidden Forest. As I make my way through the forest, I keep my wand at the ready, knowing that danger could be lurking around every corner.\n",
"\n",
"I search for any clues that might lead me to the Raven's Claw. I keep my eyes peeled for any signs of Voldemort's followers or other dangers that might be in my path.\n",
"\n",
"As I journey deeper into the forest, I begin to feel a sense of unease. The trees seem to be closing in around me, and the air is thick with an eerie silence. I know that I must stay alert and focused if I hope to find the Raven's Claw and make it out of the forest alive.\n",
"\n",
"It is your turn, Dungeon Master.\n",
"\n",
"\n",
"Storyteller (Dungeon Master):\n",
"\n",
"*As you make your way through the Forbidden Forest, you suddenly hear a rustling in the bushes. You turn your wand towards the sound, ready to defend yourself.*\n",
"\n",
"Out of the bushes emerges a group of acromantulas, their eyes gleaming with a malevolent hunger. They are massive spiders, each one the size of a small car. They hiss and bare their fangs, ready to attack.\n",
"\n",
"*Roll for initiative.*\n",
"\n",
"It is your turn, Harry Potter.\n",
"\n",
"\n",
"Protagonist (Harry Potter):\n",
"\n",
"I take a deep breath and prepare to defend myself against the acromantulas. I cast a spell to create a shield around me, protecting me from their attacks.\n",
"\n",
"Then, I aim my wand at the acromantulas and cast a spell to immobilize them. I hope to buy myself some time to figure out my next move.\n",
"\n",
"If the immobilization spell is successful, I will quickly search the area for any clues that might lead me to the Raven's Claw. If not, I will have to defend myself against the acromantulas.\n",
"\n",
"It is your turn, Dungeon Master.\n",
"\n",
"\n",
"Storyteller (Dungeon Master):\n",
"\n",
"*You cast the immobilization spell, and it hits the acromantulas with a bright flash of light. The acromantulas are frozen in place, unable to move. You take a moment to catch your breath and survey the area.*\n",
"\n",
"As you look around, you notice a faint glow coming from a nearby tree. You cautiously approach the tree and see that it is covered in ancient runes. You recognize the runes as belonging to an old language of magic that you studied at Hogwarts.\n",
"\n",
"You decipher the runes and realize that they are a clue to the location of the Raven's Claw. The clue leads you to a hidden cave deep within the forest.\n",
"\n",
"*You make your way to the hidden cave and find the Raven's Claw resting on a pedestal. You carefully pick it up, feeling its power coursing through your veins.*\n",
"\n",
"Congratulations, Harry Potter! You have found another one of Voldemort's horcruxes. But be warned, the journey ahead will only get more dangerous from here on out.\n",
"\n",
"It is your turn, Harry Potter.\n",
"\n",
"\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Protagonist (Harry Potter):\n",
"\n",
"I take a deep breath and stow the Raven's Claw away in my bag. I know that I must remain focused and vigilant if I hope to find the remaining horcruxes and defeat Voldemort once and for all.\n",
"\n",
"I make my way out of the Forbidden Forest and back to the Order of the Phoenix to report my success. I know that I must continue to rely on my friends and allies if I hope to succeed in my mission.\n",
"\n",
"I am ready for whatever challenges lie ahead, and I will not rest until Voldemort is defeated and the wizarding world is safe once again.\n",
"\n",
"It is your turn, Dungeon Master.\n",
"\n",
"\n",
"Storyteller (Dungeon Master):\n",
"\n",
"*As you make your way back to the Order of the Phoenix, you encounter a group of dementors who have been sent to stop you. They are floating ominously in the air, their tattered robes billowing in the wind. You feel their icy breath on the back of your neck, and you know that you must act quickly to defend yourself.*\n",
"\n",
"Roll for initiative.\n",
"\n",
"It is your turn, Harry Potter.\n",
"\n",
"\n",
"Protagonist (Harry Potter):\n",
"\n",
"I quickly assess the situation and prepare to defend myself against the dementors. I cast a Patronus charm to create a shield around me, protecting me from their attacks.\n",
"\n",
"Then, I aim my wand at the dementors and cast a spell to repel them. I hope to buy myself some time to figure out my next move.\n",
"\n",
"If the repelling spell is successful, I will quickly try to escape and make my way back to the Order of the Phoenix. If not, I will have to defend myself against the dementors.\n",
"\n",
"It is your turn, Dungeon Master.\n",
"\n",
"\n",
"Storyteller (Dungeon Master):\n",
"\n",
"*You cast the repelling spell, and it hits the dementors with a bright flash of light. The dementors are pushed back, giving you a moment to catch your breath.*\n",
"\n",
"You take advantage of the moment and quickly make your escape. You run through the forest, dodging obstacles and avoiding any other dangers that might cross your path.\n",
"\n",
"Eventually, you make it back to the Order of the Phoenix, where you report your success in finding the Raven's Claw. The members of the Order congratulate you on your bravery and determination, and they give you your next mission.\n",
"\n",
"You must now journey to the depths of Gringotts Bank to find the next horcrux, the Dragon's Heartstring. The journey ahead will be perilous, but you know that you have the support of the Order of the Phoenix and the power of magic on your side.\n",
"\n",
"It is your turn, Harry Potter.\n",
"\n",
"\n",
"Protagonist (Harry Potter):\n",
"\n",
"I thank the members of the Order of the Phoenix for their support and guidance, and I set out towards Gringotts Bank. As I make my way through the streets of Diagon Alley, I keep my wand at the ready, knowing that danger could be lurking around every corner.\n",
"\n",
"I search for any clues that might lead me to the Dragon's Heartstring. I keep my eyes peeled for any signs of Voldemort's followers or other dangers that might be in my path.\n",
"\n",
"As I journey deeper into Gringotts Bank, I begin to feel a sense of unease. The bank is heavily guarded, and I know that I must stay alert and focused if I hope to find the Dragon's Heartstring and make it out of the bank alive.\n",
"\n",
"It is your turn, Dungeon Master.\n",
"\n",
"\n",
"Storyteller (Dungeon Master):\n",
"\n",
"*As you make your way through Gringotts Bank, you suddenly hear a loud alarm ringing. You turn your wand towards the sound, ready to defend yourself.*\n",
"\n",
"Out of the shadows emerges a group of goblins, armed with swords and shields. They are the bank's security force, and they are ready to do whatever it takes to protect the bank's treasures.\n",
"\n",
"*Roll for initiative.*\n",
"\n",
"It is your turn, Harry Potter.\n",
"\n",
"\n",
"Protagonist (Harry Potter):\n",
"\n",
"I quickly assess the situation and prepare to defend myself against the goblins. I cast a spell to create a shield around me, protecting me from their attacks.\n",
"\n",
"Then, I aim my wand at the goblins and cast a spell to stun them. I hope to buy myself some time to figure out my next move.\n",
"\n",
"If the stunning spell is successful, I will quickly search the area for any clues that might lead me to the Dragon's Heartstring. If not, I will have to defend myself against the goblins.\n",
"\n",
"It is your turn, Dungeon Master.\n",
"\n",
"\n",
"Storyteller (Dungeon Master):\n",
"\n",
"*You cast the stunning spell, and it hits the goblins with a bright flash of light. The goblins are momentarily stunned, giving you a moment to catch your breath.*\n",
"\n",
"You take advantage of the moment and quickly make your way deeper into the bank. You search for any clues that might lead you to the Dragon's Heartstring.\n",
"\n",
"As you explore the bank, you come across a hidden vault. You recognize the vault as belonging to Bellatrix Lestrange, one of Voldemort's most loyal followers. You know that the Dragon's Heartstring must be inside.\n",
"\n",
"*You make your way into the vault and find the Dragon's Heartstring resting on a pedestal. You carefully pick it up, feeling its power coursing through your veins.*\n",
"\n",
"Congratulations, Harry Potter! You have found another one of Voldemort's horcruxes. But be warned, the journey ahead will only get more dangerous from here on out.\n",
"\n",
"It is your turn, Harry Potter.\n",
"\n",
"\n" "\n"
] ]
} }
@ -550,21 +307,82 @@
"specified_quest = ChatOpenAI(temperature=1.0)(quest_specifier_prompt).content\n", "specified_quest = ChatOpenAI(temperature=1.0)(quest_specifier_prompt).content\n",
"\n", "\n",
"print(f\"Original quest:\\n{quest}\\n\")\n", "print(f\"Original quest:\\n{quest}\\n\")\n",
"print(f\"Detailed quest:\\n{specified_quest}\\n\")\n", "print(f\"Detailed quest:\\n{specified_quest}\\n\")"
"\n", ]
"max_iters = 10\n", },
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Main Loop"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"protagonist = DialogueAgent(name=protagonist_name,\n",
" system_message=protagonist_system_message, \n",
" model=ChatOpenAI(temperature=0.2))\n",
"storyteller = DialogueAgent(name=storyteller_name,\n",
" system_message=storyteller_system_message, \n",
" model=ChatOpenAI(temperature=0.2))"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(Dungeon Master): Harry Potter, you must journey to the hidden cave where one of Voldemort's horcruxes resides. The cave is guarded by enchanted creatures and curses that can only be lifted by a unique magical potion. Use your wit and skill to obtain the ingredients, brew the potion, and retrieve the horcrux before time runs out.\n",
"\n",
"\n",
"(Harry Potter): I take a deep breath and focus on the task at hand. I search my bag for any potions or ingredients that may be useful in brewing the unique magical potion. If I don't have any, I will search the surrounding area for any plants or herbs that may be useful. Once I have all the necessary ingredients, I will brew the potion and use it to lift the curses and defeat any enchanted creatures guarding the horcrux. It won't be easy, but I am determined to succeed.\n",
"It is your turn, Dungeon Master.\n",
"\n",
"\n",
"(Dungeon Master): As you search your bag, you find a few useful potions and ingredients that will aid you in your quest. You also spot some herbs growing nearby that you recognize as key ingredients for the unique magical potion. You gather everything you need and begin brewing the potion. It takes some time, but you manage to create the perfect mixture. As you approach the cave, you drink the potion and feel a surge of power coursing through your veins. The curses and creatures guarding the horcrux are no match for you now. You retrieve the horcrux and add it to your collection. Well done, Harry Potter. But beware, the next horcrux will be even more challenging to obtain.\n",
"It is your turn, Harry Potter.\n",
"\n",
"\n",
"(Harry Potter): I take a moment to catch my breath and assess my next move. I know that the next horcrux will be even more difficult to obtain, but I am ready for the challenge. I consult my map and try to determine the location of the next horcrux. Once I have a general idea, I set off on foot, keeping my wand at the ready in case of any unexpected obstacles. I am determined to find and destroy all of Voldemort's horcruxes, no matter what it takes.\n",
"It is your turn, Dungeon Master.\n",
"\n",
"\n",
"(Dungeon Master): As you consult your map, you notice that the next horcrux is located in a heavily guarded fortress. The fortress is surrounded by a moat filled with dangerous creatures and the entrance is protected by powerful spells. You will need to come up with a plan to get past the guards and break through the spells. As you approach the fortress, you notice a group of Death Eaters patrolling the perimeter. What do you do, Harry Potter?\n",
"It is your turn, Harry Potter.\n",
"\n",
"\n",
"(Harry Potter): I take cover behind a nearby tree and observe the Death Eaters' movements. I try to determine their patrol patterns and identify any weaknesses in their defenses. Once I have a plan, I use my invisibility cloak to sneak past them and make my way to the fortress entrance. I use my knowledge of spells to try and break through the protective enchantments. If that doesn't work, I will try to find another way in, perhaps through a secret passage or hidden entrance. I won't let anything stop me from finding and destroying the next horcrux.\n",
"It is your turn, Dungeon Master.\n",
"\n",
"\n",
"(Dungeon Master): As you observe the Death Eaters, you notice that they have a predictable patrol pattern. You wait for the right moment and use your invisibility cloak to sneak past them undetected. You make your way to the fortress entrance and try to break through the protective enchantments, but they prove to be too strong. You search for another way in and eventually find a hidden entrance that leads you to the horcrux. However, as you reach for it, you trigger a trap that sets off an alarm and alerts the Death Eaters to your presence. You must act quickly to escape before they catch you. What do you do, Harry Potter?\n",
"It is your turn, Harry Potter.\n",
"\n",
"\n"
]
}
],
"source": [
"max_iters = 6\n",
"n = 0\n", "n = 0\n",
"\n", "\n",
"simulator = TwoAgentSimulator(\n", "simulator = DialogueSimulator(agents=[storyteller, protagonist])\n",
" first_speaker=storyteller, \n", "simulator.reset(storyteller_name, specified_quest)\n",
" second_speaker=protagonist)\n", "print(f\"({storyteller_name}): {specified_quest}\")\n",
"\n", "print('\\n')\n",
"msg_to_protagonist = simulator.reset(specified_quest)\n",
"\n", "\n",
"while n < max_iters:\n", "while n < max_iters:\n",
" msg_to_protagonist, msg_to_storyteller = simulator.step(msg_to_protagonist)\n", " name, message = simulator.step()\n",
" print(f\"Protagonist ({protagonist_name}):\\n\\n{msg_to_storyteller.content}\\n\\n\")\n", " print(f\"({name}): {message}\")\n",
" print(f\"Storyteller ({storyteller_name}):\\n\\n{msg_to_protagonist.content}\\n\\n\")\n", " print('\\n')\n",
" n += 1" " n += 1"
] ]
}, },