前篇《 AI - 使用 Google ADK 创建你的第一个 AI Agent》用 Google ADK 创建了第一个 Agent,只有一个 Python 函数当作 tool,get_current_time(city),Agent 收到问题后会根据需要调用这个函数,返回结果。

这一篇我们沿着官方 Quickstart 的例子,升级成一个:

既能查天气、又能看时间的多工具(multi‑tool)Agent
并搞清楚一个核心问题: 多个工具放在 tools=[…] 里之后,它们是怎么被自动选择和调用的?

1. 从一个工具,到两个工具

我们第一个 Agent 只有一个工具是这样的(只演示核心代码):

from google.adk.agents.llm_agent import Agent

# Mock tool implementation
def get_current_time(city: str) -> dict:
    """Returns the current time in a specified city."""
    return {"status": "success", "city": city, "time": "10:30 AM"}

root_agent = Agent(
    model='gemini-3-pro-preview',
    name='root_agent',
    description="Tells the current time in a specified city.",
    instruction="You are a helpful assistant that tells the current time in cities. Use the 'get_current_time' tool for this purpose.",
    tools=[get_current_time],
)

现在我们想让它既能回答时间,又能回答天气。我们只需要再加一个函数 get_weather,然后把两个函数都丢进 tools=[…]。
下面是一个完整的多工具示例:

# multi_tool_agent/agent.py
import datetime
from zoneinfo import ZoneInfo
from google.adk.agents import Agent

def get_weather(city: str) -> dict:
    """
    查询指定城市的天气(示例里只支持 New York)。
    Args:
        city: 城市名,比如 "New York"
    Returns:
        dict: 包含 status, report 或 error_message。
    """
    if city.lower() == "new york":
        return {
            "status": "success",
            "report": "New York 今天天气晴,气温约 25℃(77℉)。",
        }
    else:
        return {
            "status": "error",
            "error_message": f"暂时没有 {city} 的天气数据。",
        }


def get_current_time(city: str) -> dict:
    """
    返回指定城市的当前时间(示例里只支持 New York)。
    Args:
        city: 城市名,比如 "New York"
    Returns:
        dict: 包含 status, report 或 error_message。
    """
    if city.lower() == "new york":
        tz_identifier = "America/New_York"
    else:
        return {
            "status": "error",
            "error_message": f"暂时没有 {city} 的时区信息。",
        }

    tz = ZoneInfo(tz_identifier)
    now = datetime.datetime.now(tz)
    report = f"New York 当前时间是 {now.strftime('%Y-%m-%d %H:%M:%S %Z%z')}"
    return {"status": "success", "report": report}


root_agent = Agent(
    name="weather_time_agent",
    model="gemini-3-pro-preview",
    description="回答某个城市的天气和时间问题。",
    instruction=(
        "你是一个乐于助人的助手,可以回答关于城市天气和时间的问题。"
        "如果用户问天气,就调用天气工具;如果问时间,就调用时间工具;"
        "如果两个都问,就依次调用。"
    ),
    tools=[get_weather, get_current_time],  # ✅ 多个工具就这样直接丢进列表
)

会话记录如下:
在这里插入图片描述

到这里,我们已经拥有了一个 “多工具 Agent”。但真正有意思的是:我们没有写任何 if / else 去判断“到底用哪个工具”,这个全是 Agent(背后的 LLM)自动搞定的。

接下来就拆它的工作原理。

2. 在 ADK 里,tool 本质上就是“长得好看的函数”

官方文档把这种工具叫做 Function Tool:

只要你把一个 Python 函数塞进 Agent(…, tools=[…]),
ADK 就会自动把它包装成一个 FunctionTool,供 LLM 调用。

更关键的是:ADK 会自动“读懂”你的函数签名和注释:

框架会检查函数的名字、参数、类型标注、默认值,以及 docstring, 自动生成一份 schema,告诉 LLM:

  • 这个工具是干嘛的(描述)
  • 有哪些入参(参数名 / 类型 / 是否必填)
  • 什么时候可以用它

换句话说,当你写:

def get_weather(city: str) -> dict:
    """
    查询指定城市的天气。
    Args:
        city: 城市名,比如 "New York"
    """
    ...

ADK 大概会生成一份类似这样的“工具说明”(伪代码):

{
  "name": "get_weather",
  "description": "查询指定城市的天气(示例里只支持 New York)。 Args: city: 城市名,比如 "New York" Returns: dict: 包含 status, report 或 error_message。",
  "parameters": {
    "type": "object",
    "properties": {
      "city": {
        "type": "string",
        "description": "城市名,比如 'New York'"
      }
    },
    "required": ["city"]
  }
}

这份说明会和你的系统提示、用户问题一起发给 LLM,让模型知道:

  • 有一个叫 get_weather 的工具
  • 干嘛用:查天气
  • 怎么调用:需要一个字符串参数 city

同理,get_current_time 也会被包装成另一个 Tool。
所以:只要函数签名和 docstring 写得清楚,工具的“自我介绍”就自动做好了。

3. 多个 tool 是如何被“自动选择”的?

重点来了:当我们把两个函数都写进 tools=[get_weather, get_current_time] 以后,整条调用链大概长这样:

第 1 步:Agent 接到用户问题

例如:

  • 「纽约天气怎么样?」
  • 「纽约现在几点?」
  • 「告诉我纽约现在的天气和时间。」

ADK 把这些信息组装成一个 LLM 请求:

  • Agent 的 instruction、description
  • 当前会话的历史
  • 两个工具的 schema(名字、描述、参数)

第 2 步:LLM 自己决定要不要用工具、用哪个工具

模型看到:

  • 工具 A:get_weather(city) → 描述是“查询指定城市的天气”
  • 工具 B:get_current_time(city) → 描述是“返回指定城市的当前时间”

然后根据用户问题去“匹配语义”:

  • 问“天气” → 更像 get_weather
  • 问“时间” → 更像 get_current_time
  • 问“天气 + 时间” → 很可能依次调用两个工具

这一步就是 “tool calling” / “函数调用” 能力:
模型不再只输出自然语言,而是可以输出一个「我要调用哪个工具、参数是什么」的结构化指令。

第 3 步:ADK 执行 tool,拿到结果

一旦 LLM 决定调用某个工具,ADK 会做两件事:

  1. 在 Python 里真正执行这个函数:
result = get_weather(city="New York")
  1. 把执行结果(通常是一份 dict)再当作“工具结果消息”喂回 LLM。

    官方文档建议 Function Tool 返回 dict,并包含 status 和类似 error_message 这样的字段,帮助 LLM 理解操作是否成功。

比如:

{"status": "success", "report": "New York 今天天气晴..."}

或则:

{"status": "error", "error_message": "暂时没有北京的天气数据。"}

第 4 步:LLM 根据工具结果,生成最后的自然语言回答

有了工具结果,模型会再次生成自然语言,像这样:

  • 成功时:「New York 今天天气晴朗,气温大约 25℃。」
  • 工具报错时:「我暂时查不到北京的天气数据,可以换个城市试试吗?」

如果用户问题里同时问了“天气+时间”,模型可能会:

  • 先调用 get_weather

  • 再调用 get_current_time

  • 然后把两份结果合在一起回复:

    「New York 今天天气晴,大约 25℃。
    当前时间是 2025‑05‑01 10:30 EDT。」

整个过程你都不需要写 if “天气” in question: …,
全靠工具的 schema + LLM 的推理能力来做判断。

完整过程示例

用户问题:“请问纽约的天气怎样,当前时间是多少?”

  • ADK 组装 Agent 信息加上历史会话记录向 LLM 模型发出 Request:
    在这里插入图片描述
  • LLM 模型匹配语义决定调用哪些工具 Response:
    在这里插入图片描述
  • ADK 执行 tools 拿到结果
    在这里插入图片描述
  • LLM 模型根据工具结果,生成最后的自然语言回答
    在这里插入图片描述

4. 为什么返回值里会有 status 和 report?

在示例里,你会发现两个工具的返回结构很相似:

return {
    "status": "success",
    "report": "xxx 正常情况的描述 ..."
}
# 或者
return {
    "status": "error",
    "error_message": "具体失败原因 ..."
}

这是有讲究的:

  • LLM 要“看得懂”你的返回值
    • status → 这是成功还是失败?
    • report → 能直接拿来转成自然语言的内容
    • error_message → 出错时可以直接告诉用户,或者自动换个工具 / 换种回答方式
  • 多个工具统一一个返回结构,LLM 更好“拼”结果
    当你有很多工具时,如果都用类似的结构(例如都包含 status + report),模型可以用几乎一样的逻辑来组合输出。
  • 这是官方文档推荐的最佳实践
    Function Tools 文档里明确建议:
    • 使用 dict/Map 作为返回类型
    • 尽量返回带语义的字段(例如 status, error_message),而不是冷冰冰的数字码。

5. 小结:写多工具 Agent 的几个实用建议

  • 每个工具就是一个职责单一的 Python 函数
    • 名字直白:get_weather、get_current_time
    • 参数清晰:有类型标注,必填/可选明确
    • 返回 dict:包含 status、report、error_message 等
  • Docstring 写好就是在给 LLM 做“工具使用说明书”
    ADK 会把 docstring 发给 LLM,所以越清晰,模型越容易选对工具。
  • Agent 的 instruction 里可以给一点“调度建议”
    比如:“如果用户问天气,用 get_weather;如果问时间,用 get_current_time;如果都问,就依次调用这两个工具。”
    这样模型在选择工具时就有了明确的策略提示。
  • 可以在 Dev UI / CLI 里观察工具调用情况
    • 用 adk run multi_tool_agent 在终端试:输入不同问题看看 Agent 调用哪个工具。
    • 用 adk web 打开 Dev UI,可以看到 event 流和 tool 调用记录,更方便调试。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐