Skip to main content

嵌套对话在对话式国际象棋中的工具使用

在 Colab 中打开 在 GitHub 上打开

本笔记本演示了如何创建能够在自然语言交流中进行国际象棋对弈的代理程序。本笔记本涵盖的关键概念是使用嵌套对话来实现工具使用,并将基于LLM的代理程序与工具执行代理程序打包成一个单一的代理程序。

相关教程:- 工具使用 - 嵌套对话

在这个设置中,每个玩家都是由一个LLM支持的代理程序,配备了两个工具:- get_legal_moves 用于获取当前合法移动的列表。- make_move 用于进行移动。

设置了一个棋盘代理程序来执行这些工具并管理游戏。使用棋盘代理程序作为非LLM的“防护栏”非常重要,以确保游戏的正确进行,并防止代理程序进行非法移动。

每当一个玩家代理程序收到来自另一个玩家代理程序的消息时,它会与棋盘代理程序实例化一个嵌套对话,以获取合法移动并使用给定的工具进行移动。玩家代理程序与棋盘代理程序之间的嵌套对话将继续,直到工具进行了合法移动。嵌套对话结束后,玩家代理程序向另一个玩家代理程序发送关于所做移动的消息。

安装

首先,您需要安装pyautogenchess包以使用AutoGen。

! pip install -qqq pyautogen chess

设置LLM

现在,您可以设置要使用的模型。

import os

player_white_config_list = [
{
"model": "gpt-4-turbo-preview",
"api_key": os.environ.get("OPENAI_API_KEY"),
},
]

player_black_config_list = [
{
"model": "gpt-4-turbo-preview",
"api_key": os.environ.get("OPENAI_API_KEY"),
},
]

创建工具

编写用于获取合法移动和进行移动的函数。

from typing import List

import chess
import chess.svg
from IPython.display import display
from typing_extensions import Annotated

# 初始化棋盘。
board = chess.Board()

# 记录是否已经进行了移动。
made_move = False


def get_legal_moves() -> Annotated[str, "以 UCI 格式表示的合法移动列表"]:
return "可能的移动有:" + ",".join([str(move) for move in board.legal_moves])


def make_move(move: Annotated[str, "以 UCI 格式表示的移动"]) -> Annotated[str, "移动的结果"]:
move = chess.Move.from_uci(move)
board.push_uci(str(move))
global made_move
made_move = True
# 显示棋盘。
display(
chess.svg.board(board, arrows=[(move.from_square, move.to_square)], fill={move.from_square: "gray"}, size=200)
)
# 获取棋子名称。
piece = board.piece_at(move.to_square)
piece_symbol = piece.unicode_symbol()
piece_name = (
chess.piece_name(piece.piece_type).capitalize()
if piece_symbol.isupper()
else chess.piece_name(piece.piece_type)
)
return f"将 {piece_name}{piece_symbol})从 {chess.SQUARE_NAMES[move.from_square]} 移动到 {chess.SQUARE_NAMES[move.to_square]}。"

创建代理

让我们来创建代理。我们有三个不同的代理:- player_white 是执白棋的代理。- player_black 是执黑棋的代理。- board_proxy 是移动棋子的代理。

from autogen import ConversableAgent, register_function

player_white = ConversableAgent(
name="白棋选手",
system_message="你是一名国际象棋选手,执白棋。"
"首先调用 get_legal_moves() 函数,获取合法移动的列表。"
"然后调用 make_move(move) 函数来进行移动。",
llm_config={"config_list": player_white_config_list, "cache_seed": None},
)

player_black = ConversableAgent(
name="黑棋选手",
system_message="你是一名国际象棋选手,执黑棋。"
"首先调用 get_legal_moves() 函数,获取合法移动的列表。"
"然后调用 make_move(move) 函数来进行移动。",
llm_config={"config_list": player_black_config_list, "cache_seed": None},
)

# 检查选手是否已经进行了移动,并在移动完成后重置标志位。


def check_made_move(msg):
global made_move
if made_move:
made_move = False
return True
else:
return False


board_proxy = ConversableAgent(
name="棋盘代理",
llm_config=False,
# 只有当选手进行了移动时,棋盘代理才会终止对话。
is_termination_msg=check_made_move,
# 自动回复消息设置为让选手不断尝试移动,直到移动成功为止。
default_auto_reply="请进行移动。",
human_input_mode="NEVER",
)

为代理注册工具。更多信息请参见工具使用教程章节

register_function(
make_move,
caller=player_white,
executor=board_proxy,
name="make_move",
description="调用此工具进行移动。",
)

register_function(
get_legal_moves,
caller=player_white,
executor=board_proxy,
name="get_legal_moves",
description="获取合法移动。",
)

register_function(
make_move,
caller=player_black,
executor=board_proxy,
name="make_move",
description="调用此工具进行移动。",
)

register_function(
get_legal_moves,
caller=player_black,
executor=board_proxy,
name="get_legal_moves",
description="获取合法移动。",
)

现在代理已经准备好了工具。您可以查看每个代理的自动生成工具模式。

player_black.llm_config["tools"]
[{'type': 'function',
'function': {'description': '调用此工具进行移动。',
'name': 'make_move',
'parameters': {'type': 'object',
'properties': {'move': {'type': 'string',
'description': '以 UCI 格式表示的移动。'}},
'required': ['move']}}},
{'type': 'function',
'function': {'description': '获取合法移动。',
'name': 'get_legal_moves',
'parameters': {'type': 'object', 'properties': {}, 'required': []}}}]

为玩家代理注册嵌套聊天。嵌套聊天允许每个玩家代理与棋盘代理代理进行对话,以进行移动,然后再与其他玩家代理进行通信。

在下面的代码中,在每个嵌套聊天中,棋盘代理代理使用从另一个玩家代理接收到的消息(例如,“轮到你了”)开始与玩家代理进行对话。两个代理继续对话,直到使用 make_move 工具进行合法移动。嵌套聊天中的最后一条消息是玩家代理关于所做移动的消息,然后将此消息发送给另一个玩家代理。

以下图示了玩家代理和棋盘代理之间的嵌套聊天。

对话式国际象棋

有关更多信息,请参见嵌套聊天教程章节

player_white.register_nested_chats(
trigger=player_black,
chat_queue=[
{
# 初始消息是玩家代理从其他玩家代理那里收到的消息。
"sender": board_proxy,
"recipient": player_white,
# 最后一条消息发送给玩家代理。
"summary_method": "last_msg",
}
],
)

player_black.register_nested_chats(
trigger=player_white,
chat_queue=[
{
# 初始消息是玩家代理从其他玩家代理那里收到的消息。
"sender": board_proxy,
"recipient": player_black,
# 最后一条消息发送给玩家代理。
"summary_method": "last_msg",
}
],
)

进行游戏

开始国际象棋游戏。

# 清空棋盘。
board = chess.Board()

chat_result = player_black.initiate_chat(
player_white,
message="让我们下国际象棋!轮到你了。",
max_turns=4,
)
黑方选手(对白方选手):

让我们下棋吧!轮到你了。

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...

********************************************************************************
开始新的对话....

********************************************************************************
棋盘代理(对白方选手):

让我们下棋吧!轮到你了。

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
白方选手(对棋盘代理):

***** 建议的工具调用(call_8aNbVlbAuH1l4f196x6R5Ccv):get_legal_moves *****
参数:
{}
********************************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 执行函数 get_legal_moves...
棋盘代理(对白方选手):

棋盘代理(对白方选手):

***** 调用工具的响应(call_8aNbVlbAuH1l4f196x6R5Ccv) *****
可能的走法有:g1h3,g1f3,b1c3,b1a3,h2h3,g2g3,f2f3,e2e3,d2d3,c2c3,b2b3,a2a3,h2h4,g2g4,f2f4,e2e4,d2d4,c2c4,b2b4,a2a4
**********************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
白方选手(对棋盘代理):

***** 建议的工具调用(call_BT0pL4qOUJNt4tH9JhzUWxa0):make_move *****
参数:
{"move":"e2e4"}
**************************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 执行函数 make_move...
![](agentchat_nested_chats_chess_files/figure-markdown_strict/cell-9-output-2.svg)

``` text
棋盘代理(对白方):

棋盘代理(对白方):

***** 调用工具(call_BT0pL4qOUJNt4tH9JhzUWxa0)的响应 *****
将兵(♙)从 e2 移动到 e4。
**********************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
白方选手(对棋盘代理):

我将兵从 e2 移动到 e4。轮到你了!

--------------------------------------------------------------------------------
白方选手(对黑方选手):

我将兵从 e2 移动到 e4。轮到你了!

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...

********************************************************************************
开始新的对话....

********************************************************************************
棋盘代理(对黑方):

我将兵从 e2 移动到 e4。轮到你了!

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
黑方选手(对棋盘代理):

***** 建议的工具调用(call_4kweVDAIgGqvKruWz4PvK01f):get_legal_moves *****
参数:
{}
********************************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 执行函数 get_legal_moves...
棋盘代理(对黑方):

棋盘代理(对黑方):

***** 调用工具(call_4kweVDAIgGqvKruWz4PvK01f)的响应 *****
可能的移动有:g8h6,g8f6,b8c6,b8a6,h7h6,g7g6,f7f6,e7e6,d7d6,c7c6,b7b6,a7a6,h7h5,g7g5,f7f5,e7e5,d7d5,c7c5,b7b5,a7a5
**********************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
黑方选手(对棋盘代理):

***** 建议的工具调用(call_p3asgsBvtmA7O4aAtgHhYp48):make_move *****
参数:
{"move":"e7e5"}
**************************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 执行函数 make_move...

棋盘代理(对黑方玩家):

棋盘代理(对黑方玩家):

***** 调用工具的响应(调用_p3asgsBvtmA7O4aAtgHhYp48) *****
将兵(♟)从 e7 移动到 e5。
**********************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
黑方玩家(对棋盘代理):

我将我的兵从 e7 移动到 e5。轮到你了!

--------------------------------------------------------------------------------
黑方玩家(对白方玩家):

我将我的兵从 e7 移动到 e5。轮到你了!

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...

********************************************************************************
开始新的对话....

********************************************************************************
棋盘代理(对白方玩家):

我将我的兵从 e7 移动到 e5。轮到你了!

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
白方玩家(对棋盘代理):

***** 建议的工具调用(调用_9ynncokEz6NnIAy4RWLoUSb6):get_legal_moves *****
参数:
{}
********************************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 执行函数 get_legal_moves...
棋盘代理(对白方玩家):

棋盘代理(对白方玩家):

***** 调用工具的响应(调用_9ynncokEz6NnIAy4RWLoUSb6) *****
可能的移动有:g1h3,g1f3,g1e2,f1a6,f1b5,f1c4,f1d3,f1e2,e1e2,d1h5,d1g4,d1f3,d1e2,b1c3,b1a3,h2h3,g2g3,f2f3,d2d3,c2c3,b2b3,a2a3,h2h4,g2g4,f2f4,d2d4,c2c4,b2b4,a2a4
**********************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
白方玩家(对棋盘代理):

***** 建议的工具调用(调用_ohlmvsDY5fFi9JaryU2y9IhS):make_move *****
参数:
{"move":"e2e4"}
**************************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 执行函数 make_move...
棋盘代理(对白方玩家):

棋盘代理(对白方玩家):

***** 调用工具的响应(调用_ohlmvsDY5fFi9JaryU2y9IhS) *****
错误:非法的 UCI:'e2e4',在 rnbqkbnr/pppp1ppp/8/4p3/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 2
**********************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
白方玩家(对棋盘代理):

看起来我的预期移动出现了错误,这似乎是一个误解,因为我试图进行的移动(从 e2 到 e4)在当前棋盘状态下是不合法的。由于我错误地解释了棋盘的初始设置和你的移动,让我们根据实际的游戏状态进行正确的操作。

根据可用的移动,我现在将选择另一个移动。让我们选择 d2 到 d4。
***** 建议的工具调用(调用_zNB1QN26j5T0cggskYabBJRs):make_move *****
参数:
{"move":"d2d4"}
**************************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 执行函数 make_move...

棋盘代理(对白方玩家):

棋盘代理(对白方玩家):

***** 调用工具(call_zNB1QN26j5T0cggskYabBJRs)的响应 *****
将兵(♙)从 d2 移动到 d4。
**********************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
白方玩家(对棋盘代理):

我将我的兵从 d2 移动到 d4。轮到你了!

--------------------------------------------------------------------------------
白方玩家(对黑方玩家):

我将我的兵从 d2 移动到 d4。轮到你了!

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...

********************************************************************************
开始新的对话....

********************************************************************************
棋盘代理(对黑方玩家):

我将我的兵从 d2 移动到 d4。轮到你了!

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
黑方玩家(对棋盘代理):

***** 建议的工具调用(call_NXjSt2zzC6e342henmdTTV6U):get_legal_moves *****
参数:
{}
********************************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 执行函数 get_legal_moves...
棋盘代理(对黑方玩家):

棋盘代理(对黑方玩家):

***** 调用工具(call_NXjSt2zzC6e342henmdTTV6U)的响应 *****
可能的移动有:g8e7,g8h6,g8f6,f8e7,f8d6,f8c5,f8b4,f8a3,e8e7,d8e7,d8f6,d8g5,d8h4,b8c6,b8a6,e5d4,h7h6,g7g6,f7f6,d7d6,c7c6,b7b6,a7a6,h7h5,g7g5,f7f5,d7d5,c7c5,b7b5,a7a5
**********************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
黑方玩家(对棋盘代理):

***** 建议的工具调用(call_yBiZaYTC96uQGwJWYdg1qra0):make_move *****
参数:
{"move":"d7d5"}
**************************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 执行函数 make_move...

棋盘代理(对黑方玩家):

棋盘代理(对黑方玩家):

***** 调用工具的响应(调用标识:call_yBiZaYTC96uQGwJWYdg1qra0) *****
将兵(♟)从 d7 移动到 d5。
**********************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
黑方玩家(对棋盘代理):

我将我的兵从 d7 移动到 d5。轮到你了!

--------------------------------------------------------------------------------
黑方玩家(对白方玩家):

我将我的兵从 d7 移动到 d5。轮到你了!

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...

********************************************************************************
开始新的对话....

********************************************************************************
棋盘代理(对白方玩家):

我将我的兵从 d7 移动到 d5。轮到你了!

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
白方玩家(对棋盘代理):

***** 建议的工具调用(调用标识:call_xl3cbAtWFeOX05gaOdGLnZQk):get_legal_moves *****
参数:
{}
********************************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 执行函数 get_legal_moves...
棋盘代理(对白方玩家):

棋盘代理(对白方玩家):

***** 调用工具的响应(调用标识:call_xl3cbAtWFeOX05gaOdGLnZQk) *****
可行的移动方式有:g1h3,g1f3,g1e2,f1a6,f1b5,f1c4,f1d3,f1e2,e1e2,e1d2,d1h5,d1g4,d1f3,d1d3,d1e2,d1d2,c1h6,c1g5,c1f4,c1e3,c1d2,b1c3,b1a3,b1d2,e4d5,d4e5,h2h3,g2g3,f2f3,c2c3,b2b3,a2a3,h2h4,g2g4,f2f4,c2c4,b2b4,a2a4
**********************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
白方玩家(对棋盘代理):

***** 建议的工具调用(调用标识:call_qwjiKCij3YKIdaebFwtSeU4C):make_move *****
参数:
{"move":"e4d5"}
**************************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 执行函数 make_move...

棋盘代理(对白方):

棋盘代理(对白方):

***** 调用工具的响应(call_qwjiKCij3YKIdaebFwtSeU4C) *****
将兵(♙)从 e4 移动到 d5。
**********************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
白方选手(对棋盘代理):

我将我的兵从 e4 移动到 d5。轮到你了!

--------------------------------------------------------------------------------
白方选手(对黑方选手):

我将我的兵从 e4 移动到 d5。轮到你了!

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...

********************************************************************************
开始新的对话....

********************************************************************************
棋盘代理(对黑方):

我将我的兵从 e4 移动到 d5。轮到你了!

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
黑方选手(对棋盘代理):

***** 建议的工具调用(call_zNB1QN26j5T0cggskYabBJRs):get_legal_moves *****
参数:
{}
********************************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 执行函数 get_legal_moves...
棋盘代理(对黑方):

棋盘代理(对黑方):

***** 调用工具的响应(call_zNB1QN26j5T0cggskYabBJRs) *****
可能的移动有:g8e7,g8h6,g8f6,f8e7,f8d6,f8c5,f8b4,f8a3,e8e7,e8d7,d8e7,d8d7,d8f6,d8d6,d8g5,d8d5,d8h4,c8d7,c8e6,c8f5,c8g4,c8h3,b8d7,b8c6,b8a6,e5d4,h7h6,g7g6,f7f6,c7c6,b7b6,a7a6,e5e4,h7h5,g7g5,f7f5,c7c5,b7b5,a7a5
**********************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
黑方选手(对棋盘代理):

***** 建议的工具调用(call_bBbRQByx2cqL1BrHi79qzuUj):make_move *****
参数:
{"move":"d8d5"}
**************************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 执行函数 make_move...

棋盘代理(对黑方玩家):

棋盘代理(对黑方玩家):

***** 调用工具的响应(调用_bBbRQByx2cqL1BrHi79qzuUj) *****
将皇后(♛)从 d8 移动到 d5。
**********************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
黑方玩家(对棋盘代理):

我已将我的皇后从 d8 移动到 d5。轮到你了!

--------------------------------------------------------------------------------
黑方玩家(对白方玩家):

我已将我的皇后从 d8 移动到 d5。轮到你了!

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...

********************************************************************************
开始新的对话....

********************************************************************************
棋盘代理(对白方玩家):

我已将我的皇后从 d8 移动到 d5。轮到你了!

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
白方玩家(对棋盘代理):

***** 建议的工具调用(调用_p3asgsBvtmA7O4aAtgHhYp48):get_legal_moves *****
参数:
{}
********************************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 执行函数 get_legal_moves...
棋盘代理(对白方玩家):

棋盘代理(对白方玩家):

***** 调用工具的响应(调用_p3asgsBvtmA7O4aAtgHhYp48) *****
可能的移动有:g1h3,g1f3,g1e2,f1a6,f1b5,f1c4,f1d3,f1e2,e1e2,e1d2,d1h5,d1g4,d1f3,d1d3,d1e2,d1d2,c1h6,c1g5,c1f4,c1e3,c1d2,b1c3,b1a3,b1d2,d4e5,h2h3,g2g3,f2f3,c2c3,b2b3,a2a3,h2h4,g2g4,f2f4,c2c4,b2b4,a2a4
**********************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
白方玩家(对棋盘代理):

***** 建议的工具调用(调用_NXjSt2zzC6e342henmdTTV6U):make_move *****
参数:
{"move":"d2d4"}
**************************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 执行函数 make_move...
棋盘代理(对白方玩家):

棋盘代理(对白方玩家):

***** 调用工具的响应(调用_NXjSt2zzC6e342henmdTTV6U) *****
错误:在 rnb1kbnr/ppp2ppp/8/3qp3/3P4/8/PPP2PPP/RNBQKBNR w KQkq - 0 4 的当前位置上,'d2d4' 是非法的 UCI。

**********************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
白方玩家(对棋盘代理):

看起来我在处理移动时犯了一个错误,因为我尝试的移动在当前位置上是无效的。由于我之前尝试从 d2 移动兵到 d4 在当前棋盘设置中是不合法的,让我选择一个新的移动。

根据提供的合法移动和错误,我将选择另一个移动。让我们采用不同的策略:

我将把我的马从 g1 移动到 f3。
***** 建议的工具调用(调用_hj4SR70DRGdP85yRvzFNKx62):make_move *****
参数:
{"move":"g1f3"}
**************************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 执行函数 make_move...

棋盘代理(对白方玩家):

棋盘代理(对白方玩家):

***** 调用工具(call_hj4SR70DRGdP85yRvzFNKx62)的响应 *****
将马(♘)从g1移动到f3。
**********************************************************************

--------------------------------------------------------------------------------

>>>>>>>> 使用自动回复...
白方玩家(对棋盘代理):

我将我的马从g1移动到f3。轮到你了!

--------------------------------------------------------------------------------
白方玩家(对黑方玩家):

我将我的马从g1移动到f3。轮到你了!

--------------------------------------------------------------------------------

在上面的输出中,每当棋盘代理和玩家代理之间开始一个新的嵌套对话时,都会显示“开始新的对话”。由于这是一个新的对话序列,因此“carryover”为空。