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

发布于 2024 年 12 月 31 日
在 GitHub 上更新

今天我们发布了 smolagents,这是一个非常简单的库,可为语言模型解锁智能体能力。以下是它的一些亮点:

from smolagents import CodeAgent, DuckDuckGoSearchTool, HfApiModel

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

agent.run("How many seconds would it take for a leopard at full speed to run through Pont des Arts?")

目录

🤔 什么是智能体?

任何高效使用 AI 的系统都需要为 LLM 提供对现实世界的某种访问权限:例如,调用搜索工具以获取外部信息,或对某些程序进行操作以解决任务的可能性。换句话说,LLM 应该拥有**代理能力**。智能体程序是 LLM 通往外部世界的门户。

AI 智能体是**LLM 输出控制工作流的程序**。

任何利用 LLM 的系统都会将 LLM 输出集成到代码中。LLM 输入对代码工作流的影响是 LLM 在系统中的代理能力水平。

请注意,根据此定义,“智能体”不是一个离散的 0 或 1 定义:相反,“代理能力”在一个连续的范围内演变,具体取决于您赋予 LLM 对工作流的权力大小。

下表说明了不同系统中代理能力的变化

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

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

memory = [user_defined_task]
while llm_should_continue(memory): # this loop is the multi-step part
    action = llm_get_next_action(memory) # this is the tool-calling part
    observations = execute_action(action)
    memory += [action, observations]

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

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

当您需要 LLM 来确定应用程序的工作流时,智能体非常有用。但它们通常是杀鸡用牛刀。问题是:我是否真的需要工作流的灵活性才能有效地解决手头的任务?如果预定的工作流经常不足,则意味着您需要更大的灵活性。让我们举一个例子:假设您正在开发一个处理冲浪旅行网站客户请求的应用程序。

您可以提前知道请求将属于两个类别中的一个(基于用户选择),并且您为这两个类别中的每个类别都有预定义的工作流。

  1. 想了解旅行信息?⇒ 让他们使用搜索栏搜索您的知识库
  2. 想与销售人员交谈?⇒ 让他们填写联系表格。

如果这种确定性工作流适用于所有查询,那么请尽情编写所有代码!这将为您提供一个 100% 可靠的系统,没有任何由于让不可预测的 LLM 干预您的工作流而引入的错误风险。为了简单和健壮性,建议避免使用任何代理行为。

但是,如果工作流无法提前确定得那么好呢?

例如,用户想问:“我星期一可以来,但我忘了带护照,所以可能会延迟到星期三,星期二早上可以带我和我的东西去冲浪,并购买取消保险吗?”这个问题取决于许多因素,上面预先确定的任何标准可能都不足以满足此请求。

如果预定的工作流经常不足,则意味着您需要更大的灵活性。

这就是代理设置有帮助的地方。

在上面的例子中,您只需创建一个多步代理,它可以访问天气 API 以获取天气预报、Google Maps API 以计算旅行距离、员工可用性仪表板以及知识库上的 RAG 系统。

直到最近,计算机程序还局限于预设的工作流,试图通过堆积 if/else 开关来处理复杂性。它们专注于极其狭窄的任务,例如“计算这些数字的总和”或“找到此图中的最短路径”。但实际上,大多数现实生活中的任务,例如我们上面提到的旅行示例,都不适合预设的工作流。代理系统为程序打开了广阔的现实世界任务!

代码智能体

在多步智能体中,在每一步,LLM 都可以以调用外部工具的形式编写一个动作。一种常见的(Anthropic、OpenAI 和许多其他公司使用的)编写这些动作的格式通常是不同程度的“将动作编写为工具名称和要使用的参数的 JSON,然后您解析该 JSON 以了解要执行哪个工具以及使用哪个参数”。

多篇 研究 论文 表明,在代码中调用 LLM 工具效果更好。

其原因很简单,我们**专门设计了我们的代码语言,使其成为表达计算机执行操作的最佳方式**。如果 JSON 片段是更好的表达方式,那么 JSON 将是顶级的编程语言,而编程将是人间地狱。

下图摘自 可执行代码操作能激发更好的 LLM 智能体,说明了在代码中编写操作的一些优点

在代码中而不是 JSON 类似片段中编写操作提供了更好的

  • **可组合性:** 您可以将 JSON 操作相互嵌套,或者定义一组 JSON 操作供以后重用,就像您可以定义一个 Python 函数一样吗?
  • **对象管理:** 如何在 JSON 中存储 generate_image 等操作的输出?
  • **通用性:** 代码旨在简单地表达计算机可以做的任何事情。
  • **LLM 训练数据中的表示:** 大量高质量的代码操作已包含在 LLM 的训练数据中,这意味着它们已经为此进行了训练!

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

我们构建 smolagents 的目标是

✨ **简单性**:智能体的逻辑代码只有约数千行(请参阅 此文件)。我们将抽象层保持在原始代码之上的最小形态!

🧑‍💻 **一流的代码智能体支持**,即编写代码动作的智能体(而不是“用于编写代码的智能体”)。为了确保安全,我们通过 E2B 支持在沙盒环境中执行。

🤗 **Hub 集成**:您可以将工具分享到 Hub 并从 Hub 加载工具,未来还会更多!

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

smolagentstransformers.agents 的继任者,未来将取代它,因为 transformers.agents 将被弃用。

构建智能体

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

  • tools:智能体可以访问的工具列表
  • model:将作为智能体引擎的 LLM。

对于 model,您可以使用任何 LLM,无论是使用我们利用 Hugging Face 免费推理 API 的 HfApiModel 类的开放模型(如上面的豹子示例所示),还是可以使用 LiteLLMModel 来利用 litellm 并从 100 多个不同的云 LLM 中进行选择。

对于工具,您只需创建一个带有输入和输出类型提示的函数,以及提供输入描述的文档字符串,然后使用 @tool 装饰器将其转换为工具。

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

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:
    """Gets the travel time between two places.

    Args:
        start_location: the place from which you start your ride
        destination_location: the place of arrival
        transportation_mode: The transportation mode, in 'driving', 'walking', 'bicycling', or 'transit'. Defaults to 'driving'.
    """
    import os   # All imports are placed within the function, to allow for sharing to 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), # At 11, date far in the future
        )
        if len(directions_result) == 0:
            return "No way found between these places with the required transportation mode."
        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("Can you give me a nice one-day trip around Paris with a few locations and the times? Could be in the city or outside, but should fit in one day. I'm travelling only with a rented bicycle.")

在收集旅行时间和运行计算后,智能体返回最终方案

One-day Paris bike trip itinerary:
1. Start at Eiffel Tower at 9:00 AM.
2. Sightseeing at Eiffel Tower until 10:30 AM.
3. Travel to Notre-Dame Cathedral at 10:46 AM.
4. Sightseeing at Notre-Dame Cathedral until 12:16 PM.
5. Travel to Montmartre at 12:41 PM.
6. Sightseeing at Montmartre until 2:11 PM.
7. Travel to Jardin du Luxembourg at 2:33 PM.
8. Sightseeing at Jardin du Luxembourg until 4:03 PM.
9. Travel to Louvre Museum at 4:12 PM.
10. Sightseeing at Louvre Museum until 5:42 PM.
11. Lunch break until 6:12 PM.
12. Planned end time: 6:12 PM.

构建工具后,将其分享到 Hub 就像这样简单

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

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

开放模型在智能体工作流中的能力如何?

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

在此处查找基准测试 以获取有关所用代理设置的更多详细信息,并查看代码代理与工具调用代理的比较(剧透:代码效果更好)。

benchmark of different models on agentic workflows

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

下一步 🚀

社区

这是一篇很棒的博客文章!

谢谢你澄清了智能体到底是什么,
顺便说一下,这是一篇很棒的帖子,使用智能体解决现实世界的问题将很有趣

/me 接着制作了一个简单的智能体,用于获取随机的 XKCD 漫画。看看吧!😄 👉 http://github.com/hemanth/notebooks/blob/main/notebooks/smolagents.ipynb
image.png

嗨,顺便说一句,这是一个很棒的项目,但我有一个小问题,

我正在将 smolagents 与 QWEN 32B 模型(通过 CodeAgent)一起使用,并遇到了生成代码的问题。有时,代理生成的代码包含对 final_answer 的多次调用,其中一次出现在脚本中间。由于 final_answer 旨在表示执行结束,因此其第一次出现之后的所有代码(例如,像 task_to_do() 这样的调用)都不会执行。

我尝试过什么

  • 我曾尝试“教导”模型避免将 final_answer 放在代码中间,但这种方法不可靠。

我的目标是

  • 在 smolagents 中拥有一个执行模型或变通方法,以确保完整的代码块执行,即使生成了过早的 final_answer

问题
smolagents 中是否存在可以允许所有代码执行(即使 final_answer 被提前调用)的代理配置、变通方法或最佳实践?任何指导或建议都将不胜感激。

先谢谢了!

·

是的,同样的问题

这很棒,通过智能体课程了解了这一点。

太棒了

感谢您以这种方式介绍这个领域,它让人们迫不及待地想深入探索!

这对于学习者来说非常容易,他们可以相应地提升自己的技能。

谢谢。

注册登录 发表评论