多智能体模拟环境: Petting Zoo
在这个例子中,我们展示如何使用模拟环境定义多智能体模拟。与我们的单智能体Gymnasium示例类似,我们创建了一个具有外部定义环境的智能体-环境循环。主要区别在于我们现在使用多个智能体实现这种交互循环。我们将使用Petting Zoo库,它是Gymnasium的多智能体对应版本。
安装pettingzoo
和其他依赖
!pip install pettingzoo pygame rlcard
导入模块
import collections
import inspect
import tenacity
from langchain.output_parsers import RegexParser
from langchain.schema import (
HumanMessage,
SystemMessage,
)
from langchain_openai import ChatOpenAI
GymnasiumAgent
这里我们复现了Gymnasium示例中定义的相同GymnasiumAgent
。如果多次重试后仍未采取有效行动,它会简单地采取随机行动。
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 = """
你的目标是最大化你的回报,即你收到的奖励总和。
我会给你一个观察、奖励、终止标志、截断标志和到目前为止的回报,格式如下:
Observation: <observation>
Reward: <reward>
Termination: <termination>
Truncation: <truncation>
Return: <sum_of_rewards>
你将以以下格式回应一个动作:
Action: <action>
其中你用实际的动作替换<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.invoke(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(), # 重试之间无等待时间
retry=tenacity.retry_if_exception_type(ValueError),
before_sleep=lambda retry_state: print(
f"发生ValueError: {retry_state.outcome.exception()}, 正在重试..."
),
):
with attempt:
action = self._act()
except tenacity.RetryError:
action = self.random_action()
return action
主循环
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
PettingZooAgent
扩展了GymnasiumAgent
以适应多智能体设置。主要区别是:
PettingZooAgent
接受一个name
参数来在多个智能体中识别它get_docs
函数的实现不同,因为PettingZoo
仓库结构与Gymnasium
仓库不同
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
石头剪刀布
现在我们可以使用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)
ActionMaskAgent
一些PettingZoo
环境提供action_mask
来告诉智能体哪些动作是有效的。ActionMaskAgent
继承自PettingZooAgent
,使用action_mask
中的信息来选择动作。
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 = "根据动作格式规则,生成由`action_mask`中非0索引给出的有效动作。"
self.message_history.append(HumanMessage(content=valid_action_instruction))
return super()._act()
井字棋
这里是一个使用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)
德州扑克无限注
这里是一个使用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)
总结
这个文件展示了如何使用Petting Zoo库定义和运行多智能体模拟环境。主要内容包括:
- 定义了一个基础的GymnasiumAgent类
- 扩展出PettingZooAgent类以适应多智能体环境
- 进一步定义了ActionMaskAgent类来处理带动作掩码的环境
- 展示了在石头剪刀布、井字棋和德州扑克无限注环境中使用这些智能体的例子
扩展知识
-
多智能体强化学习:与单智能体强化学习相比,多智能体强化学习涉及多个智能体在同一环境中学习和交互,这增加了问题的复杂性,因为每个智能体的行为都会影响其他智能体和环境。
-
Petting Zoo:这是一个用于多智能体强化学习的Python库,提供了多种标准化的多智能体环境,类似于单智能体强化学习中广泛使用的Gymnasium(前身是OpenAI Gym)。
-
动作掩码(Action Mask):在某些强化学习环境中,并非所有动作在任何时候都是有效的。动作掩码是一种技术,用于指示在当前状态下哪些动作是合法或可行的。这可以显著提高学习效率,因为它可以防止智能体尝试无效或非法的动作。
-
温度(Temperature):在使用语言模型(如ChatOpenAI)时,温度是一个控制输出随机性的参数。较低的温度(如0.2)会使输出更加确定和一致,而较高的温度(如1.0)会增加输出的多样性和创造性。
-
重试机制:在代码中使用了tenacity库来实现重试机制。这在处理可能失败的操作时非常有用,特别是在涉及网络请求或其他不稳定操作时。