上周,我正在写一个研究自动化脚本。我需要个能自己搜索网页、抓取实时数据,并根据这些数据做决定的工具——整个过程完全不需要我一步步盯着。我一开始还是走老路子用 LangChain,但跟链式定义、回调处理程序还有各种导入错误死磕了一个小时后,我发现自己光是为了搭个基础管线就写了 200 行代码。肯定有更简单的办法。
就在这时候,同事给我推荐了 smolagents,这是 Hugging Face 出的一个极简 Agent 库。它整个 Agent 的核心逻辑大概只有 1000 行代码。不是一万,也不是十万,就是一千行。我一开始挺怀疑的——通常说“极简”,潜台词就是“以后需要的功能肯定没有”——但我还是决定试一把。以下是我用它搭第一个 Agent 时学到的经验,包括中间踩过的一些坑。
为什么 smolagents 吸引了我
smolagents 最核心的差异化在于 Agent 推理行动的方式。大多数 Agent 框架都是生成 JSON 或结构化文本来决定调用哪个工具。Smolagents 却换了个路子:它的 Agent 是代码 Agent,意思是它们会直接写真正的 Python 代码片段来编排自己的行动。
听起来是个小细节,但实际影响巨大。当 Agent 写代码而不是 JSON 时,它就能写循环、用 try/except 处理错误、定义变量,还能很自然地组合各种操作。这就好比填表和写脚本的区别。Agent 可以尽情发挥 Python 的全部表达力。
除此之外,smolagents 不绑定特定模型。OpenAI、Anthropic、Hugging Face 自家的模型,甚至本地模型它都支持。它还支持多模态输入(文本、视觉、视频、音频),并且能跟 LangChain 的工具、Anthropic 的 MCP 以及 Hugging Face Spaces 完美配合。
搭建环境
首先——安装:
pip install smolagents
这就完了。不用折腾额外的依赖,也没有复杂的构建步骤。比起我平时搭框架的套路,这已经让我小惊喜了一把。
现在,在写 Agent 代码之前,你需要先配置好模型提供方。Smolagents 需要一个大语言模型(LLM)来驱动推理,所以你得备好 API 密钥。我一开始用的是 OpenAI 的 GPT-4o,因为我手头正好有密钥:
import os
from smolagents import CodeAgent, DuckDuckGoSearchTool, OpenAIServerModel
model = OpenAIServerModel(
model_id="gpt-4o",
api_base="https://api.openai.com/v1",
api_key=os.environ["OPENAI_API_KEY"],
)
踩坑 #1: 我一开始图省事,把 API 密钥直接硬编码在脚本里了。千万别这么干,一定要用环境变量。我在 shell 配置文件里用 export OPENAI_API_KEY="sk-..." 设置好,然后通过 os.environ 来读取。这虽然是件小事,但我见过太多人一不小心就把密钥提交到 Git 里了。
如果你更喜欢 Anthropic,或者想白嫖 Hugging Face 的免费推理端点,smolagents 也完全支持。用 HfApiModel 类,你不需要 OpenAI 账号也能用上开源模型:
from smolagents import HfApiModel
model = HfApiModel(model_id="Qwen/Qwen2.5-Coder-32B-Instruct")
不到 10 行代码搭个极简 Agent
咱们先从最简单的 Agent 搞起——一个能联网搜索的 Agent:
agent = CodeAgent(
tools=[DuckDuckGoSearchTool()],
model=model,
)
result = agent.run("What is the current population of Tokyo?")
print(result)
跑一下这段代码,Agent 会:
- 读取你的问题
- 决定需要联网搜索
- 编写调用
DuckDuckGoSearchTool的 Python 代码 - 在沙盒环境中执行这段代码
- 返回答案
我第一次跑的时候,干坐在那等,心里犯嘀咕它到底在工作没。然后输出结果出来了——不仅有最终答案,还有完整的执行轨迹,展示了 Agent 写了什么代码、调用了什么工具,以及每一步的推理过程。这种透明度是 smolagents 最强大的特性之一。你真的能看清 Agent 在做什么以及为什么这么做,这对调试来说简直是无价之宝。
搭个自定义天气 Agent
内置的搜索工具确实不错,但真正的威力在于写你自己的工具。我决定搭一个能抓取实时数据的天气 Agent。下面是完整代码——大概 40 行:
import os
import requests
from smolagents import CodeAgent, Tool, OpenAIServerModel
class WeatherTool(Tool):
name = "get_weather"
description = "Gets the current weather for a given city using Open-Meteo API."
inputs = {
"city": {
"type": "string",
"description": "The city name to get weather for, e.g. 'London' or 'Tokyo'"
}
}
output_type = "string"
def forward(self, city: str) -> str:
# 首先,对城市进行地理编码以获取坐标
geocode_url = "https://geocoding-api.open-meteo.com/v1/search"
geo_response = requests.get(geocode_url, params={"name": city, "count": 1})
geo_data = geo_response.json()
if not geo_data.get("results"):
return f"Could not find location: {city}"
lat = geo_data["results"][0]["latitude"]
lon = geo_data["results"][0]["longitude"]
# 接着获取天气
weather_url = "https://api.open-meteo.com/v1/forecast"
weather_response = requests.get(weather_url, params={
"latitude": lat,
"longitude": lon,
"current_weather": True
})
weather_data = weather_response.json()
current = weather_data["current_weather"]
return (
f"Weather in {city}: {current['temperature']}°C, "
f"wind speed {current['windspeed']} km/h, "
f"weather code {current['weathercode']}"
)
# 设置 Agent
model = OpenAIServerModel(
model_id="gpt-4o",
api_base="https://api.openai.com/v1",
api_key=os.environ["OPENAI_API_KEY"],
)
agent = CodeAgent(
tools=[WeatherTool()],
model=model,
)
# 运行
result = agent.run("What's the weather like in Paris right now? Should I bring a jacket?")
print(result)
惊喜 #1: Agent 并没有只返回干巴巴的温度。因为我问了“需要带外套吗?”,它通过我的工具获取了天气数据,然后对结果进行了推理——得出结论:14°C 加上风,大概率得穿件外套。这就是代码 Agent 的威力:大模型能解读工具的输出并做出判断,而不是单纯地搬运数据。
踩坑 #2: 我一开始忘了在虚拟环境里装 requests 库。Agent 的代码执行沙盒捕获到了 ImportError 并给出了清晰的报错,但我愣是过了一分钟我才意识到问题不是出在 smolagents 本身。一定要确保你的环境里装好了你的工具所需的各种依赖。
搞懂工具的结构
我来拆解一下在 smolagents 里自定义工具是怎么跑起来的。每个工具都需要:
name:大模型用来调用该工具的简短标识符description:清晰说明该工具的功能——这非常关键,因为大模型要根据它来决定什么时候调用工具inputs:一个字典,定义了预期的参数、参数类型和描述output_type:工具的返回类型(通常是 "string")forward():实际的执行逻辑
description 字段值得特别关注。我一开始写得很模糊,类似“获取天气数据”。结果 Agent 有时候不用这个工具,因为它搞不清这工具到底啥时候该用。当我改成“获取指定城市的当前天气”后,只要遇到跟天气相关的查询,Agent 每次都能稳定调用。把你的工具描述当成 API 文档来写——描述越清晰,工具调用越靠谱。
幕后发生了什么
当你调用 agent.run() 时,流程是这样的:
- Agent 把你的查询和工具定义一起发给大模型
- 大模型编写调用相应工具的 Python 代码
- Smolagents 在沙盒环境中执行这段代码
- 执行结果反馈给大模型
- 大模型要么接着写代码(如果还需要更多信息),要么直接给出最终答案
- 这个循环会一直持续,直到 Agent 得出结论
代码执行是在沙盒里进行的,这意味着 Agent 不会不小心删了你的文件,或者搞出系统级的破坏。话虽如此,如果在生产环境里,没有额外的安全防护,我绝不会给 Agent 不受限制的网络访问权限。
实战经验分享
用强一点的大模型起步。 我一开始试着用较小的开源模型跑,结果生成的代码很不靠谱——要么瞎编函数名,要么写出语法错误的 Python。GPT-4o 和 Claude 3.5 Sonnet 处理代码 Agent 任务要靠谱得多。如果你用的是 HfApiModel,尽量选那些大尺寸的指令微调版本。
工具描述要精准。 就像我前面说的,描述模糊会导致工具调用失败或出错。要明确说清每个工具是干嘛的、期望什么输入、返回什么结果。
注意步数限制。 默认情况下,Agent 有个最大执行步数限制,用来防止死循环。如果你发现 Agent 好像没跑完就停了,可以通过 CodeAgent 的 max_steps 参数调大这个限制。我发现简单任务用默认值通常就够了,但复杂的多步查询有时确实得调大点。
多看执行轨迹。 出问题是迟早的事——而 smolagents 详细的执行轨迹是你最好的调试利器。你能清楚地看到 Agent 到底写了什么代码、出了什么错,以及它是怎么尝试恢复的。千万别无视这些输出。
实话实说的局限性
Smolagents 并非完美无缺。沙盒代码执行会增加延迟——每一步都要启动环境、跑代码、解析结果。对于简单的查询来说,比起直接调 API,这种开销还是感觉得到的。
这个库也比较新。我碰到了几个粗糙的边界情况,报错信息不太给力。文档正在完善中,但还有缺口,尤其是高级的多 Agent 协同配置方面。
另外,虽然代码 Agent 的路子很强大,但并非总是必需的。如果你只是需要一个简单的工具调用管线,不需要复杂的逻辑,其他框架那种基于 JSON 的方式可能反而更简单、更快速。
总结
花了一个周末折腾 smolagents 后,我把我的研究自动化脚本重写了一遍,代码量只有之前方案的四分之一。对于需要组合多种操作或处理边界情况的任务,代码 Agent 的范式真的管用——Agent 能写循环、写条件判断、写错误处理,这些是 JSON 工具调用根本表达不出来的。
如果你之前被其他 Agent 框架的复杂度劝退过,smolagents 绝对值得你花点时间试试。它真正做到了极简而不简陋。从内置工具起步,再进阶到自定义工具,你几分钟就能跑通一个 Agent,不用几小时,更不用几天。