📖 引言
在上一篇文章中,我们介绍了Agno框架的基本概念,并成功构建了第一个智能体。我们学习了如何使用DeepSeek模型创建Agent,让它能够理解我们的问题并给出回答。
但你可能会发现,上一篇的智能体虽然"聪明",却有些"孤立"——它只能基于训练数据给出答案,无法获取实时信息,也不能执行具体操作。就像一个博学的学者,虽然知识渊博,却无法查询今天的天气、计算复杂公式或访问外部数据库。
那么,如何让智能体突破这些限制呢?答案就是:工具(Tools)!
本篇文章将深入讲解Agno框架中的工具系统,教你如何为智能体配备各种"超能力",让它能够:
- 🌤️ 查询实时天气信息
- 🧮 执行精确的数学计算
- 🔍 搜索互联网内容
- 📊 访问数据库和文件
- 🐍 运行Python代码
- ...以及更多可能性
让我们开始这段赋能之旅吧!
🔧 什么是工具(Tool)
工具的本质
在Agno框架中,Tool(工具)本质上是Agent用于与外部系统交互的函数。它们是智能体的"手"和"眼",让AI能够:
- 获取实时信息:查询天气、股票价格、新闻等
- 执行具体操作:发送邮件、创建文件、操作数据库
- 调用外部服务:搜索引擎、API接口、第三方平台
- 进行复杂计算:数学运算、数据分析、代码执行
工具的工作原理
当你向配备了工具的智能体提问时,它会:
用户提问 → Agent分析任务 → 选择合适的工具 → 执行工具 → 获取结果 → 组织答案 → 返回给用户
举个例子:
- 问题:"北京今天天气怎么样?"
- 分析:需要获取实时天气信息
- 选择工具:调用天气查询工具
- 执行:get_weather("北京")
- 返回结果:"晴天,温度25℃"
- 组织答案:"北京今天天气晴朗,气温25摄氏度,适合外出活动。"
这种机制让AI不再局限于预训练知识,而是能够动态获取信息并执行操作。
📝 如何定义工具
在Agno中定义工具非常简单,主要有两种方式:
方式一:普通Python函数
任何Python函数都可以作为工具,只需要遵循以下规范:
- 添加类型提示:明确参数和返回值类型
- 编写详细的docstring:描述函数功能和参数说明
- 返回清晰的结果:便于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}的天气信息暂时无法获取")
定义工具的最佳实践
- ✅ 明确的函数命名:使用描述性名称,如
get_weather而不是func1 - ✅ 完整的类型提示:帮助Agent理解参数类型
- ✅ 详细的docstring:描述功能、参数和返回值
- ✅ 错误处理:处理可能的异常情况
- ✅ 返回结构化数据:使用字符串、字典或列表等清晰的格式
🔗 如何将工具绑定到智能体
定义好工具后,需要将它们绑定到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可能会:
- 先调用
add(5, 3)得到8 - 再调用
multiply(8, 4)得到32 - 最终返回答案
💡 完整示例:实用工具集
下面提供几个完整可运行的实用工具示例:
示例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框架中的工具系统:
- ✅ 理解工具本质:工具是Agent与外部世界交互的桥梁
- ✅ 掌握工具定义:学会用Python函数创建自定义工具
- ✅ 学会工具绑定:了解多种将工具绑定到Agent的方法
- ✅ 理解执行流程:明白工具如何被选择和调用
- ✅ 实践完整案例:实现了天气查询、计算器、文件操作等实用工具
- ✅ 探索内置工具:了解Agno丰富的预构建工具生态
- ✅ 掌握最佳实践:学会编写高质量、安全的工具
通过工具系统,我们的智能体从"只会聊天"变成了"能做实事"的超级助手!
实践建议
在继续学习下一篇之前,建议你:
- 动手实践:运行本文的所有代码示例
- 创造工具:根据自己的需求创建1-2个自定义工具
- 组合尝试:尝试给Agent配备多个工具,观察它如何选择
- 探索内置工具:试用DuckDuckGoTools、YFinanceTools等内置工具
下期预告
在下一篇教程中,我们将学习:
《Agno开发教程系列(三):Agent的记忆系统——让AI记住你》
内容预告:
- 🧠 什么是Agent的记忆
- 💾 短期记忆 vs 长期记忆
- 🗄️ 记忆存储的实现
- 🔍 记忆检索与上下文管理
- 💬 构建能"记住"对话历史的智能体
- 📊 实现知识库和RAG系统
敬请期待!
学习资源
- Agno官方文档: docs.agno.ai
- 示例代码仓库: github.com/agno-ai/agn…
- DeepSeek API文档: platform.deepseek.com/docs
如果这篇教程对你有帮助,欢迎点赞、转发、收藏!有任何问题欢迎在评论区留言讨论。
让我们一起探索AI Agent的无限可能! 🚀✨
本文为Agno开发教程系列第二篇,更多精彩内容持续更新中...