随着大模型技术越来越成熟,更多的人将目光放在了智能体研发上,智能体依赖于基座大模型的能力,但也更注重协作执行能力,这篇文章我们就来分享一个OpenAI公司开源的多智能体框架--Swarm。
Swarm是一个轻量级、高可控性并且易于测试的agent框架,该框架提出了两个概念:Agents和Handoffs,一个Agent包含了instruction和tools,并且该Agent可在任何时间选择将对话权Handoffs(接力)给另外一个Agent。
一、安装&使用
# 安装需要 Python3.10+
pip install git+ssh://git@github.com/openai/swarm.git
pip install git+https://github.com/openai/swarm.git
# 如果是离线安装,可在github上下载swarm-main.zip,然后运行以下命令
pip install swarm-main.zip
【注意】 安装后需要注意一个地方,Swarm框架会将pip安装的openai依赖包改为1.33.0版本,需要自己再更改为需要的版本。
安装好之后,我们来简单看一下Swarm框架给的官方使用示例。
from swarm import Swarm, Agent
client = Swarm()
def transfer_to_agent_b():
return agent_b
agent_a = Agent(
name="Agent A",
instructions="You are a helpful agent.",
functions=[transfer_to_agent_b],
)
agent_b = Agent(
name="Agent B",
instructions="Only speak in Haikus.",
)
response = client.run(
agent=agent_a,
messages=[{"role": "user", "content": "I want to talk to agent B."}],
)
print(response.messages[-1]["content"])
# Result
"""
Hope glimmers brightly,
New paths converge gracefully,
What can I assist?
"""
在这个使用示例里,我们可以看到作者定义了两个Agent:agent_a和agent_b。之后,优先将对话权交给了agent_a,并且发送消息"I want to talk to agent B."。agent_a拿到对话权后,会选择调用transfer_to_agent_b函数,并将对话权交给agent_b。agent_b接力后,它的指令是"only speak in Haikus.",因此大模型将以Haikus(三行俳句)的形式输出结果.
上述的例子是基于chatgpt线上服务进行调用的,但我们国内大多数人并不会直接调用chatgpt,我们更多的是使用开源模型本地部署运行。下面我们就来看一下本地运行需要对官方示例修改哪些地方.
"""
Swarm框架给出的版本
"""
from swarm import Swarm, Agent
client = Swarm()
"""
因为Swarm框架中默认调用了chatgpt
如果想改为自己部署的模型需要添加本地模型api
本地模型应为标准openai输入输出,建议使用vllm启动
"""
from swarm import Swarm, Agent
client = Swarm(
client=OpenAI(
api_key="123",
base_url="http://127.0.0.1:9876/v1/"
)
)
"""
Swarm给出的agent定义
"""
agent_a = Agent(
name="Agent A",
instructions="You are a helpful agent.",
functions=[transfer_to_agent_b],
)
"""
使用本地部署模型时需要写清楚调用的模型名称,该名称与vllm启动名称保持一致
"""
agent_a = Agent(
name="Agent A",
model="qwen2.5-14b",
instructions="You are a helpful agent.",
functions=[transfer_to_agent_b],
tool_choice="auto"
)
这样,我们就对Swarm有了一个基本的认识。
二、函数描述
第二部分,我们来看一下swarm框架的代码。
swarm框架的核心代码就是swarm/types.py和swarm/cores.py,这两个代码文件分别实现了对智能体的创建(Agent类)和智能体的交互(Swarm类)。
2.1 client.run()
Swarm的run()函数类似于 Chat Completions API 中chat.completions.create()函数接收并返回messages不记录任何调用之间的状态。同时,run()函数也处理Agent的执行、handoffs(接力)、上下文变量引用,并且可以在返回给用户之前进行多轮对话。
作为Swarm框架的核心,client.run()主要实现了以下内容:
-
从当前Agent中获取一个回复。
-
执行工具调用并添加结果。
-
如果必要可切换调用Agent。
-
如有必要可更新上下文变量。
-
如果没有新的函数调用则返回结果。
client.run()入参
Argument | Type | Description | Default |
---|---|---|---|
agent | Agent | 初始要调用的代理。 | (required) |
messages | List | 一个message对象的列表,类似于 Chat Completions API中的message | (required) |
context_variables | dict | 一个上下文变量的字典,可用于函数和Agent指令 | {} |
max_turns | int | 允许的最大会话轮数 | float("inf") |
model_override | str | 可选字符串,用于更新Agent使用的模型 | None |
execute_tools | bool | 如果为False,中断执行并在Agent试图调用函数时立即返回tool_calls消息 | True |
stream | bool | 如果为True,启用流式响应 | False |
debug | bool | 如果为True,启用调试日志 | False |
client.run()响应
Field | Type | Description |
---|---|---|
messages | List | 对话期间生成的消息对象列表。和Chat Completions API中的message非常相似但是有一个sender表示哪个Agent的字段消息来源。 |
agent | Agent | 最后一个处理消息的Agent。 |
context_variables | dict | 和输入变量一样,加上任何变化。 |
2.2 Agent
Agent简单来说就是指令和函数的集合(加上一些额外的设置),并且有能力将执行权交接给另一个Agent。
Agent类参数说明
Field | Type | Description | Default |
---|---|---|---|
name | str | 代理名称,主要影响sender的输出 | Agent |
model | str | agent使用的模型,可参考上面的示例更换为自己部署的模型 | gpt-4o |
instructions | str or func() -> str | Agent的指令,可以是字符串或返回字符串的函数。 | You are a helpful agent. |
functions | List | Agent可以调用的函数列表。 | [] |
tool_choice | str | 如果指定Agent选择某一工具,可填写工具名,否则默认auto。 | None |
三、示例解析
官方在example目录下给出了以下示例
--basic:关于基础概念的简单样例,例如 setup(设置)、function calling(函数调用)、handoffs(接力)和context variables上下文变量。
--triage_agent:一个设置基本分诊步骤交接给正确的Agent的简单示例。
--weather_agent:一个调用function calling的例子。
--airline:一个用于处理航空公司乘警中不同顾客服务请求的多智能体样例。
--support_bot:一个包含用户界面Agent和带有多个工具的帮助中心Agent的客户服务中心。
--personal_shopper:一个个人购物Agent,可以帮助处理销售和退单。
在本小节,我们将基于本地模型(qwen2.5-14b)对Swarm中给出的几个基础示例调用进行解析。
3.1 示例一--Instructions(bar_minimum.py)
该示例主要描述了instruction的传递功能,Agent的Instruction会直接转化为对话的system信息。只有当前活动的Agent所给出的Instruction会存在并传递(例如,如果发生了Agent接力,那么system的prompt将会改变,但是聊天历史信息不会)。
from swarm import Swarm, Agent
client = Swarm(
client=OpenAI(
api_key="123",
base_url="http://127.0.0.1:9876/v1/"
)
)
agent = Agent(
name="Agent",
model="qwen2.5-14b",
instructions="You are a helpful agent.",
)
messages = [{"role": "user", "content": "Hi!"}]
response = client.run(agent=agent, messages=messages)
print(response.messages[-1]["content"])
我们看到每一个Agent都可以定义一个instructions,这样智能体之间接力时就可以很自然的进行instruction更换,做到不同的智能体实现不同的功能。
3.2 示例二--context_variables(context_variables.py)
Instruction可以是一个字符串,也可以是一个函数返回的字符串。函数可以选择性的接收context_variables参数,这个参数可以通过client.run()进行传递。
from swarm import Swarm, Agent
client = Swarm(
client=OpenAI(
api_key="123",
base_url="http://127.0.0.1:9876/v1/"
)
)
def instructions(context_variables):
name = context_variables.get("name", "User")
return f"You are a helpful agent. Greet the user by name ({name})."
def print_account_details(context_variables: dict):
user_id = context_variables.get("user_id", None)
name = context_variables.get("name", None)
print(f"Account Details: {name} {user_id}")
return "Success"
agent = Agent(
name="Agent",
model="qwen2.5-14b",
instructions=instructions,
functions=[print_account_details],
tool_choice="auto"
)
context_variables = {"name": "James", "user_id": 123}
response = client.run(
messages=[{"role": "user", "content": "Hi!"}],
agent=agent,
context_variables=context_variables,
)
print(response.messages[-1]["content"])
response = client.run(
messages=[{"role": "user", "content": "Print my account details!"}],
agent=agent,
context_variables=context_variables,
)
print(response.messages[-1]["content"])
【注意】在这个示例中,我们需要注意一个地方,在定义Agent时,应该给tool_choice传递auto值,否则会默认为None,这个在Qwen模型中会报错。function call在使用tool_choice时还可以指定一个tool,但需要以字典形式传递,这种方式Swarm目前是不支持的,因为Swarm代码内将tool_choice定义为了字符串,这块工程化其实是做的比较差的,读者有兴趣的可以自行将代码改为可接受字典的形式。
这个示例展示了context_variable如何进行透传的,我们可以看到不同的函数均可以直接接收传递的context_variables,后续基于用户指令及透传的上下文参数进行用户问题的回答。
3.3 示例三--Functions(function_calling.py)
Swarm框架中的Agent可以直接调用python函数,函数通常需要返回字符串,如果一个函数返回了一个Agent,执行将会被转移给那个Agent。
from swarm import Swarm, Agent
client = Swarm(
client=OpenAI(
api_key="123",
base_url="http://127.0.0.1:9876/v1/"
)
)
def get_weather(location) -> str:
return "{'temp':67, 'unit':'F'}"
agent = Agent(
name="Agent",
model="qwen2.5-14b",
instructions="You are a helpful agent.",
functions=[get_weather],
)
messages = [{"role": "user", "content": "What's the weather in NYC?"}]
response = client.run(agent=agent, messages=messages)
print(response.messages[-1]["content"])
与function calling一致,实现了调用函数的意图识别与实体识别,比如这个例子中,识别到了调用get_weather这个函数,并且函数的入参location是NYC。Swarm在这里面有个比较巧妙的构思,我们function calling里面对函数的description可以直接通过get_weather这个函数的注释获取,采用python的内置函数__doc__()。
3.4 示例四--handoffs(agent_handoff.py)
一个Agent可以通过函数调用传给另一个Agent,Swarm中将其定义为handoffs(接力)。
from swarm import Swarm, Agent
client = Swarm(
client=OpenAI(
api_key="123",
base_url="http://127.0.0.1:9876/v1/"
)
)
english_agent = Agent(
name="English Agent",
model="qwen2.5-14b",
instructions="You only speak English.",
tool_choice="auto"
)
spanish_agent = Agent(
name="Spanish Agent",
model="qwen2.5-14b",
instructions="You only speak Spanish.",
)
def transfer_to_spanish_agent():
"""Transfer spanish speaking users immediately."""
return spanish_agent
english_agent.functions.append(transfer_to_spanish_agent)
messages = [{"role": "user", "content": "Hola. ¿Como estás?"}]
response = client.run(agent=english_agent, messages=messages)
print(response.messages[-1]["content"])
这个示例可以说是Swarm这个框架的一个核心,解释了多智能体之间是如何进行通信及协作的。首先定义两个agent,english_agent用来用英文回复问题,spanish_agent用来用西班牙语回答问题;然后在english_agent中加入函数transfer_to_spanish_agent,后于后续多智能体的接力。在程序运行时,首先将对话权交给english_agent,但是用户问题是使用西班牙语问的,因此english_agent就会调用transfer_to_spanish_agent,并将对话权交给spanish_agent,最终形成西班牙语的对话形式。
四、总结
就个人理解,Swarm框架并没有给出很多传统意义上的创新型内容,它更多的可能还是通过封装对大模型原生能力的调用,来定义了一个多智能体中的通信协议,为后续建立智能体生态奠定了比较好的基础。正如作者所说,整个项目更偏向于实验性质,因此整个工程代码中仍有不少瑕疵,但瑕不掩瑜,Swarm仍是一个比较好的学习多智能体及多智能体通信的框架。