介绍 smolagents,一个构建智能体的简单库

55 阅读11分钟

介绍 smolagents,一个构建智能体的简单库

今天我们推出了smolagents,这是一个非常简单的库,它为语言模型提供了智能体能力。让我们先来看一个示例:

from smolagents import CodeAgent, DuckDuckGoSearchTool, HfApiModel

agent = CodeAgent(tools=[DuckDuckGoSearchTool()], model=HfApiModel())

agent.run("一只猎豹全速奔跑穿过艺术桥需要多少秒?")

image.png

目录

🤔 什么是智能体?

任何高效的AI系统都需要为语言模型提供某种形式的真实世界访问能力:例如,调用搜索工具获取外部信息,或者操作特定程序来解决任务。换句话说,语言模型应该具有_智能体能力_。智能体程序是语言模型通向外部世界的门户。

人工智能智能体是由语言模型输出控制工作流程的程序

任何使用语言模型的系统都会将语言模型的输出集成到代码中。语言模型输入对代码工作流的影响程度就是系统中语言模型的智能体能力水平。

需要注意的是,按照这个定义,"智能体"并不是一个非0即1的概念:相反,"智能体能力"是在一个连续的谱系上发展的,这取决于你赋予语言模型在工作流程中的权力大小。

下表说明了智能体能力在不同系统中的变化:

智能体能力级别描述如何称呼示例模式
☆☆☆语言模型输出对程序流程没有影响简单处理器process_llm_output(llm_response)
★☆☆语言模型输出决定基本控制流程路由器if llm_decision(): path_a() else: path_b()
★★☆语言模型输出决定函数执行工具调用run_function(llm_chosen_tool, llm_chosen_args)
★★★语言模型输出控制迭代和程序继续执行多步骤智能体while llm_should_continue(): execute_next_step()
★★★一个智能体工作流可以启动另一个智能体工作流多智能体if llm_trigger(): execute_agent()

多步骤智能体具有以下代码结构:

    memory = [user_defined_task]
    while llm_should_continue(memory): # 这是多步骤部分的循环
        action = llm_get_next_action(memory) # 这是工具调用部分
        observations = execute_action(action)
        memory += [action, observations]

因此,这个系统在一个循环中运行,在每一步执行一个新的动作(动作可能涉及调用一些预定义的工具,它们只是函数),直到它的观察结果表明已经达到了解决给定任务的满意状态。以下是一个多步骤智能体如何解决一个简单数学问题的示例:

Agent_ManimCE.gif

✅ 何时使用智能体 / ⛔ 何时避免使用

当你需要语言模型来决定应用程序的工作流程时,智能体就很有用。但它们经常被过度使用。关键问题是:我真的需要工作流程的灵活性来有效解决手头的任务吗?如果预定义的工作流程经常不够用,那就意味着你需要更多的灵活性。让我们举个例子:假设你正在开发一个处理冲浪旅行网站客户请求的应用程序。

你可以预先知道请求会属于以下2个类别(基于用户选择),并且你为这2种情况都有预定义的工作流程。

  1. 想了解旅行相关知识?⇒ 给他们提供搜索栏来搜索你的知识库
  2. 想要联系销售?⇒ 让他们填写联系表单

如果这种确定性的工作流程适用于所有查询,那就尽管直接编码吧!这将给你一个100%可靠的系统,不会因为让不可预测的语言模型介入工作流程而引入错误风险。为了简单性和稳健性,建议尽量不使用任何智能体行为。

但如果工作流程无法很好地预先确定呢?

例如,用户想问:"我周一可以来,但我忘记带护照了所以可能要推迟到周三,周二早上是否可以带着我的装备去冲浪,并购买取消保险?"这个问题涉及多个因素,可能上述预定义的标准都不足以处理这个请求。

如果预定义的工作流程经常不够用,那就意味着你需要更多的灵活性。

这就是智能体设置发挥作用的地方。

在上面的例子中,你可以创建一个多步骤智能体,它可以访问天气API获取天气预报,Google Maps API计算行程距离,员工可用性仪表板和基于知识库的RAG系统。

直到最近,计算机程序还局限于预定义的工作流程,试图通过堆积if/else开关来处理复杂性。它们专注于极其狭窄的任务,比如"计算这些数字的和"或"在这个图中找到最短路径"。但实际上,大多数现实生活中的任务,就像我们上面的旅行示例,并不适合预定义的工作流程。智能体系统为程序打开了通向现实世界任务的广阔天地!

代码智能体

在多步骤智能体中,每一步,语言模型都可以编写一个动作,以调用外部工具的形式。编写这些动作的常见格式(被Anthropic、OpenAI和许多其他公司使用)通常是"将动作写成工具名称和要使用的参数的JSON,然后你解析它来知道要执行哪个工具以及使用哪些参数"的不同变体。

多篇 研究 论文表明,让工具调用语言模型使用代码形式要好得多。

原因很简单,因为_我们精心设计的编程语言就是为了成为表达计算机执行动作的最佳方式_。如果JSON片段是更好的表达方式,那JSON就会成为最主流的编程语言,编程也将成为人间地狱。

下图取自Executable Code Actions Elicit Better LLM Agents,展示了用代码编写动作的一些优势:

code_vs_json_actions.png

相比JSON形式的片段,用代码编写动作提供了更好的:

  • 可组合性: 你能像定义Python函数那样将JSON动作相互嵌套,或定义一组JSON动作以供后续重用吗?
  • 对象管理: 在JSON中如何存储像generate_image这样的动作的输出?
  • 通用性: 代码是为了简单地表达任何你可以让计算机做的事情而构建的。
  • 在语言模型训练数据中的表示: 大量高质量的代码动作已经包含在语言模型的训练数据中,这意味着它们已经为此进行了训练!

介绍 smolagents:让智能体变得简单 🥳

我们构建smolagents时有以下目标:

简单性:智能体的逻辑仅用几千行代码就能实现(参见这个文件)。我们将抽象保持在最小化的原始代码之上!

🧑‍💻 代码智能体的一流支持,即用代码编写动作的智能体(与"用于编写代码的智能体"相对)。为了确保安全性,我们通过E2B支持在沙箱环境中执行。

🤗 Hub集成:你可以在Hub上共享和加载工具,未来还会有更多功能!

🌐 支持任何语言模型:它支持在Hub上托管的模型,可以通过它们的transformers版本或通过我们的推理API加载,同时通过我们的LiteLLM集成也支持来自OpenAI、Anthropic和许多其他公司的模型。

smolagentstransformers.agents的继任者,随着transformers.agents在未来被弃用,它将取而代之。

构建智能体

要构建智能体,你至少需要两个元素:

  • tools:智能体可以访问的工具列表
  • model:作为智能体引擎的语言模型

对于model,你可以使用任何语言模型,可以使用我们的HfApiModel类来使用开源模型,它利用Hugging Face的免费推理API(如上面猎豹示例所示),或者你可以使用LiteLLMModel来利用litellm并从100多个不同的云语言模型中选择。

对于工具,你只需要创建一个带有输入和输出类型提示的函数,并在文档字符串中提供输入说明,然后使用@tool装饰器将其变成工具。

以下是如何创建一个获取Google Maps旅行时间的自定义工具,并将其用于旅行规划智能体:

from typing import Optional
from smolagents import CodeAgent, HfApiModel, tool

@tool
def get_travel_duration(start_location: str, destination_location: str, transportation_mode: Optional[str] = None) -> str:
    """获取两个地点之间的旅行时间。

    Args:
        start_location: 出发地点
        destination_location: 目的地
        transportation_mode: 交通方式,可选'driving'(驾车)、'walking'(步行)、'bicycling'(骑行)或'transit'(公共交通)。默认为'driving'。
    """
    import os   # 所有导入都放在函数内,以便于在Hub上共享
    import googlemaps
    from datetime import datetime

    gmaps = googlemaps.Client(os.getenv("GMAPS_API_KEY"))

    if transportation_mode is None:
        transportation_mode = "driving"
    try:
        directions_result = gmaps.directions(
            start_location,
            destination_location,
            mode=transportation_mode,
            departure_time=datetime(2025, 6, 6, 11, 0), # 在11点,未来的日期
        )
        if len(directions_result) == 0:
            return "使用指定的交通方式无法找到这些地点之间的路线。"
        return directions_result[0]["legs"][0]["duration"]["text"]
    except Exception as e:
        print(e)
        return e

agent = CodeAgent(tools=[get_travel_duration], model=HfApiModel(), additional_authorized_imports=["datetime"])

agent.run("能给我规划一个巴黎一日游的行程吗?包括几个景点和时间安排?可以是市内或市外的景点,但要能在一天内完成。我只使用租借的自行车出行。")

经过几个步骤收集旅行时间并进行计算后,智能体返回了以下最终建议:

巴黎一日自行车游行程安排:
1. 上午9:00在埃菲尔铁塔开始。
2. 在埃菲尔铁塔游览至10:30。
3. 10:46到达巴黎圣母院。
4. 在巴黎圣母院游览至12:16。
5. 12:41到达蒙马特。
6. 在蒙马特游览至14:11。
7. 14:33到达卢森堡公园。
8. 在卢森堡公园游览至16:03。
9. 16:12到达卢浮宫。
10. 在卢浮宫游览至17:42。
11. 午餐休息至18:12。
12. 计划结束时间:18:12。

构建完工具后,将其共享到Hub非常简单:

get_travel_duration.push_to_hub("{your_username}/get-travel-duration-tool")

你可以在这个空间查看结果。你可以在空间中的tool.py文件中查看工具的逻辑。如你所见,该工具实际上被导出为一个继承自Tool类的类,这是我们所有工具的底层结构。

开源模型在智能体工作流中的表现如何?

我们使用一些领先的模型创建了CodeAgent实例,并在这个基准测试上进行了比较,该基准测试从几个不同的基准中收集问题,提供了各种挑战的混合。

在这里查看基准测试以了解更多关于所使用的智能体设置的详细信息,并查看代码智能体与工具调用智能体的比较(剧透:代码方式效果更好)。

benchmark_code_agents.png

这个比较表明开源模型现在可以与最好的闭源模型相媲美!

下一步 🚀