Skip to main content

对话模式

在Colab中打开 在GitHub上打开

在前一章中,我们使用了两个代理的对话模式,可以通过initiate_chat方法启动。

两个代理的聊天和聊天结果

两个代理的聊天是对话模式中最简单的形式。我们使用每个ConversableAgent代理的initiate_chat方法来开始两个代理的聊天。在之前的章节中,我们已经看到了多个两个代理的聊天的例子,但还没有详细介绍过细节。

下图展示了两个代理聊天的工作方式。

两个代理的聊天

两个代理的聊天需要两个输入:消息,由调用者提供的字符串;上下文,指定聊天的各种参数。发送代理使用其聊天初始化方法(即ConversableAgentgenerate_init_message方法)从输入生成初始消息,并将其发送给接收代理以启动聊天。发起聊天的代理是调用了initiate_chat方法的代理,接收聊天的代理是另一个代理。

一旦聊天结束,聊天的历史记录将由聊天摘要生成器处理。摘要生成器对聊天历史进行摘要,并计算聊天的令牌使用情况。您可以使用initiate_chat方法的summary_method参数配置摘要的类型。默认情况下,它是聊天的最后一条消息(即summary_method='last_msg')。

以下示例是学生代理和教师代理之间的两个代理聊天。其摘要生成器使用基于LLM的摘要。

import os

from autogen import ConversableAgent

student_agent = ConversableAgent(
name="Student_Agent",
system_message="You are a student willing to learn.",
llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}]},
)
teacher_agent = ConversableAgent(
name="Teacher_Agent",
system_message="You are a math teacher.",
llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}]},
)

chat_result = student_agent.initiate_chat(
teacher_agent,
message="What is triangle inequality?",
summary_method="reflection_with_llm",
max_turns=2,
)
学生代理 (给教师代理):

什么是三角不等式?

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

>>>>>>>> 使用自动回复...
教师代理 (给学生代理):

三角不等式定理是几何学中的一个基本原理,它规定三角形的任意两边的长度之和必须始终大于第三边的长度。在边长分别为 a、b 和 c 的三角形中,该定理可以写成:

a + b > c
a + c > b
b + c > a

这三个不等式分别代表了一个特定边 (a、b 或 c) 的条件。所有条件必须同时成立才能构成一个三角形。

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

>>>>>>>> 使用自动回复...
学生代理 (给教师代理):

谢谢您的解释。这个定理有助于理解三角形的基本性质。在解决几何问题或证明其他数学定理时也很有用。您能举个例子说明如何应用三角不等式定理吗?

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

>>>>>>>> 使用自动回复...
教师代理 (给学生代理):

当然!这里有一个例子:

假设给定三条长度分别为 10、7 和 3 的线段。问题是:“这三条线段能组成一个三角形吗?”

要回答这个问题,你可以使用三角不等式定理。任意两条边的长度之和必须大于第三条边的长度:

- 对于边长为 10 和 7:10 + 7 = 17,大于 3。
- 对于边长为 10 和 3:10 + 3 = 13,大于 7。
- 对于边长为 7 和 3:7 + 3 = 10,等于第三条边的长度 (10),但不大于。

因此,这三条线段不能组成一个三角形,因为并非所有边对都满足三角不等式定理。

--------------------------------------------------------------------------------
print(ConversableAgent.DEFAULT_SUMMARY_PROMPT)
总结对话的要点。不要添加任何开场白。

您还可以通过设置 `initiate_chat` 的 `summary_prompt` 参数来使用自定义提示。

`ChatResult` 对象中还包含一些其他有用的信息,包括对话历史、人类输入和令牌成本。

```python
# 获取对话历史。
import pprint

pprint.pprint(chat_result.chat_history)
[{'content': '什么是三角不等式?', 'role': 'assistant'},
{'content': '三角不等式定理是几何学中的一个基本原理,它规定三角形的任意两边的长度之和必须始终大于第三边的长度。在边长分别为 a、b 和 c 的三角形中,该定理可以写成:\n'
'\n'
'a + b > c\n'
'a + c > b\n'
'b + c > a\n'
'\n'
'每个不等式代表了一个特定边(a、b 或 c)的条件。三角形存在的条件是所有不等式都成立。',
'role': 'user'},
{'content': '感谢解释。这个定理有助于理解三角形的基本性质。在解决几何问题或证明其他数学定理时,它也很有用。你能举个例子,说明我们如何使用三角不等式定理吗?',
'role': 'assistant'},
{'content': '当然!这里有一个例子:\n'
'\n'
'假设你有三条长度分别为 10、7 和 3 的线段。问题是:“这三条线段能组成一个三角形吗?”\n'
'\n'
'要回答这个问题,你可以使用三角不等式定理。任意两条边的长度之和应大于第三条边的长度:\n'
'\n'
'- 对于边长为 10 和 7:10 + 7 = 17,大于 3。\n'
'- 对于边长为 10 和 3:10 + 3 = 13,大于 7。\n'
'- 对于边长为 7 和 3:7 + 3 = 10,等于第三条边的长度(10),但不大于它。\n'
'\n'
'因此,这三条线段不能组成一个三角形,因为并非所有边对都满足三角不等式定理。',
'role': 'user'}]

对话结果中的聊天消息是接收者代理的视角 - 发件人是“assistant”,接收者是“user”。

# 获取对话成本。
pprint.pprint(chat_result.cost)
({'gpt-4-0613': {'completion_tokens': 399,
'cost': 0.04521,
'prompt_tokens': 709,
'total_tokens': 1108},
'total_cost': 0.04521},
{'total_cost': 0})

顺序对话

这种模式的名称不言自明——它是两个代理之间的一系列聊天,通过一种称为*carryover*的机制链接在一起,将上一个聊天的摘要带入到下一个聊天的上下文中。

这种模式对于可以分解为相互依赖的子任务的复杂任务非常有用。下面的图示了这种模式的工作原理。

![initiate_chats](./assets/sequential-two-agent-chat.png)

在这种模式中,一对代理首先开始了一个两个代理的聊天,然后对话的摘要成为下一个两个代理聊天的*carryover*。下一个聊天将carryover传递给上下文的`carryover`参数,以生成其初始消息。

随着对话的进行,carryover会累积,因此每个后续聊天都以前几次聊天的所有carryover开始。

上图显示了所有聊天的不同接收代理,然而,序列中的接收代理允许重复。

为了说明这种模式,让我们考虑一个算术运算代理的简单示例。一个代理(称为“Number_Agent”)负责提供一个数字,其他代理负责对数字执行特定的算术运算,例如加1,乘以2等。
```python
# 数字代理始终返回相同的数字。
number_agent = ConversableAgent(
name="数字代理",
system_message="你将我给你的数字原样返回,每行一个数字。",
llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}]},
human_input_mode="NEVER",
)

# 加法代理将收到的每个数字加1。
adder_agent = ConversableAgent(
name="加法代理",
system_message="你将我给你的每个数字加1,然后将新的数字原样返回,每行一个数字。",
llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}]},
human_input_mode="NEVER",
)

# 乘法代理将收到的每个数字乘以2。
multiplier_agent = ConversableAgent(
name="乘法代理",
system_message="你将我给你的每个数字乘以2,然后将新的数字原样返回,每行一个数字。",
llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}]},
human_input_mode="NEVER",
)

# 减法代理将收到的每个数字减1。
subtracter_agent = ConversableAgent(
name="减法代理",
system_message="你将我给你的每个数字减1,然后将新的数字原样返回,每行一个数字。",
llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}]},
human_input_mode="NEVER",
)

# 除法代理将收到的每个数字除以2。
divider_agent = ConversableAgent(
name="除法代理",
system_message="你将我给你的每个数字除以2,然后将新的数字原样返回,每行一个数字。",
llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}]},
human_input_mode="NEVER",
)
# 开始一系列两个代理人之间的对话。
# 列表中的每个元素是一个字典,指定了 initiate_chat 方法的参数。
chat_results = number_agent.initiate_chats(
[
{
"recipient": adder_agent,
"message": "14",
"max_turns": 2,
"summary_method": "last_msg",
},
{
"recipient": multiplier_agent,
"message": "这些是我的数字",
"max_turns": 2,
"summary_method": "last_msg",
},
{
"recipient": subtracter_agent,
"message": "这些是我的数字",
"max_turns": 2,
"summary_method": "last_msg",
},
{
"recipient": divider_agent,
"message": "这些是我的数字",
"max_turns": 2,
"summary_method": "last_msg",
},
]
)

********************************************************************************
开始一个新的对话,并发送以下消息:
14

携带以下信息继续对话:


********************************************************************************
Number_Agent (发送至 Adder_Agent):

14

--------------------------------------------------------------------------------
Adder_Agent (发送至 Number_Agent):

15

--------------------------------------------------------------------------------
Number_Agent (发送至 Adder_Agent):

15

--------------------------------------------------------------------------------
Adder_Agent (发送至 Number_Agent):

16

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

********************************************************************************
开始一个新的对话,并发送以下消息:
这些是我的数字

携带以下信息继续对话:
16

********************************************************************************
Number_Agent (发送至 Multiplier_Agent):

这些是我的数字
上下文:
16

--------------------------------------------------------------------------------
Multiplier_Agent (发送至 Number_Agent):

32

--------------------------------------------------------------------------------
Number_Agent (发送至 Multiplier_Agent):

32

--------------------------------------------------------------------------------
Multiplier_Agent (发送至 Number_Agent):

64

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

********************************************************************************
开始一个新的对话,并发送以下消息:
这些是我的数字

携带以下信息继续对话:
16
64

********************************************************************************
Number_Agent (发送至 Subtracter_Agent):

这些是我的数字
上下文:
16
64

--------------------------------------------------------------------------------
Subtracter_Agent (发送至 Number_Agent):

15
63

--------------------------------------------------------------------------------
Number_Agent (发送至 Subtracter_Agent):

15
63

--------------------------------------------------------------------------------
Subtracter_Agent (发送至 Number_Agent):

14
62

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

********************************************************************************
开始一个新的对话,并发送以下消息:
这些是我的数字

携带以下信息继续对话:
16
64
14
62

********************************************************************************
Number_Agent (发送至 Divider_Agent):

这些是我的数字
上下文:
16
64
14
62

--------------------------------------------------------------------------------
Divider_Agent (发送至 Number_Agent):

8
32
7
31

--------------------------------------------------------------------------------
Number_Agent (发送至 Divider_Agent):

8
32
7
31

--------------------------------------------------------------------------------
Divider_Agent (发送至 Number_Agent):

4
16
3.5
15.5

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

首先要注意的是,initiate_chats 方法接受一个字典列表作为参数,每个字典包含 initiate_chat 方法的参数。

其次,序列中的每个对话最多进行 2 轮,这是通过设置 max_turns=2 指定的,这意味着每个算术操作会执行两次。因此,你可以看到在第一次对话中,数字 14 变成了 15,然后变成了 16,在第二次对话中,数字 16 变成了 32,然后变成了 64,依此类推。

第三,随着对话的进行,进位会累积。在第二次对话中,进位是第一次对话的总结“16”。在第三次对话中,进位是第一次和第二次对话的总结,即列表“16”和“64”,然后对这两个数字进行操作。在第四次也是最后一次对话中,进位是所有先前对话的总结,即列表“16”、“64”、“14”和“62”,然后对所有这些数字进行操作。

最后要注意的是,initiate_chats 方法返回一个 ChatResult 对象列表,每个对象对应序列中的一个对话。

print("第一次对话总结: ", chat_results[0].summary)
print("第二次对话总结: ", chat_results[1].summary)
print("第三次对话总结: ", chat_results[2].summary)
print("第四次对话总结: ", chat_results[3].summary)
第一次对话总结:  16
第二次对话总结: 64
第三次对话总结: 14
62
第四次对话总结: 4
16
3.5
15.5

除了从相同的发送者代理调用 initiate_chats 外,你还可以调用高级函数 autogen.agentchat.initiate_chats 来开始一系列包含不同发送者代理的两个代理对话。该函数允许你为序列中的每个对话指定发送者代理。

群组对话

到目前为止,我们只看到涉及两个代理或一系列两个代理对话的对话模式。AutoGen 提供了一种更一般的对话模式,称为群组对话,它涉及多于两个代理。群组对话的核心思想是,所有代理都参与单个对话线程并共享相同的上下文。这对于需要多个代理之间协作的任务非常有用。

下图说明了群组对话的工作原理。

group_chat

群组对话由特殊的代理类型 GroupChatManager 策划。在群组对话的第一步中,群组对话管理器选择一个代理发言。然后,所选代理发言并将消息发送回群组对话管理器,后者将消息广播给群组中的所有其他代理。这个过程重复进行,直到对话结束。

群组对话管理器可以使用几种策略来选择下一个代理。目前支持以下策略:

  1. round_robin:群组对话管理器根据提供的代理顺序以轮流方式选择代理。
  2. random:群组对话管理器随机选择代理。
  3. manual:群组对话管理器通过请求人工输入来选择代理。
  4. auto:默认策略,使用群聊管理器的LLM来选择代理人。

为了说明这种模式,让我们考虑一个简单的示例,与前一个示例中相同的算术运算代理人之间的群聊,其目标是使用由代理人提供动力的一系列算术运算将一个数字转换为特定目标数字。

在这个示例中,我们使用auto策略来选择下一个代理人。为了帮助群聊管理器选择下一个代理人,我们还设置了代理人的description。如果没有description,群聊管理器将使用代理人的system_message,这可能不是最佳选择。

# `description`属性是描述代理人的字符串。
# 它也可以在`ConversableAgent`构造函数中设置。
adder_agent.description = "对每个输入数字加1。"
multiplier_agent.description = "将每个输入数字乘以2。"
subtracter_agent.description = "对每个输入数字减1。"
divider_agent.description = "将每个输入数字除以2。"
number_agent.description = "返回给定的数字。"

我们首先创建一个GroupChat对象并提供代理人列表。如果我们要使用round_robin策略,这个列表将指定要选择的代理人的顺序。我们还初始化群聊,其中包括一个空消息列表和最大轮数为6,这意味着最多会有6轮选择发言者、代理人发言和广播消息。

from autogen import GroupChat

group_chat = GroupChat(
agents=[adder_agent, multiplier_agent, subtracter_agent, divider_agent, number_agent],
messages=[],
max_round=6,
)

现在我们创建一个GroupChatManager对象,并将GroupChat对象作为输入。我们还需要指定群聊管理器的llm_config,以便它可以使用LLM来选择下一个代理人(auto策略)。

from autogen import GroupChatManager

group_chat_manager = GroupChatManager(
groupchat=group_chat,
llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}]},
)

最后,我们有之前的Number Agent开始与群聊管理器进行双代理人聊天,群聊管理器在内部运行群聊,并在内部群聊完成时终止双代理人聊天。因为我们选择Number Agent发言,它算作群聊的第一轮。

chat_result = number_agent.initiate_chat(
group_chat_manager,
message="我的数字是3,我想把它变成13。",
summary_method="reflection_with_llm",
)
数字代理(给聊天管理器):

我的数字是3,我想把它变成13。

--------------------------------------------------------------------------------
乘法代理(给聊天管理器):

6

--------------------------------------------------------------------------------
加法代理(给聊天管理器):

7

--------------------------------------------------------------------------------
乘法代理(给聊天管理器):

14

--------------------------------------------------------------------------------
减法代理(给聊天管理器):

13

--------------------------------------------------------------------------------
数字代理(给聊天管理器):

13

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

你可以看到,首先选择了 Number Agent 来发言,然后 Group Chat Manager 选择了 Multiplier Agent 来发言,然后是 Adder Agent,依此类推。每个代理在群聊中对数字进行操作,最终结果是 13。

我们可以查看由 initiate_chat 方法返回的 ChatResult 对象提供的群聊摘要。

print(chat_result.summary)
代理们通过乘法、加法和减法操作合作地操纵了初始数字(3),以达到目标数字(13)。

发送介绍

在前面的例子中,我们设置了代理的 description 来帮助 Group Chat Manager 选择下一个代理。然而,这只是帮助 Group Chat Manager,却不帮助参与的代理了解彼此。有时,让每个代理向群聊中的其他代理介绍自己是有用的。这可以通过设置 send_introductions=True 来实现。

group_chat_with_introductions = GroupChat(
agents=[adder_agent, multiplier_agent, subtracter_agent, divider_agent, number_agent],
messages=[],
max_round=6,
send_introductions=True,
)

在幕后,Group Chat Manager 在群聊开始之前向所有代理发送包含代理名称和描述的消息。

在顺序聊天中进行群聊

群聊也可以作为顺序聊天的一部分。在这种情况下,Group Chat Manager 被视为两个代理聊天序列中的常规代理。

# 让我们使用上面创建的带介绍消息的群聊。
group_chat_manager_with_intros = GroupChatManager(
groupchat=group_chat_with_introductions,
llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}]},
)

# 开始数字代理和群聊管理员之间的两个代理聊天序列。
chat_result = number_agent.initiate_chats(
[
{
"recipient": group_chat_manager_with_intros,
"message": "我的数字是 3,我想把它变成 13。",
},
{
"recipient": group_chat_manager_with_intros,
"message": "把这个数字变成 32。",
},
]
)
********************************************************************************
开始一个新的对话,并发送以下消息:
我的数字是3,我想把它变成13。

带着以下进位数:


********************************************************************************
数字代理(给聊天经理):

我的数字是3,我想把它变成13。

--------------------------------------------------------------------------------
乘法代理(给聊天经理):

6

--------------------------------------------------------------------------------
加法代理(给聊天经理):

7

--------------------------------------------------------------------------------
乘法代理(给聊天经理):

14

--------------------------------------------------------------------------------
减法代理(给聊天经理):

13

--------------------------------------------------------------------------------
数字代理(给聊天经理):

你的数字是13。

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

********************************************************************************
开始一个新的对话,并发送以下消息:
把这个数字变成32。

带着以下进位数:
你的数字是13。

********************************************************************************
数字代理(给聊天经理):

把这个数字变成32。
上下文:
你的数字是13。

--------------------------------------------------------------------------------
乘法代理(给聊天经理):

26

--------------------------------------------------------------------------------
加法代理(给聊天经理):

14

--------------------------------------------------------------------------------
乘法代理(给聊天经理):

28

--------------------------------------------------------------------------------
加法代理(给聊天经理):

15

--------------------------------------------------------------------------------
乘法代理(给聊天经理):

30

--------------------------------------------------------------------------------
/Users/ekzhu/autogen/autogen/agentchat/chat.py:46: UserWarning: 检测到重复的接收者:如果一个接收者出现多次,聊天记录将默认被清除。如果要保留聊天记录,请在重复代理的配置中设置 'clear_history=False'。
warnings.warn(

在上面的示例中,群聊管理器运行了群聊两次。第一次时,数字 3 变成了 13,并且这个群聊的最后一条消息被用作下一个群聊的延续,从 13 开始。

你还可以从警告消息中看到,群聊管理器的历史在第一次群聊后被清除了,这是默认行为。要保留群聊管理器的历史,你可以在第一次聊天中设置 clear_history=False

限制发言者选择

群聊是一种强大的对话模式,但如果参与的代理数量很多,控制起来可能会很困难。AutoGen 提供了一种方式,通过使用 GroupChat 类的 allowed_or_disallowed_speaker_transitions 参数来限制下一个发言者的选择。

allowed_or_disallowed_speaker_transitions 参数是一个字典,将给定的代理映射到一个可以(或不可以)被选为下一个发言者的代理列表。speaker_transitions_type 参数指定了转换是允许还是不允许的。

以下是一个示例:

allowed_transitions = {
number_agent: [adder_agent, number_agent],
adder_agent: [multiplier_agent, number_agent],
subtracter_agent: [divider_agent, number_agent],
multiplier_agent: [subtracter_agent, number_agent],
divider_agent: [adder_agent, number_agent],
}

在这个示例中,为每个代理指定了允许的转换。数字代理可以接着加法代理和数字代理,加法代理可以接着乘法代理和数字代理,依此类推。让我们将其放入群聊中,看看它是如何工作的。speaker_transitions_type 被设置为 allowed,所以这些转换是积极的约束。

constrained_graph_chat = GroupChat(
agents=[adder_agent, multiplier_agent, subtracter_agent, divider_agent, number_agent],
allowed_or_disallowed_speaker_transitions=allowed_transitions,
speaker_transitions_type="allowed",
messages=[],
max_round=12,
send_introductions=True,
)

constrained_group_chat_manager = GroupChatManager(
groupchat=constrained_graph_chat,
llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}]},
)

chat_result = number_agent.initiate_chat(
constrained_group_chat_manager,
message="My number is 3, I want to turn it into 10. Once I get to 10, keep it there.",
summary_method="reflection_with_llm",
)
数字代理(给聊天经理):

我的数字是3,我想把它变成10。一旦达到10,请保持在那里。

--------------------------------------------------------------------------------
加法代理(给聊天经理):

4

--------------------------------------------------------------------------------
乘法代理(给聊天经理):

8

--------------------------------------------------------------------------------
减法代理(给聊天经理):

7

--------------------------------------------------------------------------------
除法代理(给聊天经理):

3.5

--------------------------------------------------------------------------------
加法代理(给聊天经理):

4.5

--------------------------------------------------------------------------------
乘法代理(给聊天经理):

9

--------------------------------------------------------------------------------
减法代理(给聊天经理):

8

--------------------------------------------------------------------------------
除法代理(给聊天经理):

4

--------------------------------------------------------------------------------
加法代理(给聊天经理):

5

--------------------------------------------------------------------------------
乘法代理(给聊天经理):

10

--------------------------------------------------------------------------------
数字代理(给聊天经理):

10

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

更改选择发言者角色名称

作为群聊过程的一部分,当 select_speaker_method 被设置为 "auto"(默认值)时,会向 LLM 发送一个选择发言者的消息,以确定下一个发言者。

在聊天序列中,每条消息都有一个 role 属性,通常为 userassistantsystem。当使用时,选择发言者消息是聊天序列中的最后一条消息,并且默认情况下其角色为 system

在使用一些模型时,比如通过 Mistral.AI 的 API 使用 Mistral 模型时,聊天序列中最后一条消息的角色必须为 user

为了改变默认行为,Autogen 提供了一种方法,即通过设置 GroupChat 构造函数中的 role_for_select_speaker_messages 参数,将选择发言者消息的角色值设置为任意字符串值。默认值为 system,将其设置为 user 可以满足 Mistral.AI API 的最后消息角色要求。

嵌套聊天

之前的对话模式(双代理聊天、顺序聊天和群聊)对于构建复杂的工作流非常有用,但是它们并没有提供单一的对话界面,而这在问答机器人和个人助手等场景中经常需要。在其他一些情况下,将工作流打包到单个代理中以便在更大的工作流中重复使用也非常有用。AutoGen 提供了一种通过使用嵌套聊天来实现这一点的方法。

嵌套聊天由嵌套聊天处理程序提供支持,该处理程序是 ConversableAgent 的可插拔组件。下图说明了嵌套聊天处理程序在接收到消息时触发一系列嵌套聊天的方式。

nested_chat

当一条消息进来并通过 人在回路组件 时,嵌套聊天处理程序会检查消息是否应该根据用户指定的条件触发嵌套聊天。如果条件满足,嵌套聊天处理程序将启动一系列使用顺序聊天模式指定的嵌套聊天序列。在每个嵌套聊天中,发送者代理始终是触发嵌套聊天的相同代理。最后,嵌套聊天处理程序使用嵌套聊天的结果来产生对原始消息的响应。默认情况下,嵌套聊天处理程序使用最后一次聊天的摘要作为响应。

以下是使用嵌套聊天构建算术代理的示例,该代理将算术运算、基于代码的验证和诗歌打包到单个代理中。这个算术代理接受一个数字转换请求,比如 "将数字 3 转换为 13",并返回描述转换尝试的诗歌。

首先,我们定义代理。我们重用了前面示例中的 group_chat_manager_with_intros 来编排算术运算。

import tempfile

temp_dir = tempfile.gettempdir()

arithmetic_agent = ConversableAgent(
name="算术代理",
llm_config=False,
human_input_mode="ALWAYS",
# 这个代理将始终需要人工输入,以确保代码执行安全。
code_execution_config={"use_docker": False, "work_dir": temp_dir},
)

code_writer_agent = ConversableAgent(
name="代码撰写代理",
system_message="你是一名代码撰写者。你可以在 Markdown 代码块中编写 Python 脚本。",
llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}]},
human_input_mode="NEVER",
)

poetry_agent = ConversableAgent(
name="诗歌代理",
system_message="你是一位 AI 诗人。",
llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}]},
human_input_mode="NEVER",
)

现在我们使用顺序聊天模式定义嵌套对话。所有发送者始终是 arithmetic_agent

nested_chats = [
{
"recipient": group_chat_manager_with_intros,
"summary_method": "reflection_with_llm",
"summary_prompt": "总结用于将源数转换为目标数的操作序列。",
},
{
"recipient": code_writer_agent,
"message": "编写一个Python脚本来验证算术操作是否正确。",
"summary_method": "reflection_with_llm",
},
{
"recipient": poetry_agent,
"message": "为此写一首诗。",
"max_turns": 1,
"summary_method": "last_msg",
},
]

现在我们将嵌套对话处理器注册到 arithmetic_agent,并设置触发嵌套对话的条件。

arithmetic_agent.register_nested_chats(
nested_chats,
# 触发函数用于确定是否应该由代理开始嵌套对话,
# 给定发送者代理。
# 在这种情况下,如果发送者是嵌套对话的接收者之一,则算术代理不会开始嵌套对话,以避免递归调用。
trigger=lambda sender: sender not in [group_chat_manager_with_intros, code_writer_agent, poetry_agent],
)

最后,我们调用 generate_reply 方法从 arithmetic_agent 获取响应 - 这将触发一系列嵌套对话,并将最后一个嵌套对话的总结作为响应返回。

# 而不是使用 `initiate_chat` 方法启动另一个对话,
# 我们可以直接使用 `generate_reply` 方法来获取单个消息的响应。
reply = arithmetic_agent.generate_reply(
messages=[{"role": "user", "content": "我有一个数是3,我想把它变成7。"}]
)

>>>>>>>> 未收到人类输入。

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

********************************************************************************
发送以下消息开始新的聊天:
我有一个数字 3,想把它变成 7。

带着以下继续:


********************************************************************************
Arithmetic_Agent (to chat_manager):

我有一个数字 3,想把它变成 7。

--------------------------------------------------------------------------------
Adder_Agent (to chat_manager):

为了给你结果,我会在你给我的数字上加 1。所以你的新数字是 4。

--------------------------------------------------------------------------------
Multiplier_Agent (to chat_manager):

8

--------------------------------------------------------------------------------
Subtracter_Agent (to chat_manager):

7

--------------------------------------------------------------------------------
Number_Agent (to chat_manager):

7

--------------------------------------------------------------------------------
Number_Agent (to chat_manager):

7

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

********************************************************************************
发送以下消息开始新的聊天:
编写一个 Python 脚本来验证算术运算是否正确。

带着以下继续:
首先,初始数字 3 加上 1 变成 4。然后乘以 2 得到 8。最后,从 8 减去 1 得到目标数字 7。

********************************************************************************
Arithmetic_Agent (to Code_Writer_Agent):

编写一个 Python 脚本来验证算术运算是否正确。
上下文:
首先,初始数字 3 加上 1 变成 4。然后乘以 2 得到 8。最后,从 8 减去 1 得到目标数字 7。

--------------------------------------------------------------------------------
Code_Writer_Agent (to Arithmetic_Agent):

以下是一个 Python 脚本,用于验证上述算术运算:

```python```
```mdx
# 定义初始值
initial_number = 3

# 将初始数值增加1
initial_number += 1
assert initial_number == 4, "第一步操作失败!"

# 将结果乘以2
initial_number *= 2
assert initial_number == 8, "第二步操作失败!"

# 从结果中减去1
initial_number -= 1
assert initial_number == 7, "最后一步操作失败!"

print("所有操作成功执行!")
```
```
在脚本中,整个过程被分解为几个步骤。`assert` 函数用于验证每个步骤的结果。如果任何操作未产生预期结果,将引发 `AssertionError` 异常。如果所有操作都通过了,将打印消息 "All operations were carried out successfully!"。

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

>>>>>>>> 没有接收到人类输入。

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

>>>>>>>> 执行代码块 0 (推断语言为 python)...
算术代理 (到 代码编写代理):

退出码: 0 (执行成功)
代码输出:
All operations were carried out successfully!


--------------------------------------------------------------------------------
代码编写代理 (到 算术代理):

当然,这意味着 Python 脚本成功执行,并且每个算术操作都按照初始输入和执行步骤正确完成。

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

********************************************************************************
使用以下消息开始新的对话:
Write a poem about it.

携带以下继续内容:
First, 1 was added to the initial number 3 to make it 4. Then it was multiplied by 2 which resulted in 8. Finally, 1 was subtracted from 8 to reach the target number 7.
The Python script successfully performed and verified the arithmetic operations on the initial number provided. The steps included adding 1 to the initial number, multiplying the result by 2, and finally subtracting 1. The assert function was used to check the result at each step, and confirmed that all operations were carried out correctly.

********************************************************************************
算术代理 (到 诗歌代理):

Write a poem about it.
Context:
First, 1 was added to the initial number 3 to make it 4. Then it was multiplied by 2 which resulted in 8. Finally, 1 was subtracted from 8 to reach the target number 7.
The Python script successfully performed and verified the arithmetic operations on the initial number provided. The steps included adding 1 to the initial number, multiplying the result by 2, and finally subtracting 1. The assert function was used to check the result at each step, and confirmed that all operations were carried out correctly.

--------------------------------------------------------------------------------
诗歌代理 (到 算术代理):

From numbers, logic, pure mathematical creation,
Ponder this tale of numeric manipulation.
In the universe of Python where operations exist,
A story of integers and functions persist.

Three was the number from where we began,
Oblivious to the journey and its grandiosely plan.
Added with 1, the sum it adorned,
A sweet quadruple in the dawn was formed.

The saga continued with a twist of the tale,
The four was multiplied, while the winds wail.
The duo of four unfolded its wings,
An octet presence in our midst it brings.

Then enters subtraction, sly and clever,
Removing one to alter the endeavor.
From eight, subtracted one in delight,
To finally bask in the glow of seven's light.

Each operation, together they conspired,
In this tale of integers, creatively inspired.
Through life's equation, the script ran so free,
Amidst the language of Python, a symphony, you see.

Tested with assert, cross-checked the chain,
Confirming accuracy in program's domain.
Each move calculated, each step so right,
In the maze of coding, found was the light.

Such is the tale, of numbers and operations,
A dance among digits, logical iterations,
Just another day, in this AI poet's life,
Cutting through ambiguity, like a razor-sharp knife.

--------------------------------------------------------------------------------
```
一首诗作为回应,描述了从3到7的转变尝试。

嵌套聊天处理程序的实现利用了[`register_reply`](../reference/agentchat/conversable_agent/#register_reply)方法,这使您可以对`ConversableAgent`进行广泛的定制。GroupChatManager使用相同的机制来实现群聊。

嵌套聊天是一种强大的对话模式,允许您将复杂的工作流打包到单个代理中。您可以通过让工具调用代理开始与工具执行代理的嵌套聊天,然后使用嵌套聊天的结果来生成响应,从而隐藏[工具使用](../../docs/tutorial/tool-use)。请参阅[用于工具使用的嵌套聊天笔记本](../../docs/notebooks/agentchat_nested_chats_chess)以获取示例。

## 摘要

在本章中,我们介绍了双代理聊天、顺序聊天、群聊和嵌套聊天模式。您可以像乐高积木一样组合这些模式,创建复杂的工作流。您还可以使用[`register_reply`](../reference/agentchat/conversable_agent/#register_reply)来创建新模式。

这是关于基本AutoGen概念的最后一章。在下一章中,我们将为您提供一些接下来要做的提示。