smolagents 文档

代理 - 引导式教程

Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

代理 - 引导式教程

在本引导式教程中,您将学习如何构建代理、如何运行代理以及如何自定义代理以使其更好地适用于您的用例。

选择代理类型:CodeAgent 或 ToolCallingAgent

smolagents 提供两个代理类:CodeAgentToolCallingAgent,它们代表了代理与工具交互的两种不同范式。关键区别在于如何指定和执行操作:代码生成与结构化工具调用。

  • CodeAgent 生成作为 Python 代码片段的工具调用。

    • 代码在本地(可能不安全)或安全沙盒中执行。
    • 工具作为 Python 函数(通过绑定)公开。
    • 工具调用示例
      result = search_docs("What is the capital of France?")
      print(result)
    • 优点
      • 高度表达:允许复杂的逻辑和控制流,可以组合工具、循环、转换、推理。
      • 灵活:无需预定义所有可能的操作,可以动态生成新的操作/工具。
      • 涌现推理:适用于多步问题或动态逻辑。
    • 局限性
      • 错误风险:必须处理语法错误、异常。
      • 可预测性较差:更容易产生意外或不安全的输出。
      • 需要安全的执行环境。
  • ToolCallingAgent 将工具调用写入结构化 JSON。

    • 这是许多框架(OpenAI API)中使用的常见格式,允许结构化工具交互而无需代码执行。
    • 工具通过 JSON 模式定义:名称、描述、参数类型等。
    • 工具调用示例
      {
        "tool_call": {
          "name": "search_docs",
          "arguments": {
            "query": "What is the capital of France?"
          }
        }
      }
    • 优点
      • 可靠:不易产生幻觉,输出结构化且经过验证。
      • 安全:参数经过严格验证,没有任意代码运行的风险。
      • 互操作性:易于映射到外部 API 或服务。
    • 局限性
      • 表达能力低:无法轻松动态组合或转换结果,或执行复杂的逻辑或控制流。
      • 不灵活:必须预先定义所有可能的操作,仅限于预定义工具。
      • 无代码合成:仅限于工具功能。

何时使用哪种代理类型

  • 在以下情况下使用 CodeAgent

    • 您需要推理、链式操作或动态组合。
    • 工具是可组合的函数(例如,解析 + 数学 + 查询)。
    • 您的代理是问题解决者或程序员。
  • 在以下情况下使用 ToolCallingAgent

    • 您拥有简单、原子化的工具(例如,调用 API、获取文档)。
    • 您希望实现高可靠性和明确验证。
    • 您的代理类似于调度器或控制器。

CodeAgent

CodeAgent 生成 Python 代码片段来执行操作和解决任务。

默认情况下,Python 代码在您的本地环境中执行。这应该是安全的,因为唯一可以调用的函数是您提供的工具(特别是如果它仅是 Hugging Face 的工具)以及一组预定义的安全函数,例如 printmath 模块中的函数,因此您已经受到执行内容的限制。

Python 解释器默认也不允许在安全列表之外进行导入,因此所有最明显的攻击都不应成为问题。您可以通过在初始化 CodeAgent 时将授权模块作为字符串列表传递给参数 additional_authorized_imports 来授权其他导入

model = InferenceClientModel()
agent = CodeAgent(tools=[], model=model, additional_authorized_imports=['requests', 'bs4'])
agent.run("Could you get me the title of the page at url 'https://huggingface.co/blog'?")

此外,作为额外的安全层,子模块的访问默认是禁止的,除非在导入列表中明确授权。例如,要访问 numpy.random 子模块,您需要将 'numpy.random' 添加到 additional_authorized_imports 列表中。这也可以通过使用 numpy.* 来授权,这将允许 numpy 以及任何子包,例如 numpy.random 及其自己的子包。

LLM 可以生成任意代码,然后执行:请勿添加任何不安全的导入!

当任何代码尝试执行非法操作或代理生成的代码存在常规 Python 错误时,执行将停止。

您也可以使用 E2B 代码执行器 或 Docker 而非本地 Python 解释器。对于 E2B,首先设置 E2B_API_KEY 环境变量,然后在代理初始化时传递 executor_type="e2b"。对于 Docker,在初始化时传递 executor_type="docker"

通过此教程了解更多关于代码执行的信息。

ToolCallingAgent

ToolCallingAgent 输出 JSON 工具调用,这是许多框架(OpenAI API)中使用的常见格式,允许结构化工具交互而无需代码执行。

它的工作方式与 CodeAgent 大致相同,当然没有 additional_authorized_imports,因为它不执行代码

from smolagents import ToolCallingAgent

agent = ToolCallingAgent(tools=[], model=model)
agent.run("Could you get me the title of the page at url 'https://huggingface.co/blog'?")

构建您的代理

要初始化一个最小代理,您至少需要以下两个参数

  • model,一个驱动代理的文本生成模型——因为代理不同于简单的 LLM,它是一个使用 LLM 作为其引擎的系统。您可以使用以下任何选项

    • TransformersModel 使用预初始化的 transformers 管道在本地机器上运行推理。
    • InferenceClientModel 在底层利用 huggingface_hub.InferenceClient,并支持 Hub 上的所有推理提供商:Cerebras、Cohere、Fal、Fireworks、HF-Inference、Hyperbolic、Nebius、Novita、Replicate、SambaNova、Together 等。
    • LiteLLMModel 同样允许您通过 LiteLLM 调用 100 多个不同的模型和提供商!
    • AzureOpenAIServerModel 允许您使用部署在 Azure 中的 OpenAI 模型。
    • AmazonBedrockServerModel 允许您在 AWS 中使用 Amazon Bedrock。
    • MLXModel 创建一个 mlx-lm 管道以在您的本地机器上运行推理。
  • tools,代理可用于解决任务的 Tools 列表。它可以是一个空列表。您还可以通过定义可选参数 add_base_tools=True 将默认工具箱添加到您的 tools 列表之上。

一旦您拥有这两个参数 toolsmodel,您就可以创建一个代理并运行它。您可以使用任何您喜欢的 LLM,无论是通过 Inference ProviderstransformersollamaLiteLLMAzure OpenAIAmazon Bedrockmlx-lm

推理服务提供商
本地 Transformers 模型
OpenAI 或 Anthropic API
Ollama
Azure OpenAI
Amazon Bedrock
mlx-lm

推理提供商需要 HF_TOKEN 进行身份验证,但免费的 HF 账户已经包含积分。升级到 PRO 账户以提高您的包含积分。

要使用 PRO 账户访问受限模型或提高您的速率限制,您需要设置环境变量 HF_TOKEN 或在初始化 InferenceClientModel 时传递 token 变量。您可以从您的设置页面获取您的令牌。

from smolagents import CodeAgent, InferenceClientModel

model_id = "meta-llama/Llama-3.3-70B-Instruct" 

model = InferenceClientModel(model_id=model_id, token="<YOUR_HUGGINGFACEHUB_API_TOKEN>") # You can choose to not pass any model_id to InferenceClientModel to use a default model
# you can also specify a particular provider e.g. provider="together" or provider="sambanova"
agent = CodeAgent(tools=[], model=model, add_base_tools=True)

agent.run(
    "Could you give me the 118th number in the Fibonacci sequence?",
)

高级代理配置

自定义代理终止条件

默认情况下,代理会一直运行,直到它调用 final_answer 函数或达到最大步数。final_answer_checks 参数让您可以更好地控制代理何时以及如何终止其执行

from smolagents import CodeAgent, InferenceClientModel

# Define a custom final answer check function
def is_integer(final_answer: str, agent_memory=None) -> bool:
    """Return True if final_answer is an integer."""
    try:
        int(final_answer)
        return True
    except ValueError:
        return False

# Initialize agent with custom final answer check
agent = CodeAgent(
    tools=[],
    model=InferenceClientModel(),
    final_answer_checks=[is_integer]
)

agent.run("Calculate the least common multiple of 3 and 7")

final_answer_checks 参数接受一个函数列表,每个函数都

  • 将代理的最终答案字符串和代理的内存作为参数
  • 返回一个布尔值,指示最终答案是否有效(True)或无效(False)

如果任何函数返回 False,代理将记录错误消息并继续运行。此验证机制允许

  • 强制执行输出格式要求(例如,确保数学问题的数字答案)
  • 实施领域特定的验证规则
  • 创建更健壮的代理来验证自己的输出

检查代理运行

以下是一些有用的属性,用于检查运行后发生的情况

  • agent.logs 存储代理的详细日志。在代理运行的每一步,所有内容都存储在一个字典中,然后附加到 agent.logs 中。
  • 运行 agent.write_memory_to_messages() 会将代理的内存作为聊天消息列表写入,供模型查看。此方法会遍历日志的每一步,并且只将它感兴趣的内容作为消息存储:例如,它会将系统提示和任务存储在单独的消息中,然后对于每一步,它会将 LLM 输出存储为消息,将工具调用输出存储为另一条消息。如果您想对发生的事情有一个更高层次的了解,请使用此方法 - 但并非所有日志都会通过此方法进行转录。

工具

工具是代理使用的原子函数。要被 LLM 使用,它还需要一些属性来构成其 API,这些属性将用于向 LLM 描述如何调用此工具

  • 一个名字
  • 一个描述
  • 输入类型和描述
  • 一个输出类型

例如,您可以查看 PythonInterpreterTool:它有一个名称、描述、输入描述、输出类型以及执行操作的 forward 方法。

当代理初始化时,工具属性用于生成工具描述,该描述烘焙到代理的系统提示中。这让代理知道它可以使用哪些工具以及原因。

默认工具箱

如果您安装 smolagents 时带有“toolkit”额外功能,它会附带一个默认工具箱,用于增强代理功能,您可以在初始化时使用参数 add_base_tools=True 将其添加到您的代理中

  • DuckDuckGo 网页搜索*:使用 DuckDuckGo 浏览器执行网页搜索。
  • Python 代码解释器:在安全环境中运行 LLM 生成的 Python 代码。此工具仅在您使用 add_base_tools=True 初始化 ToolCallingAgent 时才会添加到其中,因为基于代码的代理已经可以原生执行 Python 代码
  • 转录器:一个基于 Whisper-Turbo 构建的语音转文本管道,将音频转录为文本。

您可以通过调用工具并传递其参数来手动使用工具。

# !pip install smolagents[toolkit]
from smolagents import WebSearchTool

search_tool = WebSearchTool()
print(search_tool("Who's the current president of Russia?"))

创建一个新工具

您可以创建自己的工具,用于 Hugging Face 默认工具未涵盖的用例。例如,让我们创建一个工具,该工具返回 Hub 上给定任务的下载量最多的模型。

您将从下面的代码开始。

from huggingface_hub import list_models

task = "text-classification"

most_downloaded_model = next(iter(list_models(filter=task, sort="downloads", direction=-1)))
print(most_downloaded_model.id)

此代码可以通过将其包装在函数中并添加 tool 装饰器来快速转换为工具:这不是构建工具的唯一方法:您可以直接将其定义为 Tool 的子类,这为您提供了更大的灵活性,例如初始化重型类属性的可能性。

让我们看看这两种选项是如何工作的

用 @tool 装饰一个函数
工具子类
from smolagents import tool

@tool
def model_download_tool(task: str) -> str:
    """
    This is a tool that returns the most downloaded model of a given task on the Hugging Face Hub.
    It returns the name of the checkpoint.

    Args:
        task: The task for which to get the download count.
    """
    most_downloaded_model = next(iter(list_models(filter=task, sort="downloads", direction=-1)))
    return most_downloaded_model.id

该函数需要

  • 一个清晰的名称。名称应足以描述此工具的作用,以帮助驱动代理的 LLM 大脑。由于此工具返回任务中下载量最多的模型,我们将其命名为 model_download_tool
  • 输入和输出的类型提示
  • 一个描述,其中包含一个“Args:”部分,其中描述了每个参数(此时没有类型指示,它将从类型提示中提取)。与工具名称一样,此描述是驱动代理的 LLM 的说明手册,因此请不要忽视它。

所有这些元素都将在初始化时自动烘焙到代理的系统提示中:因此请努力使它们尽可能清晰!

此定义格式与 apply_chat_template 中使用的工具模式相同,唯一的区别是添加了 tool 装饰器:在此阅读更多关于我们的工具使用 API

然后您可以直接初始化您的代理

from smolagents import CodeAgent, InferenceClientModel
agent = CodeAgent(tools=[model_download_tool], model=InferenceClientModel())
agent.run(
    "Can you give me the name of the model that has the most downloads in the 'text-to-video' task on the Hugging Face Hub?"
)

您将获得以下日志

╭──────────────────────────────────────── New run ─────────────────────────────────────────╮
│                                                                                          │
│ Can you give me the name of the model that has the most downloads in the 'text-to-video' │
│ task on the Hugging Face Hub?                                                            │
│                                                                                          │
╰─ InferenceClientModel - Qwen/Qwen2.5-Coder-32B-Instruct ───────────────────────────────────────────╯
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 0 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
╭─ Executing this code: ───────────────────────────────────────────────────────────────────╮
│   1 model_name = model_download_tool(task="text-to-video")                               │
│   2 print(model_name)                                                                    │
╰──────────────────────────────────────────────────────────────────────────────────────────╯
Execution logs:
ByteDance/AnimateDiff-Lightning

Out: None
[Step 0: Duration 0.27 seconds| Input tokens: 2,069 | Output tokens: 60]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Step 1 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
╭─ Executing this code: ───────────────────────────────────────────────────────────────────╮
│   1 final_answer("ByteDance/AnimateDiff-Lightning")                                      │
╰──────────────────────────────────────────────────────────────────────────────────────────╯
Out - Final answer: ByteDance/AnimateDiff-Lightning
[Step 1: Duration 0.10 seconds| Input tokens: 4,288 | Output tokens: 148]
Out[20]: 'ByteDance/AnimateDiff-Lightning'

专用教程中阅读更多关于工具的信息。

多代理

多代理系统已通过 Microsoft 的框架 Autogen 引入。

在这种类型的框架中,您有多个代理协同工作来解决您的任务,而不是只有一个。经验上,它在大多数基准测试中都能产生更好的性能。这种更好性能的原因在概念上很简单:对于许多任务,您宁愿将单元专门化于子任务,而不是使用一个全能系统。在这里,拥有具有独立工具集和内存的代理可以实现高效的专业化。例如,为什么要用网页搜索代理访问的所有网页内容来填充代码生成代理的内存呢?最好将它们分开。

您可以使用 smolagents 轻松构建分层多代理系统。

为此,只需确保您的代理具有 namedescription 属性,然后这些属性将被嵌入到管理器代理的系统提示中,以使其知道如何调用此受管代理,就像我们对工具所做的那样。然后您可以在管理器代理初始化时在参数 managed_agents 中传递此受管代理。

这是一个使用我们原生 WebSearchTool 构建管理特定网页搜索代理的代理示例

from smolagents import CodeAgent, InferenceClientModel, WebSearchTool

model = InferenceClientModel()

web_agent = CodeAgent(
    tools=[WebSearchTool()],
    model=model,
    name="web_search_agent",
    description="Runs web searches for you. Give it your query as an argument."
)

manager_agent = CodeAgent(
    tools=[], model=model, managed_agents=[web_agent]
)

manager_agent.run("Who is the CEO of Hugging Face?")

有关高效多代理实现深入示例,请参阅我们如何将多代理系统推向 GAIA 排行榜榜首

与您的代理对话,并在炫酷的 Gradio 界面中可视化其思想

您可以使用 GradioUI 交互式地向您的代理提交任务,并观察其思维和执行过程,示例如下

from smolagents import (
    load_tool,
    CodeAgent,
    InferenceClientModel,
    GradioUI
)

# Import tool from Hub
image_generation_tool = load_tool("m-ric/text-to-image", trust_remote_code=True)

model = InferenceClientModel(model_id=model_id)

# Initialize the agent with the image generation tool
agent = CodeAgent(tools=[image_generation_tool], model=model)

GradioUI(agent).launch()

在底层,当用户输入新答案时,代理将通过 agent.run(user_request, reset=False) 启动。reset=False 标志意味着在启动此新任务之前不会刷新代理的内存,这使得对话可以继续进行。

您也可以使用此 reset=False 参数来在任何其他代理应用程序中保持对话的连续性。

在 Gradio UI 中,如果您想允许用户中断正在运行的代理,可以通过一个触发 agent.interrupt() 方法的按钮来实现。这将在代理当前步骤结束时停止代理,然后引发错误。

下一步

最后,当您根据需要配置好代理后,就可以将其分享到 Hub!

agent.push_to_hub("m-ric/my_agent")

类似地,要加载已推送到 Hub 的代理,如果您信任其工具的代码,请使用

agent.from_hub("m-ric/my_agent", trust_remote_code=True)

有关更深入的用法,您将需要查看我们的教程

< > 在 GitHub 上更新