Hello everyone, I am Student Xiao Zhang, continuously learning C++ advanced knowledge and practical cases of AI large models. I will keep sharing, and welcome everyone to like + follow for mutual learning and progress.
This article will explore a practical case of MetaGPT – the Werewolf game. The source code for this case can be found on MetaGPT GitHub repository (https://github.com/geekan/MetaGPT/tree/main/examples/werewolf_game).
0. Werewolf Game Rules
If you are unfamiliar, please refer to the game rules below to better understand the content that follows.
The Werewolf game is a social deduction game involving multiple participants, with roles divided into three main categories: werewolves, villagers, and special roles.
Basic Rules
1. Role Assignment: Before the game starts, each player is randomly assigned a role, including werewolves, ordinary villagers, and villagers with special abilities (e.g., seer, witch, hunter, etc.).
2. Game Process: The game is divided into two phases: night and day. At night, werewolves open their eyes and kill one player; during the day, all players discuss and vote to execute one player. This process continues until a victory condition is met.
3. Victory Conditions: The victory conditions are divided into werewolf camp victory and villager camp victory.
• Werewolf Victory: The werewolf camp wins when the number of werewolves equals the number of villagers.
• Villager Victory: The villager camp wins when all werewolves are identified and executed.
Special Role Introductions
• Seer: Can check the identity of one player each night.
• Witch: Has a potion to save a player; has a poison to kill one player. Usually can only use the antidote on the first night.
• Hunter: Can shoot and take one player with them when executed.
• Guard: Can protect one player each night from being killed by werewolves.
The overall game process can be represented in the flowchart below:

1. Let’s Run It and See the Effect
Following my steps, the first thing to do is to run the program and see what it looks like.
Run it directly in the source code by executing .
MetaGPT
eexamples
ewerewolf_gameestart_game.py
1.1 The Effect After Running
(1) The game starts, role assignment

(2) The host proceeds, night, the guard speaks

(3) Werewolves kill


(4) Similar processes continue until the game ends.
Special Reminder: Before running this game, please modify the n_round in the main function to a smaller value, otherwise your wallet may not be safe!!!
def main(
investment: float = 20.0,
n_round: int = 16, # Modify here! Modify here! Modify here!
shuffle: bool = True,
add_human: bool = False,
use_reflection: bool = True,
use_experience: bool = False,
use_memory_selection: bool = False,
new_experience_version: str = "",
):
1.2 Errors Encountered During Running
1.2.1 Various No Module Named ‘llama_index.xxxxxx’
Install the missing packages, for example, I was missing the following packages:
pip install llama-index-vector-stores-postgres -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install llama-index-vector-stores-chroma -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install llama-index-vector-stores-elasticsearch -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install llama-index-vector-stores-faiss -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install llama-index-retrievers-bm25 -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install llama-index-embeddings-azure-openai -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install llama-index-embeddings-gemini -i https://pypi.tuna.tsinghua.edu.cn/simple
pip install llama-index-embeddings-ollama -i https://pypi.tuna.tsinghua.edu.cn/simple
To install the missing packages, you can either search online or find out how to install llama-index related packages here: https://llamahub.ai/?tab=retrievers
2. Case Breakdown
This case is relatively complex, with a lot of code and many details, so this article will first break down the overall framework, and more code and implementation details will be covered in dedicated follow-up articles.
For multi-agent programs based on MetaGPT, the core generally involves three parts: Role
, Action
, and Environment
. Once you identify these three parts, the overall framework should become clear.
Role represents the agent’s identity, Action corresponds to the actions of that role, and Environment connects the messages of various Roles to facilitate information exchange among multiple agents.
2.1 Role Settings and Corresponding Action Settings
2.1.0 Required Roles and Role Framework – BasePlayer
2.1.0.1 Required Roles
First, let’s look at the role settings. According to the rules of the Werewolf game, we need the following types of roles:
-
• Moderator
-
• Guard
-
• Villager
-
• Werewolf
-
• Seer
-
• Witch
Thus, the code first defines these types of roles:
class RoleType(Enum):
VILLAGER = "Villager" # Villager
WEREWOLF = "Werewolf" # Werewolf
GUARD = "Guard" # Guard
SEER = "Seer" # Seer
WITCH = "Witch" # Witch
MODERATOR = "Moderator" # Moderator
2.1.0.2 Role Framework – BasePlayer
The code includes a BasePlayer class that encapsulates the basic behaviors and attributes of roles, from which all roles inherit and derive. Its basic attributes and initialization are as follows:
class BasePlayer(Role):
name: str = "PlayerXYZ"
profile: str = "BasePlayer"
special_action_names: list[str] = []
use_reflection: bool = True
use_experience: bool = False
use_memory_selection: bool = False
new_experience_version: str = ""
status: RoleState = RoleState.ALIVE
special_actions: list[SerializeAsAny[Action]] = Field(default=[], validate_default=True)
experiences: list[RoleExperience] = []
def __init__(self, **kwargs):
super().__init__(**kwargs)
# Skills and listening configuration
self._watch([InstructSpeak]) # Listen for Moderator instructions to take action
special_actions = [ACTIONS[action_name] for action_name in self.special_action_names]
capable_actions = [Speak] + special_actions
self.set_actions(capable_actions) # Assign action skills to the role
self.special_actions = special_actions
if not self.use_reflection and self.use_experience:
logger.warning("You must enable use_reflection before using experience")
self.use_experience = False
(1) First, all roles need to listen for messages generated by the InstructSpeak action: self._watch([InstructSpeak])
(2) Role behavior settings: self.set_actions(capable_actions)
, including the incoming special_actions
and Speak
Action.
(3) The meanings of other parameters will be explored later. For now, you just need to understand how each role’s behavior is derived.
2.1.0.3 Common Action for All Roles – Speak
As we saw above, there is a Speak
Action in BasePlayer
, which all derived roles from BasePlayer
possess by default.
The core idea is that each role assembles a prompt based on their information and inputs it to the large model to obtain the model’s output.

Next, let’s take a look at the design of each role.
2.1.1 Moderator – Moderator
.
MetaGPT ext
ewerewolf
oles
ewoderator.py
2.1.1.1 Role Setting
The role setting for the moderator is as follows:
class Moderator(BasePlayer):
name: str = RoleType.MODERATOR.value
profile: str = RoleType.MODERATOR.value
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._watch([UserRequirement, InstructSpeak, ParseSpeak])
self.set_actions([InstructSpeak, ParseSpeak, AnnounceGameResult])
-
• Actions it receives:
UserRequirement
user input,InstructSpeak
,ParseSpeak
-
• Actions it needs to execute:
InstructSpeak
,ParseSpeak
,AnnounceGameResult
-
• Of course, it also inherits from
BasePlayer
, so it also has theSpeak
Action that needs to be executed.
2.1.1.2 Role Action: InstructSpeak
The code is as follows:
class InstructSpeak(Action):
name: str = "InstructSpeak"
async def run(self, step_idx, living_players, werewolf_players, player_hunted, player_current_dead):
instruction_info = STEP_INSTRUCTIONS.get(
step_idx, {"content": "Unknown instruction.", "send_to": {}, "restricted_to": {}}
)
content = instruction_info["content"]
if "{living_players}" in content and "{werewolf_players}" in content:
content = content.format(
living_players=living_players, werewolf_players=werewolf_players, werewolf_num=len(werewolf_players)
)
if "{living_players}" in content:
content = content.format(living_players=living_players)
if "{werewolf_players}" in content:
content = content.format(werewolf_players=werewolf_players)
if "{player_hunted}" in content:
content = content.format(player_hunted=player_hunted)
if "{player_current_dead}" in content:
player_current_dead = "No one" if not player_current_dead else player_current_dead
content = content.format(player_current_dead=player_current_dead)
return content, instruction_info["send_to"], instruction_info["restricted_to"]
The purpose of this action is to host the entire game process, with a parameter step_idx
indicating which step the game is currently at. Based on this step, it retrieves the information for this step from the pre-set process. A brief look at the preset process steps (too many to display all, just a portion to give you an idea):

These steps are preset prompts and messages indicating who should speak next based on the process outlined at the beginning of the article.
2.1.1.3 Role Action: ParseSpeak
This action is used to parse the information spoken by roles.
2.1.1.4 Role Action: AnnounceGameResult
Announces the end of the game, the game result, and the reason for the game’s conclusion.
class AnnounceGameResult(Action):
async def run(self, winner: str, win_reason: str):
return f"Game over! {win_reason}. The winner is the {winner}"
2.1.1.5 Observational Action: UserRequirement
Used to start the game.
2.1.1.6 Observational Action: InstructSpeak
Receives messages from InstructSpeak
; if the message needs to be handled by the moderator, it processes that message.
For example, from round 0 to round 1, after the moderator says “night falls, please close your eyes,” they will continue to say, “please guard open your eyes.” The message for InstructSpeak in round 0 is directed to the moderator. So, the code states:
"send_to": {RoleType.MODERATOR.value}, # for moderator to continue speaking
2.1.1.7 Observational Action: ParseSpeak
After parsing the role speeches, the moderator continues speaking to host the process.
2.1.1.8 Summary
Thus, the main responsibilities of the Moderator are: starting the game, hosting the process, parsing role speeches, and announcing game results.
2.1.2 Villager – Villager
class Villager(BasePlayer):
name: str = RoleType.VILLAGER.value
profile: str = RoleType.VILLAGER.value
special_action_names: list[str] = []
The villager inherits from BasePlayer
, so they possess the Speak
behavior; besides that, they have no special abilities and can only Speak
.
2.1.3 Werewolf – Werewolf
2.1.3.1 Role Setting
class Werewolf(BasePlayer):
name: str = RoleType.WEREWOLF.value
profile: str = RoleType.WEREWOLF.value
special_action_names: list[str] = ["Hunt"]
async def _think(self):
"""When the werewolf speaks during the day, they need to disguise themselves, which is different from other roles, so _think needs to be overridden"""
await super()._think()
if isinstance(self.rc.todo, Speak):
self.rc.todo = Impersonate()
(1) In addition to being able to Speak
(inherited from BasePlayer
), the werewolf possesses the special skill Hunt
.
(2) During the day, the werewolf must disguise themselves as a good person while speaking, which requires an additional Action: Impersonate
The werewolf has two actions: one is to kill at night, and the other is to disguise themselves as a good person during the day.
2.1.3.2 Role Action: Hunt
This action is when the werewolf decides who to kill at night.
class Hunt(NighttimeWhispers):
name: str = "Hunt"
2.1.3.3 Role Action: Impersonate
Inherits from Speak, mainly used to disguise as a good person while speaking.
class Impersonate(Speak):
"""Action: werewolf impersonating a good guy in daytime speak"""
STRATEGY: str = """
Try continuously impersonating a role, such as Seer, Guard, Villager, etc., in order to mislead
other players, make them trust you, and thus hiding your werewolf identity. However, pay attention to what your werewolf partner said,
DONT claim the same role as your werewolf partner. Remmber NOT to reveal your real identity as a werewolf!
"""
name: str = "Impersonate"
2.1.4 Guard – Guard
2.1.4.1 Role Setting
class Guard(BasePlayer):
name: str = RoleType.GUARD.value
profile: str = RoleType.GUARD.value
special_action_names: list[str] = ["Protect"]
The guard’s special skill is Protect
, which is to protect someone.
2.1.4.2 Role Action: Protect
class Protect(NighttimeWhispers):
name: str = "Protect"
2.1.5 Seer – Seer
2.1.5.1 Role Setting
class Seer(BasePlayer):
name: str = RoleType.SEER.value
profile: str = RoleType.SEER.value
special_action_names: list[str] = ["Verify"]
The seer’s special skill is Verify
, which verifies the identity of other roles.
2.1.5.2 Role Action: Verify
class Verify(NighttimeWhispers):
name: str = "Verify"
2.1.6 Witch – Witch
2.1.6.1 Role Setting
class Witch(BasePlayer):
name: str = RoleType.WITCH.value
profile: str = RoleType.WITCH.value
special_action_names: list[str] = ["Save", "Poison"]
The witch has two special skills: Save
and Poison
, which are to save and poison.
2.1.6.2 Role Action: Save
class Save(NighttimeWhispers):
name: str = "Save"
2.1.6.3 Role Action: Poison
class Poison(NighttimeWhispers):
STRATEGY: str = """
Only poison a player if you are confident he/she is a werewolf. Don't poison a player randomly or at first night.
If someone claims to be the witch, poison him/her, because you are the only witch, he/she can only be a werewolf.
"""
name: str = "Poison"
2.1.7 Common Action at Night – NighttimeWhispers
From the Action code above, you may have noticed that most Actions inherit from NighttimeWhispers
. This Action is set for quiet thinking and speaking during the night.
2.2 Environment – WerewolfGame, WerewolfEnv
The environment is used for message passing between roles. Additionally, round_cnt
controls the maximum number of interactions. WerewolfExtEnv also updates the game and the states of each role. Let’s take a general look at the environment encapsulation:
class WerewolfGame(Team):
"""Use the "software company paradigm" to hold a werewolf game"""
env: Optional[WerewolfEnv] = None
def __init__(self, context: Context = None, **data: Any):
super(Team, self).__init__(**data)
ctx = context or Context()
if not self.env:
self.env = WerewolfEnv(context=ctx)
else:
self.env.context = ctx # The `env` object is allocated by deserialization
class WerewolfEnv(WerewolfExtEnv, Environment):
round_cnt: int = Field(default=0)
class WerewolfExtEnv(ExtEnv):
model_config = ConfigDict(arbitrary_types_allowed=True)
players_state: dict[str, tuple[str, RoleState]] = Field(
default_factory=dict, description="the player's role type and state by player_name"
)
round_idx: int = Field(default=0) # the current round
step_idx: int = Field(default=0) # the current step of current round
eval_step_idx: list[int] = Field(default=[])
per_round_steps: int = Field(default=len(STEP_INSTRUCTIONS))
# game global states
game_setup: str = Field(default="", description="game setup including role and its num")
special_role_players: list[str] = Field(default=[])
winner: Optional[str] = Field(default=None)
win_reason: Optional[str] = Field(default=None)
witch_poison_left: int = Field(default=1, description="should be 1 or 0")
witch_antidote_left: int = Field(default=1, description="should be 1 or 0")
# game current round states, a round is from closing your eyes to the next time you close your eyes
round_hunts: dict[str, str] = Field(default_factory=dict, description="nighttime wolf hunt result")
round_votes: dict[str, str] = Field(
default_factory=dict, description="daytime all players vote result, key=voter, value=voted one"
)
player_hunted: Optional[str] = Field(default=None)
player_protected: Optional[str] = Field(default=None)
is_hunted_player_saved: bool = Field(default=False)
player_poisoned: Optional[str] = Field(default=None)
player_current_dead: list[str] = Field(default=[])
3. Summary
This article concludes the breakdown. In summary, we have looked at the basic rules of the Werewolf game, from which we understand the basic process and required roles, as well as the skill settings for each role.
Understanding the basic process, the most fundamental process of organizing agents in MetaGPT is established (SOP). However, the SOP here is expressed differently from what we’ve seen before. Previously, I believe the SOP was implicit, meaning it is hidden within each role, connecting the entire SOP through the role’s set_actions
and watch
. In this game, in addition to this implicit SOP, there is also an explicit SOP process, which is the moderator’s role in stringing together the process. The code also clearly provides the complete process through STEP_INSTRUCTIONS
. This is another way of thinking about SOP that can be referenced: when the process is too complex, it might be helpful to write the SOP in this way, retrieving the next step based on the step number.
With the SOP established, the framework of MetaGPT consists of three elements: roles, actions, and environment. We know the roles and their actions based on the game rules. The environment mainly provides a channel for communication between roles, and implementing the encapsulation of the environment in this direction is sufficient.
In conclusion, this article covers the overall framework of this case. With the framework in place, what remains are the details of the actions and interactions within it, which are the most challenging to implement. We will continue to conduct detailed breakdowns in separate articles.
If you find this article helpful, please give it a like and follow ~~~ Click the official account above to follow ↑↑↑
• Hello everyone, I am Student Xiao Zhang, daily sharing AI knowledge and practical cases.
• Welcome like + follow 👏, continuous learning, continuous output of valuable content.
• +v: jasper_8017 Let’s communicate 💬 and progress together 💪.
Overview of articles in the official account
