备注
Go to the end to download the full example code.
Multi-Agent Debate¶
Multi-Agent debate 模拟不同智能体之间的多轮讨论场景,通常包括几个 solver 和一个 aggregator。 典型情况下,solver 生成并交换他们的答案,而 aggregator 收集并总结答案。
我们实现了 EMNLP 2024 中的示例,其中两个 solver 智能体将按固定顺序讨论一个话题,根据先前的辩论历史表达他们的论点。 在每一轮中,主持人智能体将决定是否可以在当前轮获得最终的正确答案。
import asyncio
import os
from pydantic import Field, BaseModel
from agentscope.agent import ReActAgent
from agentscope.formatter import (
DashScopeMultiAgentFormatter,
)
from agentscope.message import Msg
from agentscope.model import DashScopeChatModel
from agentscope.pipeline import MsgHub
# 准备一个话题
topic = "两个圆外切且没有相对滑动。圆A的半径是圆B半径的1/3。圆A绕圆B滚动一圈回到起点。圆A总共会旋转多少次?"
# 创建两个辩论者智能体,Alice 和 Bob,他们将讨论这个话题。
def create_solver_agent(name: str) -> ReActAgent:
"""获取一个解决者智能体。"""
return ReActAgent(
name=name,
sys_prompt=f"你是一个名为 {name} 的辩论者。你好,欢迎来到"
"辩论比赛。我们的目标是找到正确答案,因此你没有必要完全同意对方"
f"的观点。辩论话题如下所述:{topic}",
model=DashScopeChatModel(
model_name="qwen-max",
api_key=os.environ["DASHSCOPE_API_KEY"],
stream=False,
),
formatter=DashScopeMultiAgentFormatter(),
)
alice, bob = [create_solver_agent(name) for name in ["Alice", "Bob"]]
# 创建主持人智能体
moderator = ReActAgent(
name="Aggregator",
sys_prompt=f"""你是一个主持人。将有两个辩论者参与辩论比赛。他们将就以下话题提出观点并进行讨论:
``````
{topic}
``````
在每轮讨论结束时,你将评估辩论是否结束,以及话题正确的答案。""",
model=DashScopeChatModel(
model_name="qwen-max",
api_key=os.environ["DASHSCOPE_API_KEY"],
stream=False,
),
# 使用多智能体格式化器,因为主持人将接收来自多于用户和助手的消息
formatter=DashScopeMultiAgentFormatter(),
)
# 主持人的结构化输出模型
class JudgeModel(BaseModel):
"""主持人的结构化输出模型。"""
finished: bool = Field(description="辩论是否结束。")
correct_answer: str | None = Field(
description="辩论话题的正确答案,仅当辩论结束时提供该字段。否则保留为 None。",
default=None,
)
async def run_multiagent_debate() -> None:
"""运行多智能体辩论工作流。"""
while True:
# MsgHub 中参与者的回复消息将广播给所有参与者。
async with MsgHub(participants=[alice, bob, moderator]):
await alice(
Msg(
"user",
"你是正方,请表达你的观点。",
"user",
),
)
await bob(
Msg(
"user",
"你是反方。你不同意正方的观点。请表达你的观点和理由。",
"user",
),
)
# Alice 和 Bob 不需要知道主持人的消息,所以主持人在 MsgHub 外部调用。
msg_judge = await moderator(
Msg(
"user",
"现在你已经听到了他们的辩论,现在判断辩论是否结束,以及你能得到正确答案吗?",
"user",
),
structured_model=JudgeModel,
)
if msg_judge.metadata.get("finished"):
print(
"\n辩论结束,正确答案是:",
msg_judge.metadata.get("correct_answer"),
)
break
asyncio.run(run_multiagent_debate())
Alice: 谢谢,我将从正方的角度来阐述这个问题。首先,我们设定圆B的半径为R,那么根据题目条件,圆A的半径就是r = R/3。
当圆A绕着圆B外切滚动一圈回到原点时,实际上圆A走过的路径长度等于圆B的周长加上两倍的圆A半径(因为它们是外切的),即路径长度为\(2\pi R + 2r = 2\pi R + \frac{2}{3}\pi R = \frac{8}{3}\pi R\)。
但是,这里需要注意的是,对于圆A而言,它在移动过程中不仅沿直线前进还围绕自身轴旋转。因此,我们要计算的是圆A相对于其自身的旋转次数,而不是它相对于外部固定点的位移距离。
圆A每自转一周,它沿其边缘移动的距离等于自己的周长,即\(2\pi r = 2\pi (R/3) = \frac{2}{3}\pi R\)。
所以,要计算圆A完全绕圆B一圈的过程中总共旋转了多少次,我们需要用圆A实际走过的路径长度除以其自身周长:
\[ \text{旋转次数} = \frac{\frac{8}{3}\pi R}{\frac{2}{3}\pi R} = 4 \]
因此,我的观点是:当圆A绕圆B滚动一圈回到起点时,圆A总共会旋转4次。
Bob: 感谢Alice的详细解释,但我想提出一个不同的观点。根据题目描述,圆A绕着圆B外切滚动一圈回到起点的过程中,关键在于理解“没有相对滑动”的含义。这意味着圆A在接触点上与圆B之间没有发生滑动,因此两者的接触点总是保持静止。
我们来重新审视这个问题。设圆B的半径为R,圆A的半径为r=R/3。当圆A围绕圆B完整地转一圈时,实际上圆A中心走过的路径是一个半径为\(R+r=\frac{4}{3}R\)的大圆周长,即\(\frac{8}{3}\pi R\)。
然而,对于圆A来说,它每自转一周,其边缘移动的距离等于自身的周长,即\(2\pi r = \frac{2}{3}\pi R\)。但是,由于圆A是在围绕圆B旋转的同时也在自转,我们需要考虑的是,当圆A完成这一圈运动时,相对于自身轴线的旋转次数。
考虑到圆A和圆B之间没有相对滑动,那么圆A表面上的一个特定点在与圆B接触后,直到再次接触到同一位置之前,圆A必须恰好完成整数倍的自转。而这个过程中,圆A的中心沿着大圆轨迹移动了\(\frac{8}{3}\pi R\)的距离,这相当于圆A自身周长的4倍(因为\(\frac{8}{3}\pi R / (\frac{2}{3}\pi R) = 4\))。但这并不意味着圆A自转了4次。
实际上,当圆A绕圆B一圈时,它不仅因为沿着大圆轨迹移动而自转,还额外完成了1次自转以确保接触点不变。因此,正确的计算应该是:圆A为了覆盖\(\frac{8}{3}\pi R\)的距离需要自转3次(基于路径长度除以自身周长),再加上因绕行圆B而额外发生的1次自转,总共是4次减去1次额外计算的自转,最终结果是圆A自转了3次。
综上所述,我的观点是:当圆A绕圆B滚动一圈回到起点时,圆A总共会旋转3次。
/home/runner/work/agentscope/agentscope/src/agentscope/model/_dashscope_model.py:231: DeprecationWarning: 'required' is not supported by DashScope API. It will be converted to 'auto'.
warnings.warn(
Aggregator: {
"type": "tool_use",
"name": "generate_response",
"input": {
"finished": true,
"correct_answer": "4"
},
"id": "call_6d9567818a3b43728facc3"
}
system: {
"type": "tool_result",
"id": "call_6d9567818a3b43728facc3",
"name": "generate_response",
"output": [
{
"type": "text",
"text": "Successfully generated response."
}
]
}
Aggregator: 感谢两位辩论者的精彩讨论。经过仔细分析,我们可以得出结论:当圆A绕着圆B滚动一圈回到起点时,圆A总共会旋转4次。
这里的关键在于理解圆A在滚动过程中同时完成了两种运动:一是沿着一个更大的圆形路径移动(这个路径的半径等于两个圆的半径之和),二是围绕自身轴线进行自转。根据题目条件,圆A每自转一周,它沿其边缘移动的距离等于自己的周长。因此,要计算圆A完全绕圆B一圈的过程中总共旋转了多少次,我们需要用圆A实际走过的路径长度除以其自身周长,结果为4次。
所以,Alice的观点是正确的。再次感谢两位辩论者的积极参与!
辩论结束,正确答案是: 4
进一步阅读¶
Encouraging Divergent Thinking in Large Language Models through Multi-Agent Debate. EMNLP 2024.
Total running time of the script: (1 minutes 1.952 seconds)