智能体课程文档

构建使用代码的 Agent

Hugging Face's logo
加入 Hugging Face 社区

并获得增强的文档体验

开始使用

Ask a Question Open In Colab

构建使用代码的 Agent

代码 Agent 是 smolagents 中的默认 Agent 类型。它们生成 Python 工具调用来执行动作,从而实现高效、富有表现力且准确的动作表示。

它们精简的方法减少了所需动作的数量,简化了复杂操作,并实现了对现有代码函数的重用。smolagents 为构建代码 Agent 提供了一个轻量级框架,代码量约 1000 行。

代码动作 vs JSON 动作 图片来自论文 Executable Code Actions Elicit Better LLM Agents

如果你想了解更多关于代码 Agent 为何有效的信息,请查阅 smolagents 文档中的这篇指南

为何选择代码 Agent?

在多步 Agent 流程中,LLM 编写并执行动作,通常涉及外部工具调用。传统方法使用 JSON 格式指定工具名称和参数(作为字符串),系统必须解析这些信息才能确定执行哪个工具

然而,研究表明,直接使用代码能让工具调用型 LLM 更有效地工作。这是 smolagents 的核心原则,正如上图所示(该图来自论文 Executable Code Actions Elicit Better LLM Agents)。

用代码而非 JSON 编写动作有以下几个关键优势:

  • 可组合性:轻松组合和重用动作
  • 对象管理:直接处理复杂结构,如图像
  • 通用性:能够表达任何计算上可能的任务
  • 对 LLM 更自然:高质量的代码已经存在于 LLM 的训练数据中

代码 Agent 如何工作?

From https://huggingface.co/docs/smolagents/conceptual_guides/react

上图展示了 CodeAgent.run() 的工作方式,它遵循了我们在第一单元中提到的 ReAct 框架。smolagents 中 Agent 的主要抽象是 MultiStepAgent,它是核心构建块。CodeAgent 是一种特殊的 MultiStepAgent,我们将在下面的例子中看到。

一个 CodeAgent 通过一系列步骤的循环来执行动作,现有的变量和知识被整合到 Agent 的上下文中,并保存在执行日志中。

  1. 系统提示存储在 SystemPromptStep 中,用户查询则记录在 TaskStep 中。

  2. 然后,执行以下 while 循环:

    2.1 方法 agent.write_memory_to_messages() 将 Agent 的日志写入一个 LLM 可读的聊天消息列表。

    2.2 这些消息被发送给一个 Model,该模型生成一个补全。

    2.3 解析补全以提取动作,在我们的例子中,由于我们使用的是 CodeAgent,这个动作应该是一个代码片段。

    2.4 执行该动作。

    2.5 结果被记录到内存中的一个 ActionStep 里。

在每一步结束时,如果 Agent 包含任何函数调用(在 agent.step_callback 中),它们将被执行。

看几个例子

你可以跟随这个笔记本中的代码,并使用 Google Colab 运行它。

阿福(Alfred)正在韦恩庄园策划一场派对,他需要你的帮助来确保一切顺利进行。为了协助他,我们将运用所学的关于多步 CodeAgent 如何运作的知识。

Alfred Party

如果你还没有安装 smolagents,可以运行以下命令进行安装:

pip install smolagents -U

我们还要登录到 Hugging Face Hub,以便访问无服务器推理 API(Serverless Inference API)。

from huggingface_hub import login

login()

使用 smolagents 为派对选择播放列表

音乐是一场成功派对的重要组成部分!阿福需要一些帮助来选择播放列表。幸运的是,smolagents 能帮我们解决这个问题!我们可以构建一个能够使用 DuckDuckGo 在网上搜索的 Agent。为了让 Agent 能够使用这个工具,我们在创建 Agent 时将其包含在工具列表中。

Alfred Playlist

对于模型,我们将依赖 InferenceClientModel,它提供了对 Hugging Face 的无服务器推理 API 的访问。默认模型是 "Qwen/Qwen2.5-Coder-32B-Instruct",它性能优越且可用于快速推理,但你也可以从 Hub 中选择任何兼容的模型。

运行一个 Agent 非常直接:

from smolagents import CodeAgent, DuckDuckGoSearchTool, InferenceClientModel

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

agent.run("Search for the best music recommendations for a party at the Wayne's mansion.")

当你运行这个例子时,输出将显示正在执行的工作流步骤的跟踪信息。它还会打印出相应的 Python 代码以及消息。

 ─ Executing parsed code: ──────────────────────────────────────────────────────────────────────────────────────── 
  results = web_search(query="best music for a Batman party")                                                      
  print(results)                                                                                                   
 ───────────────────────────────────────────────────────────────────────────────────────────────────────────────── 

几步之后,你就会看到生成的播放列表,阿福可以用它来为派对助兴!🎵

使用自定义工具准备菜单

Alfred Menu

现在我们已经选好了播放列表,我们需要为客人准备菜单。阿福同样可以利用 smolagents 来完成这项工作。在这里,我们使用 @tool 装饰器来定义一个作为工具的自定义函数。我们稍后会更详细地介绍工具的创建,所以现在,我们只需运行代码即可。

正如你在下面的例子中看到的,我们将使用 @tool 装饰器创建一个工具,并将其包含在 tools 列表中。

from smolagents import CodeAgent, tool, InferenceClientModel

# Tool to suggest a menu based on the occasion
@tool
def suggest_menu(occasion: str) -> str:
    """
    Suggests a menu based on the occasion.
    Args:
        occasion (str): The type of occasion for the party. Allowed values are:
                        - "casual": Menu for casual party.
                        - "formal": Menu for formal party.
                        - "superhero": Menu for superhero party.
                        - "custom": Custom menu.
    """
    if occasion == "casual":
        return "Pizza, snacks, and drinks."
    elif occasion == "formal":
        return "3-course dinner with wine and dessert."
    elif occasion == "superhero":
        return "Buffet with high-energy and healthy food."
    else:
        return "Custom menu for the butler."

# Alfred, the butler, preparing the menu for the party
agent = CodeAgent(tools=[suggest_menu], model=InferenceClientModel())

# Preparing the menu for the party
agent.run("Prepare a formal menu for the party.")

Agent 将会运行几步直到找到答案。在文档字符串中明确允许的值有助于引导 Agent 使用存在的 `occasion` 参数值,并减少幻觉。

菜单准备好了!🥗

在 Agent 内部使用 Python 导入

我们已经准备好了播放列表和菜单,但还需要检查一个至关重要的细节:准备时间!

阿福需要计算一下,如果他现在开始准备,所有东西什么时候能准备好,以防万一需要其他超级英雄的帮助。

smolagents 专注于编写和执行 Python 代码片段的 Agent,并提供沙盒执行以确保安全。

代码执行有严格的安全措施 —— 默认情况下,不在预定义安全列表中的导入会被阻止。但是,你可以通过在 additional_authorized_imports 中以字符串形式传递它们来授权额外的导入。有关安全代码执行的更多详细信息,请参阅官方指南

在创建 Agent 时,我们将使用 additional_authorized_imports 来允许导入 datetime 模块。

from smolagents import CodeAgent, InferenceClientModel
import numpy as np
import time
import datetime

agent = CodeAgent(tools=[], model=InferenceClientModel(), additional_authorized_imports=['datetime'])

agent.run(
    """
    Alfred needs to prepare for the party. Here are the tasks:
    1. Prepare the drinks - 30 minutes
    2. Decorate the mansion - 60 minutes
    3. Set up the menu - 45 minutes
    4. Prepare the music and playlist - 45 minutes

    If we start right now, at what time will the party be ready?
    """
)

这些例子只是你能用代码 Agent 做的开始,我们已经开始看到它们在准备派对方面的实用性。你可以在 smolagents 文档中了解更多关于如何构建代码 Agent 的信息。

总而言之,smolagents 专注于编写和执行 Python 代码片段的 Agent,并提供沙盒执行以确保安全。它同时支持本地和基于 API 的语言模型,使其能够适应各种开发环境。

将我们的自定义派对准备 Agent 分享到 Hub

如果能把我们自己制作的阿福 Agent 分享给社区,那该多棒啊!这样一来,任何人都可以轻松地从 Hub 下载并直接使用这个 Agent,将哥谭市的终极派对策划师带到他们的指尖!让我们实现它吧!🎉

smolagents 库使这成为可能,它允许你与社区分享一个完整的 Agent,并下载其他人制作的 Agent 以便立即使用。就像下面这样简单:

# Change to your username and repo name
agent.push_to_hub('sergiopaniego/AlfredAgent')

要再次下载这个 Agent,请使用以下代码:

# Change to your username and repo name
alfred_agent = agent.from_hub('sergiopaniego/AlfredAgent', trust_remote_code=True)

alfred_agent.run("Give me the best playlist for a party at Wayne's mansion. The party idea is a 'villain masquerade' theme")  

更令人兴奋的是,分享的 Agent 可以直接作为 Hugging Face Spaces 使用,让你能够实时与它们互动。你可以在这里探索其他 Agent。

例如,AlfredAgent 可以在这里找到。你可以直接在下方试用它:

你可能想知道——阿福是如何使用 smolagents 构建出这样一个 Agent 的?通过集成多个工具,他可以像下面这样生成一个 Agent。现在不用担心工具的问题,因为本单元后面会有一个专门的部分详细探讨这个问题:

from smolagents import CodeAgent, DuckDuckGoSearchTool, FinalAnswerTool, InferenceClientModel, Tool, tool, VisitWebpageTool

@tool
def suggest_menu(occasion: str) -> str:
    """
    Suggests a menu based on the occasion.
    Args:
        occasion: The type of occasion for the party.
    """
    if occasion == "casual":
        return "Pizza, snacks, and drinks."
    elif occasion == "formal":
        return "3-course dinner with wine and dessert."
    elif occasion == "superhero":
        return "Buffet with high-energy and healthy food."
    else:
        return "Custom menu for the butler."

@tool
def catering_service_tool(query: str) -> str:
    """
    This tool returns the highest-rated catering service in Gotham City.
    
    Args:
        query: A search term for finding catering services.
    """
    # Example list of catering services and their ratings
    services = {
        "Gotham Catering Co.": 4.9,
        "Wayne Manor Catering": 4.8,
        "Gotham City Events": 4.7,
    }
    
    # Find the highest rated catering service (simulating search query filtering)
    best_service = max(services, key=services.get)
    
    return best_service

class SuperheroPartyThemeTool(Tool):
    name = "superhero_party_theme_generator"
    description = """
    This tool suggests creative superhero-themed party ideas based on a category.
    It returns a unique party theme idea."""
    
    inputs = {
        "category": {
            "type": "string",
            "description": "The type of superhero party (e.g., 'classic heroes', 'villain masquerade', 'futuristic Gotham').",
        }
    }
    
    output_type = "string"

    def forward(self, category: str):
        themes = {
            "classic heroes": "Justice League Gala: Guests come dressed as their favorite DC heroes with themed cocktails like 'The Kryptonite Punch'.",
            "villain masquerade": "Gotham Rogues' Ball: A mysterious masquerade where guests dress as classic Batman villains.",
            "futuristic Gotham": "Neo-Gotham Night: A cyberpunk-style party inspired by Batman Beyond, with neon decorations and futuristic gadgets."
        }
        
        return themes.get(category.lower(), "Themed party idea not found. Try 'classic heroes', 'villain masquerade', or 'futuristic Gotham'.")


# Alfred, the butler, preparing the menu for the party
agent = CodeAgent(
    tools=[
        DuckDuckGoSearchTool(), 
        VisitWebpageTool(),
        suggest_menu,
        catering_service_tool,
        SuperheroPartyThemeTool(),
	FinalAnswerTool()
    ], 
    model=InferenceClientModel(),
    max_steps=10,
    verbosity_level=2
)

agent.run("Give me the best playlist for a party at the Wayne's mansion. The party idea is a 'villain masquerade' theme")

如你所见,我们创建了一个带有多项工具的 CodeAgent,这些工具增强了 Agent 的功能,使其成为终极派对策划师,并准备好与社区分享!🎉

现在轮到你了:利用我们刚刚学到的知识,构建你自己的 Agent 并与社区分享吧!🕵️‍♂️💡

如果你想分享你的 Agent 项目,可以创建一个 Space 并在 Hugging Face Hub 上标记 agents-course。我们很期待看到你的创作!

使用 OpenTelemetry 和 Langfuse 检查我们的派对准备 Agent 📡

在阿福微调派对准备 Agent 的过程中,他对调试运行过程感到越来越厌倦。Agent 天生具有不可预测性,难以检查。但既然他的目标是构建终极的派对准备 Agent 并将其部署到生产环境中,他需要强大的可追溯性以便未来进行监控和分析。

smolagents 再次挺身而出!它采用了 OpenTelemetry 标准来检测 Agent 的运行情况,实现了无缝的检查和日志记录。在 LangfuseSmolagentsInstrumentor 的帮助下,阿福可以轻松地跟踪和分析他的 Agent 的行为。

设置过程非常简单!

首先,我们需要安装必要的依赖项:

pip install opentelemetry-sdk opentelemetry-exporter-otlp openinference-instrumentation-smolagents langfuse

接下来,阿福已经在 Langfuse 上创建了一个账户并准备好了他的 API 密钥。如果你还没有这样做,可以在这里注册 Langfuse Cloud,或探索其他替代方案

一旦你有了你的 API 密钥,需要像下面这样正确配置它们:

import os

# Get keys for your project from the project settings page: https://cloud.langfuse.com
os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-lf-..." 
os.environ["LANGFUSE_SECRET_KEY"] = "sk-lf-..." 
os.environ["LANGFUSE_HOST"] = "https://cloud.langfuse.com" # 🇪🇺 EU region
# os.environ["LANGFUSE_HOST"] = "https://us.cloud.langfuse.com" # 🇺🇸 US region

设置好环境变量后,我们现在可以初始化 Langfuse 客户端了。get_client() 使用环境变量中提供的凭据来初始化 Langfuse 客户端。

from langfuse import get_client
 
langfuse = get_client()
 
# Verify connection
if langfuse.auth_check():
    print("Langfuse client is authenticated and ready!")
else:
    print("Authentication failed. Please check your credentials and host.")

最后,阿福准备好初始化 SmolagentsInstrumentor 并开始跟踪他的 Agent 的性能了。

from openinference.instrumentation.smolagents import SmolagentsInstrumentor

SmolagentsInstrumentor().instrument()

阿福现在连接上了 🔌!来自 smolagents 的运行记录正在 Langfuse 中被记录下来,这让他可以全面了解 Agent 的行为。有了这个设置,他就可以回顾之前的运行情况,并进一步完善他的派对准备 Agent。

要了解更多关于追踪你的 Agent 以及如何使用收集到的数据来评估其性能的信息,请查看附加单元 2
from smolagents import CodeAgent, InferenceClientModel

agent = CodeAgent(tools=[], model=InferenceClientModel())
alfred_agent = agent.from_hub('sergiopaniego/AlfredAgent', trust_remote_code=True)
alfred_agent.run("Give me the best playlist for a party at Wayne's mansion. The party idea is a 'villain masquerade' theme")  

阿福现在可以在这里访问这些日志,以进行审查和分析。

实际上,在执行过程中发生了一个小错误。你能在日志中发现它吗?试着追踪 Agent 是如何处理它并仍然返回一个有效答案的。这里是错误的直接链接,如果你想验证你的答案的话。当然,这个错误在此期间已经被修复了,更多细节可以在这个 issue 中找到。

与此同时,建议的播放列表为派对准备营造了完美的氛围。很酷,对吧?🎶


现在我们已经创建了我们的第一个代码 Agent,让我们来学习如何创建工具调用 Agent,这是 smolagents 中可用的第二种 Agent 类型。

资源

< > 在 GitHub 上更新