Day 3:多工具时代,Agent 自己选——加入计算器和时间工具

0 阅读5分钟

Day 3:多工具时代,Agent 自己选——加入计算器和时间工具

《7天从零手搓 AI Agent》第3篇 · 今日成果:Agent 同时拥有4个工具,能根据问题自动选对

day3.png 大家好,欢迎来到小撒的私房菜,我是小撒。

昨天 Agent 有2个工具,今天加到4个。

但今天真正有趣的不是"工具变多了",而是一个问题:

AI 面对多个工具,它怎么选?会不会选错?

答案是:大部分时候能选对,偶尔选错。而你能通过改工具描述来提高选对率。

这一章我们来把这个问题摸透。


两个新工具

工具1:计算器

# tools/calculator.py

def calculate(expression: str) -> str:
    """
    计算数学表达式。
    只允许数字和基本运算符,防止代码注入。
    """
    allowed_chars = set("0123456789+-*/()., ")
    if not all(c in allowed_chars for c in expression):
        return f"表达式包含不允许的字符:{expression!r}"

    try:
        result = eval(expression)
        return f"{expression} = {result}"
    except ZeroDivisionError:
        return "错误:除数不能为零"
    except SyntaxError:
        return f"表达式语法错误:{expression!r}"
    except Exception as e:
        return f"计算出错:{e}"

关于 eval

很多人一看到 eval 就觉得不安全,没错,eval 确实可以执行任意 Python 代码。但我们在前面加了字符白名单过滤,只允许数字和 +-*/()., 这几个字符,importos 这些危险关键词全都会被拦截。

这是教学项目的合理实现。生产环境可以换 sympy 或者 numexpr

工具2:查当前时间

# tools/datetime_tool.py
from datetime import datetime
import pytz

def get_current_time(timezone: str = "Asia/Shanghai") -> str:
    """获取当前时间,默认北京时间。"""
    try:
        tz = pytz.timezone(timezone)
    except pytz.exceptions.UnknownTimeZoneError:
        # 时区名写错了,回退到北京时间
        tz = pytz.timezone("Asia/Shanghai")
        timezone = "Asia/Shanghai(时区名无效,已回退)"

    now = datetime.now(tz)
    return now.strftime(f"%Y年%m月%d日 %H:%M:%S({timezone})")

更新工具注册表

tool_registry.py 里加两条记录:

from tools.search import web_search
from tools.weather import get_weather
from tools.calculator import calculate
from tools.datetime_tool import get_current_time

TOOLS: dict[str, dict] = {
    "web_search": {
        "function": web_search,
        "description": "搜索互联网上的信息。适合查找新闻、事实、最新资讯。不适合计算。",
        "parameters": {"query": "搜索关键词,字符串"},
    },
    "get_weather": {
        "function": get_weather,
        "description": "查询某个城市的天气情况。",
        "parameters": {"city": "城市名称,字符串,例如:北京"},
    },
    "calculate": {
        "function": calculate,
        "description": "计算数学表达式,支持加减乘除和括号。只用于数学计算,不用于其他问题。",
        "parameters": {"expression": "数学表达式字符串,例如:(3+5)*2"},
    },
    "get_current_time": {
        "function": get_current_time,
        "description": "获取当前日期和时间。",
        "parameters": {"timezone": "时区名称,默认 Asia/Shanghai"},
    },
}

注意 calculate 的描述:"只用于数学计算,不用于其他问题。"

这句话很重要。如果不加,AI 可能会把"帮我计算一下这件事的影响"这种模糊问题也分配给计算器,但那不是数学表达式。

描述越清晰,工具选择越准确。


运行效果

你:现在几点了?
[AI 决策]: {"action": "use_tool", "tool": "get_current_time", "params": {"timezone": "Asia/Shanghai"}}
Agent:2024031514:30:22(Asia/Shanghai)

你:(123 + 456) * 2 等于多少?
[AI 决策]: {"action": "use_tool", "tool": "calculate", "params": {"expression": "(123 + 456) * 2"}}
Agent:(123 + 456) * 2 = 1158

你:上海今天天气怎么样,适合出门吗?
[AI 决策]: {"action": "use_tool", "tool": "get_weather", "params": {"city": "上海"}}
Agent:多云,18°C,南风2级

你:Python 是什么时候发布的?
[AI 决策]: {"action": "use_tool", "tool": "web_search", "params": {"query": "Python 编程语言发布时间"}}
Agent:摘要:Python 于1991年由...

AI 对4个不同类型的问题,全部选对了工具。


工具选错了怎么办

偶尔 AI 会选错,比如把"帮我计算一下北京到上海的距离"分配给计算器(但其实应该搜索)。

两个调试方法:

方法1:改工具描述

在出问题的工具描述里加明确限制:

"calculate": {
    "description": "计算纯数学表达式,如 '2+3' 或 '(100*5)/2'。"
                   "不适合地理、文字、概念类问题。"
    ...
}

方法2:打印 AI 决策,理解它为什么选错

print(f"[AI 决策]: {ai_response}")

看 AI 的 thought(如果你在 prompt 里要求它说出理由)或者分析它选错的原因,然后针对性修改描述。

这就像调参。工具描述是 Agent 的"超参数"。


如何自己加一个新工具

步骤:

  1. tools/ 下新建 tools/my_tool.py,写一个函数
# tools/my_tool.py
def my_tool(param1: str) -> str:
    # 做些什么
    return "结果"
  1. tool_registry.py 里加一条:
from tools.my_tool import my_tool

TOOLS = {
    # ... 已有的工具 ...
    "my_tool": {
        "function": my_tool,
        "description": "这个工具用来做什么,什么时候用它。",
        "parameters": {"param1": "参数说明"},
    },
}
  1. 运行。搞定。

不需要改 agent.py,不需要改 main.py,不需要改任何其他文件。


今天的项目结构

my_agent/
├── .env
├── llm.py
├── agent.py
├── tool_registry.py    # 新增了两条工具记录
├── tools/
│   ├── __init__.py
│   ├── search.py
│   ├── weather.py
│   ├── calculator.py   # 新增
│   └── datetime_tool.py # 新增
└── main.py

小结

今天做的事情比代码量看起来要重要:

  • 工具数量增加了,但工具注册表的结构没变
  • 工具选择靠的是描述质量,不是魔法
  • 加新工具是两步操作,不需要动核心代码

这就是"开放-封闭原则"在 Agent 系统里的体现:对扩展开放,对修改封闭。


明天,Day 4:《让 Agent 记住你——短期记忆实现》

今天的 Agent 还有个大问题:它不记得你说过什么。每次对话都是全新的。

明天解决这个问题。


代码同步在 GitHub,文末有链接。

如果本教程对你有所帮助,留下一个免费的三连吧 ♥️!