前言
在上一期的文章《Multi-Agent实践第2期: @智能体 你怎么看?》中,我们简要介绍了如何使用AgentScope快速构建一个多智能体聊天场景以及可以直接"@"提及某个智能体的群聊功能。本期,我们将带你体验如何实现用AgentScope如何快速实现一个游戏向应用:试试让两个智能体下五子棋~ 因为有些功能性代码比较琐碎,为了文章简洁可能被部分隐去;完整的代码可以参见(https://github.com/modelscope/agentscope/tree/main/examples/game_gomoku)
欢迎大家关注AgentScope,在github上(https://github.com/modelscope/agentscope) 为我们star 🌟。我们会在接下来几天,继续推出一系列教程,让大家每天花5~10分钟,搭建出不同的由简单到复杂的有趣的多智能体应用!
开始构建应用
首先,我们还是需要载入模型配置。因为下五子棋的例子需要大模型有较强的服从指令的能力和逻辑推理能力,我们在这个例子里默认会用到gpt-4的模型。关于怎么配置模型,欢迎大家回顾我们前两期的内容;这里我们会从model_configs.json中读入取模型配置。
import agentscope agentscope.init(model_configs="./model_configs.json") # 替换成自己的model config name YOUR_MODEL_CONFIGURATION_NAME = "{YOUR_MODEL_CONFIGURATION_NAME}"
除此之外,我们也需要先给出一些固定的提示词模板和游戏设定,包括:
- CURRENT_BOARD_PROMPT_TEMPLATE:当前游戏情况和哪个玩家的回合;
- Alice执黑(表示为“o”),Bob执白(表示为“x”)
import numpy as np from agentscope.message import Msg from typing import Tuple from agentscope.agents import AgentBase CURRENT_BOARD_PROMPT_TEMPLATE = """The current board is as follows: {board} {player}, it's your turn. )""" NAME_BLACK = "Alice" NAME_WHITE = "Bob" # The mapping from name to piece NAME_TO_PIECE = { NAME_BLACK: "o", NAME_WHITE: "x", } EMPTY_PIECE = "0" piece_black = NAME_TO_PIECE[NAME_BLACK] piece_white = NAME_TO_PIECE[NAME_WHITE]
创建一个棋盘智能体记录情况、维护全局
下五子棋,我们肯定需要一个棋盘。在多智能体的环境里,我们首先可以创建一个棋盘“智能体”。我们默认会把棋盘的大小设置成15x15。需要注意的是,这个棋盘智能体并不需要连接大模型,它的功能主要是rule-based的判断,包括了接受新的落子,检查智能体/玩家的落子是否合法(是不是在棋盘内?格子是不是已经被占据了?),判断落子之后是否带来胜利或者平局,以及将棋盘信息转化成文字分享给其他智能体。
class BoardAgent(AgentBase): def __init__(self, name): super().__init__(name=name, use_memory=False) # Init the board self.size = 15 self.board = np.full((self.size, self.size), 0) # Record the status of the game self.game_end = False def reply(self, x: dict = None) -> dict: if x is None: # 开始游戏 content = "Welcome to the Gomoku game! Black player goes first. Please make your move." else: # 在游戏过程中 # 1. 检查落子是否合法; # 2. 检查落子是否能带来胜利或平局 # 3. 若游戏继续,更新棋盘信息 # 4. 打印当前棋局 (依赖完整代码中的board2img函数) # 代码参见文章开头链接 # ... # 是否合法落子 def assert_valid_move(self, x: int, y: int) -> bool: return 0 <= x < self.size and 0 <= y < self.size and self.board[x, y] is None #检查胜利与否 def check_win(self, move: Tuple[int, int], piece: str) -> bool: # 代码参见代码参见文章开头链接 # ... #检查平局 def check_draw(self) -> bool: return np.all(self.board != None) #棋盘状态to文字 def board2text(self) -> str: return "\n".join([str(_)[1:-1].replace(", ", " ") for _ in self.board])
创建下五子棋的智能体
下五子棋,起码需要智能体返回一个明确的落子位置(x, y)。除此之外,根据CoT的思想,如果让大模型同时返回一段分析(智能体的想法thought),可能可以让回答更加合理。
这种具有明确格式的需求,我们除了可以在给大模型的提示词中明确要求,也同时可以用上AgentScope里面处理大模型返回的接口,用一个parse_func去解析大模型的结构化回答、提取有效信息。
import json from agentscope.models import ModelResponse from typing import Optional # 说明游戏规则和agent回答规则 (样例) SYS_PROMPT_TEMPLATE = """ You're a skillful Gomoku player. You should play against your opponent according to the following rules: Game Rules: 1. This Gomoku board is a 15*15 grid. Moves are made by specifying row and column indexes, with [0, 0] marking the top-left corner and [14, 14] indicating the bottom-right corner. 2. The goal is to be the first player to form an unbroken line of your pieces horizontally, vertically, or diagonally. 3. If the board is completely filled with pieces and no player has formed a row of five, the game is declared a draw. Note: 1. Your pieces are represented by '{}', your opponent's by '{}'. 0 represents an empty spot on the board. 2. You should think carefully about your strategy and moves, considering both your and your opponent's subsequent moves. 3. Make sure you don't place your piece on a spot that has already been occupied. 4. Only an unbroken line of five same pieces will win the game. For example, "xxxoxx" won't be considered a win. 5. Note the unbroken line can be formed in any direction: horizontal, vertical, or diagonal. """ # 提示词(样例) HINT_PROMPT = """You should respond in the following format, which can be loaded by json.loads in Python: {{ "thought": "what you thought", "move": [row, col] }} """ # 提取大模型回答中的格式化/有效信息 def parse_func(response: ModelResponse) -> ModelResponse: res_dict = json.loads(response.text) if "move" in res_dict and "thought" in res_dict: return ModelResponse(raw=res_dict) else: raise ValueError(f"Invalid response format in parse_func with response: {response.text}") # 下五子棋的智能体 class GomokuAgent(AgentBase): """A Gomoku agent that can play the game with another agent.""" def __init__(self, name, sys_prompt, model_config_name): super().__init__(name=name, sys_prompt=sys_prompt, model_config_name=model_config_name) self.memory.add(Msg("system", sys_prompt, role="system")) def reply(self, x: Optional[dict] = None) -> dict: if self.memory: self.memory.add(x) msg_hint = Msg("system", HINT_PROMPT, role="system") # 组织prompt prompt = self.model.format( self.memory.get_memory(), msg_hint, ) response = self.model( prompt, parse_func=parse_func, max_retries=3, ).raw self.speak(Msg(self.name, json.dumps(response, indent=4, ensure_ascii=False), role="assistant")) if self.memory: self.memory.add(Msg(self.name, response, role="assistant")) # 隐去想法(thought) return Msg(self.name, response["move"], "assistant")
在开发的时候,大家可以自己尝试调整SYS_PROMPT_TEMPLATE,看能不能有更好的效果~
游戏主逻辑
我们现在可以创建我们需要的三个智能体:一个棋盘,两个五子棋玩家。
black = GomokuAgent( NAME_BLACK, model_config_name=YOUR_MODEL_CONFIGURATION_NAME, sys_prompt=SYS_PROMPT_TEMPLATE.format(piece_black, piece_white), ) white = GomokuAgent( NAME_WHITE, model_config_name=YOUR_MODEL_CONFIGURATION_NAME, sys_prompt=SYS_PROMPT_TEMPLATE.format(piece_white, piece_black), ) board = BoardAgent(name="Host")
创建好了智能体,可以把他们都放进一个群聊的环境中,让他们的沟通更加高效(一名五子棋玩家分享的落子信息可以被另一名玩家和棋盘同时获取)。
from agentscope import msghub MAX_STEPS = 10 msg = None i = 0 # Use a msg hub to share conversation between two players, e.g. white player can hear what black player says to the board with msghub(participants=[black, white, board]): while not board.game_end and i < MAX_STEPS: for player in [black, white]: # receive the move from the player, judge if the game ends and remind the player to make a move msg = board(msg) # end the game if draw or win if board.game_end: break # make a move msg = player(msg) i += 1
效果展示图
大家可以尝试我们的完整版代码(https://github.com/modelscope/agentscope/tree/main/examples/game_gomoku),可以看到在jupyter notebook上打印出每一步以及智能体的想法。
总结
这个五子棋小游戏可以作为一个抛砖引玉的例子,但是我们可以看到大模型的表现其实并没有很智能。我们也会和大家一起,持续探索如何能让大模型在下五子棋方面更加“聪明”。接下来几期,我们会介绍如何在AgentScope里用上让智能体更全能、更聪明的技术(比如ReAct, RAG),敬请期待!
欢迎大家关注AgentScope,在github上为我们star 🌟。
延伸阅读和资源
- 完整的中文文档和教程:https://modelscope.github.io/agentscope/zh_CN/index.html
- AgentScope的GitHub示例库:https://github.com/modelscope/agentscope/tree/main/examples
- 论文及研究资料:https://arxiv.org/abs/2402.14034
- 加入我们的讨论组:扫描GitHub项目首页的二维码,或点击阅读原文链接加入。
比赛
看到这里,如果你有好玩的想法,不如实践起来,还可以顺便参加下面的比赛~
点击即可跳转~