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: 正方认为当圆A绕着圆B滚动一圈回到起点时,圆A会旋转4次。

这里的关键在于理解“绕着圆B滚动一圈”意味着什么。它指的是圆A的中心围绕圆B的中心走过了一个完整的圆周长。由于圆A和圆B是外切的,并且没有相对滑动,那么圆A沿着圆B外缘所走过的路径长度就是圆B的周长加上两倍的圆A半径(因为圆A在圆B的一侧接触)。但因为我们讨论的是圆心轨迹,所以实际上我们只关心圆B的周长,即2πR_B,其中R_B是圆B的半径。

现在,圆A的半径为R_A = R_B/3,因此圆A的周长是2πR_A = 2π(R_B/3)。

当圆A的中心走过距离等于圆B周长的距离时,它必须旋转的次数是该距离除以圆A自己的周长:

\[ \text{旋转次数} = \frac{\text{圆B的周长}}{\text{圆A的周长}} = \frac{2\pi R_B}{2\pi (R_B/3)} = \frac{R_B}{(R_B/3)} = 3 \]

然而,这仅考虑了圆A绕圆B公转的情况。此外,每当圆A沿着圆B边缘移动时,它也会自转。由于圆A与圆B之间的无滑动条件,每前进圆A自身的一个周长,它就会完成一次自转。因此,除了上述的3次旋转之外,圆A还会因为其自身的滚动而额外旋转1次,总共是4次旋转。

综上所述,当圆A绕圆B滚动一圈回到起点时,圆A会旋转4次。
Alice: 总结一下我的观点:当较小的圆A绕着较大的圆B滚动一圈并且回到起点时,它不仅会因为绕行圆B一周而旋转3次,还会因为它自身的滚动而额外旋转1次。因此,圆A总共会旋转4次。接下来,请反方发言。
Bob: 感谢Alice的总结。我不同意圆A总共会旋转4次的观点。让我们来仔细分析一下这个问题。

当圆A绕着圆B滚动时,确实存在两个运动:一个是沿着圆B周长的公转,另一个是圆A自身的自转。但关键在于如何计算这些转动。

1. 公转部分:圆A绕圆B一周的距离等于圆B的周长,即2πR_B。因为圆A与圆B外切且没有相对滑动,所以圆A走过的弧长也是2πR_B。考虑到圆A的半径是圆B的1/3,即R_A = R_B / 3,那么这段距离对于圆A而言相当于其自身周长的3倍(2πR_B = 3 * 2πR_A)。因此,仅从公转角度来看,圆A需要旋转3圈才能完成这一路径。

2. 自转部分:然而,这里有一个常见的误解——认为除了这3圈之外,圆A还需要额外自转一圈。实际上,在这个过程中,并不存在所谓的'额外'一圈自转。当圆A完成围绕圆B的一周旅行回到起点时,它的总自转数已经包含了所有必要的转动。这是因为,每当圆A沿着圆B边缘前进一段距离,它同时也在进行等量的自转以保持接触点不变。最终结果就是,整个过程中的自转次数正好匹配了上述公转所需的圈数。

综上所述,我的结论是:当圆A绕圆B滚动一圈并且回到起点时,它总共只旋转了3次,而非4次。
Aggregator: 双方都提出了各自的论点,Alice认为圆A绕圆B滚动一圈回到起点时会总共旋转4次,而Bob则认为是3次。从数学上讲,当一个小圆沿着一个大圆的外侧无滑动地滚动时,小圆的自转圈数等于它所走过的路径长度除以自己的周长。由于圆A的半径是圆B的1/3,所以圆A绕圆B一周的距离相当于圆A自身周长的3倍。因此,仅考虑公转部分,圆A需要旋转3圈才能完成这一路径。

然而,这里有一个关键点:当圆A完成围绕圆B的一周旅行回到起点时,实际上它已经完成了额外的一圈自转。这是因为圆A不仅在沿圆B边缘移动(公转),同时也在进行等量的自转以保持接触点不变。因此,在整个过程中,除了那3圈之外,确实存在额外的一圈自转。

综上所述,正确的答案是圆A总共会旋转4次。

辩论结束,正确答案是: 4

进一步阅读

Encouraging Divergent Thinking in Large Language Models through Multi-Agent Debate. EMNLP 2024.

Total running time of the script: (1 minutes 8.910 seconds)

Gallery generated by Sphinx-Gallery