中文译本。原文见同目录 README.md

第 3 课:工具 - 给 Agent 双手

引言

在第 1 课中,我们确立了 Agent 是大脑(LLM)加双手(工具)加控制循环(编排)。第 2 课探索了大脑。现在我们给 Agent 双手。

没有工具,LLM 只能生成文本。它可以推理一个问题,但不能在数据库里查答案;可以规划一次部署,但不能真正跑部署脚本;可以起草邮件,但不能发出去。

工具弥合「思考」与「行动」之间的鸿沟。它们是 Agent 与外部世界交互的机制——读数据、调 API、执行代码、采取行动。

本课讲解工具是什么、不同类型的工具、函数调用底层如何工作、设计工具的最佳实践,以及即便有经验的工程师也会踩的常见坑。


为什么 Agent 需要工具

考虑这个场景。用户问你的 Agent:「有多少未关闭的 bug 分配给我?」

没有工具时: LLM 无法访问你的 bug 跟踪器。它可能说「我无法访问你的 bug 跟踪器」(运气好),或幻觉出一个数字(运气不好)。

有工具时: Agent 想「我需要查 bug 跟踪器。」它调用 get_bugs(assignee="current_user", status="open") 工具。工具查询你的 Jira/Linear/GitHub Issues 实例,返回 [{id: 1234, title: "Login timeout"}, {id: 1235, title: "CSS overflow on mobile"}]。Agent 统计结果并回复:「你有 2 个未关闭 bug:Login timeout(#1234)和 CSS overflow on mobile(#1235)。」

工具把 Agent 从聪明的对话伙伴,变成真正能把事情做完的能干助手。

工具能带来什么

没有工具 有工具
根据训练数据回答(可能过时) 根据实时数据回答
描述应该做什么 真正去做
推理代码 运行代码并看到输出
猜测当前状态 查询并观察真实状态
起草消息 发送消息

工具的类型

工具有多种形态。理解不同类型有助于为你的 Agent 选择正确做法。

1. 函数工具(自定义函数)

这些是你定义、Agent 可调用的函数。你控制实现、输入、输出与错误处理。

例子:

  • search_database(query, filters) - 查询内部数据库
  • create_ticket(title, description, priority) - 创建支持工单
  • send_notification(user_id, message) - 发送推送通知
  • get_weather(city) - 从第三方 API 获取天气
  • run_test_suite(test_path) - 执行测试并返回结果

函数工具是最常见的类型。它们是你把 Agent 能力扩展到具体用例的主要方式。

2. 内置工具(平台提供)

这些是你所用平台或框架提供的工具。预构建、开箱即用。

Google Cloud / Gemini 内置工具:

工具 做什么
Google Search 用实时网页搜索结果锚定模型回复。通过提供当前信息减少幻觉。
Code Execution 在沙箱环境中运行 Python 代码。适合数学、数据分析与生成图表。
URL Context 获取并处理给定 URL 的内容。

何时用内置工具:

  • 你需要的功能已作为内置存在
  • 希望平台管理执行、沙箱与扩缩容
  • 希望避免自行维护常见能力的实现

何时改为构建自定义函数工具:

  • 需要访问自己的系统与数据
  • 需要自定义逻辑或业务规则
  • 内置工具覆盖不了你的用例

3. Agent 工具(以 Agent 为工具)

这是一种强大模式:一个 Agent 把另一个 Agent 当作工具使用。调用方 Agent 把子任务委派给专业化 Agent,拿回结果,再继续自己的工作流。

例子:

Main Agent (Trip Planner)
    |
    +-- calls --> Flight Search Agent
    |                (specialized in finding flights)
    |
    +-- calls --> Hotel Agent
    |                (specialized in hotel bookings)
    |
    +-- calls --> Activity Agent
                     (specialized in local activities)

主 Agent 协调整体行程规划。每个子 Agent 是其领域专家,有自己的工具与指令。主 Agent 不必知道航班搜索如何工作——它只需问 Flight Search Agent 并拿回结果。

何时用 Agent 工具:

  • 子任务足够复杂,值得有自己的专业化工具与指令
  • 希望分离关注点、让每个 Agent 更简单
  • 不同子任务受益于不同模型或配置

函数调用逐步讲解

函数调用是启用工具使用的核心机制。下面逐步说明它如何工作。

流程

Step 1: You define tools and send them to the model with the user's message
Step 2: The model decides whether to call a tool (and which one)
Step 3: The model returns a structured tool call (function name + arguments)
Step 4: YOUR CODE executes the function (the model never runs it)
Step 5: You send the function result back to the model
Step 6: The model uses the result to generate a response (or call another tool)

这一点至关重要:模型不执行工具。它提出工具调用。你的代码执行它们。模型从不直接碰你的数据库、API 或系统。你始终掌控一切。

逐步例子

我们用天气 Agent 走一遍具体例子。

步骤 1:定义工具并发送请求

tools = [
    {
        "name": "get_weather",
        "description": "Get the current weather for a given city. Returns temperature, conditions, and humidity.",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {
                    "type": "string",
                    "description": "The city name, e.g. 'London' or 'San Francisco'"
                },
                "units": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "description": "Temperature units"
                }
            },
            "required": ["city"]
        }
    }
]

# Send to model with user message
response = model.generate(
    messages=[{"role": "user", "content": "What is the weather in Tokyo?"}],
    tools=tools
)

步骤 2-3:模型返回工具调用

模型不以文本回复。相反,它返回结构化的工具调用:

{
    "tool_call": {
        "name": "get_weather",
        "arguments": {
            "city": "Tokyo",
            "units": "celsius"
        }
    }
}

模型弄清了:

  • 需要天气工具(不是别的工具)
  • 城市是 "Tokyo"(从用户消息抽取)
  • 对东京用摄氏度可能合适(从上下文推断)

步骤 4:你的代码执行函数

# This is YOUR code - you control what happens here
def get_weather(city, units="celsius"):
    # Call a real weather API
    response = weather_api.get(city=city, units=units)
    return {
        "city": city,
        "temperature": response.temp,
        "conditions": response.conditions,
        "humidity": response.humidity
    }

# Execute the tool call
result = get_weather(**tool_call.arguments)
# result = {"city": "Tokyo", "temperature": 18, "conditions": "Partly cloudy", "humidity": "65%"}

步骤 5-6:把结果送回并得到回复

response = model.generate(
    messages=[
        {"role": "user", "content": "What is the weather in Tokyo?"},
        {"role": "assistant", "tool_call": tool_call},
        {"role": "tool", "content": json.dumps(result)}
    ],
    tools=tools
)
# Model responds: "The weather in Tokyo is 18 degrees Celsius and partly cloudy
#                  with 65% humidity."

多次工具调用

有时模型需要调用多个工具才能回答问题。可能有两种方式:

顺序(一个接一个):

User: "Compare the weather in Tokyo and London"

Step 1: Model calls get_weather(city="Tokyo")
Step 2: You execute, return result
Step 3: Model calls get_weather(city="London")
Step 4: You execute, return result
Step 5: Model synthesizes both results into a comparison

并行(同时): 有些模型支持并行工具调用,模型在单次响应中提出多个调用。对同一天气对比,模型可一次返回 get_weather("Tokyo") 与 get_weather("London")。你执行两者,把两个结果送回,模型再综合。这更快,因为省了一次往返。

带工具的完整 Agent 循环

在真实 Agent 中,工具调用发生在循环里:把用户消息与工具发给模型,检查响应是文本答案(完成)还是工具调用(执行它,把结果追加到对话,再循环回去)。持续进行,直到模型判定已有足够信息可以回复。

工具调用流程:逐步演示

观看 Agent 如何处理「东京天气怎么样?」——用控件逐步前进。

数据流

      

工具设计最佳实践

如何设计工具,直接影响 Agent 表现。模型需要理解你的工具才能正确使用它们。以下是最重要的原则。

1. 使用清晰、描述性的名称

工具名称是模型首先看到的东西。它应立刻传达工具做什么。

差名称 好名称 原因
do_thing search_knowledge_base 具体说明搜索什么
api_call create_support_ticket 描述动作与目标
process validate_email_address 清晰说明处理什么、如何处理
get_data get_order_by_id 指定什么数据、如何查找
run execute_sql_query 明确运行什么

2. 单一职责

每个工具应把一件事做好。就像代码中的函数,工具应有单一、清晰的目的。

差:一个包揽一切的工具

{
    "name": "manage_orders",
    "description": "Create, read, update, or delete orders",
    "parameters": {
        "action": {"type": "string", "enum": ["create", "read", "update", "delete"]},
        "order_id": {"type": "string"},
        "order_data": {"type": "object"}
    }
}

模型现在既要弄清用哪个 action,又要弄清每个 action 需要什么参数。这会导致错误。

好:每个动作单独工具

[
    {
        "name": "get_order",
        "description": "Look up an order by its ID. Returns order status, items, and shipping info.",
        "parameters": {
            "order_id": {"type": "string", "description": "The order ID, e.g. 'ORD-12345'"}
        }
    },
    {
        "name": "create_order",
        "description": "Create a new order with the specified items.",
        "parameters": {
            "items": {"type": "array", "description": "List of item IDs and quantities"},
            "shipping_address": {"type": "string"}
        }
    },
    {
        "name": "cancel_order",
        "description": "Cancel an existing order. Only works for orders not yet shipped.",
        "parameters": {
            "order_id": {"type": "string"},
            "reason": {"type": "string", "description": "Reason for cancellation"}
        }
    }
]

每个工具有清晰目的与具体参数。模型容易选对。

3. 写描述性参数

参数描述告诉模型传什么。对格式、约束与默认值要具体。

差:

{
    "name": "search",
    "parameters": {
        "q": {"type": "string"},
        "n": {"type": "integer"}
    }
}

模型只能猜 q 和 n 是什么意思。

好:

{
    "name": "search_products",
    "parameters": {
        "query": {
            "type": "string",
            "description": "Search query for product name or description, e.g. 'wireless headphones'"
        },
        "max_results": {
            "type": "integer",
            "description": "Maximum number of results to return. Range: 1-50. Default: 10"
        }
    }
}

4. 返回简洁、有用的输出

工具结果会回到模型的上下文窗口。返回太多数据浪费 token,并可能迷惑模型。

差:返回带一切信息的原始 API 响应

{
    "status": 200,
    "headers": {"content-type": "application/json", "x-request-id": "abc123", ...},
    "data": {
        "order": {
            "id": "ORD-12345",
            "internal_id": "a1b2c3d4-e5f6-7890",
            "created_at": "2024-03-15T10:30:00Z",
            "updated_at": "2024-03-15T14:22:00Z",
            "customer_id": "CUST-789",
            "customer_hash": "sha256:abcdef...",
            "status": "shipped",
            "status_code": 3,
            "status_history": [...50 entries...],
            "items": [...full product details with all fields...],
            "shipping": {...full carrier details...},
            "billing": {...full payment details...},
            "metadata": {...internal tracking data...}
        }
    }
}

好:只返回 Agent 需要的内容

{
    "order_id": "ORD-12345",
    "status": "shipped",
    "items": ["Wireless Headphones x1", "USB-C Cable x2"],
    "tracking_number": "1Z999AA10123456784",
    "estimated_delivery": "March 20, 2026"
}

5. 包含清晰的错误消息

工具调用失败时,错误消息应帮助模型理解哪里错了、下一步试什么。

差错误:

{"error": "Failed"}

好错误:

{
    "error": "ORDER_NOT_FOUND",
    "message": "No order found with ID 'ORD-99999'. Please verify the order ID and try again. Order IDs follow the format ORD-XXXXX."
}

好的错误消息给模型足够信息,要么纠正做法(也许 ID 格式错了),要么向用户说清楚。

6. 在描述中记录工具行为

工具描述是你告诉模型何时以及如何使用工具的机会。包含:

  • 工具做什么(主要动作)
  • 何时使用(条件与触发)
  • 返回什么(输出格式)
  • 局限(它不能做什么)

例子:

{
    "name": "search_knowledge_base",
    "description": "Search the company knowledge base for relevant articles and documentation. Use this tool when the user asks a question about company policies, product features, or internal processes. Returns the top 5 most relevant articles with titles, summaries, and links. Does not search external websites - use web_search for that."
}

N x M 集成问题

随着 Agent 系统增长,工具集成成为重大挑战。原因如下。

问题

想象你有 N 个不同的 AI 应用(编程助手、研究 Agent、支持机器人)和 M 个不同服务(Slack、Jira、GitHub、Salesforce)。没有标准化时,每个应用都需要自定义代码对接每个服务——那是 N x M 个集成要构建与维护。

为何标准化重要

像 Model Context Protocol(MCP) 这类标准,旨在通过在 AI 应用与工具提供方之间创建通用接口来解决这个问题。每个应用实现一次协议。每个工具提供方实现一次协议。新应用自动与所有现有工具兼容,反之亦然。

可以想成 USB。 USB 之前,每个设备有自己的专有线缆。USB 之后,任何设备都能连任何电脑。工具标准旨在成为「AI 工具的 USB」。

选择框架或构建工具时,考虑其他 Agent 能否复用你的工具(建成 MCP server 以便移植),以及你的 Agent 能否使用其他来源的工具(支持 MCP 以获得广泛兼容)。标准仍在演进,请按你的时间线评估。


工具设计的常见坑

1. 工具太多淹没上下文

每个工具定义都占用模型上下文窗口空间。更重要的是,模型必须从全集中推理该用哪个工具。工具太多会导致:

  • 决策瘫痪:模型难以选对工具
  • 选错工具:相似工具多时,模型选错
  • 浪费上下文:工具定义挤占有用的对话历史

多少工具算太多?

没有硬性规则,但有这些指引:

工具数量 指引
1-5 对多数模型没问题。无需特殊处理。
5-15 名称与描述清晰、彼此区分时效果好。
15-30 考虑分组相关工具,或用工具选择步骤。
30+ 可能太多。用两阶段:先选类别,再选具体工具。

大型工具集的两阶段做法:

Stage 1: Model picks a category
  "User wants to manage their order -> Category: Order Management"

Stage 2: Model sees only the tools in that category
  [get_order, create_order, cancel_order, update_shipping]
  -> Model picks: get_order

这让每个单独决策保持可管理。

2. 模糊的工具描述

若模型分不清何时用某个工具,就会用错或根本不用。

差:

{
    "name": "lookup",
    "description": "Looks up information"
}

关于什么的信息?模型何时该调用这个而不是别的工具?响应是什么格式?

好:

{
    "name": "lookup_employee",
    "description": "Look up an employee by name or employee ID. Returns their department, role, email, and manager. Use this when the user asks about a specific person at the company."
}

3. 薄 API 包装(泄漏实现细节)

常见错误是把原始 API 端点包装成工具,没有任何抽象。通用的 api_request(method, url, headers, body) 工具迫使模型知道你的 URL 结构、认证头与请求格式——它经常会弄错。

相反,构建像 get_customer_orders(customer_email, status_filter) 这样的专用工具,把 HTTP 细节藏在干净接口后面。你的代码处理认证、URL 构造与响应解析。模型只需说它想要什么。

4. 缺少错误信息

工具调用失败且错误消息无用时,Agent 会卡在重试循环或彻底放弃。

常见失败模式:

错误类型 差响应 好响应
未找到 {"error": true} {"error": "CUSTOMER_NOT_FOUND", "message": "No customer with email 'jn@example.com'. Did you mean 'jane@example.com'?"}
无效输入 500 Internal Server Error {"error": "INVALID_DATE_FORMAT", "message": "Expected date in YYYY-MM-DD format, got '03/15/2024'"}
限流 {"error": "fail"} {"error": "RATE_LIMITED", "message": "Too many requests. Try again in 30 seconds."}
认证失败 null {"error": "UNAUTHORIZED", "message": "API key expired. This tool is temporarily unavailable."}

5. 返回太多数据

大工具响应会吃掉上下文窗口,并可能用不相关细节迷惑模型。管理策略:

  • 在源头过滤:只从数据库/API 查询你需要的
  • 选择相关字段:不要返回每个字段——挑 Agent 需要的
  • 分页:返回子集,并提供获取更多的选项
  • 摘要:对大文本响应,返回前先摘要
  • 截断:限制长字符串字段(例如 message[:200])

6. 未处理工具超时

外部 API 可能慢或无响应。始终为 HTTP 调用设置超时,失败时返回描述性错误消息,而不是让 Agent 无限挂起。


工具设计检查清单

为 Agent 设计工具时用这份检查清单:

[ ] Name clearly describes the action (verb + noun)
[ ] Description explains what, when, and what it returns
[ ] Each tool has a single responsibility
[ ] Parameters have descriptive names (not abbreviations)
[ ] Parameters include descriptions with format examples
[ ] Required vs optional parameters are clearly marked
[ ] Output is concise - only fields the agent needs
[ ] Error messages are descriptive and actionable
[ ] Timeouts are implemented for external calls
[ ] Authentication is handled in the tool code, not by the model
[ ] Output size is bounded (truncation, pagination, or summarization)
[ ] Tool is tested independently before connecting to the agent

ELI5:什么是工具?

工具就像手机上的 App

想想你的智能手机。手机本身很聪明——有强处理器、好屏幕和好操作系统。但没有 App,它做不了多少事。

  • 想查天气?需要天气 App。
  • 想发消息?需要消息 App。
  • 想导航?需要地图 App。
  • 想拍照?需要相机 App。

每个 App 给手机新能力。手机本身不知道如何预报天气——它只知道如何打开天气 App、要预报、并显示结果。

LLM 就像没有 App 的手机。 它很聪明,但不能查真实天气、发真实消息或查真实路线。它只能根据训练时学到的内容谈论这些事。

工具就像给手机装 App。 每个工具给 Agent 新能力:

  • get_weather 是天气 App
  • send_email 是邮件 App
  • search_database 像针对你数据的专用搜索 App
  • run_code 像编程 App

就像用 App 一样:

  • 手机(模型)根据你问什么决定打开哪个 App
  • App(工具)做实际工作
  • 手机把结果展示给你

设计工具时,你本质上是在为 Agent 建应用商店。名称清晰、功能有用的好 App 让手机更能干。名称混乱、行为有 bug 的烂 App 令人沮丧。


Google Cloud 上的工具

Google Cloud 提供多种方式给 Agent 工具:

Vertex AI 函数调用

Vertex AI 支持用 Gemini 模型做函数调用。你把工具定义为函数声明,模型会在适当时生成结构化函数调用。

了解更多: Vertex AI Function Calling

Agent Development Kit(ADK)工具

Agent Development Kit(ADK)提供结构化方式为 Agent 定义与管理工具。ADK 支持:

  • 函数工具:把任意 Python 函数包装成 Agent 工具
  • 内置工具:Google Search、Code Execution 等
  • Agent 工具:把另一个 ADK Agent 当作工具
  • 第三方工具:与 LangChain 工具、CrewAI 工具和 MCP server 集成

ADK 处理工具定义格式、执行与结果传递,让你专注工具逻辑而非管道。

了解更多: ADK Tools Documentation

内置锚定工具

Google Search grounding 可作为 Vertex AI 上 Gemini 模型的内置工具。启用后,模型可搜索网页,把回复锚定在当前信息上——无需自定义工具代码。


综合起来:一个实践例子

让我们为一个简单的 DevOps Agent 设计工具集,帮助工程师调查生产问题。

Agent 的目的

帮助 on-call 工程师诊断并响应生产告警。

工具集设计

工具 目的 何时使用
get_alert_details 获取特定告警详情 工程师询问某个告警时
query_metrics 获取服务的时间序列指标 调查性能问题时
search_logs 按服务与时间范围搜索应用日志 查找错误或模式时
list_recent_deployments 显示服务的近期部署 检查是否部署引起问题时
get_runbook 获取特定告警类型的 runbook Agent 需要建议修复步骤时
create_incident 创建事件工单 问题已确认且需要跟踪时

注意每个工具如何遵循最佳实践:清晰的动词+名词名称、解释「做什么/何时/返回什么」的描述、单一职责,以及带例子的描述性参数。

交互例子

Engineer: "Alert ALT-9876 just fired. What is going on?"

Agent thinking: "I need to look up this alert first."
-> Calls: get_alert_details(alert_id="ALT-9876")
<- Returns: {service: "auth-service", type: "high_latency", severity: "P1",
             description: "p99 latency >2s for 5 minutes"}

Agent thinking: "High latency on auth-service. Let me check metrics and recent deploys."
-> Calls: query_metrics(service="auth-service", metric="latency_p99", time_range="1h")
-> Calls: list_recent_deployments(service="auth-service", limit=5)
<- Metrics show latency spike starting at 14:05
<- Deploy #456 happened at 14:02

Agent thinking: "Latency spiked 3 minutes after a deploy. Let me check error logs."
-> Calls: search_logs(service="auth-service", query="timeout OR slow", level="error", time_range="1h")
<- Logs show: "Database connection pool exhausted" errors starting at 14:05

Agent thinking: "The deploy likely changed something that exhausts the DB connection pool.
                Let me get the runbook for guidance."
-> Calls: get_runbook(alert_type="high_latency")
<- Runbook: Step 1: Check recent deploys. Step 2: Compare config changes...

Agent responds:
"Here is what I found for ALT-9876:

**Issue:** auth-service p99 latency spiked above 2 seconds starting at 14:05.

**Likely cause:** Deploy #456 (at 14:02) appears to have triggered database connection pool
exhaustion. Error logs show 'Database connection pool exhausted' errors starting 3 minutes
after the deploy.

**Recommended actions:**
1. Review the changes in deploy #456, particularly any database configuration changes
2. Consider rolling back deploy #456 if the latency is impacting users
3. Check the connection pool size setting - it may need to be increased

Would you like me to create an incident ticket for this?"

此例说明:设计良好、名称清晰、描述到位、输出简洁的工具,如何让 Agent 通过多步高效调查问题。


关键要点

  1. 工具弥合思考与行动之间的鸿沟。 没有工具,Agent 只是聊天机器人。有了工具,它可以与真实系统和数据交互。

  2. 模型从不直接执行工具。 它提出工具调用。你的代码执行它们。你始终掌控实际发生什么。

  3. 三类工具:函数工具(自定义)、内置工具(平台提供)、Agent 工具(把另一个 Agent 当作工具)。对你的具体用例从函数工具开始。

  4. 工具设计是面向模型的 UX 设计。 清晰名称、描述性参数、简洁输出与有用的错误消息,决定模型是用得好还是挣扎。

  5. 留意工具数量。 更多工具意味着更多上下文占用与更难的决策。让工具集聚焦 Agent 真正需要的。

  6. 标准化降低集成负担。 MCP 等协议旨在通过 AI 应用与工具之间的通用接口解决 N x M 问题。


接下来?

现在我们理解了大脑(LLM)与双手(工具),下一课用编排层把它们合在一起——管理 Agent 如何思考、行动、观察并重复直到完成工作的控制循环。

下一课:第 4 课 - 编排:Agent 循环 -->