Agno开发教程系列(二):赋予智能体超能力——工具的使用

241 阅读16分钟

📖 引言

在上一篇文章中,我们介绍了Agno框架的基本概念,并成功构建了第一个智能体。我们学习了如何使用DeepSeek模型创建Agent,让它能够理解我们的问题并给出回答。

但你可能会发现,上一篇的智能体虽然"聪明",却有些"孤立"——它只能基于训练数据给出答案,无法获取实时信息,也不能执行具体操作。就像一个博学的学者,虽然知识渊博,却无法查询今天的天气、计算复杂公式或访问外部数据库。

那么,如何让智能体突破这些限制呢?答案就是:工具(Tools)!

本篇文章将深入讲解Agno框架中的工具系统,教你如何为智能体配备各种"超能力",让它能够:

  • 🌤️ 查询实时天气信息
  • 🧮 执行精确的数学计算
  • 🔍 搜索互联网内容
  • 📊 访问数据库和文件
  • 🐍 运行Python代码
  • ...以及更多可能性

让我们开始这段赋能之旅吧!


🔧 什么是工具(Tool)

工具的本质

在Agno框架中,Tool(工具)本质上是Agent用于与外部系统交互的函数。它们是智能体的"手"和"眼",让AI能够:

  1. 获取实时信息:查询天气、股票价格、新闻等
  2. 执行具体操作:发送邮件、创建文件、操作数据库
  3. 调用外部服务:搜索引擎、API接口、第三方平台
  4. 进行复杂计算:数学运算、数据分析、代码执行

工具的工作原理

当你向配备了工具的智能体提问时,它会:

用户提问 → Agent分析任务 → 选择合适的工具 → 执行工具 → 获取结果 → 组织答案 → 返回给用户

举个例子

  • 问题:"北京今天天气怎么样?"
  • 分析:需要获取实时天气信息
  • 选择工具:调用天气查询工具
  • 执行:get_weather("北京")
  • 返回结果:"晴天,温度25℃"
  • 组织答案:"北京今天天气晴朗,气温25摄氏度,适合外出活动。"

这种机制让AI不再局限于预训练知识,而是能够动态获取信息并执行操作。


📝 如何定义工具

在Agno中定义工具非常简单,主要有两种方式:

方式一:普通Python函数

任何Python函数都可以作为工具,只需要遵循以下规范:

  1. 添加类型提示:明确参数和返回值类型
  2. 编写详细的docstring:描述函数功能和参数说明
  3. 返回清晰的结果:便于Agent理解

示例:简单的计算器工具

def add_numbers(a: float, b: float) -> float:
    """
    将两个数字相加
    
    Args:
        a (float): 第一个数字
        b (float): 第二个数字
    
    Returns:
        float: 两个数字的和
    """
    return a + b

def multiply_numbers(a: float, b: float) -> float:
    """
    将两个数字相乘
    
    Args:
        a (float): 第一个数字
        b (float): 第二个数字
    
    Returns:
        float: 两个数字的乘积
    """
    return a * b

为什么需要详细的docstring?

Agent会读取docstring来理解工具的功能和使用方法,从而决定何时以及如何调用这个工具。这就像给工具写了一份"使用说明书"。

方式二:使用@tool装饰器(高级用法)

对于更复杂的场景,可以使用@tool装饰器添加额外功能:

from agno.tools import tool

@tool(
    name="weather_query",  # 工具名称
    description="查询指定城市的天气信息",  # 工具描述
    show_result=True,  # 是否显示工具执行结果
)
def get_weather(city: str) -> str:
    """
    查询城市天气(示例函数)
    
    Args:
        city (str): 城市名称
    
    Returns:
        str: 天气描述
    """
    # 实际应用中这里应该调用真实的天气API
    # 这里为了演示使用模拟数据
    weather_data = {
        "北京": "晴天,25℃,空气质量良好",
        "上海": "多云,22℃,湿度较高",
        "深圳": "小雨,28℃,建议携带雨具",
    }
    return weather_data.get(city, f"{city}的天气信息暂时无法获取")

定义工具的最佳实践

  1. 明确的函数命名:使用描述性名称,如get_weather而不是func1
  2. 完整的类型提示:帮助Agent理解参数类型
  3. 详细的docstring:描述功能、参数和返回值
  4. 错误处理:处理可能的异常情况
  5. 返回结构化数据:使用字符串、字典或列表等清晰的格式

🔗 如何将工具绑定到智能体

定义好工具后,需要将它们绑定到Agent才能使用。Agno提供了多种绑定方式:

方法一:初始化时绑定(推荐)

在创建Agent时通过tools参数传入:

from agno.agent import Agent
from agno.models.deepseek import DeepSeek

# 定义工具
def get_current_time() -> str:
    """
    获取当前时间
    
    Returns:
        str: 当前时间字符串
    """
    from datetime import datetime
    return datetime.now().strftime("%Y-%m-%d %H:%M:%S")

# 创建Agent并绑定工具
agent = Agent(
    model=DeepSeek(api_key="DEEPSEEK_API_KEY"),
    tools=[get_current_time],  # 绑定工具列表
    show_tool_calls=True,  # 显示工具调用过程
    markdown=True,  # 使用Markdown格式输出
)

# 使用Agent
agent.print_response("现在几点了?")

参数说明

  • tools:工具函数列表
  • show_tool_calls:是否显示工具调用详情(调试时很有用)
  • markdown:是否使用Markdown格式美化输出

方法二:动态添加工具

在Agent创建后动态添加:

# 创建Agent
agent = Agent(model=DeepSeek(api_key="DEEPSEEK_API_KEY"))

# 动态添加单个工具
agent.add_tool(get_current_time)

# 动态设置多个工具(会覆盖已有工具)
agent.set_tools([get_current_time, get_weather])

方法三:使用工具包(Toolkit)

Agno提供了许多预构建的工具包,可以直接使用:

from agno.agent import Agent
from agno.models.deepseek import DeepSeek
from agno.tools.duckduckgo import DuckDuckGoTools  # 搜索工具包

# 创建配备搜索能力的Agent
agent = Agent(
    model=DeepSeek(api_key="DEEPSEEK_API_KEY"),
    tools=[DuckDuckGoTools()],  # 使用DuckDuckGo搜索工具
    show_tool_calls=True,
)

agent.print_response("搜索最新的AI技术新闻")

⚙️ 工具的执行流程

理解工具的执行流程有助于更好地设计和调试工具。下面是完整的执行过程:

流程图解

1. 用户输入问题
   ↓
2. Agent分析问题,判断是否需要工具
   ↓
3. 从可用工具中选择最合适的工具
   ↓
4. 确定工具调用参数
   ↓
5. 执行工具函数
   ↓
6. 获取工具返回结果
   ↓
7. 基于结果生成最终答案
   ↓
8. 返回给用户

执行示例

让我们通过一个实际例子来看执行过程:

from agno.agent import Agent
from agno.models.deepseek import DeepSeek

def calculate_square(number: float) -> float:
    """
    计算数字的平方
    
    Args:
        number (float): 要计算平方的数字
    
    Returns:
        float: 计算结果
    """
    return number ** 2

# 创建Agent
agent = Agent(
    model=DeepSeek(api_key="DEEPSEEK_API_KEY"),
    tools=[calculate_square],
    show_tool_calls=True,  # 开启这个可以看到执行过程
)

# 提问
agent.print_response("15的平方是多少?")

执行过程输出示例

🔧 Tool Call: calculate_square
   Parameters: {"number": 15}
   Result: 225

📝 Agent Response:
15的平方是225。

多工具协同

当Agent配备多个工具时,它会智能选择并可能多次调用:

from agno.agent import Agent
from agno.models.deepseek import DeepSeek

def add(a: float, b: float) -> float:
    """加法运算"""
    return a + b

def multiply(a: float, b: float) -> float:
    """乘法运算"""
    return a * b

agent = Agent(
    model=DeepSeek(api_key="DEEPSEEK_API_KEY"),
    tools=[add, multiply],
    show_tool_calls=True,
)

# 这个问题需要多步计算
agent.print_response("计算(5 + 3) × 4的结果")

Agent可能会:

  1. 先调用add(5, 3)得到8
  2. 再调用multiply(8, 4)得到32
  3. 最终返回答案

💡 完整示例:实用工具集

下面提供几个完整可运行的实用工具示例:

示例1:天气查询助手

from agno.agent import Agent
from agno.models.deepseek import DeepSeek
import random

def get_weather(city: str) -> str:
    """
    查询指定城市的实时天气信息
    
    Args:
        city (str): 城市名称,例如"北京"、"上海"
    
    Returns:
        str: 包含温度、天气状况、空气质量等信息的描述
    """
    # 注意:这是模拟数据,实际应用中应调用真实的天气API
    # 例如:OpenWeatherMap、和风天气等
    
    weather_conditions = ["晴天", "多云", "小雨", "阴天"]
    temperature = random.randint(15, 35)
    condition = random.choice(weather_conditions)
    air_quality = random.choice(["优", "良", "轻度污染"])
    
    return f"{city}当前天气:{condition},温度{temperature}℃,空气质量{air_quality}"

def get_weather_forecast(city: str, days: int = 3) -> str:
    """
    查询未来几天的天气预报
    
    Args:
        city (str): 城市名称
        days (int): 预报天数,默认3天
    
    Returns:
        str: 天气预报信息
    """
    forecast = []
    for i in range(days):
        temp = random.randint(15, 35)
        condition = random.choice(["晴", "多云", "雨"])
        forecast.append(f"第{i+1}天:{condition}{temp}℃")
    
    return f"{city}未来{days}天天气预报:\n" + "\n".join(forecast)

# 创建天气查询智能体
weather_agent = Agent(
    name="天气助手",
    model=DeepSeek(api_key="DEEPSEEK_API_KEY"),
    tools=[get_weather, get_weather_forecast],
    instructions=[
        "你是一个专业的天气查询助手",
        "当用户询问天气时,使用工具查询准确信息",
        "提供友好、详细的天气建议"
    ],
    show_tool_calls=True,
    markdown=True,
)

# 测试
if __name__ == "__main__":
    weather_agent.print_response("北京今天天气怎么样?")
    print("\n" + "="*50 + "\n")
    weather_agent.print_response("上海未来3天的天气预报")

示例2:智能计算器

from agno.agent import Agent
from agno.models.deepseek import DeepSeek
import math

def add_numbers(a: float, b: float) -> float:
    """
    加法运算
    
    Args:
        a (float): 第一个数字
        b (float): 第二个数字
    
    Returns:
        float: 和
    """
    return a + b

def subtract_numbers(a: float, b: float) -> float:
    """
    减法运算
    
    Args:
        a (float): 被减数
        b (float): 减数
    
    Returns:
        float: 差
    """
    return a - b

def multiply_numbers(a: float, b: float) -> float:
    """
    乘法运算
    
    Args:
        a (float): 第一个数字
        b (float): 第二个数字
    
    Returns:
        float: 积
    """
    return a * b

def divide_numbers(a: float, b: float) -> float:
    """
    除法运算
    
    Args:
        a (float): 被除数
        b (float): 除数
    
    Returns:
        float: 商
    """
    if b == 0:
        return "错误:除数不能为0"
    return a / b

def calculate_power(base: float, exponent: float) -> float:
    """
    计算幂运算
    
    Args:
        base (float): 底数
        exponent (float): 指数
    
    Returns:
        float: base的exponent次方
    """
    return base ** exponent

def calculate_sqrt(number: float) -> float:
    """
    计算平方根
    
    Args:
        number (float): 要计算平方根的数字
    
    Returns:
        float: 平方根
    """
    if number < 0:
        return "错误:负数没有实数平方根"
    return math.sqrt(number)

# 创建计算器智能体
calculator_agent = Agent(
    name="智能计算器",
    model=DeepSeek(api_key="DEEPSEEK_API_KEY"),
    tools=[
        add_numbers,
        subtract_numbers,
        multiply_numbers,
        divide_numbers,
        calculate_power,
        calculate_sqrt,
    ],
    instructions=[
        "你是一个智能计算器助手",
        "帮助用户进行各种数学计算",
        "对于复杂表达式,分步骤计算",
        "清晰地展示计算过程"
    ],
    show_tool_calls=True,
    markdown=True,
)

# 测试
if __name__ == "__main__":
    calculator_agent.print_response("计算(12 + 8) × 5的结果")
    print("\n" + "="*50 + "\n")
    calculator_agent.print_response("16的平方根是多少?")
    print("\n" + "="*50 + "\n")
    calculator_agent.print_response("2的10次方等于多少?")

示例3:文件操作助手

from agno.agent import Agent
from agno.models.deepseek import DeepSeek
import os
from datetime import datetime

def read_file(file_path: str) -> str:
    """
    读取文件内容
    
    Args:
        file_path (str): 文件路径
    
    Returns:
        str: 文件内容
    """
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()
        return f"成功读取文件,内容:\n{content}"
    except FileNotFoundError:
        return f"错误:文件 {file_path} 不存在"
    except Exception as e:
        return f"读取文件时出错:{str(e)}"

def write_file(file_path: str, content: str) -> str:
    """
    写入内容到文件
    
    Args:
        file_path (str): 文件路径
        content (str): 要写入的内容
    
    Returns:
        str: 操作结果
    """
    try:
        with open(file_path, 'w', encoding='utf-8') as f:
            f.write(content)
        return f"成功将内容写入文件:{file_path}"
    except Exception as e:
        return f"写入文件时出错:{str(e)}"

def list_files(directory: str = ".") -> str:
    """
    列出目录下的所有文件
    
    Args:
        directory (str): 目录路径,默认为当前目录
    
    Returns:
        str: 文件列表
    """
    try:
        files = os.listdir(directory)
        if not files:
            return f"目录 {directory} 为空"
        
        file_list = "\n".join([f"- {f}" for f in files])
        return f"目录 {directory} 下的文件:\n{file_list}"
    except Exception as e:
        return f"列出文件时出错:{str(e)}"

def get_file_info(file_path: str) -> str:
    """
    获取文件信息
    
    Args:
        file_path (str): 文件路径
    
    Returns:
        str: 文件信息
    """
    try:
        stat = os.stat(file_path)
        size = stat.st_size
        modified_time = datetime.fromtimestamp(stat.st_mtime)
        
        return f"""文件信息:
- 路径:{file_path}
- 大小:{size} 字节
- 修改时间:{modified_time.strftime('%Y-%m-%d %H:%M:%S')}
"""
    except FileNotFoundError:
        return f"错误:文件 {file_path} 不存在"
    except Exception as e:
        return f"获取文件信息时出错:{str(e)}"

# 创建文件操作智能体
file_agent = Agent(
    name="文件助手",
    model=DeepSeek(api_key="DEEPSEEK_API_KEY"),
    tools=[read_file, write_file, list_files, get_file_info],
    instructions=[
        "你是一个文件操作助手",
        "帮助用户读取、写入和管理文件",
        "操作前确认用户意图,避免误操作",
        "提供清晰的操作结果反馈"
    ],
    show_tool_calls=True,
    markdown=True,
)

# 测试
if __name__ == "__main__":
    # 示例:列出当前目录文件
    file_agent.print_response("列出当前目录下的所有文件")

🎁 内置工具介绍

Agno框架提供了丰富的预构建工具包,开箱即用:

1. 搜索工具

DuckDuckGoTools - 网页搜索

from agno.tools.duckduckgo import DuckDuckGoTools

agent = Agent(
    model=DeepSeek(api_key="DEEPSEEK_API_KEY"),
    tools=[DuckDuckGoTools()],
)

功能:搜索网页内容、新闻、图片等

ExaTools - 语义搜索

from agno.tools.exa import ExaTools

agent = Agent(
    model=DeepSeek(api_key="DEEPSEEK_API_KEY"),
    tools=[ExaTools(api_key="EXA_API_KEY")],
)

功能:基于语义的智能搜索,理解查询意图

2. 金融工具

YFinanceTools - 股票查询

from agno.tools.yfinance import YFinanceTools

agent = Agent(
    model=DeepSeek(api_key="DEEPSEEK_API_KEY"),
    tools=[YFinanceTools()],
)

功能:查询股票价格、历史数据、财务信息等

3. 代码执行工具

PythonTools - Python代码执行

from agno.tools.python import PythonTools

agent = Agent(
    model=DeepSeek(api_key="DEEPSEEK_API_KEY"),
    tools=[PythonTools()],
)

功能:在沙箱环境中执行Python代码

4. 数据库工具

PostgresTools - PostgreSQL数据库

from agno.tools.postgres import PostgresTools

agent = Agent(
    model=DeepSeek(api_key="DEEPSEEK_API_KEY"),
    tools=[PostgresTools(connection_string="postgresql://...")],
)

SQLiteTools - SQLite数据库

from agno.tools.sqlite import SQLiteTools

agent = Agent(
    model=DeepSeek(api_key="DEEPSEEK_API_KEY"),
    tools=[SQLiteTools(db_path="database.db")],
)

5. 文件处理工具

PdfTools - PDF文件处理

from agno.tools.pdf import PdfTools

agent = Agent(
    model=DeepSeek(api_key="DEEPSEEK_API_KEY"),
    tools=[PdfTools()],
)

DocxTools - Word文档处理

from agno.tools.docx import DocxTools

agent = Agent(
    model=DeepSeek(api_key="DEEPSEEK_API_KEY"),
    tools=[DocxTools()],
)

6. 其他实用工具

  • BraveTools: Brave搜索引擎
  • GoogleSearchTools: Google搜索
  • ArxivTools: 学术论文搜索
  • WikipediaTools: 维基百科查询
  • EmailTools: 邮件发送
  • SlackTools: Slack消息发送

使用内置工具的完整示例

from agno.agent import Agent
from agno.models.deepseek import DeepSeek
from agno.tools.duckduckgo import DuckDuckGoTools
from agno.tools.yfinance import YFinanceTools

# 创建多功能智能助手
assistant = Agent(
    name="多功能助手",
    model=DeepSeek(api_key="DEEPSEEK_API_KEY"),
    tools=[
        DuckDuckGoTools(),  # 网页搜索
        YFinanceTools(),    # 股票查询
    ],
    instructions=[
        "你是一个多功能智能助手",
        "可以搜索网页信息和查询股票数据",
        "提供准确、及时的信息"
    ],
    show_tool_calls=True,
    markdown=True,
)

# 测试网页搜索
assistant.print_response("搜索最新的人工智能发展趋势")

print("\n" + "="*50 + "\n")

# 测试股票查询
assistant.print_response("查询苹果公司(AAPL)的股票价格")

✨ 最佳实践和注意事项

最佳实践

1. 工具命名规范

# ✅ 好的命名
def get_weather(city: str) -> str:
    pass

def calculate_mortgage_payment(principal: float, rate: float, years: int) -> float:
    pass

# ❌ 不好的命名
def func1(x):
    pass

def do_stuff(data):
    pass

2. 完善的文档字符串

# ✅ 详细的文档
def search_products(keyword: str, max_results: int = 10) -> list:
    """
    在商品库中搜索产品
    
    这个函数会在所有可用商品中搜索包含关键词的产品,
    并返回最相关的结果。
    
    Args:
        keyword (str): 搜索关键词,例如"手机"、"笔记本"
        max_results (int): 最大返回结果数,默认10条
    
    Returns:
        list: 商品信息列表,每个元素包含商品名称、价格等
    
    Example:
        >>> search_products("iPhone", max_results=5)
        [{'name': 'iPhone 15', 'price': 5999}, ...]
    """
    pass

# ❌ 缺少文档
def search(k, n=10):
    pass

3. 错误处理

# ✅ 处理异常情况
def divide_safe(a: float, b: float) -> str:
    """
    安全的除法运算
    
    Args:
        a (float): 被除数
        b (float): 除数
    
    Returns:
        str: 计算结果或错误信息
    """
    try:
        if b == 0:
            return "错误:除数不能为零"
        result = a / b
        return f"计算结果:{result}"
    except Exception as e:
        return f"计算出错:{str(e)}"

# ❌ 没有错误处理
def divide(a: float, b: float) -> float:
    return a / b  # b为0时会崩溃

4. 返回清晰的结果

# ✅ 结构化、易理解的返回值
def get_user_info(user_id: int) -> str:
    """获取用户信息"""
    user = {
        "id": user_id,
        "name": "张三",
        "email": "zhangsan@example.com"
    }
    return f"用户信息:\n姓名:{user['name']}\n邮箱:{user['email']}"

# ❌ 难以理解的返回值
def get_user(uid):
    return (123, "张三", "zhangsan@example.com")  # Agent难以理解

5. 工具的单一职责

# ✅ 每个工具专注做一件事
def get_temperature(city: str) -> float:
    """获取温度"""
    pass

def get_humidity(city: str) -> float:
    """获取湿度"""
    pass

# ❌ 一个工具做太多事
def get_all_weather_data(city: str, include_forecast: bool, 
                         days: int, units: str) -> dict:
    """获取所有天气数据(功能太复杂)"""
    pass

注意事项

⚠️ 1. API密钥安全

# ✅ 使用环境变量
import os
api_key = os.getenv("DEEPSEEK_API_KEY")

agent = Agent(
    model=DeepSeek(api_key=api_key),
    tools=[...]
)

# ❌ 硬编码密钥(危险!)
agent = Agent(
    model=DeepSeek(api_key="sk-1234567890abcdef"),  # 不要这样做!
    tools=[...]
)

⚠️ 2. 工具执行成本

某些工具可能会产生费用(API调用、计算资源等),建议:

  • 设置合理的超时时间
  • 限制调用频率
  • 监控工具使用情况
from agno.tools import tool

@tool(
    timeout=30,  # 30秒超时
    max_retries=3,  # 最多重试3次
)
def expensive_api_call(query: str) -> str:
    """调用付费API"""
    pass

⚠️ 3. 工具数量的平衡

  • 太少:Agent能力受限
  • 太多:选择困难,响应变慢

建议

  • 单个Agent配备5-15个工具
  • 将相关工具组织成工具包
  • 使用多Agent系统处理复杂任务

⚠️ 4. 调试技巧

# 开启详细日志
agent = Agent(
    model=DeepSeek(api_key="DEEPSEEK_API_KEY"),
    tools=[...],
    show_tool_calls=True,  # 显示工具调用
    debug_mode=True,  # 调试模式
)

⚠️ 5. 工具的幂等性

对于有副作用的操作(如发送邮件、创建文件),要注意:

def send_email_safe(to: str, subject: str, body: str) -> str:
    """
    发送邮件(带确认机制)
    
    Args:
        to: 收件人
        subject: 主题
        body: 正文
    
    Returns:
        str: 操作结果
    """
    # 实际应用中可能需要用户确认
    confirmation = f"确认要发送邮件给 {to} 吗?主题:{subject}"
    # 这里应该有确认逻辑
    return f"邮件已发送给 {to}"

📚 小结与下期预告

本篇回顾

在这篇教程中,我们深入学习了Agno框架中的工具系统:

  1. 理解工具本质:工具是Agent与外部世界交互的桥梁
  2. 掌握工具定义:学会用Python函数创建自定义工具
  3. 学会工具绑定:了解多种将工具绑定到Agent的方法
  4. 理解执行流程:明白工具如何被选择和调用
  5. 实践完整案例:实现了天气查询、计算器、文件操作等实用工具
  6. 探索内置工具:了解Agno丰富的预构建工具生态
  7. 掌握最佳实践:学会编写高质量、安全的工具

通过工具系统,我们的智能体从"只会聊天"变成了"能做实事"的超级助手!

实践建议

在继续学习下一篇之前,建议你:

  1. 动手实践:运行本文的所有代码示例
  2. 创造工具:根据自己的需求创建1-2个自定义工具
  3. 组合尝试:尝试给Agent配备多个工具,观察它如何选择
  4. 探索内置工具:试用DuckDuckGoTools、YFinanceTools等内置工具

下期预告

在下一篇教程中,我们将学习:

《Agno开发教程系列(三):Agent的记忆系统——让AI记住你》

内容预告:

  • 🧠 什么是Agent的记忆
  • 💾 短期记忆 vs 长期记忆
  • 🗄️ 记忆存储的实现
  • 🔍 记忆检索与上下文管理
  • 💬 构建能"记住"对话历史的智能体
  • 📊 实现知识库和RAG系统

敬请期待!


学习资源


如果这篇教程对你有帮助,欢迎点赞、转发、收藏!有任何问题欢迎在评论区留言讨论。

让我们一起探索AI Agent的无限可能! 🚀✨


本文为Agno开发教程系列第二篇,更多精彩内容持续更新中...