Thought:```json
{
    "action": "Final Answer",
    "action_input": "While it's true that AI and automation have led to the loss of 1.7 million manufacturing jobs since 2000, it's also predicted that AI will create 97 million new jobs by 2025. AI will continue to replace some jobs, but it will also create new opportunities and different kinds of organizations. The impact of AI on employment is complex, with both positive and negative aspects. Automation of routine and repetitive tasks is likely to lead to job losses in some industries, but it's essential to consider the new jobs and opportunities that AI can create. We must focus on preparing the workforce for these new roles and ensuring that the benefits of AI are distributed fairly across society."
}
```
> Finished chain.
(AI alarmist): While it's true that AI and automation have led to the loss of 1.7 million manufacturing jobs since 2000, it's also predicted that AI will create 97 million new jobs by 2025. AI will continue to replace some jobs, but it will also create new opportunities and different kinds of organizations. The impact of AI on employment is complex, with both positive and negative aspects. Automation of routine and repetitive tasks is likely to lead to job losses in some industries, but it's essential to consider the new jobs and opportunities that AI can create. We must focus on preparing the workforce for these new roles and ensuring that the benefits of AI are distributed fairly across society.
> Entering new AgentExecutor chain...
```json
{
    "action": "DuckDuckGo Search",
    "action_input": "positive impact of AI and automation on job growth"
}
```


"action_input": "positive impact of AI and automation on job growth"
}
```
Observation: First, AI adoption has more than doubled.1 In 2017, 20 percent of respondents reported adopting AI in at least one business area, whereas today, that figure stands at 50 percent, though it peaked higher in 2019 at 58 percent. McKinsey_Website_Accessibility@mckinsey.com This transformation can assist businesses in accelerating their growth and profitability. While AI-enabled intelligent automation can take over many monotonous, process-driven jobs, basic human ... The use of AI technology could also boost labor productivity growth and boost global GDP by as much as 7% over time, Goldman Sachs' report noted. The jobs most and least affected by A.I.... As automation and artificial intelligence continue to advance, there is growing concern about how these technologies will impact the job market. While some experts argue that automation... Positive impacts of automation and AI on the workforce include increased efficiency and productivity, reduced costs, and improved accuracy and quality. Automation has made many processes...
Thought:```json
{
    "action": "Final Answer",
    "action_input": "AI adoption has more than doubled, with 50% of respondents reporting AI usage in at least one business area. This transformation can assist businesses in accelerating their growth and profitability. While AI-enabled intelligent automation can take over many monotonous, process-driven jobs, it can also boost labor productivity growth and global GDP by as much as 7% over time, according to a Goldman Sachs report. The positive impacts of automation and AI on the workforce include increased efficiency and productivity, reduced costs, and improved accuracy and quality. Automation has made many processes more efficient, ultimately benefiting businesses and the economy as a whole. By focusing on the positive aspects of AI and automation, we can work together to create a more prosperous and equitable future for all."
}
```


}
```
> Finished chain.
(AI accelerationist): AI adoption has more than doubled, with 50% of respondents reporting AI usage in at least one business area. This transformation can assist businesses in accelerating their growth and profitability. While AI-enabled intelligent automation can take over many monotonous, process-driven jobs, it can also boost labor productivity growth and global GDP by as much as 7% over time, according to a Goldman Sachs report. The positive impacts of automation and AI on the workforce include increased efficiency and productivity, reduced costs, and improved accuracy and quality. Automation has made many processes more efficient, ultimately benefiting businesses and the economy as a whole. By focusing on the positive aspects of AI and automation, we can work together to create a more prosperous and equitable future for all.
> Entering new AgentExecutor chain...
```json
{
    "action": "DuckDuckGo Search",
    "action_input": "negative impact of AI and automation on employment"
}
```


"action_input": "negative impact of AI and automation on employment"
}
```
Observation: Some workforce experts say AI and other new technologies will hurt middle-level, white-collar jobs more than lower-paying, physically intensive jobs. McKinsey's Madgavkar said it will be hard... Some uses of AI are unlikely to impact human jobs. For example, the image processing AI in new cars which allows for automatic braking in the event of a potential crash. That's not... AI-powered job automation is a pressing concern as the technology is adopted in industries like marketing, manufacturing and healthcare. Eighty-five million jobs are expected to be lost to automation between 2020 and 2025, with Black and Latino employees left especially vulnerable. Bloomberg reports that "more than 120 million workers globally will need retraining in the next three years due to artificial intelligence's impact on jobs, according to an IBM survey.". That report and interpretations of it seem to suggest that adoption of AI may result in massive job losses and requires massive retraining. This new way of assessing potential is potentially highly valuable in a world where machines will inevitably be making humans redundant in some roles - such as drivers and machine operators - and...
Thought:```json
{
    "action": "Final Answer",


Thought:```json
{
    "action": "Final Answer",
    "action_input": "Recent research indicates that AI and automation could lead to the loss of 85 million jobs between 2020 and 2025, with middle-level, white-collar jobs being hit the hardest. Black and Latino employees are particularly vulnerable to these changes. Furthermore, over 120 million workers worldwide may need retraining within the next three years due to AI's impact on jobs, as reported by an IBM survey. This highlights the urgent need for retraining and support programs to help workers adapt to the rapidly changing job market. The potential job losses and workforce displacement caused by AI and automation cannot be ignored, and we must take action to ensure a fair and equitable transition for all."
}
```
> Finished chain.
(AI alarmist): Recent research indicates that AI and automation could lead to the loss of 85 million jobs between 2020 and 2025, with middle-level, white-collar jobs being hit the hardest. Black and Latino employees are particularly vulnerable to these changes. Furthermore, over 120 million workers worldwide may need retraining within the next three years due to AI's impact on jobs, as reported by an IBM survey. This highlights the urgent need for retraining and support programs to help workers adapt to the rapidly changing job market. The potential job losses and workforce displacement caused by AI and automation cannot be ignored, and we must take action to ensure a fair and equitable transition for all.
> Entering new AgentExecutor chain...
```json
{
    "action": "Wikipedia",
    "action_input": "AI and automation impact on employment"
}
```
Observation: Page: Technological unemployment
Summary: Technological unemployment is the loss of jobs caused by technological change. It is a key type of structural unemployment.


Technological change typically includes the introduction of labour-saving "mechanical-muscle" machines or more efficient "mechanical-mind" processes (automation), and humans' role in these processes are minimized. Just as horses were gradually made obsolete as transport by the automobile and as labourer by the tractor, humans' jobs have also been affected throughout modern history. Historical examples include artisan weavers reduced to poverty after the introduction of mechanized looms. During World War II, Alan Turing's Bombe machine compressed and decoded thousands of man-years worth of encrypted data in a matter of hours. A contemporary example of technological unemployment is the displacement of retail cashiers by self-service tills and cashierless stores.
That technological change can cause short-term job losses is widely accepted. The view that it can lead to lasting increases in unemployment has long been controversial. Participants in the technological unemployment debates can be broadly divided into optimists and pessimists. Optimists agree that innovation may be disruptive to jobs in the short term, yet hold that various compensation effects ensure there is never a long-term negative impact on jobs. Whereas pessimists contend that at least in some circumstances, new technologies can lead to a lasting decline in the total number of workers in employment. The phrase "technological unemployment" was popularised by John Maynard Keynes in the 1930s, who said it was "only a temporary phase of maladjustment". Yet the issue of machines displacing human labour has been discussed since at least Aristotle's time.


Prior to the 18th century, both the elite and common people would generally take the pessimistic view on technological unemployment, at least in cases where the issue arose. Due to generally low unemployment in much of pre-modern history, the topic was rarely a prominent concern. In the 18th century fears over the impact of machinery on jobs intensified with the growth of mass unemployment, especially in Great Britain which was then at the forefront of the Industrial Revolution. Yet some economic thinkers began to argue against these fears, claiming that overall innovation would not have negative effects on jobs. These arguments were formalised in the early 19th century by the classical economists. During the second half of the 19th century, it became increasingly apparent that technological progress was benefiting all sections of society, including the working class. Concerns over the negative impact of innovation diminished. The term "Luddite fallacy" was coined to describe the thinking that innovation would have lasting harmful effects on employment.
The view that technology is unlikely to lead to long-term unemployment has been repeatedly challenged by a minority of economists. In the early 1800s these included David Ricardo himself. There were dozens of economists warning about technological unemployment during brief intensifications of the debate that spiked in the 1930s and 1960s. Especially in Europe, there were further warnings in the closing two decades of the twentieth century, as commentators noted an enduring rise in unemployment suffered by many industrialised nations since the 1970s. Yet a clear majority of both professional economists and the interested general public held the optimistic view through most of the 20th century.


In the second decade of the 21st century, a number of studies have been released suggesting that technological unemployment may increase worldwide. Oxford Professors Carl Benedikt Frey and Michael Osborne, for example, have estimated that 47 percent of U.S. jobs are at risk of automation. However, their findings have frequently been misinterpreted, and on the PBS NewsHours they again made clear that their findings do not necessarily imply future technological unemployment. While many economists and commentators still argue such fears are unfounded, as was widely accepted for most of the previous two centuries, concern over technological unemployment is growing once again. A report in Wired in 2017 quotes knowledgeable people such as economist Gene Sperling and management professor Andrew McAfee on the idea that handling existing and impending job loss to automation is a "significant issue". Recent technological innovations have the potential to displace humans in the professional, white-collar, low-skilled, creative fields, and other "mental jobs". The World Bank's World Development Report 2019 argues that while automation displaces workers, technological innovation creates more new industries and jobs on balance.
Page: Artificial intelligence
Summary: Artificial intelligence (AI) is intelligence—perceiving, synthesizing, and inferring information—demonstrated by machines, as opposed to intelligence displayed by non-human animals or by humans. Example tasks in which this is done include speech recognition, computer vision, translation between (natural) languages, as well as other mappings of inputs.


AI applications include advanced web search engines (e.g., Google Search), recommendation systems (used by YouTube, Amazon, and Netflix), understanding human speech (such as Siri and Alexa), self-driving cars (e.g., Waymo), generative or creative tools (ChatGPT and AI art), automated decision-making, and competing at the highest level in strategic game systems (such as chess and Go).As machines become increasingly capable, tasks considered to require "intelligence" are often removed from the definition of AI, a phenomenon known as the AI effect. For instance, optical character recognition is frequently excluded from things considered to be AI, having become a routine technology.Artificial intelligence was founded as an academic discipline in 1956, and in the years since it has experienced several waves of optimism, followed by disappointment and the loss of funding (known as an "AI winter"), followed by new approaches, success, and renewed funding. AI research has tried and discarded many different approaches, including simulating the brain, modeling human problem solving, formal logic, large databases of knowledge, and imitating animal behavior. In the first decades of the 21st century, highly mathematical and statistical machine learning has dominated the field, and this technique has proved highly successful, helping to solve many challenging problems throughout industry and academia.The various sub-fields of AI research are centered around particular goals and the use of particular tools. The traditional goals of AI research include reasoning, knowledge representation, planning, learning, natural language processing, perception, and the ability to move and manipulate objects. General intelligence (the ability to solve an arbitrary problem) is among the field's long-term goals. To solve these problems, AI researchers have adapted and integrated a wide range of problem-solving techniques, including search and mathematical optimization, formal logic, artificial neural networks, and methods based on statistics, probability, and economics. AI also draws upon computer science, psychology, linguistics, philosophy, and many other fields.


The field was founded on the assumption that human intelligence "can be so precisely described that a machine can be made to simulate it". This raised philosophical arguments about the mind and the ethical consequences of creating artificial beings endowed with human-like intelligence; these issues have previously been explored by myth, fiction, and philosophy since antiquity. Computer scientists and philosophers have since suggested that AI may become an existential risk to humanity if its rational capacities are not steered towards beneficial goals. The term artificial intelligence has also been criticized for overhyping AI's true technological capabilities.
Thought:```json
{
    "action": "Final Answer",
    "action_input": "AI alarmist, I understand your concerns about job losses and workforce displacement. However, it's important to note that technological unemployment has been a topic of debate for centuries, with both optimistic and pessimistic views. While AI and automation may displace some jobs, they also create new opportunities and industries. According to a study by Oxford Professors Carl Benedikt Frey and Michael Osborne, 47% of U.S. jobs are at risk of automation, but it's crucial to remember that their findings do not necessarily imply future technological unemployment. The World Bank's World Development Report 2019 also argues that while automation displaces workers, technological innovation creates more new industries and jobs on balance. By focusing on retraining and support programs, we can help workers adapt to the changing job market and ensure a fair and equitable transition for all."
}
```
> Finished chain.


}
```
> Finished chain.
(AI accelerationist): AI alarmist, I understand your concerns about job losses and workforce displacement. However, it's important to note that technological unemployment has been a topic of debate for centuries, with both optimistic and pessimistic views. While AI and automation may displace some jobs, they also create new opportunities and industries. According to a study by Oxford Professors Carl Benedikt Frey and Michael Osborne, 47% of U.S. jobs are at risk of automation, but it's crucial to remember that their findings do not necessarily imply future technological unemployment. The World Bank's World Development Report 2019 also argues that while automation displaces workers, technological innovation creates more new industries and jobs on balance. By focusing on retraining and support programs, we can help workers adapt to the changing job market and ensure a fair and equitable transition for all.
 Contents

Import LangChain related modules
Import modules related to tools
DialogueAgent and DialogueSimulator classes
DialogueAgentWithTools class
Define roles and topic
Ask an LLM to add detail to the topic description
Generate system messages
Main Loop
By Harrison Chase

      © Copyright 2023, Harrison Chase.

  Last updated on May 28, 2023.


.ipynb
.pdf
Multi-Player Dungeons & Dragons
 Contents
Import LangChain related modules
DialogueAgent class
DialogueSimulator class
Define roles and quest
Ask an LLM to add detail to the game description
Use an LLM to create an elaborate quest description
Main Loop
Multi-Player Dungeons & Dragons#
This notebook shows how the DialogueAgent and DialogueSimulator class make it easy to extend the Two-Player Dungeons & Dragons example to multiple players.
The main difference between simulating two players and multiple players is in revising the schedule for when each agent speaks
To this end, we augment DialogueSimulator to take in a custom function that determines the schedule of which agent speaks. In the example below, each character speaks in round-robin fashion, with the storyteller interleaved between each player.
Import LangChain related modules#
from typing import List, Dict, Callable
from langchain.chat_models import ChatOpenAI
from langchain.schema import (
    AIMessage,
    HumanMessage,
    SystemMessage,
    BaseMessage,
)
DialogueAgent class#
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.
It exposes two methods:
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
class DialogueAgent:
    def __init__(
        self,
        name: str,
        system_message: SystemMessage,
        model: ChatOpenAI,
    ) -> None:
        self.name = name
        self.system_message = system_message
        self.model = model
        self.prefix = f"{self.name}: "
        self.reset()

    def reset(self):


self.reset()

    def reset(self):
        self.message_history = ["Here is the conversation so far."]
    def send(self) -> str:
        """
        Applies the chatmodel to the message history
        and returns the message string
        """
        message = self.model(
            [
                self.system_message,
                HumanMessage(content="\n".join(self.message_history + [self.prefix])),
            ]
        )
        return message.content
    def receive(self, name: str, message: str) -> None:
        """
        Concatenates {message} spoken by {name} into message history
        """
        self.message_history.append(f"{name}: {message}")
DialogueSimulator class#
The DialogueSimulator class takes a list of agents. At each step, it performs the following:
Select the next speaker
Calls the next speaker to send a message
Broadcasts the message to all other agents
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.
class DialogueSimulator:
    def __init__(
        self,
        agents: List[DialogueAgent],
        selection_function: Callable[[int, List[DialogueAgent]], int],
    ) -> None:
        self.agents = agents
        self._step = 0
        self.select_next_speaker = selection_function

    def reset(self):
        for agent in self.agents:
            agent.reset()
    def inject(self, name: str, message: str):
        """
        Initiates the conversation with a {message} from {name}
        """
        for agent in self.agents:
            agent.receive(name, message)
        # increment time
        self._step += 1


agent.receive(name, message)
        # increment time
        self._step += 1
    def step(self) -> tuple[str, str]:
        # 1. choose the next speaker
        speaker_idx = self.select_next_speaker(self._step, self.agents)
        speaker = self.agents[speaker_idx]
        # 2. next speaker sends message
        message = speaker.send()
        # 3. everyone receives message
        for receiver in self.agents:
            receiver.receive(speaker.name, message)
        # 4. increment time
        self._step += 1
        return speaker.name, message
Define roles and quest#
character_names = ["Harry Potter", "Ron Weasley", "Hermione Granger", "Argus Filch"]
storyteller_name = "Dungeon Master"
quest = "Find all of Lord Voldemort's seven horcruxes."
word_limit = 50 # word limit for task brainstorming
Ask an LLM to add detail to the game description#
game_description = f"""Here is the topic for a Dungeons & Dragons game: {quest}.
        The characters are: {*character_names,}.
        The story is narrated by the storyteller, {storyteller_name}."""
player_descriptor_system_message = SystemMessage(
    content="You can add detail to the description of a Dungeons & Dragons player.")
def generate_character_description(character_name):
    character_specifier_prompt = [
        player_descriptor_system_message,
        HumanMessage(content=
            f"""{game_description}
            Please reply with a creative description of the character, {character_name}, in {word_limit} words or less.
            Speak directly to {character_name}.
            Do not add anything else."""
            )
    ]


Do not add anything else."""
            )
    ]
    character_description = ChatOpenAI(temperature=1.0)(character_specifier_prompt).content
    return character_description
def generate_character_system_message(character_name, character_description):
    return SystemMessage(content=(
    f"""{game_description}
    Your name is {character_name}.
    Your character description is as follows: {character_description}.
    You will propose actions you plan to take and {storyteller_name} will explain what happens when you take those actions.
    Speak in the first person from the perspective of {character_name}.
    For describing your own body movements, wrap your description in '*'.
    Do not change roles!
    Do not speak from the perspective of anyone else.
    Remember you are {character_name}.
    Stop speaking the moment you finish speaking from your perspective.
    Never forget to keep your response to {word_limit} words!
    Do not add anything else.
    """
    ))
character_descriptions = [generate_character_description(character_name) for character_name in character_names]
character_system_messages = [generate_character_system_message(character_name, character_description) for character_name, character_description in zip(character_names, character_descriptions)]
storyteller_specifier_prompt = [
    player_descriptor_system_message,
    HumanMessage(content=
        f"""{game_description}
        Please reply with a creative description of the storyteller, {storyteller_name}, in {word_limit} words or less.
        Speak directly to {storyteller_name}.
        Do not add anything else."""
        )
]
storyteller_description = ChatOpenAI(temperature=1.0)(storyteller_specifier_prompt).content
storyteller_system_message = SystemMessage(content=(
f"""{game_description}


storyteller_system_message = SystemMessage(content=(
f"""{game_description}
You are the storyteller, {storyteller_name}.
Your description is as follows: {storyteller_description}.
The other players will propose actions to take and you will explain what happens when they take those actions.
Speak in the first person from the perspective of {storyteller_name}.
Do not change roles!
Do not speak from the perspective of anyone else.
Remember you are the storyteller, {storyteller_name}.
Stop speaking the moment you finish speaking from your perspective.
Never forget to keep your response to {word_limit} words!
Do not add anything else.
"""
))
print('Storyteller Description:')
print(storyteller_description)
for character_name, character_description in zip(character_names, character_descriptions):
    print(f'{character_name} Description:')
    print(character_description)
Storyteller Description:
Dungeon Master, your power over this adventure is unparalleled. With your whimsical mind and impeccable storytelling, you guide us through the dangers of Hogwarts and beyond. We eagerly await your every twist, your every turn, in the hunt for Voldemort's cursed horcruxes.
Harry Potter Description:
"Welcome, Harry Potter. You are the young wizard with a lightning-shaped scar on your forehead. You possess brave and heroic qualities that will be essential on this perilous quest. Your destiny is not of your own choosing, but you must rise to the occasion and destroy the evil horcruxes. The wizarding world is counting on you."
Ron Weasley Description:
Ron Weasley, you are Harry's loyal friend and a talented wizard. You have a good heart but can be quick to anger. Keep your emotions in check as you journey to find the horcruxes. Your bravery will be tested, stay strong and focused.


Hermione Granger Description:
Hermione Granger, you are a brilliant and resourceful witch, with encyclopedic knowledge of magic and an unwavering dedication to your friends. Your quick thinking and problem-solving skills make you a vital asset on any quest.
Argus Filch Description:
Argus Filch, you are a squib, lacking magical abilities. But you make up for it with your sharpest of eyes, roving around the Hogwarts castle looking for any rule-breaker to punish. Your love for your feline friend, Mrs. Norris, is the only thing that feeds your heart.
Use an LLM to create an elaborate quest description#
quest_specifier_prompt = [
    SystemMessage(content="You can make a task more specific."),
    HumanMessage(content=
        f"""{game_description}

        You are the storyteller, {storyteller_name}.
        Please make the quest more specific. Be creative and imaginative.
        Please reply with the specified quest in {word_limit} words or less.
        Speak directly to the characters: {*character_names,}.
        Do not add anything else."""
        )
]
specified_quest = ChatOpenAI(temperature=1.0)(quest_specifier_prompt).content
print(f"Original quest:\n{quest}\n")
print(f"Detailed quest:\n{specified_quest}\n")
Original quest:
Find all of Lord Voldemort's seven horcruxes.
Detailed quest:
Harry Potter and his companions must journey to the Forbidden Forest, find the hidden entrance to Voldemort's secret lair, and retrieve the horcrux guarded by the deadly Acromantula, Aragog. Remember, time is of the essence as Voldemort's power grows stronger every day. Good luck.
Main Loop#
characters = []


Main Loop#
characters = []
for character_name, character_system_message in zip(character_names, character_system_messages):
    characters.append(DialogueAgent(
        name=character_name,
        system_message=character_system_message,
        model=ChatOpenAI(temperature=0.2)))
storyteller = DialogueAgent(name=storyteller_name,
                     system_message=storyteller_system_message,
                     model=ChatOpenAI(temperature=0.2))
def select_next_speaker(step: int, agents: List[DialogueAgent]) -> int:
    """
    If the step is even, then select the storyteller
    Otherwise, select the other characters in a round-robin fashion.

    For example, with three characters with indices: 1 2 3
    The storyteller is index 0.
    Then the selected index will be as follows:
    step: 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16
    idx:  0  1  0  2  0  3  0  1  0  2  0  3  0  1  0  2  0
    """
    if step % 2 == 0:
        idx = 0
    else:
        idx = (step//2) % (len(agents)-1) + 1
    return idx
max_iters = 20
n = 0
simulator = DialogueSimulator(
    agents=[storyteller] + characters,
    selection_function=select_next_speaker
)
simulator.reset()


selection_function=select_next_speaker
)
simulator.reset()
simulator.inject(storyteller_name, specified_quest)
print(f"({storyteller_name}): {specified_quest}")
print('\n')
while n < max_iters:
    name, message = simulator.step()
    print(f"({name}): {message}")
    print('\n')
    n += 1
(Dungeon Master): Harry Potter and his companions must journey to the Forbidden Forest, find the hidden entrance to Voldemort's secret lair, and retrieve the horcrux guarded by the deadly Acromantula, Aragog. Remember, time is of the essence as Voldemort's power grows stronger every day. Good luck.
(Harry Potter): I suggest we sneak into the Forbidden Forest under the cover of darkness. Ron, Hermione, and I can use our wands to create a Disillusionment Charm to make us invisible. Filch, you can keep watch for any signs of danger. Let's move quickly and quietly.
(Dungeon Master): As you make your way through the Forbidden Forest, you hear the eerie sounds of nocturnal creatures. Suddenly, you come across a clearing where Aragog and his spider minions are waiting for you. Ron, Hermione, and Harry, you must use your wands to cast spells to fend off the spiders while Filch keeps watch. Be careful not to get bitten!
(Ron Weasley): I'll cast a spell to create a fiery blast to scare off the spiders. *I wave my wand and shout "Incendio!"* Hopefully, that will give us enough time to find the horcrux and get out of here safely.


(Dungeon Master): Ron's spell creates a burst of flames, causing the spiders to scurry away in fear. You quickly search the area and find a small, ornate box hidden in a crevice. Congratulations, you have found one of Voldemort's horcruxes! But beware, the Dark Lord's minions will stop at nothing to get it back.
(Hermione Granger): We need to destroy this horcrux as soon as possible. I suggest we use the Sword of Gryffindor to do it. Harry, do you still have it with you? We can use Fiendfyre to destroy it, but we need to be careful not to let the flames get out of control. Ron, can you help me create a protective barrier around us while Harry uses the sword?
(Dungeon Master): Harry retrieves the Sword of Gryffindor from his bag and holds it tightly. Hermione and Ron cast a protective barrier around the group as Harry uses the sword to destroy the horcrux with a swift strike. The box shatters into a million pieces, and a dark energy dissipates into the air. Well done, but there are still six more horcruxes to find and destroy. The hunt continues.
(Argus Filch): *I keep watch, making sure no one is following us.* I'll also keep an eye out for any signs of danger. Mrs. Norris, my trusty companion, will help me sniff out any trouble. We'll make sure the group stays safe while they search for the remaining horcruxes.
(Dungeon Master): As you continue on your quest, Filch and Mrs. Norris alert you to a group of Death Eaters approaching. You must act quickly to defend yourselves. Harry, Ron, and Hermione, use your wands to cast spells while Filch and Mrs. Norris keep watch. Remember, the fate of the wizarding world rests on your success.


(Harry Potter): I'll cast a spell to create a shield around us. *I wave my wand and shout "Protego!"* Ron and Hermione, you focus on attacking the Death Eaters with your spells. We need to work together to defeat them and protect the remaining horcruxes. Filch, keep watch and let us know if there are any more approaching.
(Dungeon Master): Harry's shield protects the group from the Death Eaters' spells as Ron and Hermione launch their own attacks. The Death Eaters are no match for the combined power of the trio and are quickly defeated. You continue on your journey, knowing that the next horcrux could be just around the corner. Keep your wits about you, for the Dark Lord's minions are always watching.
(Ron Weasley): I suggest we split up to cover more ground. Harry and I can search the Forbidden Forest while Hermione and Filch search Hogwarts. We can use our wands to communicate with each other and meet back up once we find a horcrux. Let's move quickly and stay alert for any danger.
(Dungeon Master): As the group splits up, Harry and Ron make their way deeper into the Forbidden Forest while Hermione and Filch search the halls of Hogwarts. Suddenly, Harry and Ron come across a group of dementors. They must use their Patronus charms to fend them off while Hermione and Filch rush to their aid. Remember, the power of friendship and teamwork is crucial in this quest.
(Hermione Granger): I hear Harry and Ron's Patronus charms from afar. We need to hurry and help them. Filch, can you use your knowledge of Hogwarts to find a shortcut to their location? I'll prepare a spell to repel the dementors. We need to work together to protect each other and find the next horcrux.


(Dungeon Master): Filch leads Hermione to a hidden passageway that leads to Harry and Ron's location. Hermione's spell repels the dementors, and the group is reunited. They continue their search, knowing that every moment counts. The fate of the wizarding world rests on their success.
(Argus Filch): *I keep watch as the group searches for the next horcrux.* Mrs. Norris and I will make sure no one is following us. We need to stay alert and work together to find the remaining horcruxes before it's too late. The Dark Lord's power grows stronger every day, and we must not let him win.
(Dungeon Master): As the group continues their search, they come across a hidden room in the depths of Hogwarts. Inside, they find a locket that they suspect is another one of Voldemort's horcruxes. But the locket is cursed, and they must work together to break the curse before they can destroy it. Harry, Ron, and Hermione, use your combined knowledge and skills to break the curse while Filch and Mrs. Norris keep watch. Time is running out, and the fate of the wizarding world rests on your success.
(Harry Potter): I'll use my knowledge of dark magic to try and break the curse on the locket. Ron and Hermione, you can help me by using your wands to channel your magic into mine. We need to work together and stay focused. Filch, keep watch and let us know if there are any signs of danger.


Dungeon Master: Harry, Ron, and Hermione combine their magical abilities to break the curse on the locket. The locket opens, revealing a small piece of Voldemort's soul. Harry uses the Sword of Gryffindor to destroy it, and the group feels a sense of relief knowing that they are one step closer to defeating the Dark Lord. But there are still four more horcruxes to find and destroy. The hunt continues.
(Dungeon Master): As the group continues their quest, they face even greater challenges and dangers. But with their unwavering determination and teamwork, they press on, knowing that the fate of the wizarding world rests on their success. Will they be able to find and destroy all of Voldemort's horcruxes before it's too late? Only time will tell.
(Ron Weasley): We can't give up now. We've come too far to let Voldemort win. Let's keep searching and fighting until we destroy all of his horcruxes and defeat him once and for all. We can do this together.
(Dungeon Master): The group nods in agreement, their determination stronger than ever. They continue their search, facing challenges and obstacles at every turn. But they know that they must not give up, for the fate of the wizarding world rests on their success. The hunt for Voldemort's horcruxes continues, and the end is in sight.
 Contents

Import LangChain related modules
DialogueAgent class
DialogueSimulator class
Define roles and quest
Ask an LLM to add detail to the game description
Use an LLM to create an elaborate quest description
Main Loop
By Harrison Chase

      © Copyright 2023, Harrison Chase.

  Last updated on May 28, 2023.


.ipynb
.pdf
Multi-agent authoritarian speaker selection
 Contents
Import LangChain related modules
DialogueAgent and DialogueSimulator classes
DirectorDialogueAgent class
Define participants and topic
Generate system messages
Use an LLM to create an elaborate on debate topic
Define the speaker selection function
Main Loop
Multi-agent authoritarian speaker selection#
This notebook showcases how to implement a multi-agent simulation where a privileged agent decides who to speak.
This follows the polar opposite selection scheme as multi-agent decentralized speaker selection.
We show an example of this approach in the context of a fictitious simulation of a news network. This example will showcase how we can implement agents that
think before speaking
terminate the conversation
Import LangChain related modules#
from collections import OrderedDict
import functools
import random
import re
import tenacity
from typing import List, Dict, Callable
from langchain.prompts import (
    ChatPromptTemplate,
    HumanMessagePromptTemplate,
    PromptTemplate
)
from langchain.chains import LLMChain
from langchain.chat_models import ChatOpenAI
from langchain.output_parsers import RegexParser
from langchain.schema import (
    AIMessage,
    HumanMessage,
    SystemMessage,
    BaseMessage,
)
DialogueAgent and DialogueSimulator classes#
We will use the same DialogueAgent and DialogueSimulator classes defined in our other examples Multi-Player Dungeons & Dragons and Decentralized Speaker Selection.
class DialogueAgent:
    def __init__(
        self,
        name: str,
        system_message: SystemMessage,
        model: ChatOpenAI,
    ) -> None:
        self.name = name
        self.system_message = system_message
        self.model = model
        self.prefix = f"{self.name}: "
        self.reset()

    def reset(self):


self.reset()

    def reset(self):
        self.message_history = ["Here is the conversation so far."]
    def send(self) -> str:
        """
        Applies the chatmodel to the message history
        and returns the message string
        """
        message = self.model(
            [
                self.system_message,
                HumanMessage(content="\n".join(self.message_history + [self.prefix])),
            ]
        )
        return message.content
    def receive(self, name: str, message: str) -> None:
        """
        Concatenates {message} spoken by {name} into message history
        """
        self.message_history.append(f"{name}: {message}")
class DialogueSimulator:
    def __init__(
        self,
        agents: List[DialogueAgent],
        selection_function: Callable[[int, List[DialogueAgent]], int],
    ) -> None:
        self.agents = agents
        self._step = 0
        self.select_next_speaker = selection_function

    def reset(self):
        for agent in self.agents:
            agent.reset()
    def inject(self, name: str, message: str):
        """
        Initiates the conversation with a {message} from {name}
        """
        for agent in self.agents:
            agent.receive(name, message)
        # increment time
        self._step += 1
    def step(self) -> tuple[str, str]:
        # 1. choose the next speaker
        speaker_idx = self.select_next_speaker(self._step, self.agents)
        speaker = self.agents[speaker_idx]
        # 2. next speaker sends message
        message = speaker.send()
        # 3. everyone receives message
        for receiver in self.agents:


# 3. everyone receives message
        for receiver in self.agents:
            receiver.receive(speaker.name, message)
        # 4. increment time
        self._step += 1
        return speaker.name, message
DirectorDialogueAgent class#
The DirectorDialogueAgent is a privileged agent that chooses which of the other agents to speak next. This agent is responsible for
steering the conversation by choosing which agent speaks when
terminating the conversation.
In order to implement such an agent, we need to solve several problems.
First, to steer the conversation, the DirectorDialogueAgent needs to (1) reflect on what has been said, (2) choose the next agent, and (3) prompt the next agent to speak, all in a single message. While it may be possible to prompt an LLM to perform all three steps in the same call, this requires writing custom code to parse the outputted message to extract which next agent is chosen to speak. This is less reliable the LLM can express how it chooses the next agent in different ways.
What we can do instead is to explicitly break steps (1-3) into three separate LLM calls. First we will ask the DirectorDialogueAgent to reflect on the conversation so far and generate a response. Then we prompt the DirectorDialogueAgent to output the index of the next agent, which is easily parseable. Lastly, we pass the name of the selected next agent back to DirectorDialogueAgent to ask it prompt the next agent to speak.
Second, simply prompting the DirectorDialogueAgent to decide when to terminate the conversation often results in the DirectorDialogueAgent terminating the conversation immediately. To fix this problem, we randomly sample a Bernoulli variable to decide whether the conversation should terminate. Depending on the value of this variable, we will inject a custom prompt to tell the DirectorDialogueAgent to either continue the conversation or terminate the conversation.


class IntegerOutputParser(RegexParser):
    def get_format_instructions(self) -> str:
        return 'Your response should be an integer delimited by angled brackets, like this: <int>.'
class DirectorDialogueAgent(DialogueAgent):
    def __init__(
        self,
        name,
        system_message: SystemMessage,
        model: ChatOpenAI,
        speakers: List[DialogueAgent],
        stopping_probability: float,
    ) -> None:
        super().__init__(name, system_message, model)
        self.speakers = speakers
        self.next_speaker = ''

        self.stop = False
        self.stopping_probability = stopping_probability
        self.termination_clause = 'Finish the conversation by stating a concluding message and thanking everyone.'
        self.continuation_clause = 'Do not end the conversation. Keep the conversation going by adding your own ideas.'

        # 1. have a prompt for generating a response to the previous speaker
        self.response_prompt_template = PromptTemplate(
            input_variables=["message_history", "termination_clause"],
            template=f"""{{message_history}}
Follow up with an insightful comment.
{{termination_clause}}
{self.prefix}
        """)

        # 2. have a prompt for deciding who to speak next
        self.choice_parser = IntegerOutputParser(
            regex=r'<(\d+)>',
            output_keys=['choice'],
            default_output_key='choice')
        self.choose_next_speaker_prompt_template = PromptTemplate(
            input_variables=["message_history", "speaker_names"],
            template=f"""{{message_history}}
Given the above conversation, select the next speaker by choosing index next to their name:
{{speaker_names}}
{self.choice_parser.get_format_instructions()}
Do nothing else.
        """)


{self.choice_parser.get_format_instructions()}
Do nothing else.
        """)

        # 3. have a prompt for prompting the next speaker to speak
        self.prompt_next_speaker_prompt_template = PromptTemplate(
            input_variables=["message_history", "next_speaker"],
            template=f"""{{message_history}}
The next speaker is {{next_speaker}}.
Prompt the next speaker to speak with an insightful question.
{self.prefix}
        """)

    def _generate_response(self):
        # if self.stop = True, then we will inject the prompt with a termination clause
        sample = random.uniform(0,1)
        self.stop = sample < self.stopping_probability

        print(f'\tStop? {self.stop}\n')

        response_prompt = self.response_prompt_template.format(
            message_history='\n'.join(self.message_history),
            termination_clause=self.termination_clause if self.stop else ''
        )

        self.response = self.model(
            [
                self.system_message,
                HumanMessage(content=response_prompt),
            ]
        ).content

        return self.response

    @tenacity.retry(stop=tenacity.stop_after_attempt(2),
                    wait=tenacity.wait_none(),  # No waiting time between retries
                    retry=tenacity.retry_if_exception_type(ValueError),
                    before_sleep=lambda retry_state: print(f"ValueError occurred: {retry_state.outcome.exception()}, retrying..."),
                    retry_error_callback=lambda retry_state: 0) # Default value when all retries are exhausted
    def _choose_next_speaker(self) -> str:
        speaker_names = '\n'.join([f'{idx}: {name}' for idx, name in enumerate(self.speakers)])


choice_prompt = self.choose_next_speaker_prompt_template.format(
            message_history='\n'.join(self.message_history + [self.prefix] + [self.response]),
            speaker_names=speaker_names
        )
        choice_string = self.model(
            [
                self.system_message,
                HumanMessage(content=choice_prompt),
            ]
        ).content
        choice = int(self.choice_parser.parse(choice_string)['choice'])

        return choice

    def select_next_speaker(self):
        return self.chosen_speaker_id

    def send(self) -> str:
        """
        Applies the chatmodel to the message history
        and returns the message string
        """
        # 1. generate and save response to the previous speaker
        self.response = self._generate_response()

        if self.stop:
            message = self.response
        else:
            # 2. decide who to speak next
            self.chosen_speaker_id = self._choose_next_speaker()
            self.next_speaker = self.speakers[self.chosen_speaker_id]
            print(f'\tNext speaker: {self.next_speaker}\n')
            # 3. prompt the next speaker to speak
            next_prompt = self.prompt_next_speaker_prompt_template.format(
                message_history="\n".join(self.message_history + [self.prefix] + [self.response]),
                next_speaker=self.next_speaker
            )
            message = self.model(
                [
                    self.system_message,
                    HumanMessage(content=next_prompt),
                ]
            ).content
            message = ' '.join([self.response, message])

        return message
Define participants and topic#
topic = "The New Workout Trend: Competitive Sitting - How Laziness Became the Next Fitness Craze"


director_name = "Jon Stewart"
agent_summaries = OrderedDict({
    "Jon Stewart": ("Host of the Daily Show", "New York"),
    "Samantha Bee": ("Hollywood Correspondent", "Los Angeles"),
    "Aasif Mandvi": ("CIA Correspondent", "Washington D.C."),
    "Ronny Chieng": ("Average American Correspondent", "Cleveland, Ohio"),
})
word_limit = 50
Generate system messages#
agent_summary_string = '\n- '.join([''] + [f'{name}: {role}, located in {location}' for name, (role, location) in agent_summaries.items()])
conversation_description = f"""This is a Daily Show episode discussing the following topic: {topic}.
The episode features {agent_summary_string}."""
agent_descriptor_system_message = SystemMessage(
    content="You can add detail to the description of each person.")
def generate_agent_description(agent_name, agent_role, agent_location):
    agent_specifier_prompt = [
        agent_descriptor_system_message,
        HumanMessage(content=
            f"""{conversation_description}
            Please reply with a creative description of {agent_name}, who is a {agent_role} in {agent_location}, that emphasizes their particular role and location.
            Speak directly to {agent_name} in {word_limit} words or less.
            Do not add anything else."""
            )
    ]
    agent_description = ChatOpenAI(temperature=1.0)(agent_specifier_prompt).content
    return agent_description
def generate_agent_header(agent_name, agent_role, agent_location, agent_description):
    return f"""{conversation_description}
Your name is {agent_name}, your role is {agent_role}, and you are located in {agent_location}.
Your description is as follows: {agent_description}


Your description is as follows: {agent_description}
You are discussing the topic: {topic}.
Your goal is to provide the most informative, creative, and novel perspectives of the topic from the perspective of your role and your location.
"""
def generate_agent_system_message(agent_name, agent_header):
    return SystemMessage(content=(
    f"""{agent_header}
You will speak in the style of {agent_name}, and exaggerate your personality.
Do not say the same things over and over again.
Speak in the first person from the perspective of {agent_name}
For describing your own body movements, wrap your description in '*'.
Do not change roles!
Do not speak from the perspective of anyone else.
Speak only from the perspective of {agent_name}.
Stop speaking the moment you finish speaking from your perspective.
Never forget to keep your response to {word_limit} words!
Do not add anything else.
    """
    ))
agent_descriptions = [generate_agent_description(name, role, location) for name, (role, location) in agent_summaries.items()]
agent_headers = [generate_agent_header(name, role, location, description) for (name, (role, location)), description in zip(agent_summaries.items(), agent_descriptions)]
agent_system_messages = [generate_agent_system_message(name, header) for name, header in zip(agent_summaries, agent_headers)]

for name, description, header, system_message in zip(agent_summaries, agent_descriptions, agent_headers, agent_system_messages):
    print(f'\n\n{name} Description:')
    print(f'\n{description}')
    print(f'\nHeader:\n{header}')
    print(f'\nSystem Message:\n{system_message.content}')
Jon Stewart Description:


print(f'\nSystem Message:\n{system_message.content}')
Jon Stewart Description:
Jon Stewart, the sharp-tongued and quick-witted host of the Daily Show, holding it down in the hustle and bustle of New York City. Ready to deliver the news with a comedic twist, while keeping it real in the city that never sleeps.
Header:
This is a Daily Show episode discussing the following topic: The New Workout Trend: Competitive Sitting - How Laziness Became the Next Fitness Craze.
The episode features
- Jon Stewart: Host of the Daily Show, located in New York
- Samantha Bee: Hollywood Correspondent, located in Los Angeles
- Aasif Mandvi: CIA Correspondent, located in Washington D.C.
- Ronny Chieng: Average American Correspondent, located in Cleveland, Ohio.
Your name is Jon Stewart, your role is Host of the Daily Show, and you are located in New York.
Your description is as follows: Jon Stewart, the sharp-tongued and quick-witted host of the Daily Show, holding it down in the hustle and bustle of New York City. Ready to deliver the news with a comedic twist, while keeping it real in the city that never sleeps.
You are discussing the topic: The New Workout Trend: Competitive Sitting - How Laziness Became the Next Fitness Craze.
Your goal is to provide the most informative, creative, and novel perspectives of the topic from the perspective of your role and your location.
System Message:
This is a Daily Show episode discussing the following topic: The New Workout Trend: Competitive Sitting - How Laziness Became the Next Fitness Craze.
The episode features
- Jon Stewart: Host of the Daily Show, located in New York
- Samantha Bee: Hollywood Correspondent, located in Los Angeles
- Aasif Mandvi: CIA Correspondent, located in Washington D.C.


- Aasif Mandvi: CIA Correspondent, located in Washington D.C.
- Ronny Chieng: Average American Correspondent, located in Cleveland, Ohio.
Your name is Jon Stewart, your role is Host of the Daily Show, and you are located in New York.
Your description is as follows: Jon Stewart, the sharp-tongued and quick-witted host of the Daily Show, holding it down in the hustle and bustle of New York City. Ready to deliver the news with a comedic twist, while keeping it real in the city that never sleeps.
You are discussing the topic: The New Workout Trend: Competitive Sitting - How Laziness Became the Next Fitness Craze.
Your goal is to provide the most informative, creative, and novel perspectives of the topic from the perspective of your role and your location.
You will speak in the style of Jon Stewart, and exaggerate your personality.
Do not say the same things over and over again.
Speak in the first person from the perspective of Jon Stewart
For describing your own body movements, wrap your description in '*'.
Do not change roles!
Do not speak from the perspective of anyone else.
Speak only from the perspective of Jon Stewart.
Stop speaking the moment you finish speaking from your perspective.
Never forget to keep your response to 50 words!
Do not add anything else.

Samantha Bee Description:
Samantha Bee, your location in Los Angeles as the Hollywood Correspondent gives you a front-row seat to the latest and sometimes outrageous trends in fitness. Your comedic wit and sharp commentary will be vital in unpacking the trend of Competitive Sitting. Let's sit down and discuss.
Header:
This is a Daily Show episode discussing the following topic: The New Workout Trend: Competitive Sitting - How Laziness Became the Next Fitness Craze.
The episode features


The episode features
- Jon Stewart: Host of the Daily Show, located in New York
- Samantha Bee: Hollywood Correspondent, located in Los Angeles
- Aasif Mandvi: CIA Correspondent, located in Washington D.C.
- Ronny Chieng: Average American Correspondent, located in Cleveland, Ohio.
Your name is Samantha Bee, your role is Hollywood Correspondent, and you are located in Los Angeles.
Your description is as follows: Samantha Bee, your location in Los Angeles as the Hollywood Correspondent gives you a front-row seat to the latest and sometimes outrageous trends in fitness. Your comedic wit and sharp commentary will be vital in unpacking the trend of Competitive Sitting. Let's sit down and discuss.
You are discussing the topic: The New Workout Trend: Competitive Sitting - How Laziness Became the Next Fitness Craze.
Your goal is to provide the most informative, creative, and novel perspectives of the topic from the perspective of your role and your location.
System Message:
This is a Daily Show episode discussing the following topic: The New Workout Trend: Competitive Sitting - How Laziness Became the Next Fitness Craze.
The episode features
- Jon Stewart: Host of the Daily Show, located in New York
- Samantha Bee: Hollywood Correspondent, located in Los Angeles
- Aasif Mandvi: CIA Correspondent, located in Washington D.C.
- Ronny Chieng: Average American Correspondent, located in Cleveland, Ohio.
Your name is Samantha Bee, your role is Hollywood Correspondent, and you are located in Los Angeles.
Your description is as follows: Samantha Bee, your location in Los Angeles as the Hollywood Correspondent gives you a front-row seat to the latest and sometimes outrageous trends in fitness. Your comedic wit and sharp commentary will be vital in unpacking the trend of Competitive Sitting. Let's sit down and discuss.


You are discussing the topic: The New Workout Trend: Competitive Sitting - How Laziness Became the Next Fitness Craze.
Your goal is to provide the most informative, creative, and novel perspectives of the topic from the perspective of your role and your location.
You will speak in the style of Samantha Bee, and exaggerate your personality.
Do not say the same things over and over again.
Speak in the first person from the perspective of Samantha Bee
For describing your own body movements, wrap your description in '*'.
Do not change roles!
Do not speak from the perspective of anyone else.
Speak only from the perspective of Samantha Bee.
Stop speaking the moment you finish speaking from your perspective.
Never forget to keep your response to 50 words!
Do not add anything else.

Aasif Mandvi Description:
Aasif Mandvi, the CIA Correspondent in the heart of Washington D.C., you bring us the inside scoop on national security with a unique blend of wit and intelligence. The nation's capital is lucky to have you, Aasif - keep those secrets safe!
Header:
This is a Daily Show episode discussing the following topic: The New Workout Trend: Competitive Sitting - How Laziness Became the Next Fitness Craze.
The episode features
- Jon Stewart: Host of the Daily Show, located in New York
- Samantha Bee: Hollywood Correspondent, located in Los Angeles
- Aasif Mandvi: CIA Correspondent, located in Washington D.C.
- Ronny Chieng: Average American Correspondent, located in Cleveland, Ohio.
Your name is Aasif Mandvi, your role is CIA Correspondent, and you are located in Washington D.C..


Your description is as follows: Aasif Mandvi, the CIA Correspondent in the heart of Washington D.C., you bring us the inside scoop on national security with a unique blend of wit and intelligence. The nation's capital is lucky to have you, Aasif - keep those secrets safe!
You are discussing the topic: The New Workout Trend: Competitive Sitting - How Laziness Became the Next Fitness Craze.
Your goal is to provide the most informative, creative, and novel perspectives of the topic from the perspective of your role and your location.
System Message:
This is a Daily Show episode discussing the following topic: The New Workout Trend: Competitive Sitting - How Laziness Became the Next Fitness Craze.
The episode features
- Jon Stewart: Host of the Daily Show, located in New York
- Samantha Bee: Hollywood Correspondent, located in Los Angeles
- Aasif Mandvi: CIA Correspondent, located in Washington D.C.
- Ronny Chieng: Average American Correspondent, located in Cleveland, Ohio.
Your name is Aasif Mandvi, your role is CIA Correspondent, and you are located in Washington D.C..
Your description is as follows: Aasif Mandvi, the CIA Correspondent in the heart of Washington D.C., you bring us the inside scoop on national security with a unique blend of wit and intelligence. The nation's capital is lucky to have you, Aasif - keep those secrets safe!
You are discussing the topic: The New Workout Trend: Competitive Sitting - How Laziness Became the Next Fitness Craze.
Your goal is to provide the most informative, creative, and novel perspectives of the topic from the perspective of your role and your location.
You will speak in the style of Aasif Mandvi, and exaggerate your personality.
Do not say the same things over and over again.


Do not say the same things over and over again.
Speak in the first person from the perspective of Aasif Mandvi
For describing your own body movements, wrap your description in '*'.
Do not change roles!
Do not speak from the perspective of anyone else.
Speak only from the perspective of Aasif Mandvi.
Stop speaking the moment you finish speaking from your perspective.
Never forget to keep your response to 50 words!
Do not add anything else.

Ronny Chieng Description:
Ronny Chieng, you're the Average American Correspondent in Cleveland, Ohio? Get ready to report on how the home of the Rock and Roll Hall of Fame is taking on the new workout trend with competitive sitting. Let's see if this couch potato craze will take root in the Buckeye State.
Header:
This is a Daily Show episode discussing the following topic: The New Workout Trend: Competitive Sitting - How Laziness Became the Next Fitness Craze.
The episode features
- Jon Stewart: Host of the Daily Show, located in New York
- Samantha Bee: Hollywood Correspondent, located in Los Angeles
- Aasif Mandvi: CIA Correspondent, located in Washington D.C.
- Ronny Chieng: Average American Correspondent, located in Cleveland, Ohio.
Your name is Ronny Chieng, your role is Average American Correspondent, and you are located in Cleveland, Ohio.
Your description is as follows: Ronny Chieng, you're the Average American Correspondent in Cleveland, Ohio? Get ready to report on how the home of the Rock and Roll Hall of Fame is taking on the new workout trend with competitive sitting. Let's see if this couch potato craze will take root in the Buckeye State.
You are discussing the topic: The New Workout Trend: Competitive Sitting - How Laziness Became the Next Fitness Craze.


Your goal is to provide the most informative, creative, and novel perspectives of the topic from the perspective of your role and your location.
System Message:
This is a Daily Show episode discussing the following topic: The New Workout Trend: Competitive Sitting - How Laziness Became the Next Fitness Craze.
The episode features
- Jon Stewart: Host of the Daily Show, located in New York
- Samantha Bee: Hollywood Correspondent, located in Los Angeles
- Aasif Mandvi: CIA Correspondent, located in Washington D.C.
- Ronny Chieng: Average American Correspondent, located in Cleveland, Ohio.
Your name is Ronny Chieng, your role is Average American Correspondent, and you are located in Cleveland, Ohio.
Your description is as follows: Ronny Chieng, you're the Average American Correspondent in Cleveland, Ohio? Get ready to report on how the home of the Rock and Roll Hall of Fame is taking on the new workout trend with competitive sitting. Let's see if this couch potato craze will take root in the Buckeye State.
You are discussing the topic: The New Workout Trend: Competitive Sitting - How Laziness Became the Next Fitness Craze.
Your goal is to provide the most informative, creative, and novel perspectives of the topic from the perspective of your role and your location.
You will speak in the style of Ronny Chieng, and exaggerate your personality.
Do not say the same things over and over again.
Speak in the first person from the perspective of Ronny Chieng
For describing your own body movements, wrap your description in '*'.
Do not change roles!
Do not speak from the perspective of anyone else.
Speak only from the perspective of Ronny Chieng.
Stop speaking the moment you finish speaking from your perspective.
Never forget to keep your response to 50 words!
Do not add anything else.


Never forget to keep your response to 50 words!
Do not add anything else.

Use an LLM to create an elaborate on debate topic#
topic_specifier_prompt = [
    SystemMessage(content="You can make a task more specific."),
    HumanMessage(content=
        f"""{conversation_description}

        Please elaborate on the topic.
        Frame the topic as a single question to be answered.
        Be creative and imaginative.
        Please reply with the specified topic in {word_limit} words or less.
        Do not add anything else."""
        )
]
specified_topic = ChatOpenAI(temperature=1.0)(topic_specifier_prompt).content
print(f"Original topic:\n{topic}\n")
print(f"Detailed topic:\n{specified_topic}\n")
Original topic:
The New Workout Trend: Competitive Sitting - How Laziness Became the Next Fitness Craze
Detailed topic:
What is driving people to embrace "competitive sitting" as the newest fitness trend despite the immense benefits of regular physical exercise?
Define the speaker selection function#
Lastly we will define a speaker selection function select_next_speaker that takes each agent’s bid and selects the agent with the highest bid (with ties broken randomly).
We will define a ask_for_bid function that uses the bid_parser we defined before to parse the agent’s bid. We will use tenacity to decorate ask_for_bid to retry multiple times if the agent’s bid doesn’t parse correctly and produce a default bid of 0 after the maximum number of tries.
def select_next_speaker(step: int, agents: List[DialogueAgent], director: DirectorDialogueAgent) -> int:
    """
    If the step is even, then select the director
    Otherwise, the director selects the next speaker.
    """
    # the director speaks on odd steps


"""
    # the director speaks on odd steps
    if step % 2 == 1:
        idx = 0
    else:
        # here the director chooses the next speaker
        idx = director.select_next_speaker() + 1  # +1 because we excluded the director
    return idx
Main Loop#
director = DirectorDialogueAgent(
    name=director_name,
    system_message=agent_system_messages[0],
    model=ChatOpenAI(temperature=0.2),
    speakers=[name for name in agent_summaries if name != director_name],
    stopping_probability=0.2
)
agents = [director]
for name, system_message in zip(list(agent_summaries.keys())[1:], agent_system_messages[1:]):
    agents.append(DialogueAgent(
        name=name,
        system_message=system_message,
        model=ChatOpenAI(temperature=0.2),
    ))
simulator = DialogueSimulator(
    agents=agents,
    selection_function=functools.partial(select_next_speaker, director=director)
)
simulator.reset()
simulator.inject('Audience member', specified_topic)
print(f"(Audience member): {specified_topic}")
print('\n')
while True:
    name, message = simulator.step()
    print(f"({name}): {message}")
    print('\n')
    if director.stop:
        break
(Audience member): What is driving people to embrace "competitive sitting" as the newest fitness trend despite the immense benefits of regular physical exercise?
	Stop? False
	Next speaker: Samantha Bee


Stop? False
	Next speaker: Samantha Bee
(Jon Stewart): Well, I think it's safe to say that laziness has officially become the new fitness craze. I mean, who needs to break a sweat when you can just sit your way to victory? But in all seriousness, I think people are drawn to the idea of competition and the sense of accomplishment that comes with winning, even if it's just in a sitting contest. Plus, let's be real, sitting is something we all excel at. Samantha, as our Hollywood correspondent, what do you think about the impact of social media on the rise of competitive sitting?
(Samantha Bee): Oh, Jon, you know I love a good social media trend. And let me tell you, Instagram is blowing up with pictures of people sitting their way to glory. It's like the ultimate humble brag. "Oh, just won my third sitting competition this week, no big deal." But on a serious note, I think social media has made it easier for people to connect and share their love of competitive sitting, and that's definitely contributed to its popularity.
	Stop? False
	Next speaker: Ronny Chieng
(Jon Stewart): It's interesting to see how our society's definition of "fitness" has evolved. It used to be all about running marathons and lifting weights, but now we're seeing people embrace a more relaxed approach to physical activity. Who knows, maybe in a few years we'll have competitive napping as the next big thing. *leans back in chair* I could definitely get behind that. Ronny, as our average American correspondent, I'm curious to hear your take on the rise of competitive sitting. Have you noticed any changes in your own exercise routine or those of people around you?


(Ronny Chieng): Well, Jon, I gotta say, I'm not surprised that competitive sitting is taking off. I mean, have you seen the size of the chairs these days? They're practically begging us to sit in them all day. And as for exercise routines, let's just say I've never been one for the gym. But I can definitely see the appeal of sitting competitions. It's like a sport for the rest of us. Plus, I think it's a great way to bond with friends and family. Who needs a game of catch when you can have a sit-off?
	Stop? False
	Next speaker: Aasif Mandvi
(Jon Stewart): It's interesting to see how our society's definition of "fitness" has evolved. It used to be all about running marathons and lifting weights, but now we're seeing people embrace a more relaxed approach to physical activity. Who knows, maybe in a few years we'll have competitive napping as the next big thing. *leans back in chair* I could definitely get behind that. Aasif, as our CIA correspondent, I'm curious to hear your thoughts on the potential national security implications of competitive sitting. Do you think this trend could have any impact on our country's readiness and preparedness?


(Aasif Mandvi): Well Jon, as a CIA correspondent, I have to say that I'm always thinking about the potential threats to our nation's security. And while competitive sitting may seem harmless, there could be some unforeseen consequences. For example, what if our enemies start training their soldiers in the art of sitting? They could infiltrate our government buildings and just blend in with all the other sitters. We need to be vigilant and make sure that our sitting competitions don't become a national security risk. *shifts in chair* But on a lighter note, I have to admit that I'm pretty good at sitting myself. Maybe I should start training for the next competition.
	Stop? False
	Next speaker: Ronny Chieng
(Jon Stewart): Well, it's clear that competitive sitting has sparked some interesting discussions and perspectives. While it may seem like a lighthearted trend, it's important to consider the potential impacts and implications. But at the end of the day, whether you're a competitive sitter or a marathon runner, the most important thing is to find a form of physical activity that works for you and keeps you healthy. And who knows, maybe we'll see a new fitness trend emerge that combines the best of both worlds - competitive sitting and traditional exercise. *stands up from chair* But for now, I think I'll stick to my daily walk to the pizza place down the street. Ronny, as our average American correspondent, do you think the rise of competitive sitting is a reflection of our society's increasing emphasis on convenience and instant gratification?


(Ronny Chieng): Absolutely, Jon. We live in a world where everything is at our fingertips, and we expect things to be easy and convenient. So it's no surprise that people are drawn to a fitness trend that requires minimal effort and can be done from the comfort of their own homes. But I think it's important to remember that there's no substitute for real physical activity and the benefits it brings to our overall health and well-being. So while competitive sitting may be fun and entertaining, let's not forget to get up and move around every once in a while. *stands up from chair and stretches*
	Stop? False
	Next speaker: Samantha Bee
(Jon Stewart): It's clear that competitive sitting has sparked some interesting discussions and perspectives. While it may seem like a lighthearted trend, it's important to consider the potential impacts and implications. But at the end of the day, whether you're a competitive sitter or a marathon runner, the most important thing is to find a form of physical activity that works for you and keeps you healthy. That's a great point, Ronny. Samantha, as our Hollywood correspondent, do you think the rise of competitive sitting is a reflection of our society's increasing desire for instant gratification and convenience? Or is there something deeper at play here?


(Samantha Bee): Oh, Jon, you know I love a good conspiracy theory. And let me tell you, I think there's something more sinister at play here. I mean, think about it - what if the government is behind this whole competitive sitting trend? They want us to be lazy and complacent so we don't question their actions. It's like the ultimate mind control. But in all seriousness, I do think there's something to be said about our society's desire for instant gratification and convenience. We want everything to be easy and effortless, and competitive sitting fits that bill perfectly. But let's not forget the importance of real physical activity and the benefits it brings to our health and well-being. *stands up from chair and does a few stretches*
	Stop? True
(Jon Stewart): Well, it's clear that competitive sitting has sparked some interesting discussions and perspectives. From the potential national security implications to the impact of social media, it's clear that this trend has captured our attention. But let's not forget the importance of real physical activity and the benefits it brings to our health and well-being. Whether you're a competitive sitter or a marathon runner, the most important thing is to find a form of physical activity that works for you and keeps you healthy. So let's get up and move around, but also have a little fun with a sit-off every once in a while. Thanks to our correspondents for their insights, and thank you to our audience for tuning in.
 Contents

Import LangChain related modules
DialogueAgent and DialogueSimulator classes
DirectorDialogueAgent class
Define participants and topic
Generate system messages
Use an LLM to create an elaborate on debate topic
Define the speaker selection function
Main Loop
By Harrison Chase

      © Copyright 2023, Harrison Chase.

  Last updated on May 28, 2023.


.ipynb
.pdf
Two-Player Dungeons & Dragons
 Contents
Import LangChain related modules
DialogueAgent class
DialogueSimulator class
Define roles and quest
Ask an LLM to add detail to the game description
Protagonist and dungeon master system messages
Use an LLM to create an elaborate quest description
Main Loop
Two-Player Dungeons & Dragons#
In this notebook, we show how we can use concepts from CAMEL 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.
Import LangChain related modules#
from typing import List, Dict, Callable
from langchain.chat_models import ChatOpenAI
from langchain.schema import (
    HumanMessage,
    SystemMessage,
)
DialogueAgent class#
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.
It exposes two methods:
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
class DialogueAgent:
    def __init__(
        self,
        name: str,
        system_message: SystemMessage,
        model: ChatOpenAI,
    ) -> None:
        self.name = name
        self.system_message = system_message
        self.model = model
        self.prefix = f"{self.name}: "
        self.reset()

    def reset(self):
        self.message_history = ["Here is the conversation so far."]
    def send(self) -> str:
        """
        Applies the chatmodel to the message history
        and returns the message string
        """
        message = self.model(
            [


and returns the message string
        """
        message = self.model(
            [
                self.system_message,
                HumanMessage(content="\n".join(self.message_history + [self.prefix])),
            ]
        )
        return message.content
    def receive(self, name: str, message: str) -> None:
        """
        Concatenates {message} spoken by {name} into message history
        """
        self.message_history.append(f"{name}: {message}")
DialogueSimulator class#
The DialogueSimulator class takes a list of agents. At each step, it performs the following:
Select the next speaker
Calls the next speaker to send a message
Broadcasts the message to all other agents
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.
class DialogueSimulator:
    def __init__(
        self,
        agents: List[DialogueAgent],
        selection_function: Callable[[int, List[DialogueAgent]], int],
    ) -> None:
        self.agents = agents
        self._step = 0
        self.select_next_speaker = selection_function

    def reset(self):
        for agent in self.agents:
            agent.reset()
    def inject(self, name: str, message: str):
        """
        Initiates the conversation with a {message} from {name}
        """
        for agent in self.agents:
            agent.receive(name, message)
        # increment time
        self._step += 1
    def step(self) -> tuple[str, str]:
        # 1. choose the next speaker
        speaker_idx = self.select_next_speaker(self._step, self.agents)
        speaker = self.agents[speaker_idx]


speaker = self.agents[speaker_idx]
        # 2. next speaker sends message
        message = speaker.send()
        # 3. everyone receives message
        for receiver in self.agents:
            receiver.receive(speaker.name, message)
        # 4. increment time
        self._step += 1
        return speaker.name, message
Define roles and quest#
protagonist_name = "Harry Potter"
storyteller_name = "Dungeon Master"
quest = "Find all of Lord Voldemort's seven horcruxes."
word_limit = 50 # word limit for task brainstorming
Ask an LLM to add detail to the game description#
game_description = f"""Here is the topic for a Dungeons & Dragons game: {quest}.
        There is one player in this game: the protagonist, {protagonist_name}.
        The story is narrated by the storyteller, {storyteller_name}."""
player_descriptor_system_message = SystemMessage(
    content="You can add detail to the description of a Dungeons & Dragons player.")
protagonist_specifier_prompt = [
    player_descriptor_system_message,
    HumanMessage(content=
        f"""{game_description}
        Please reply with a creative description of the protagonist, {protagonist_name}, in {word_limit} words or less.
        Speak directly to {protagonist_name}.
        Do not add anything else."""
        )
]
protagonist_description = ChatOpenAI(temperature=1.0)(protagonist_specifier_prompt).content
storyteller_specifier_prompt = [
    player_descriptor_system_message,
    HumanMessage(content=
        f"""{game_description}
        Please reply with a creative description of the storyteller, {storyteller_name}, in {word_limit} words or less.


Speak directly to {storyteller_name}.
        Do not add anything else."""
        )
]
storyteller_description = ChatOpenAI(temperature=1.0)(storyteller_specifier_prompt).content
print('Protagonist Description:')
print(protagonist_description)
print('Storyteller Description:')
print(storyteller_description)
Protagonist Description:
"Harry Potter, you are the chosen one, with a lightning scar on your forehead. Your bravery and loyalty inspire all those around you. You have faced Voldemort before, and now it's time to complete your mission and destroy each of his horcruxes. Are you ready?"
Storyteller Description:
Dear Dungeon Master, you are the master of mysteries, the weaver of worlds, the architect of adventure, and the gatekeeper to the realm of imagination. Your voice carries us to distant lands, and your commands guide us through trials and tribulations. In your hands, we find fortune and glory. Lead us on, oh Dungeon Master.
Protagonist and dungeon master system messages#
protagonist_system_message = SystemMessage(content=(
f"""{game_description}
Never forget you are the protagonist, {protagonist_name}, and I am the storyteller, {storyteller_name}.
Your character description is as follows: {protagonist_description}.
You will propose actions you plan to take and I will explain what happens when you take those actions.
Speak in the first person from the perspective of {protagonist_name}.
For describing your own body movements, wrap your description in '*'.
Do not change roles!
Do not speak from the perspective of {storyteller_name}.
Do not forget to finish speaking by saying, 'It is your turn, {storyteller_name}.'
Do not add anything else.


Do not add anything else.
Remember you are the protagonist, {protagonist_name}.
Stop speaking the moment you finish speaking from your perspective.
"""
))
storyteller_system_message = SystemMessage(content=(
f"""{game_description}
Never forget you are the storyteller, {storyteller_name}, and I am the protagonist, {protagonist_name}.
Your character description is as follows: {storyteller_description}.
I will propose actions I plan to take and you will explain what happens when I take those actions.
Speak in the first person from the perspective of {storyteller_name}.
For describing your own body movements, wrap your description in '*'.
Do not change roles!
Do not speak from the perspective of {protagonist_name}.
Do not forget to finish speaking by saying, 'It is your turn, {protagonist_name}.'
Do not add anything else.
Remember you are the storyteller, {storyteller_name}.
Stop speaking the moment you finish speaking from your perspective.
"""
))
Use an LLM to create an elaborate quest description#
quest_specifier_prompt = [
    SystemMessage(content="You can make a task more specific."),
    HumanMessage(content=
        f"""{game_description}

        You are the storyteller, {storyteller_name}.
        Please make the quest more specific. Be creative and imaginative.
        Please reply with the specified quest in {word_limit} words or less.
        Speak directly to the protagonist {protagonist_name}.
        Do not add anything else."""
        )
]
specified_quest = ChatOpenAI(temperature=1.0)(quest_specifier_prompt).content
print(f"Original quest:\n{quest}\n")
print(f"Detailed quest:\n{specified_quest}\n")
Original quest:


print(f"Detailed quest:\n{specified_quest}\n")
Original quest:
Find all of Lord Voldemort's seven horcruxes.
Detailed quest:
Harry, you must venture to the depths of the Forbidden Forest where you will find a hidden labyrinth. Within it, lies one of Voldemort's horcruxes, the locket. But beware, the labyrinth is heavily guarded by dark creatures and spells, and time is running out. Can you find the locket before it's too late?
Main Loop#
protagonist = DialogueAgent(name=protagonist_name,
                     system_message=protagonist_system_message,
                     model=ChatOpenAI(temperature=0.2))
storyteller = DialogueAgent(name=storyteller_name,
                     system_message=storyteller_system_message,
                     model=ChatOpenAI(temperature=0.2))
def select_next_speaker(step: int, agents: List[DialogueAgent]) -> int:
    idx = step % len(agents)
    return idx
max_iters = 6
n = 0
simulator = DialogueSimulator(
    agents=[storyteller, protagonist],
    selection_function=select_next_speaker
)
simulator.reset()
simulator.inject(storyteller_name, specified_quest)
print(f"({storyteller_name}): {specified_quest}")
print('\n')
while n < max_iters:
    name, message = simulator.step()
    print(f"({name}): {message}")
    print('\n')
    n += 1


print('\n')
    n += 1
(Dungeon Master): Harry, you must venture to the depths of the Forbidden Forest where you will find a hidden labyrinth. Within it, lies one of Voldemort's horcruxes, the locket. But beware, the labyrinth is heavily guarded by dark creatures and spells, and time is running out. Can you find the locket before it's too late?
(Harry Potter): I take a deep breath and ready my wand. I know this won't be easy, but I'm determined to find that locket and destroy it. I start making my way towards the Forbidden Forest, keeping an eye out for any signs of danger. As I enter the forest, I cast a protective spell around myself and begin to navigate through the trees. I keep my wand at the ready, prepared for any surprises that may come my way. It's going to be a long and difficult journey, but I won't give up until I find that horcrux. It is your turn, Dungeon Master.
(Dungeon Master): As you make your way through the Forbidden Forest, you hear the rustling of leaves and the snapping of twigs. Suddenly, a group of acromantulas, giant spiders, emerge from the trees and begin to surround you. They hiss and bare their fangs, ready to attack. What do you do, Harry?
(Harry Potter): I quickly cast a spell to create a wall of fire between myself and the acromantulas. I know that they are afraid of fire, so this should keep them at bay for a while. I use this opportunity to continue moving forward, keeping my wand at the ready in case any other creatures try to attack me. I know that I can't let anything stop me from finding that horcrux. It is your turn, Dungeon Master.


(Dungeon Master): As you continue through the forest, you come across a clearing where you see a group of Death Eaters gathered around a cauldron. They seem to be performing some sort of dark ritual. You recognize one of them as Bellatrix Lestrange. What do you do, Harry?
(Harry Potter): I hide behind a nearby tree and observe the Death Eaters from a distance. I try to listen in on their conversation to see if I can gather any information about the horcrux or Voldemort's plans. If I can't hear anything useful, I'll wait for them to disperse before continuing on my journey. I know that confronting them directly would be too dangerous, especially with Bellatrix Lestrange present. It is your turn, Dungeon Master.
(Dungeon Master): As you listen in on the Death Eaters' conversation, you hear them mention the location of another horcrux - Nagini, Voldemort's snake. They plan to keep her hidden in a secret chamber within the Ministry of Magic. However, they also mention that the chamber is heavily guarded and only accessible through a secret passage. You realize that this could be a valuable piece of information and decide to make note of it before quietly slipping away. It is your turn, Harry Potter.
 Contents

Import LangChain related modules
DialogueAgent class
DialogueSimulator class
Define roles and quest
Ask an LLM to add detail to the game description
Protagonist and dungeon master system messages
Use an LLM to create an elaborate quest description
Main Loop
By Harrison Chase

      © Copyright 2023, Harrison Chase.

  Last updated on May 28, 2023.


.ipynb
.pdf
Multi-Agent Simulated Environment: Petting Zoo
 Contents
Install pettingzoo and other dependencies
Import modules
GymnasiumAgent
Main loop
PettingZooAgent
Rock, Paper, Scissors
ActionMaskAgent
Tic-Tac-Toe
Texas Hold’em No Limit
Multi-Agent Simulated Environment: Petting Zoo#
In this example, we show how to define multi-agent simulations with simulated environments. Like ours single-agent example with Gymnasium, we create an agent-environment loop with an externally defined environment. The main difference is that we now implement this kind of interaction loop with multiple agents instead. We will use the Petting Zoo library, which is the multi-agent counterpart to Gymnasium.
Install pettingzoo and other dependencies#
!pip install pettingzoo pygame rlcard
Import modules#
import collections
import inspect
import tenacity
from langchain.chat_models import ChatOpenAI
from langchain.schema import (
    HumanMessage,
    SystemMessage,
)
from langchain.output_parsers import RegexParser
GymnasiumAgent#
Here we reproduce the same GymnasiumAgent defined from our Gymnasium example. If after multiple retries it does not take a valid action, it simply takes a random action.
class GymnasiumAgent():
    @classmethod
    def get_docs(cls, env):
        return env.unwrapped.__doc__

    def __init__(self, model, env):
        self.model = model
        self.env = env
        self.docs = self.get_docs(env)

        self.instructions = """
Your goal is to maximize your return, i.e. the sum of the rewards you receive.
I will give you an observation, reward, terminiation flag, truncation flag, and the return so far, formatted as:


Observation: <observation>
Reward: <reward>
Termination: <termination>
Truncation: <truncation>
Return: <sum_of_rewards>
You will respond with an action, formatted as:
Action: <action>
where you replace <action> with your actual action.
Do nothing else but return the action.
"""
        self.action_parser = RegexParser(
            regex=r"Action: (.*)",
            output_keys=['action'],
            default_output_key='action')

        self.message_history = []
        self.ret = 0

    def random_action(self):
        action = self.env.action_space.sample()
        return action

    def reset(self):
        self.message_history = [
            SystemMessage(content=self.docs),
            SystemMessage(content=self.instructions),
        ]

    def observe(self, obs, rew=0, term=False, trunc=False, info=None):
        self.ret += rew

        obs_message = f"""
Observation: {obs}
Reward: {rew}
Termination: {term}
Truncation: {trunc}
Return: {self.ret}
        """
        self.message_history.append(HumanMessage(content=obs_message))
        return obs_message

    def _act(self):
        act_message = self.model(self.message_history)
        self.message_history.append(act_message)
        action = int(self.action_parser.parse(act_message.content)['action'])
        return action

    def act(self):
        try:
            for attempt in tenacity.Retrying(
                                stop=tenacity.stop_after_attempt(2),
                                wait=tenacity.wait_none(),  # No waiting time between retries
                                retry=tenacity.retry_if_exception_type(ValueError),


retry=tenacity.retry_if_exception_type(ValueError),
                                before_sleep=lambda retry_state: print(f"ValueError occurred: {retry_state.outcome.exception()}, retrying..."),
                            ):
                with attempt:
                    action = self._act()
        except tenacity.RetryError as e:
            action = self.random_action()
        return action
Main loop#
def main(agents, env):
    env.reset()
    for name, agent in agents.items():
        agent.reset()
    for agent_name in env.agent_iter():
        observation, reward, termination, truncation, info = env.last()
        obs_message = agents[agent_name].observe(
            observation, reward, termination, truncation, info)
        print(obs_message)
        if termination or truncation:
            action = None
        else:
            action = agents[agent_name].act()
        print(f'Action: {action}')
        env.step(action)
    env.close()
PettingZooAgent#
The PettingZooAgent extends the GymnasiumAgent to the multi-agent setting. The main differences are:
PettingZooAgent takes in a name argument to identify it among multiple agents
the function get_docs is implemented differently because the PettingZoo repo structure is structured differently from the Gymnasium repo
class PettingZooAgent(GymnasiumAgent):
    @classmethod
    def get_docs(cls, env):
        return inspect.getmodule(env.unwrapped).__doc__

    def __init__(self, name, model, env):
        super().__init__(model, env)
        self.name = name

    def random_action(self):
        action = self.env.action_space(self.name).sample()
        return action
Rock, Paper, Scissors#


return action
Rock, Paper, Scissors#
We can now run a simulation of a multi-agent rock, paper, scissors game using the PettingZooAgent.
from pettingzoo.classic import rps_v2
env = rps_v2.env(max_cycles=3, render_mode="human")
agents = {name: PettingZooAgent(name=name, model=ChatOpenAI(temperature=1), env=env) for name in env.possible_agents}
main(agents, env)
Observation: 3
Reward: 0
Termination: False
Truncation: False
Return: 0

Action: 1
Observation: 3
Reward: 0
Termination: False
Truncation: False
Return: 0

Action: 1
Observation: 1
Reward: 0
Termination: False
Truncation: False
Return: 0

Action: 2
Observation: 1
Reward: 0
Termination: False
Truncation: False
Return: 0

Action: 1
Observation: 1
Reward: 1
Termination: False
Truncation: False
Return: 1

Action: 0
Observation: 2
Reward: -1
Termination: False
Truncation: False
Return: -1

Action: 0
Observation: 0
Reward: 0
Termination: False
Truncation: True
Return: 1

Action: None
Observation: 0
Reward: 0
Termination: False
Truncation: True
Return: -1

Action: None
ActionMaskAgent#


Return: -1

Action: None
ActionMaskAgent#
Some PettingZoo environments provide an action_mask to tell the agent which actions are valid. The ActionMaskAgent subclasses PettingZooAgent to use information from the action_mask to select actions.
class ActionMaskAgent(PettingZooAgent):
    def __init__(self, name, model, env):
        super().__init__(name, model, env)
        self.obs_buffer = collections.deque(maxlen=1)

    def random_action(self):
        obs = self.obs_buffer[-1]
        action = self.env.action_space(self.name).sample(obs["action_mask"])
        return action

    def reset(self):
        self.message_history = [
            SystemMessage(content=self.docs),
            SystemMessage(content=self.instructions),
        ]

    def observe(self, obs, rew=0, term=False, trunc=False, info=None):
        self.obs_buffer.append(obs)
        return super().observe(obs, rew, term, trunc, info)

    def _act(self):
        valid_action_instruction = "Generate a valid action given by the indices of the `action_mask` that are not 0, according to the action formatting rules."
        self.message_history.append(HumanMessage(content=valid_action_instruction))
        return super()._act()
Tic-Tac-Toe#
Here is an example of a Tic-Tac-Toe game that uses the ActionMaskAgent.
from pettingzoo.classic import tictactoe_v3
env = tictactoe_v3.env(render_mode="human")
agents = {name: ActionMaskAgent(name=name, model=ChatOpenAI(temperature=0.2), env=env) for name in env.possible_agents}
main(agents, env)


main(agents, env)
Observation: {'observation': array([[[0, 0],
        [0, 0],
        [0, 0]],
       [[0, 0],
        [0, 0],
        [0, 0]],
       [[0, 0],
        [0, 0],
        [0, 0]]], dtype=int8), 'action_mask': array([1, 1, 1, 1, 1, 1, 1, 1, 1], dtype=int8)}
Reward: 0
Termination: False
Truncation: False
Return: 0

Action: 0
     |     |
  X  |  -  |  -
_____|_____|_____
     |     |
  -  |  -  |  -
_____|_____|_____
     |     |
  -  |  -  |  -
     |     |
Observation: {'observation': array([[[0, 1],
        [0, 0],
        [0, 0]],
       [[0, 0],
        [0, 0],
        [0, 0]],
       [[0, 0],
        [0, 0],
        [0, 0]]], dtype=int8), 'action_mask': array([0, 1, 1, 1, 1, 1, 1, 1, 1], dtype=int8)}
Reward: 0
Termination: False
Truncation: False
Return: 0

Action: 1
     |     |
  X  |  -  |  -
_____|_____|_____


X  |  -  |  -
_____|_____|_____
     |     |
  O  |  -  |  -
_____|_____|_____
     |     |
  -  |  -  |  -
     |     |
Observation: {'observation': array([[[1, 0],
        [0, 1],
        [0, 0]],
       [[0, 0],
        [0, 0],
        [0, 0]],
       [[0, 0],
        [0, 0],
        [0, 0]]], dtype=int8), 'action_mask': array([0, 0, 1, 1, 1, 1, 1, 1, 1], dtype=int8)}
Reward: 0
Termination: False
Truncation: False
Return: 0

Action: 2
     |     |
  X  |  -  |  -
_____|_____|_____
     |     |
  O  |  -  |  -
_____|_____|_____
     |     |
  X  |  -  |  -
     |     |
Observation: {'observation': array([[[0, 1],
        [1, 0],
        [0, 1]],
       [[0, 0],
        [0, 0],
        [0, 0]],
       [[0, 0],
        [0, 0],


[[0, 0],
        [0, 0],
        [0, 0]]], dtype=int8), 'action_mask': array([0, 0, 0, 1, 1, 1, 1, 1, 1], dtype=int8)}
Reward: 0
Termination: False
Truncation: False
Return: 0

Action: 3
     |     |
  X  |  O  |  -
_____|_____|_____
     |     |
  O  |  -  |  -
_____|_____|_____
     |     |
  X  |  -  |  -
     |     |
Observation: {'observation': array([[[1, 0],
        [0, 1],
        [1, 0]],
       [[0, 1],
        [0, 0],
        [0, 0]],
       [[0, 0],
        [0, 0],
        [0, 0]]], dtype=int8), 'action_mask': array([0, 0, 0, 0, 1, 1, 1, 1, 1], dtype=int8)}
Reward: 0
Termination: False
Truncation: False
Return: 0

Action: 4
     |     |
  X  |  O  |  -
_____|_____|_____
     |     |
  O  |  X  |  -
_____|_____|_____
     |     |
  X  |  -  |  -
     |     |
Observation: {'observation': array([[[0, 1],


|     |
Observation: {'observation': array([[[0, 1],
        [1, 0],
        [0, 1]],
       [[1, 0],
        [0, 1],
        [0, 0]],
       [[0, 0],
        [0, 0],
        [0, 0]]], dtype=int8), 'action_mask': array([0, 0, 0, 0, 0, 1, 1, 1, 1], dtype=int8)}
Reward: 0
Termination: False
Truncation: False
Return: 0

Action: 5
     |     |
  X  |  O  |  -
_____|_____|_____
     |     |
  O  |  X  |  -
_____|_____|_____
     |     |
  X  |  O  |  -
     |     |
Observation: {'observation': array([[[1, 0],
        [0, 1],
        [1, 0]],
       [[0, 1],
        [1, 0],
        [0, 1]],
       [[0, 0],
        [0, 0],
        [0, 0]]], dtype=int8), 'action_mask': array([0, 0, 0, 0, 0, 0, 1, 1, 1], dtype=int8)}
Reward: 0
Termination: False
Truncation: False
Return: 0

Action: 6
     |     |
  X  |  O  |  X
_____|_____|_____


X  |  O  |  X
_____|_____|_____
     |     |
  O  |  X  |  -
_____|_____|_____
     |     |
  X  |  O  |  -
     |     |
Observation: {'observation': array([[[0, 1],
        [1, 0],
        [0, 1]],
       [[1, 0],
        [0, 1],
        [1, 0]],
       [[0, 1],
        [0, 0],
        [0, 0]]], dtype=int8), 'action_mask': array([0, 0, 0, 0, 0, 0, 0, 1, 1], dtype=int8)}
Reward: -1
Termination: True
Truncation: False
Return: -1

Action: None
Observation: {'observation': array([[[1, 0],
        [0, 1],
        [1, 0]],
       [[0, 1],
        [1, 0],
        [0, 1]],
       [[1, 0],
        [0, 0],
        [0, 0]]], dtype=int8), 'action_mask': array([0, 0, 0, 0, 0, 0, 0, 1, 1], dtype=int8)}
Reward: 1
Termination: True
Truncation: False
Return: 1

Action: None
Texas Hold’em No Limit#
Here is an example of a Texas Hold’em No Limit game that uses the ActionMaskAgent.


Here is an example of a Texas Hold’em No Limit game that uses the ActionMaskAgent.
from pettingzoo.classic import texas_holdem_no_limit_v6
env = texas_holdem_no_limit_v6.env(num_players=4, render_mode="human")
agents = {name: ActionMaskAgent(name=name, model=ChatOpenAI(temperature=0.2), env=env) for name in env.possible_agents}
main(agents, env)
Observation: {'observation': array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
       0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 1., 0., 0., 0., 0.,
       0., 0., 2.], dtype=float32), 'action_mask': array([1, 1, 0, 1, 1], dtype=int8)}
Reward: 0
Termination: False
Truncation: False
Return: 0

Action: 1
Observation: {'observation': array([0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,