智能体课程文档

多智能体系统

Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

Ask a Question Open In Colab

多智能体系统

多智能体系统使 专门的智能体能够协作完成复杂任务,从而提高了模块性、可扩展性和鲁棒性。它不是依赖单个智能体,而是将任务分配给具有不同能力的多个智能体。

smolagents 中,可以组合不同的智能体来生成 Python 代码、调用外部工具、执行网页搜索等。通过协调这些智能体,我们可以创建强大的工作流。

一个典型的设置可能包括

  • 一个用于任务委派的 管理者智能体
  • 一个用于代码执行的 代码解释器智能体
  • 一个用于信息检索的 网页搜索智能体

下图展示了一个简单的多智能体架构,其中一个 管理者智能体 协调一个 代码解释器工具 和一个 网页搜索智能体,而后者又利用像 DuckDuckGoSearchToolVisitWebpageTool 这样的工具来收集相关信息。

多智能体系统的实际应用

一个多智能体系统由多个专门的智能体组成,它们在一个 协调器智能体 的协调下协同工作。这种方法通过将任务分配给具有不同角色的智能体,使得复杂的工作流成为可能。

例如,一个 多智能体 RAG 系统 可以集成

  • 一个用于浏览互联网的 网页智能体
  • 一个用于从知识库中获取信息的 检索器智能体
  • 一个用于生成视觉内容的 图像生成智能体

所有这些智能体都在一个协调器的管理下运行,该协调器负责任务的委派和交互。

使用多智能体层级结构解决复杂任务

你可以按照这个 notebook 中的代码进行操作,你可以在 Google Colab 中运行它。

招待会快要开始了!在你的帮助下,阿尔弗雷德现在几乎完成了所有准备工作。

但现在出了个问题:蝙蝠车不见了。阿尔弗雷德需要尽快找到一辆替代品。

幸运的是,关于布鲁斯·韦恩生平的传记电影已经拍了几部,所以也许阿尔弗雷德可以从某个电影片场弄到一辆留下的车,并将其重新改装以达到现代标准,这当然会包括一个全自动驾驶选项。

但这辆车可能在世界各地的任何一个拍摄地点——这些地点可能有很多。

所以阿尔弗雷德需要你的帮助。你能构建一个能够解决这个任务的智能体吗?

👉 找出世界上所有蝙蝠侠电影的拍摄地点,计算通过船只运输到那里的时间,并在地图上表示出来,用颜色区分船只运输时间。同时用相同的船只运输时间表示一些超级跑车工厂的位置。

让我们开始构建吧!

这个例子需要一些额外的软件包,所以我们先安装它们。

pip install 'smolagents[litellm]' plotly geopandas shapely kaleido -q

我们首先创建一个工具来获取货机运输时间。

import math
from typing import Optional, Tuple

from smolagents import tool


@tool
def calculate_cargo_travel_time(
    origin_coords: Tuple[float, float],
    destination_coords: Tuple[float, float],
    cruising_speed_kmh: Optional[float] = 750.0,  # Average speed for cargo planes
) -> float:
    """
    Calculate the travel time for a cargo plane between two points on Earth using great-circle distance.

    Args:
        origin_coords: Tuple of (latitude, longitude) for the starting point
        destination_coords: Tuple of (latitude, longitude) for the destination
        cruising_speed_kmh: Optional cruising speed in km/h (defaults to 750 km/h for typical cargo planes)

    Returns:
        float: The estimated travel time in hours

    Example:
        >>> # Chicago (41.8781° N, 87.6298° W) to Sydney (33.8688° S, 151.2093° E)
        >>> result = calculate_cargo_travel_time((41.8781, -87.6298), (-33.8688, 151.2093))
    """

    def to_radians(degrees: float) -> float:
        return degrees * (math.pi / 180)

    # Extract coordinates
    lat1, lon1 = map(to_radians, origin_coords)
    lat2, lon2 = map(to_radians, destination_coords)

    # Earth's radius in kilometers
    EARTH_RADIUS_KM = 6371.0

    # Calculate great-circle distance using the haversine formula
    dlon = lon2 - lon1
    dlat = lat2 - lat1

    a = (
        math.sin(dlat / 2) ** 2
        + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2
    )
    c = 2 * math.asin(math.sqrt(a))
    distance = EARTH_RADIUS_KM * c

    # Add 10% to account for non-direct routes and air traffic controls
    actual_distance = distance * 1.1

    # Calculate flight time
    # Add 1 hour for takeoff and landing procedures
    flight_time = (actual_distance / cruising_speed_kmh) + 1.0

    # Format the results
    return round(flight_time, 2)


print(calculate_cargo_travel_time((41.8781, -87.6298), (-33.8688, 151.2093)))

设置智能体

对于模型提供商,我们使用 Together AI,它是 Hub 上新的推理提供商之一!

GoogleSearchTool 使用 Serper API 进行网页搜索,因此需要设置环境变量 SERPAPI_API_KEY 并传递 provider="serpapi",或者设置 SERPER_API_KEY 并传递 provider=serper

如果你没有设置任何 Serp API 提供商,你可以使用 DuckDuckGoSearchTool,但请注意它有速率限制。

import os
from PIL import Image
from smolagents import CodeAgent, GoogleSearchTool, InferenceClientModel, VisitWebpageTool

model = InferenceClientModel(model_id="Qwen/Qwen2.5-Coder-32B-Instruct", provider="together")

我们可以先创建一个简单的智能体作为基线,给我们一份简单的报告。

task = """Find all Batman filming locations in the world, calculate the time to transfer via cargo plane to here (we're in Gotham, 40.7128° N, 74.0060° W), and return them to me as a pandas dataframe.
Also give me some supercar factories with the same cargo plane transfer time."""
agent = CodeAgent(
    model=model,
    tools=[GoogleSearchTool("serper"), VisitWebpageTool(), calculate_cargo_travel_time],
    additional_authorized_imports=["pandas"],
    max_steps=20,
)
result = agent.run(task)
result

在我们的例子中,它生成了这个输出

|  | Location                                             | Travel Time to Gotham (hours) |
|--|------------------------------------------------------|------------------------------|
| 0  | Necropolis Cemetery, Glasgow, Scotland, UK         | 8.60                         |
| 1  | St. George's Hall, Liverpool, England, UK         | 8.81                         |
| 2  | Two Temple Place, London, England, UK             | 9.17                         |
| 3  | Wollaton Hall, Nottingham, England, UK           | 9.00                         |
| 4  | Knebworth House, Knebworth, Hertfordshire, UK    | 9.15                         |
| 5  | Acton Lane Power Station, Acton Lane, Acton, UK  | 9.16                         |
| 6  | Queensboro Bridge, New York City, USA            | 1.01                         |
| 7  | Wall Street, New York City, USA                  | 1.00                         |
| 8  | Mehrangarh Fort, Jodhpur, Rajasthan, India       | 18.34                        |
| 9  | Turda Gorge, Turda, Romania                      | 11.89                        |
| 10 | Chicago, USA                                     | 2.68                         |
| 11 | Hong Kong, China                                 | 19.99                        |
| 12 | Cardington Studios, Northamptonshire, UK        | 9.10                         |
| 13 | Warner Bros. Leavesden Studios, Hertfordshire, UK | 9.13                         |
| 14 | Westwood, Los Angeles, CA, USA                  | 6.79                         |
| 15 | Woking, UK (McLaren)                             | 9.13                         |

我们可以通过加入一些专门的规划步骤和添加更多的提示来稍微改进这一点。

规划步骤允许智能体提前思考并计划其下一步行动,这对于更复杂的任务非常有用。

agent.planning_interval = 4

detailed_report = agent.run(f"""
You're an expert analyst. You make comprehensive reports after visiting many websites.
Don't hesitate to search for many queries at once in a for loop.
For each data point that you find, visit the source url to confirm numbers.

{task}
""")

print(detailed_report)
detailed_report

在我们的例子中,它生成了这个输出

|  | Location                                         | Travel Time (hours) |
|--|--------------------------------------------------|---------------------|
| 0  | Bridge of Sighs, Glasgow Necropolis, Glasgow, UK | 8.6                 |
| 1  | Wishart Street, Glasgow, Scotland, UK         | 8.6                 |

благодаря этим быстрым изменениям, мы получили гораздо более лаконичный отчет, просто предоставив нашему агенту подробную подсказку и дав ему возможности планирования!

模型的上下文窗口很快就会被填满。因此,如果我们要求智能体将详细搜索的结果与另一个搜索结果相结合,它会变得更慢,并迅速增加 token 数量和成本

➡️ 我们需要改进我们系统的结构。

✌️ 将任务分配给两个智能体

多智能体结构允许在不同子任务之间分离记忆,这有两个巨大的好处

  • 每个智能体更专注于其核心任务,因此性能更高
  • 分离记忆减少了每一步的输入 token 数量,从而降低了延迟和成本。

让我们创建一个团队,其中包含一个专门的网页搜索智能体,由另一个智能体管理。

管理者智能体应该具备绘图能力来撰写最终报告:因此,让我们给它访问额外的导入库,包括 `plotly`,以及用于空间绘图的 `geopandas` + `shapely`。

model = InferenceClientModel(
    "Qwen/Qwen2.5-Coder-32B-Instruct", provider="together", max_tokens=8096
)

web_agent = CodeAgent(
    model=model,
    tools=[
        GoogleSearchTool(provider="serper"),
        VisitWebpageTool(),
        calculate_cargo_travel_time,
    ],
    name="web_agent",
    description="Browses the web to find information",
    verbosity_level=0,
    max_steps=10,
)

管理者智能体需要进行一些繁重的脑力劳动。

所以我们给它更强大的模型 DeepSeek-R1,并加入一个 `planning_interval`。

from smolagents.utils import encode_image_base64, make_image_url
from smolagents import OpenAIServerModel


def check_reasoning_and_plot(final_answer, agent_memory):
    multimodal_model = OpenAIServerModel("gpt-4o", max_tokens=8096)
    filepath = "saved_map.png"
    assert os.path.exists(filepath), "Make sure to save the plot under saved_map.png!"
    image = Image.open(filepath)
    prompt = (
        f"Here is a user-given task and the agent steps: {agent_memory.get_succinct_steps()}. Now here is the plot that was made."
        "Please check that the reasoning process and plot are correct: do they correctly answer the given task?"
        "First list reasons why yes/no, then write your final decision: PASS in caps lock if it is satisfactory, FAIL if it is not."
        "Don't be harsh: if the plot mostly solves the task, it should pass."
        "To pass, a plot should be made using px.scatter_map and not any other method (scatter_map looks nicer)."
    )
    messages = [
        {
            "role": "user",
            "content": [
                {
                    "type": "text",
                    "text": prompt,
                },
                {
                    "type": "image_url",
                    "image_url": {"url": make_image_url(encode_image_base64(image))},
                },
            ],
        }
    ]
    output = multimodal_model(messages).content
    print("Feedback: ", output)
    if "FAIL" in output:
        raise Exception(output)
    return True


manager_agent = CodeAgent(
    model=InferenceClientModel("deepseek-ai/DeepSeek-R1", provider="together", max_tokens=8096),
    tools=[calculate_cargo_travel_time],
    managed_agents=[web_agent],
    additional_authorized_imports=[
        "geopandas",
        "plotly",
        "shapely",
        "json",
        "pandas",
        "numpy",
    ],
    planning_interval=5,
    verbosity_level=2,
    final_answer_checks=[check_reasoning_and_plot],
    max_steps=15,
)

让我们来看看这个团队是什么样子的

manager_agent.visualize()

这将生成类似这样的东西,帮助我们理解智能体和所用工具之间的结构和关系

CodeAgent | deepseek-ai/DeepSeek-R1
├── ✅ Authorized imports: ['geopandas', 'plotly', 'shapely', 'json', 'pandas', 'numpy']
├── 🛠️ Tools:
│   ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
│   ┃ Name                        ┃ Description                           ┃ Arguments                             ┃
│   ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│   │ calculate_cargo_travel_time │ Calculate the travel time for a cargo │ origin_coords (`array`): Tuple of     │
│   │                             │ plane between two points on Earth     │ (latitude, longitude) for the         │
│   │                             │ using great-circle distance.          │ starting point                        │
│   │                             │                                       │ destination_coords (`array`): Tuple   │
│   │                             │                                       │ of (latitude, longitude) for the      │
│   │                             │                                       │ destination                           │
│   │                             │                                       │ cruising_speed_kmh (`number`):        │
│   │                             │                                       │ Optional cruising speed in km/h       │
│   │                             │                                       │ (defaults to 750 km/h for typical     │
│   │                             │                                       │ cargo planes)                         │
│   │ final_answer                │ Provides a final answer to the given  │ answer (`any`): The final answer to   │
│   │                             │ problem.                              │ the problem                           │
│   └─────────────────────────────┴───────────────────────────────────────┴───────────────────────────────────────┘
└── 🤖 Managed agents:
    └── web_agent | CodeAgent | Qwen/Qwen2.5-Coder-32B-Instruct
        ├── ✅ Authorized imports: []
        ├── 📝 Description: Browses the web to find information
        └── 🛠️ Tools:
            ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
            ┃ Name                        ┃ Description                       ┃ Arguments                         ┃
            ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
            │ web_search                  │ Performs a google web search for  │ query (`string`): The search      │
            │                             │ your query then returns a string  │ query to perform.                 │
            │                             │ of the top search results.        │ filter_year (`integer`):          │
            │                             │                                   │ Optionally restrict results to a  │
            │                             │                                   │ certain year                      │
            │ visit_webpage               │ Visits a webpage at the given url │ url (`string`): The url of the    │
            │                             │ and reads its content as a        │ webpage to visit.                 │
            │                             │ markdown string. Use this to      │                                   │
            │                             │ browse webpages.                  │                                   │
            │ calculate_cargo_travel_time │ Calculate the travel time for a   │ origin_coords (`array`): Tuple of │
            │                             │ cargo plane between two points on │ (latitude, longitude) for the     │
            │                             │ Earth using great-circle          │ starting point                    │
            │                             │ distance.                         │ destination_coords (`array`):     │
            │                             │                                   │ Tuple of (latitude, longitude)    │
            │                             │                                   │ for the destination               │
            │                             │                                   │ cruising_speed_kmh (`number`):    │
            │                             │                                   │ Optional cruising speed in km/h   │
            │                             │                                   │ (defaults to 750 km/h for typical │
            │                             │                                   │ cargo planes)                     │
            │ final_answer                │ Provides a final answer to the    │ answer (`any`): The final answer  │
            │                             │ given problem.                    │ to the problem                    │
            └─────────────────────────────┴───────────────────────────────────┴───────────────────────────────────┘
manager_agent.run("""
Find all Batman filming locations in the world, calculate the time to transfer via cargo plane to here (we're in Gotham, 40.7128° N, 74.0060° W).
Also give me some supercar factories with the same cargo plane transfer time. You need at least 6 points in total.
Represent this as spatial map of the world, with the locations represented as scatter points with a color that depends on the travel time, and save it to saved_map.png!

Here's an example of how to plot and return a map:
import plotly.express as px
df = px.data.carshare()
fig = px.scatter_map(df, lat="centroid_lat", lon="centroid_lon", text="name", color="peak_hour", size=100,
     color_continuous_scale=px.colors.sequential.Magma, size_max=15, zoom=1)
fig.show()
fig.write_image("saved_image.png")
final_answer(fig)

Never try to process strings using code: when you have a string to read, just print it and you'll see it.
""")

我不知道你的运行结果如何,但在我的运行中,管理者智能体熟练地将任务分配给网页智能体,首先是 `1. 搜索蝙蝠侠拍摄地点`,然后是 `2. 寻找超级跑车工厂`,最后汇总列表并绘制地图。

让我们通过直接检查智能体状态来看看地图长什么样

manager_agent.python_executor.state["fig"]

这将输出地图

Multiagent system example output map

资源

< > 在 GitHub 上更新