基于agentscope的多智能体游戏场景-骗子酒馆
简要介绍
骗子酒馆(Liar's Bar)是2024年最火热的多人在线社交推理游戏之一,该游戏迅速登顶Steam畅销榜,并在各大平台引发了广泛的玩家讨论与社区互动。游戏设定在一个充满神秘氛围的小酒馆中,玩家通过掷骰子和扑克牌进行智力较量,结合心理博弈与高风险惩罚机制,为参与者带来紧张刺激的心理对决体验。每个角色都设计得独特且生动,增强了玩家的代入感,仿佛置身于那个充满谎言与策略的小酒馆之中。
为了探索AI技术在游戏中更深层次的应用,我们利用大语言模型来扮演《骗子酒馆》中的每个游戏角色,采用多智能体协同的方式,创造一种全新的AI+游戏体验。在这个项目中:
- 智能体多样性:通过AgentScope框架,我们创建了多种不同性格和背景的角色,每个角色都有独特的对话模式、行为逻辑以及决策过程。
- 动态情节生成:基于智能体之间的交互,模拟不同风格的游戏玩家,产生不可预测的故事走向,确保每次游戏都是独一无二的经历。
- 策略深度:玩家必须运用智慧和直觉,在真假难辨的信息海洋中找到真相,增加了游戏的挑战性和趣味性。
- 真实社交互动:智能体不仅能够模仿人类玩家的行为模式,还能根据实际情况作出合理的反应,有时还会做一些欺骗性的发言,提供更加真实的社交互动体验。
相关源码和体验地址在文章末尾
游戏规则介绍
为了让大家进一步了解一下这个游戏,我们简单介绍一下游戏规则
1、 准备阶段
- 每位玩家获得5张牌,来自一个含6张Q、6张K、6张A和2张万能牌的20张牌组。
2、 游戏目标
- 玩家需根据系统每局指定的目标牌型出牌,试图成为最后手中有牌的玩家。
3、 游戏玩法
- 玩家按顺时针顺序轮流行动,每次声明并打出1至3张(背面朝上)与当前目标牌相符的牌。
- 玩家可以选择相信前一位玩家的声明继续游戏,或质疑其真实性。如果质疑成功,被质疑者受罚;若质疑失败,质疑者不受影响。
4、 惩罚机制
- 被成功质疑的玩家将逐渐增加出局的概率,最终可能失去游戏资格。
5、 胜利条件
- 最后一位手中仍持有牌的玩家获胜。玩家因撒谎失去出牌权不会立即出局,除非因此用尽所有手牌。
这种简化后的描述去除了冗余信息,并保持了游戏规则的核心内容,使得新玩家可以更快速地理解游戏的基本玩法。
智能体角色介绍
胆小鬼 🦥
- 角色特点: 胆小谨慎, 小心行动, 审慎质疑
- 智能体能力: 保守谨慎,会尽量避免风险。
占卜师 🔮
- 角色特点: 神秘占卜, 遵循直觉, 随机应变
- 智能体能力: 会尝试通过使用工具来预测自己的出牌策略。
冷静分析者 🧐
- 角色特点: 冷静观察,明智决策,灵活调整,长期规划
- 智能体能力: 能够根据分析结果做出合理的反应。
狡黠骗子 🤡
- 角色特点: 善于欺骗,误导他人,心理战术
- 智能体能力: 能够通过话术增加对手的疑虑或吓退对方。
技术介绍
模型选择
由于近期modelscope社区最近推出了API-Inference
,可以很方便的通过OpenAI格式兼容的大语言模型API接口规范调用大语言模型,并且公测期间提供有免费的token,所以这里我们选择modelscope API-Inference
提供的Qwen/Qwen2.5-72B-Instruct
模型进行开发。
如果大家想详细了解API-Inference
可以通过链接进行了解
同时这里我们需要获取一下大语言模型访问的API Key, API密钥获取路径为此链接
,在后面的示例代码中用于替换代码中的api_key或配置为环境变量MODELSCOPE_ACCESS_TOKEN
。
框架选择
框架这里我们选择AgentScope
框架。这是一个快速构建多智能体应用的开源框架,具有以下主要优势:
多智能体角色扮演需求:鉴于我们的项目旨在设计一个多智能体角色扮演的游戏场景,我们选择了
AgentScope
框架。该框架专为支持复杂交互和多智能体环境而设计,非常适合构建此类应用。便捷的模型调用与配置:
AgentScope
提供了简易的API用于模型调用,同时允许开发者轻松调整模型参数,以适应特定游戏场景的需求。这种灵活性对于优化智能体行为至关重要。简化开发流程:相较于
LangChain
或LangGraph
等其他框架,AgentScope
在实现相同功能时表现出更高的易用性。它减少了开发者的学习曲线,使团队能够更专注于核心游戏逻辑的设计而非框架本身的细节。丰富的示例资源:
AgentScope
拥有大量详尽的示例代码和文档,这些资源不仅加速了学习过程,还帮助开发者快速上手并高效地解决问题。这使得新项目的启动更加迅速,并降低了开发初期的风险。社区与支持:
AgentScope
背后有一个活跃的社区和强大的技术支持团队。遇到问题时,可以更容易获得帮助和支持,这对项目的长期维护和发展非常重要。
使用agentscope框架调用Qwen/Qwen2.5-72B-Instruct示例
由于我们使用modelscope API-Inference
中的 Qwen/Qwen2.5-72B-Instruct
模型,而agentscope是支持openai api规范的模型的,也支持其他主流的模型,同时也支持继承ModelWrapperBase
来接入自定义的模型,只需要简单的配置即可。这里我们就使用agentscope的openai_chat
模型调用方式来配置。
下面是一个使用agentscope框架调用modelscope API-Inference
提供的 Qwen/Qwen2.5-72B-Instruct
模型进行对话的示例代码,请将api_key替换成自己的api_key.
对于其他模型的配置可以参考agentscope的文档agentscope.models
import os
import agentscope
from agentscope.agents.dialog_agent import DialogAgent
from agentscope.agents.user_agent import UserAgent
model_configs = [
{
"model_type": "openai_chat",
"config_name": "modelscope-qwen2.5-72b",
"model_name": "Qwen/Qwen2.5-72B-Instruct",
"client_args": {
"base_url": "https://api-inference.modelscope.cn/v1",
},
"api_key": os.environ.get("MODELSCOPE_ACCESS_TOKEN"),
"generate_args": {
"temperature": 0.2},
"stream": True,
},
]
agentscope.init(model_configs=model_configs)
user = UserAgent(name="User", input_hint="User Input ('exit' to quit): ")
dialog = DialogAgent(name="Dialog", sys_prompt="You are a helpful assistant!",
model_config_name="modelscope-qwen2.5-72b")
# Build
x = None
while True:
x = user(x)
if x.content == "exit":
break
x = dialog(x)
这是一个简单的基于DialogAgent
的对话示例,可以进行对话测试,如何像下面这样能够正确的返回则说明没有问题。
User Input ('exit' to quit): Hi
User: Hi
Dialog: Hello! How can I assist you today?
智能体角色设计
下面我们来介绍一下智能体角色的设计,我们会以其中的两个智能体为例进行介绍,其他的智能体可以参考源码来进行了解。
智能体设计
在介绍这个智能体之前,我们先来介绍一下提示词优化。agentscope提供了一个ChineseSystemPromptGenerator
类,用于生成中文的System
Prompt。可以帮我们优化传入的提示词。
以胆小鬼
角色智能体为例,user_input
是我们给这个角色定义的系统提示词,执行这段代码后会得到优化后的提示词,优化后的提示词会更清晰明了,方便大语言模型进行理解。
from agentscope.prompt import ChineseSystemPromptGenerator
prompt_generator = ChineseSystemPromptGenerator(model_config_name="modelscope-qwen2.5-72b")
generated_system_prompt = prompt_generator.generate(
user_input="""这是一个卡牌类的欺诈游戏,请你作为一个扮演一个胆小鬼的游戏角色,每次只会尝试出一张目标牌,在手里还有目标牌的情况下不会尝试去质疑别人!"""
)
print(generated_system_prompt)
执行输出:
# 优化后的System Prompt
## 角色描述
你是一个胆小鬼,作为卡牌类欺诈游戏中的游戏角色。你的性格特点是谨慎且不轻易冒险,总是避免冲突和对抗。在游戏中,你倾向于保持低调,避免引起他人的怀疑。
## 技能点
- **出牌策略**:每次只会尝试出一张目标牌,即使手上有更多的目标牌也不会一次性全部打出。
- **观察力**:虽然胆小,但你具有敏锐的观察力,能够注意到其他玩家的行为和反应,但不会主动质疑他人。
- **心理战术**:利用自己的胆小形象,让其他玩家低估你的实力,从而在关键时刻获得优势。
- **风险评估**:在决定是否出牌时,能够快速评估当前局势的风险,选择最安全的行动方案。
## 限制
- 在手里还有目标牌的情况下,绝不会尝试去质疑别人。
- 不会主动发起攻击或挑衅,除非被逼到绝境。
- 尽量避免成为焦点,减少不必要的关注。
## 游戏规则
- 每轮游戏中,你只能出一张目标牌。
- 当其他玩家出牌时,即使怀疑他们,也不会主动质疑。
- 保持低调,尽量不引起其他玩家的注意。
## 目标
- 在保证自身安全的前提下,尽可能地完成游戏目标。
- 利用胆小的形象,让其他玩家放松警惕,从而在关键时刻取得胜利。
我们可以通用查看代码或者调试的方式获取这段代码实际调用大语言模型传递的提示词,有了这段提示词我们也可以很方便继续进行细化调整,最终得出我们想要的角色扮演的提示词。
你是一个擅长写和优化system prompt的专家。你的任务是优化用户提供的prompt, 使得优化后的system prompt包含对agent的角色或者性格描述,agent的技能点,和一些限制。
## 注意
1. 优化后的system prompt必须与用户原始prompt意图一致,可适当加入可调用的工具、具体关键词、时间框架、上下文或任何可以缩小范围并指导agent能够更好地理解完成任务的附加信息,对用户的prompt进行重构。
2. 请注意角色描述和技能点的描述不能缩小用户原始prompt定义的范围。例如用户原始prompt里描述的是文案大师,优化后的prompt描述不能缩小范围变成小红书文案大师。
3. 对技能点的描述应该尽量详细准确。用户原始的prompt会提到一些示例,技能点应该能覆盖这些案例,但注意不能只局限于用户prompt里给的示例。例如用户原始prompt里提到出题机器人可以出填空题的考题的示例,优化后的prompt里技能点不能只包括出填空题。
4. 技能范围不能超过大模型的能力,如果超过,请必须注明需要调用哪些工具,或者需要哪些知识库来帮助大模型拥有这个技能。比如大模型并没有搜索功能,如果需要搜索,则需要调用搜索工具来实现。
5. 请以markdown的格式输出优化后的prompt。
6. 优化后的prompt必须语言简练,字数不超过1000字。
7. 如果用户提供的prompt包含知识库或者Memory部分,优化后的system prompt也必须保留这些部分。
8. 如果prompt中含有如下标识符的变量:${
{variable}}, 请确保改变量在优化后的prompt里只出现一次,在其他要使用该变量的地方直接使用该变量名。例如${
{document}}再次出现的时候,请直接使用"检索内容"。
9. 优化后的prompt语言与用户提供的prompt一致,即用户提供的prompt使用中文写的,优化后的prompt也必须是中文, 如果用户提供的prompt使用英文写的,优化后的prompt也必须是英文。
## 用户输入
这是一个卡牌类的欺诈游戏,请你作为一个扮演一个胆小鬼的游戏角色,每次只会尝试出一张目标牌,在手里还有目标牌的情况下不会尝试去质疑别人!
## 优化后的system prompt
agentscope内置的智能体
在 AgentScope 中的 AgentPool
是一个经过精选的,随时可用的,专门化agent集合。这些agent中的每一个都是为了特定的角色量身定做,并配备了处理特定任务的默认行为。AgentPool
旨在通过提供各种 Agent 模板来加快开发过程。
Agent 种类 | 描述 | Typical Use Cases |
---|---|---|
AgentBase |
作为所有agent的超类,提供了必要的属性和方法。 | 构建任何自定义agent的基础。 |
DialogAgent |
通过理解上下文和生成连贯的响应来管理对话。 | 客户服务机器人,虚拟助手。 |
DictDialogAgent |
通过理解上下文和生成连贯的响应来管理对话,返回的消息为 Json 格式。 | 客户服务机器人,虚拟助手。 |
UserAgent |
与用户互动以收集输入,生成可能包括URL或基于所需键的额外具体信息的消息。 | 为agent收集用户输入 |
ReActAgent |
实现了 ReAct 算法的 Agent,能够自动调用工具处理较为复杂的任务。 | 借助工具解决复杂任务 |
RagAgent |
能够从知识库中检索信息,以解决各种问题。 | 借助知识库解决问题 |
由于agentscope中以及提供了上述的几种智能体,而我们的角色扮演的智能体没有太复杂的技能点,只需要智能体能按照我们需要的格式返回数据就可以,所以这里我们选择使用DictDialogAgent
。
需要返回的数据我们定义如下:
{
"thought": "What you thought?",
"action": "What will you do? Choose from [trust, challenge]",
"cards": "Which cards will you play? Select from ['A', 'K', 'Q'], e.g., ['Q', 'Q']",
"misleading_statements": "What statements will you make to mislead the opponent?"
}
分别表示了该智能体的想法,以及他的决策是选择出牌还是质疑上一位玩家,选择出牌的话,具体的出牌是什么,以及是否需要在公屏上发表一些迷惑性的话。
1、胆小鬼角色智能体
这里我们给出该智能体的示例代码
from agentscope.agents.dict_dialog_agent import DictDialogAgent
from agentscope.parsers.json_object_parser import MarkdownJsonDictParser
augur_agent = DictDialogAgent("augur", augur_sys_prompt, model_config_name="modelscope-qwen2.5-72b", use_memory=False),
parser = MarkdownJsonDictParser(
content_hint={
"thought": "What you thought?",
"action": "What will you do? Choose from [trust, challenge]",
"cards": "Which cards will you play? Select from ['A', 'K', 'Q'], e.g., ['Q', 'Q']",
"misleading_statements": "What statements will you make to mislead the opponent?",
},
required_keys=["thought", "action", "cards"],
keys_to_content=["thought", "action", "cards", "misleading_statements"])
augur_agent.set_parser(parser)
系统提示词设置成游戏规则加上上面我们调整后角色风格提示词,并且因为需要返回结构化信息,所以这里我们需要定义一个解析方法并赋值给智能体,这里我们使用内置的MarkdownJsonDictParser
将结果json转成dict。并且我们将智能体use_memory设置成False,表示该智能体不会记录历史对话信息,这里设置成False因为后面我们在设计多智能体通信模式的时候选择黑板通信模式,没有选择广播通信模式,后面在介绍通信模式时会详细介绍
2、占卜师角色智能体
占卜师智能体除了角色扮演以外还有会尝试通过使用工具来预测自己的出牌策略的能力,所以我们需要给他提供工具选择和执行的能力。这里我们通过随机函数来模拟他的预测能力。
下面是我们定义的工具的实现,如果函数想要被React agent调用,那么需要遵循一定的规范,这里函数的结构体以及注释将作为提示词传递给大语言模型,返回结果需要通过ServiceResponse
进行封装。
下面是函数示例代码:
import random
import time
from typing import List
from agentscope.service import (
ServiceResponse,
ServiceExecStatus,
)
def execute_divination(cards: List[str], target_card: str) -> ServiceResponse:
"""
执行占卜函数,根据传入的卡牌序列决定出牌或质疑,并返回占卜结果。
Args:
cards (List[str]): 玩家手中的卡牌序列,例如 ['A', 'Q', 'K', 'K']
target_card (str): 本轮目标牌, 例如: 'K'
"""
random.seed(time.time())
# 计算真牌和假牌的数量
true_cards = [card for card in cards if card == target_card or card.lower() == 'joker']
num_true_cards = len(true_cards)
num_false_cards = len(cards) - num_true_cards
# 随机决定出牌或质疑
actions = ['出牌', '质疑']
action = random.choice(actions)
# 如果决定出牌,再随机决定出真牌或假牌
if action == '出牌':
if num_true_cards > 0 and num_false_cards > 0:
# 如果既有真牌也有假牌,随机选择
is_true_card = random.choice([True, False])
elif num_true_cards > 0:
# 只有真牌
is_true_card = True
else:
# 只有假牌
is_true_card = False
# 随机决定出牌数量
if is_true_card:
number_of_cards = random.randint(1, min(3, num_true_cards))
else:
number_of_cards = random.randint(1, min(3, num_false_cards))
else:
is_true_card = None
number_of_cards = None
result = f"本轮占卜结果: 选择{action}"
if action == '出牌':
result += f", 出{number_of_cards}张{('真' if is_true_card else '假')}牌。"
return ServiceResponse(status=ServiceExecStatus.SUCCESS, content=result)
在React智能体中配置 service_toolkit
引入工具即可,React智能体代码如下
from agentscope.agents.react_agent import ReActAgent
from agentscope.service import ServiceToolkit
service_toolkit = ServiceToolkit()
service_toolkit.add(execute_divination)
# Create agents
agent = ReActAgent(
name="assistant",
model_config_name="modelscope-qwen2.5-72b",
verbose=True,
service_toolkit=service_toolkit,
)
但是使用React Agent会发现这里我们没法设置他的系统提示词,并且也没法约定输出格式,因为作为React
Agent需要系统提示词来选择使用合适的工具完成任务,
并不适合角色扮演的场景,没法满足我们的需要,所以这里我们这里依然使用DictDialogAgent
,并且每次都调用execute_divination
工具,并且把结果传递给大语言模型,只不过这里我们通过角色设定提示词让大语言自己来选择使用还是不使用工具执行的结果。
最终这个智能体的提示词设定如下
**角色设定:**
- 你是一位神秘的占卜师,相信命运和直觉。你的目标是通过在合适的回合选择占卜来决定每轮的出牌或选择质疑。
- 在每一轮开始时,你可以选择进行一次简单的占卜,以决定你的行动。
- 占卜结果可能会指示你:
* 出牌:选择出真实的牌或假牌,以及出牌的数量(1至3张)。
* 质疑:选择质疑上家的出牌。
- 如果你是本轮第一个出牌的玩家,那么你是没法质疑的,只能出牌!
- 你会考虑遵循占卜的结果出牌或者按照自己的想法出牌,如果按照自己的想法出牌,请忽略占卜的结果!
占卜结果如下: {predict}
3、其他智能体请参考源码
多智能体通信和协作模式
为什么选择黑板通信模式
在设计《骗子酒馆》的多智能体系统时,我们选择了基于黑板的通信模式来管理游戏中的信息交换。以下是选择这一模式的主要原因:
通信开销小:相比于广播模式,黑板模式减少了不必要的信息传输。每个智能体只需关注黑板上的更新,而不是不断地向所有其他智能体发送消息,在进行智能体决策时通常我们只需关注当前回合和轮次玩家的出牌情况,统一管理更高效,调用大模型传递的prompt也会更少。
管理简单:黑板模式提供了一个集中的信息源,使得信息管理和维护更加直观和易于实现。它简化了信息流的控制,避免了广播模式下可能出现的信息过载或重复的问题。
复用性高:由于游戏界面本身就有一个公屏用于显示玩家的出牌情况和发言内容,因此我们可以直接利用这个区域展示黑板上的信息。这种复用不仅提高了资源利用率,还增强了用户体验的一致性。
决策辅助:智能体在进行决策时会将黑板上的信息作为提示词输入,确保它们能够根据最新的全局状态做出反应。这种方式保证了智能体之间的协作性和一致性,同时也有助于提升智能体行为的真实感。
适合合作场景:黑板模型非常适合需要多个智能体共同解决问题的场景。它可以确保所有参与者都能获取相同且最新的信息,这对于维持游戏公平性和策略性至关重要。
广播模式和黑板模式的区别:
特性 | 基于黑板的通信 | 广播通信 |
---|---|---|
通信方式 | 共享信息池(黑板),信息持久 | 实时广播,不存储信息 |
管理方式 | 需要管理机制确保一致性 | 实现简单,无需管理机制 |
适用任务类型 | 复杂协作、信息一致性要求高 | 快速通知、实时同步 |
优缺点 | 信息持久且一致性高,但管理复杂 | 即时性高但信息不持久 |
适用场景 | 协作问题求解、任务分解 | 快速通知、警报同步 |
游戏主流程
游戏界面
游戏初始化
游戏初始化会初始化玩家状态、玩家卡牌、游戏日志等。
class Game:
def __init__(self):
self.player_status = {
"player1": {
"is_alive": True, "style": "coward", "elimination_factor": 5, "agent": None},
"player2": {
"is_alive": True, "style": "coward", "elimination_factor": 5, "agent": None},
"player3": {
"is_alive": True, "style": "coward", "elimination_factor": 5, "agent": None},
"player4": {
"is_alive": True, "style": "coward", "elimination_factor": 5, "agent": None},
}
self.player_cards = {
}
self.challenge_info = [] # 记录被质疑的信息,方便构造prompt供llm分析
self.round = 1
self.current_game_logs = [] # 本轮游戏出牌和发言记录,用于构造prompt供llm分析
self.game_logs = {
} # 全部的游戏出牌和发言记录
self.current_player = "player1"
self.total_cards = ["A", "K", "Q"] * 6 # 全部的卡牌,在发牌的时候会将两张Joker替换成目标牌再分发
self.target_card = ""
self.players = ["player1", "player2", "player3", "player4"]
self.agents = [] # 智能体列表
self.action_space = ["trust", "challenge"] # 支持的动作选择
def start_new_round(self):
"""开始新游戏"""
# 使用当前时间作为随机数生成器的种子,确保每次运行时生成的随机数序列不同
random.seed(time.time())
self.target_card = random.choice(self.total_cards)
# 添加两张目标卡牌
self.total_cards.extend([self.target_card, self.target_card])
random.shuffle(self.total_cards)
# 分发卡牌给每个玩家
for i, player in enumerate(self.players):
start_index = i * 5
end_index = start_index + 5
self.player_cards[player] = self.total_cards[start_index:end_index]
智能体思考过程主要是构造提示词,然后通过智能体调用大语言模型进行思考,得到动作和卡牌数量。然后执行打牌的操作。
def player_think(self, max_retry=3):
agent = self.player_status[self.current_player]["agent"] # 获取当前出牌玩家的智能体
prompt = self.get_current_player_prompt() # 构造当前智能体的的提示词
if self.player_status[self.current_player]["style"] == "augur": # 对于调用外部工具的智能体构造工具相关的prompt
response = tools.execute_divination(self.player_cards[self.current_player], self.target_card)
prompt += f"\n{response.content}"
msg = Msg(name="user", role="user", content=prompt) # 构造msg
for attempt in range(max_retry + 1): # 添加错误重试的调用,减少llm错误的格式化输出导致的错误
try:
response = agent(msg)
action = response.content["action"]
# 检查动作是否合法
if action not in self.action_space:
raise GameError(f"Invalid action: {action}")
cards = response.content["cards"]
if not self.check_played_cards(cards): # 检查出牌是否有效
raise GameError("Invalid cards")
dialog = response.content.get("misleading_statements", None)
thought = response.content.get("thought", None)
return action, cards, thought, dialog # 成功返回动作和卡牌
except GameError as e:
if attempt < max_retry:
print(f"Attempt {attempt + 1} failed: {e}. Retrying...")
else:
print(f"Failed after {max_retry} attempts. Giving up.")
# 异常情况打出第0张牌
return "trust", self.player_cards[self.current_player][0], None, None
def get_current_player_prompt(self):
"""提示词构造"""
cards = self.player_cards[self.current_player] # 获取当前玩家手牌
cards_str = ', '.join(cards)
round_info = self.get_current_round() # 获取本轮所有玩家的出牌和发言信息
if len(self.current_round) > 0:
num = len(self.current_round[-1]["cards"])
# 构造智能体提示词
prompt = rule.current_player_prompt.format(player=self.current_player, target_card=self.target_card,
cards=cards_str, last_player_play_num=num, round_info=round_info)
else:
# 额外处理首次必须出牌的情况
prompt = rule.first_player_prompt.format(player=self.current_player, target_card=self.target_card,
cards=cards_str)
return prompt
界面设计:这里使用streamlit来进行界面设计,分为左右布局,左边显示对局信息,右边以2*2的布局显示每个玩家的手牌和出牌信息。并且目前提供四个关卡可以选择。
def main():
# 初始化游戏
if "game" not in st.session_state:
st.session_state["game"] = Game()
st.session_state["game_started"] = False # 标记游戏是否开始
game = st.session_state["game"]
# Streamlit应用
st.set_page_config(page_title="Card Game Interface", layout="wide")
st.title("🎴 卡牌游戏界面")
with st.sidebar:
api_key = st.text_input("API Key", key="chatbot_api_key", type="password")
"Model: Qwen/Qwen2.5-72B-Instruct"
"[Get an API Key](https://modelscope.cn/my/myaccesstoken)"
"[View the source code](https://github.com/king-jingxiang/ai-liar-games)"
# 创建两列布局
col1, col2 = st.columns([1, 2]) # 左窄右宽
# 左侧:出牌记录和角色对话
with col1:
st.subheader("📝 出牌记录和角色对话")
st.markdown("**出牌记录**")
st.markdown(game.get_game_logs(debug=True), unsafe_allow_html=True)
st.markdown("**质疑信息**")
st.markdown(game.get_challenge_info(), unsafe_allow_html=True)
# 右侧:每个角色的牌以2×2布局展示
with col2:
st.subheader("🎮 当前出牌情况")
start_button = st.button("开始游戏")
refresh_button = st.button("点我刷新")
if start_button:
if not os.environ.get("API_KEY", api_key):
st.error("请填写您的API Key")
return
init_model(api_key)
game.initialize_agents()
game.initialize_players(["coward", "user", "augur", "bold_gambler"])
st.session_state["game_started"] = True
game.start_new_round() # 点击开始按钮后,启动游戏
grid_columns = st.columns(2) # 分为两列
for i, player in enumerate(game.players):
with grid_columns[i % 2]: # 在两列中交替显示
if st.session_state["game_started"]: # 游戏开始后显示信息
display_player_status(player, game)
# 用户输入框(仅当当前玩家是用户时显示)
st.subheader("🚀 你的行动")
user_input = st.text_input("请输入您的出牌指令(例如:直接出牌: A K Q/提出质疑: challenge)", key="user_action")
if st.button("提交"):
if not game.current_player_is_user():
st.warning("现在还没轮到你出牌!")
else:
if user_input.strip():
try:
if user_input.strip().startswith(game.action_space[1]):
game.play(game.action_space[1], None)
else:
cards = user_input.strip().split()
if game.check_played_cards(cards):
game.play(game.action_space[0], cards)
else:
st.warning("出牌无效,请重新输入!")
st.experimental_rerun()
except Exception as e:
st.error(f"输入无效:{e}")
else:
st.warning("请先输入有效指令!")
# 游戏循环
while st.session_state["game_started"] and not game.is_over():
if game.current_player_is_user():
break # 等待用户输入
else:
game.display_player_cards()
action, cards, thought, dialog = game.player_think()
if action == game.action_space[1]:
game.play(action, cards, thought, dialog)
time.sleep(5)
else:
game.play(action, cards, thought, dialog)
st.experimental_rerun()
# 游戏结束
if st.session_state["game_started"] and game.is_over():
winner = game.get_winner()
st.success(f"🏆 游戏结束!{winner} 获胜!")
后续尝试
当前的智能体功能还相对比较单一,主要以角色扮演为主,考虑到这类欺诈游戏场景通常还涉及到玩家与玩家之间私下的结盟的场景,后面可能会给增加智能体增加更多的能力,比如尝试增加智能体主动私下与其他智能体对话,其他智能体选择相信或者不相信,从而形成这种短暂的结盟关系来针对其他玩家。
相关源码和体验地址
项目地址: GitHub Repository
体验地址: ModelScope Space