Hugging Face 发布 smolagents:用代码编写动作的轻量智能体库
Introducing smolagents: simple agents that write actions in code.
Hugging Face 发布 smolagents 库,让语言模型具备智能体能力,核心是 CodeAgent——智能体用代码而非 JSON 编写动作,并支持通过 E2B 在沙箱环境执行。
Hugging Face 官方介绍 smolagents 库的 CodeAgent 设计与上手方式,可作为编写代码动作智能体的参考。
今天我们推出 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 来决定应用的工作流时,智能体很有用。但它们往往是大材小用。问题是:我真的需要工作流的灵活性来高效解决手头的任务吗? 如果预先确定的工作流经常不够用,那就意味着你需要更多灵活性。 举个例子:假设你在为一个冲浪旅行网站制作一个处理客户请求的应用。
你可以预先知道请求将属于两类之一(基于用户选择),并且你为这两种情况都预定义了工作流。
- 想了解一些旅行知识?⇒ 让他们访问一个搜索栏,以搜索你的知识库
- 想联系销售?⇒ 让他们填写联系表单。
如果这个确定性工作流适合所有查询,那就尽管全部用代码实现!这将为你提供一个 100% 可靠的系统,并且没有让不可预测的 LLM 插手你的工作流而引入错误的风险。为了简单和稳健,建议尽量规范化,避免使用任何智能体行为。
但如果工作流无法提前很好地确定呢?
例如,一位用户想询问:"I can come on Monday, but I forgot my passport so risk being delayed to Wednesday, is it possible to take me and my stuff to surf on Tuesday morning, with a cancellation insurance?" 这个问题取决于许多因素,而且上述预先确定的标准可能都不足以应对这个请求。
如果预先确定的工作流经常不够用,那就意味着你需要更高的灵活性。
这正是代理式(agentic)设置能派上用场的地方。
在上面的例子中,你完全可以构建一个多步骤代理,让它能够访问用于天气预报的天气 API、用于计算行程距离的 Google Maps API、一个员工可用性仪表板,以及基于你的知识库的 RAG 系统。
直到最近,计算机程序仍受限于预先确定的工作流,试图通过堆积大量的 if/else 分支来应对复杂性。它们专注于极其狭窄的任务,比如“计算这些数字的和”或“找到该图中的最短路径”。但实际上,大多数现实生活中的任务,比如我们上面的旅行例子,并不适合预先确定的工作流。代理式系统为程序打开了现实世界任务的广阔天地!
在多步骤代理中,每一步,LLM 都可以以调用外部工具的形式编写一个动作。用于编写这些动作的一种常见格式(被 Anthropic、OpenAI 及许多其他公司使用)大体上都是“将动作写成工具名称和要使用参数的 JSON,然后你解析它以了解要执行哪个工具以及使用哪些参数”的不同变体。
多篇 研究 论文 已经表明,让工具调用 LLM 以代码形式进行要好得多。
原因很简单,我们专门打造了我们的代码语言,使其成为表达计算机所执行动作的最佳可能方式。如果 JSON 片段是更好的表达方式,那么 JSON 就会成为顶级编程语言,而编程将会是人间的炼狱。
下图取自 Executable Code Actions Elicit Better LLM Agents,展示了用代码编写动作的一些优势:

用代码而非类似 JSON 的片段编写动作,可以提供更好的:
- 可组合性:你能像直接定义一个 Python 函数那样,把 JSON 动作相互嵌套,或者定义一组 JSON 动作以便以后复用吗?
- 对象管理:你如何在 JSON 中存储像
generate_image这样的动作的输出? - 通用性:代码的构建就是为了简单地表达你能让计算机做的任何事情。
- 在 LLM 训练数据中的表示:LLM 的训练数据中已经包含了大量高质量的代码动作,这意味着它们已经为此接受了训练!
推出 smolagents:让代理变得简单 🥳
我们基于这些目标构建了 smolagents:
✨ 简单性:代理的逻辑只需约几千行代码(参见 此文件)。我们将抽象保持在原始代码之上的最小形态!
🧑💻 对代码代理的一流支持,即以代码编写其动作的代理(而不是“用于编写代码的代理”)。为了使其安全,我们支持通过 E2B 在沙箱环境中执行。
- 在此
CodeAgent类之上,我们仍然支持标准的ToolCallingAgent,它把动作写为 JSON/文本块。
🤗 Hub 集成:你可以向 Hub 分享工具或从 Hub 加载工具,未来还会有更多功能!
🌐 支持任何 LLM:它支持托管在 Hub 上并以 transformers 版本加载的模型,或通过我们的推理 API 加载的模型,但也通过我们的 LiteLLM 集成支持来自 OpenAI、Anthropic 及许多其他公司的模型。
smolagents 是 transformers.agents 的后继者,并将在 transformers.agents 未来被弃用时代替它。
构建一个代理
要构建一个代理,你至少需要两个元素:
tools:代理可以访问的工具列表model:将作为代理引擎的 LLM。
对于 model,你可以使用任何 LLM,无论是使用我们的 HfApiModel 类来利用 HF Mirror 的免费推理 API 的开源模型(如上面的豹示例所示),还是使用 LiteLLMModel 来利用 litellm 并从 100 多种不同的云 LLM 中选择。
对于工具,你只需创建一个带有输入和输出类型提示的函数,并用文档字符串提供输入描述,然后使用 @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:
"""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 实例,并在 这个基准 上进行了比较,该基准从几个不同的基准中收集问题,以提出各种各样的挑战。
在这里找到基准 以获取有关所用代理设置更多细节,并查看代码代理与工具调用代理的比较(剧透:代码效果更好)。
这个比较表明,开源模型现在可以挑战最好的闭源模型!
下一步 🚀
来源:Hugging Face Blog · hf-mirror.com