# AutoGen: Enabling Next-Gen LLM Applications via Multi-Agent Conversation
## 摘要
AutoGen2 是一个开源框架,允许开发者通过多个可以相互对话的代理来构建 LLM 应用程序,以完成任务。AutoGen 代理是可定制的、可对话的,并且可以在各种模式下运行,这些模式结合了 LLM、人类输入和工具。使用 AutoGen,开发者还可以灵活地定义代理交互行为。自然语言和计算机代码都可以用来为不同的应用程序编程灵活的对话模式。AutoGen 作为一个通用的框架,用于构建各种复杂度和 LLM 容量的多样化应用程序。实证研究表明,该框架在许多示例应用程序中表现出了有效性,涉及的领域包括数学、编码、问答、运筹学、在线决策、娱乐等。
## 引入
鉴于可以从LLMs中受益的任务范围不断扩大以及任务复杂性的增加,增强代理能力的一种直观方法是使用多个合作的代理。
三个原因:首先,因为聊天优化的LLMs(例如,GPT-4)展示了整合反馈的能力,LLM代理可以通过彼此或人类之间的对话进行合作,例如,代理提供和寻求推理、观察、批评和验证的对话。因为单个LLM可以展示广泛的技能(特别是在配置了正确的提示和推理设置时),不同配置的代理之间的对话可以帮助以模块化和互补的方式结合这些广泛的LLM能力。第三,LLMs已经展示了在将任务分解为更简单的子任务时解决复杂任务的能力。第三,LLMs已经展示了在将任务分解为更简单的子任务时解决复杂任务的能力。多代理对话可以直观地实现这种划分和整合。
两个问题:如何设计能够在多代理合作中胜任、可重用、可定制和有效的单个代理?如何开发一个直接、统一的接口,可以容纳广泛的代理对话模式?
AutoGen:1. 可定制和可对话的代理。AutoGen使用了一种通用的代理设计,可以利用LLMs、人类输入、工具或它们的组合。结果是开发者可以轻松快速地创建具有不同角色(例如,编写代码、执行代码、接入人类反馈、验证输出等的代理)的代理,通过选择和配置一组内置能力。2. 对话编程。一个基于以下新概念的广义多代理对话框架。可定制和可对话的代理,对话编程。AutoGen的一个基本洞察是将复杂的LLM应用程序工作流程简化和统一为多代理对话。因此,AutoGen采用了一种以这些代理间对话为中心的编程范式。我们将这种范式称为对话编程,它通过两个主要步骤简化了复杂应用程序的开发:(1)定义一组具有特定能力和角色的可对话代理(如上所述);(2)通过以对话为中心的计算和控制编程代理之间的交互行为。
## AutoGen框架
AutoGen的核心设计原则是通过多代理对话来简化和整合多代理工作流程。这种方法还旨在最大化已实现代理的可重用性。
### 可对话代理
在AutoGen中,一个可对话代理是一个具有特定角色的实体,它可以通过传递消息向其他可对话代理发送和接收信息,例如,开始或继续对话。它根据发送和接收的消息维护其内部上下文,并且可以配置为具有一组能力,例如,由LLMs、工具或人类输入等启用。代理可以根据接下来描述的编程行为模式进行操作。
由LLMs、人类和工具支持的代理能力。
代理定制和合作。
### 对话编程
AutoGen利用了对话编程,这是一种范式,考虑两个概念:第一个是计算——代理在多代理对话中采取行动来计算其响应。第二个是控制流——这些计算发生的顺序(或条件)。正如我们将在应用程序部分展示的那样,编程这些能力有助于实现许多灵活的多代理对话模式。在AutoGen中,这些计算是以对话为中心的。代理采取与其参与的对话相关的行动,并且其行动导致后续对话的消息传递(除非满足终止条件)。同样,控制流是对话驱动的——参与代理的决定向哪些代理发送消息以及计算过程的程序是代理间对话的函数。这种范式有助于直观地推理复杂的流程,将其视为代理行动和代理间对话消息传递。
拥有:
1. 统一的接口和自动回复机制,用于自动代理聊天
2. 通过编程和自然语言的融合进行控制
## AutoGen的应用
我们展示了六个使用AutoGen的应用(见图3),以说明其在简化高性能多代理应用程序开发方面的潜力。这些应用是根据它们在现实世界中的相关性(A1、A2、A4、A5、A6)、问题难度以及AutoGen启用的解决能力(A1、A2、A3、A4)和创新潜力(A5、A6)选择的。这些标准共同展示了AutoGen在推动LLM应用领域发展中的作用。
A1:数学问题解决
A2:检索增强的代码生成和问答
A3:文本世界环境中的决策制定
A4:多代理编码
A5:动态群聊
A6:对话式国际象棋
# 食用手册
## 初始化
result = joe.initiate_chat(cathy, message="Cathy, tell me a joke.", max_turns=2)
## 人类对话
在 AutoGen 中,人机交互组件位于 自动回复组件的前面。它可以拦截传入的 消息,并决定是否将它们传递给自动回复组件,或者 提供人工反馈
目前,AutoGen 支持三种人工输入模式。模式是 通过 的参数指定。这三种模式是:human_input_modeConversableAgent
NEVER:从不请求人工输入。
TERMINATE(默认):仅当 满足终止条件。请注意,在此模式下,如果人类 选择拦截和回复,对话继续,然后 使用的计数器被重置。max_consecutive_auto_reply
ALWAYS:始终请求人工输入,人工可以选择 跳过并触发自动回复、拦截并提供反馈, 或终止对话。请注意,在此模式下终止 based on 被忽略。max_consecutive_auto_reply
## 执行LLM的代码?
用不到
## 工具使用
可以注册一个函数工具
使用typing库:一种在代码中明确指出变量、函数参数和返回值类型的方法,目的是提高代码的可读性和可维护性。
```python
from typing import Annotated, Literal
Operator = Literal["+", "-", "*", "/"]
def calculator(a: int, b: int, operator: Annotated[Operator, "operator"]) -> int:
if operator == "+":
return a + b
elif operator == "-":
return a - b
elif operator == "*":
return a * b
elif operator == "/":
return int(a / b)
else:
raise ValueError("Invalid operator")
import os
from autogen import ConversableAgent
# Let's first define the assistant agent that suggests tool calls.
assistant = ConversableAgent(
name="Assistant",
system_message="You are a helpful AI assistant. "
"You can help with simple calculations. "
"Return 'TERMINATE' when the task is done.",
llm_config={"config_list": [{"model": "gpt-4", "api_key": os.environ["OPENAI_API_KEY"]}]},
)
# The user proxy agent is used for interacting with the assistant agent
# and executes tool calls.
user_proxy = ConversableAgent(
name="User",
llm_config=False,
is_termination_msg=lambda msg: msg.get("content") is not None and "TERMINATE" in msg["content"],
human_input_mode="NEVER",
)
# Register the tool signature with the assistant agent.
assistant.register_for_llm(name="calculator", description="A simple calculator")(calculator)
# Register the tool function with the user proxy agent.
user_proxy.register_for_execution(name="calculator")(calculator)
同时向两个代理注册工具:
from autogen import register_function
# Register the calculator function to the two agents.
register_function(
calculator,
caller=assistant, # The assistant agent can suggest calls to the calculator.
executor=user_proxy, # The user proxy agent can execute the calculator calls.
name="calculator", # By default, the function name is used as the tool name.
description="A simple calculator", # A description of the tool.
)
```
## 对话模式
- 两个代理聊天
最简单的形式
- 顺序聊天:两个代理之间的一系列聊天,通过一个传递机制串联起来,该机制将前一次聊天的总结带入下一次聊天的上下文中。
- 群聊:涉及两个以上代理的单一聊天。群聊中的一个重要问题是:下一个应该由哪个代理发言?为了支持不同的场景,我们提供了不同的方式来组织群聊中的代理
支持多种策略来选择下一个发言的代理:轮询(round_robin)、随机(random)、手动选择(manual selection),以及默认方式(使用LLM决定)。允许传入一个函数来自定义下一个发言者的选择。通过这一功能,可以构建一个StateFlow模型,该模型允许在代理之间实现确定性的工作流程。更多详情,请参阅此指南和关于StateFlow的博客文章。
- 嵌套聊天:将一个工作流程打包成一个单一的代理,以便在更大的工作流程中重复使用。
### 两个代理聊天和聊天结果
双代理聊天接受两个输入:一条消息,这是一个由调用者提供的字符串;一个上下文,它指定了聊天的各种参数。发送代理使用其聊天初始化方法(即 的方法)从输入生成一条初始消息,并将其发送给接收代理以启动聊天。发送代理是调用其方法的代理,而接收代理是另一个代理。
agent.generate_init_message ConversableAgent initiate_chat
一旦聊天结束,聊天历史将由一个聊天总结器处理。总结器总结聊天历史并计算聊天的令牌使用量。您可以使用 方法的 参数来配置总结的类型。默认情况下,它是聊天的最后一条消息(即 )。
.summary_method initiate_chat summary_method='last_msg'
```python
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,
)
```
print(chat_result.summary)
default prompt: reflection_with_llm : Summarize the takeaway from the conversation. Do not add any introductory phrases.
打印聊天历史:
Get the chat history.
import pprint
pprint.pprint(chat_result.chat_history)
打印聊天花销:
Get the cost of the chat.
pprint.pprint(chat_result.cost)
### 顺序聊天
![alt text](image-4.png)
这种模式的名称本身就说明了其含义——它是两个代理之间的一系列聊天,通过一种称为“传递”的机制串联起来,该机制将前一次聊天的总结带入下一次聊天的上下文中。
这种模式对于可以分解为相互依赖的子任务的复杂任务非常有用。下图说明了这种模式的工作原理。在这种模式中,一对代理首先开始一个双代理聊天,然后对话的总结成为下一次双代理聊天的传递内容。下一次聊天将传递内容传递给上下文的传递参数,以生成其初始消息。
随着对话的推进,传递内容会累积,因此每个后续聊天都以前面所有聊天的传递内容开始。
上图显示了所有聊天的不同接收代理,然而,序列中的接收代理是允许重复的。
```python
# Start a sequence of two-agent chats.
# Each element in the list is a dictionary that specifies the arguments
# for the initiate_chat method.
chat_results = number_agent.initiate_chats(
[
{
"recipient": adder_agent,
"message": "14",
"max_turns": 2,
"summary_method": "last_msg",
},
{
"recipient": multiplier_agent,
"message": "These are my numbers",
"max_turns": 2,
"summary_method": "last_msg",
},
{
"recipient": subtracter_agent,
"message": "These are my numbers",
"max_turns": 2,
"summary_method": "last_msg",
},
{
"recipient": divider_agent,
"message": "These are my numbers",
"max_turns": 2,
"summary_method": "last_msg",
},
]
)
```
其次,序列中的每个聊天最多进行两轮,如设置max_turns=2所示,这意味着每个算术操作执行两次。因此,在第一个聊天中,数字14变为15,然后变为16;在第二个聊天中,数字16变为32,然后变为64,依此类推。
第三,随着聊天的进行,传递内容会累积。在第二个聊天中,传递内容是第一个聊天的总结“16”。在第三个聊天中,传递内容是第一个和第二个聊天的总结,即列表“16”和“64”,这两个数字都会被操作。在第四个也是最后一个聊天中,传递内容是所有先前聊天的总结,即列表“16”、“64”、“14”和“62”,所有这些数字都会被操作。
### 组内聊天
到目前为止,我们只看到了涉及两个代理或一系列双代理聊天的对话模式。AutoGen提供了一种更通用的对话模式,称为群聊,它涉及两个以上的代理。群聊的核心思想是所有代理都参与到一个单一的对话线程中,并共享相同的上下文。这对于需要多个代理协作的任务非常有用。
群聊由一种特殊的代理类型GroupChatManager协调。在群聊的第一步,群聊管理器选择一个代理发言。然后,被选中的代理发言,消息被发送回群聊管理器,后者将消息广播给群组中的所有其他代理。这个过程重复进行,直到对话停止。
群聊管理器可以使用多种策略来选择下一个代理。目前,支持以下策略:
round_robin:群聊管理器根据提供的代理顺序,以轮询方式选择代理。
random:群聊管理器随机选择代理。
manual:群聊管理器通过请求人工输入来选择代理。
auto:默认策略,使用群聊管理器的LLM来选择代理。
为了说明这种模式,让我们考虑一个简单的例子,涉及与前面例子中相同的算术操作代理,目标是使用代理提供的算术操作序列将一个数字转换为特定的目标数字。
在这个例子中,我们使用auto策略来选择下一个代理。为了帮助群聊管理器选择下一个代理,我们还设置了代理的描述。如果没有描述,群聊管理器将使用代理的system_message,这可能不是最佳选择。
```python
# The `description` attribute is a string that describes the agent.
# It can also be set in `ConversableAgent` constructor.
adder_agent.description = "Add 1 to each input number."
multiplier_agent.description = "Multiply each input number by 2."
subtracter_agent.description = "Subtract 1 from each input number."
divider_agent.description = "Divide each input number by 2."
number_agent.description = "Return the numbers given."
group_chat = GroupChat(
agents=[adder_agent, multiplier_agent, subtracter_agent, divider_agent, number_agent],
messages=[],
max_round=6,
)
```
在前面的例子中,我们设置了代理的描述,以帮助群聊管理器选择下一个代理。然而,这仅帮助了群聊管理器,并没有帮助参与的代理了解彼此。有时,让每个代理在群聊中向其他代理介绍自己是有用的。这可以通过设置send_introductions=True来实现。
```python
group_chat_with_introductions = GroupChat(
agents=[adder_agent, multiplier_agent, subtracter_agent, divider_agent, number_agent],
messages=[],
max_round=6,
send_introductions=True,
)
```
管理器就会发送代理的名字和描述给组里所有代码
### 顺序聊天内的组聊天
### 带约束选择说话的人
群聊是一种强大的对话模式,但如果参与的代理数量众多,控制起来可能会很困难。AutoGen提供了一种通过使用GroupChat类的allowed_or_disallowed_speaker_transitions参数来限制下一个发言者选择的方法。
allowed_or_disallowed_speaker_transitions参数是一个字典,它将给定的代理映射到一个可以(或不可以)被选为下一个发言者的代理列表。speaker_transitions_type参数指定这些转换是允许的还是不允许的。
```python
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],
}
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",
)
```
在这个例子中,为每个代理指定了允许的转换。Number Agent可以由Adder Agent和Number Agent跟随,Adder Agent可以由Multiplier Agent和Number Agent跟随,依此类推。让我们将这个应用到群聊中,看看它是如何工作的。speaker_transitions_type设置为allowed,因此这些转换是正约束。
在群聊过程中,当select_speaker_method设置为‘auto’(默认值)时,会向LLM发送一个选择发言者的消息,以确定下一个发言者。
聊天序列中的每条消息都有一个角色属性,通常是user、assistant或system。选择发言者的消息是聊天序列中的最后一条,默认情况下,它的角色是system。
### 嵌套聊天
前面的对话模式(双代理聊天、顺序聊天和群聊)对于构建复杂的工作流程非常有用,但它们没有暴露一个单一的对话接口,这在问答机器人和个人助手等场景中通常是需要的。在其他一些情况下,将一个工作流程打包成一个单一的代理以便在更大的工作流程中重复使用也是很有用的。AutoGen通过使用嵌套聊天提供了一种实现这一点的方法。
嵌套聊天由嵌套聊天处理器驱动,它是ConversableAgent的一个可插拔组件。
当一条消息进来并通过人机交互组件时,嵌套聊天处理器会根据用户指定的条件检查该消息是否应该触发嵌套聊天。如果条件满足,嵌套聊天处理器会使用顺序聊天模式启动一系列指定的嵌套聊天。在每个嵌套聊天中,发送代理始终是触发嵌套聊天的同一个代理。最终,嵌套聊天处理器使用嵌套聊天的结果来生成对原始消息的响应。默认情况下,嵌套聊天处理器使用最后一次聊天的总结作为响应。