Day 24:Tool Calling 深入

学习目标

昨天建立了 Agent 的基础认知,今天要深入 Agent 的”手和脚”:Tool Calling。

Tool Calling 是让大模型从”只会说话”进化到”能做事”的关键技术。没有 Tool Calling,大模型只能生成文本;有了 Tool Calling,大模型能调用外部工具、查询数据库、执行计算、发送消息,真正参与到业务流程中去。

今天的目标是深入理解 Tool Calling 的每个环节:工具怎么定义、模型怎么选择工具、参数怎么传递、结果怎么返回、失败了怎么办、权限怎么控制、怎么审计。学完这一天,你应该能为一个行业研究 Agent 设计出完整的工具集,并定义每个工具的 Schema、权限级别和调用规范。


核心概念

Tool Schema

Tool Schema 是工具的”身份证”。它告诉模型:这个工具叫什么、能做什么、需要什么参数、返回什么格式。

一个完整的 Tool Schema 包含四个部分。

第一部分是工具名称(name)。名称要简洁、有意义、符合命名规范。好的命名让模型一看就知道这个工具做什么。比如 search_web 比 tool_1 好,calculate_roi 比 compute 好。命名建议用”动词_名词”格式,全小写,下划线分隔。

第二部分是工具描述(description)。描述要准确、具体、无歧义。这是模型选择工具的主要依据。差的描述:“搜索信息”。好的描述:“根据关键词搜索互联网上的公开信息,返回搜索结果的标题、摘要和链接。适用于需要获取最新资讯、查找公开数据、了解行业动态的场景。不适用于查询内部数据库或私有知识库。”

描述中建议包含三块内容:这个工具做什么、适用什么场景、不适用什么场景。特别是”不适用场景”的说明,能有效减少模型误选工具的情况。

第三部分是输入参数(parameters)。用 JSON Schema 格式定义,包含每个参数的名称、类型、描述、是否必填。参数设计的原则是”最少必要参数”——不要让模型传它不需要的参数,也不要遗漏关键参数。

第四部分是输出格式(returns)。定义工具返回数据的结构。输出格式要稳定、可预测,不要这次返回一个结构下次返回另一个结构。

举个具体例子。search_web 工具的完整 Schema:

名称:search_web 描述:根据关键词搜索互联网上的公开信息,返回搜索结果的标题、摘要和链接 参数:

  • query(string,必填):搜索关键词,建议用简短的名词短语
  • max_results(integer,可选):返回结果数量,默认 5,范围 1-10
  • language(string,可选):搜索语言,默认 zh-CN

输出:

  • results(array):搜索结果列表
    • title(string):结果标题
    • snippet(string):内容摘要
    • url(string):链接地址
    • source(string):来源网站

设计 Tool Schema 时最常犯的错误是描述太简略。很多人写一句”搜索工具”就完事了,结果模型不知道什么时候该用这个工具、什么时候不该用。花时间把描述写好,是 Tool Calling 成功的一半。

工具选择

工具选择是 Tool Calling 的核心决策环节。模型收到用户请求后,需要从可用工具列表中判断:这次应该调用哪个工具?还是不需要调用工具,直接生成文本回答?

模型做工具选择的依据有三个。第一,用户请求的语义。用户说”查一下半导体行业的市场规模”,语义上需要外部信息,应该调搜索工具。用户说”帮我整理一下这些数据”,语义上需要结构化处理,可能用计算工具。第二,每个工具的描述。模型会对比用户请求和工具描述,计算语义相似度,选择最匹配的工具。第三,对话上下文。前面的对话可能已经暗示了需要什么工具。比如前面在讨论 ROI 计算,用户接着说”帮我算一下”,模型知道要调计算工具。

工具选择的难点在于”选错”的情况很多。常见的选错模式有三种。

第一种是选了不该选的工具。比如用户问”半导体是什么”,这是一个知识性问题,直接回答就行,但模型可能错误地调用了搜索工具。原因往往是工具描述太宽泛,模型觉得”好像也能用”。

第二种是没选该选的工具。比如用户问”这个行业的最新政策变化”,这需要搜索工具获取最新信息,但模型可能凭自己的知识回答,给了过时的信息。原因是模型的”自信”——它觉得自己的知识足够,不需要外部工具。

第三种是同时选了多个工具但顺序不对。某些情况下需要串行调用多个工具(先搜索再计算),模型可能错误地并行调用或者调换了顺序。

减少选错的方法:一是优化工具描述,让每个工具的适用场景更加明确;二是控制工具数量,可用工具越少,选错概率越低;三是在系统提示中明确告知模型什么场景用什么工具。

工具参数

工具参数是模型传给工具的具体数据。模型不仅要选对工具,还要构造正确的参数。

参数构造的质量取决于两个因素。第一,模型对任务的理解深度。如果模型没有充分理解用户意图,构造的参数可能南辕北辙。比如用户说”查一下苹果”,模型需要判断这是指”苹果公司”还是”水果苹果”,才能构造正确的搜索关键词。第二,参数定义的清晰度。如果参数描述模糊,模型可能不知道该传什么值。

参数设计有几个实用技巧。

第一,给参数提供枚举值。如果一个参数只有几个合法值,就用枚举类型定义。比如 language 参数可以定义为 enum: [“zh-CN”, “en-US”, “ja-JP”],这样模型不会传一个不合法的语言代码。

第二,给参数设置默认值。可选参数应该有合理的默认值,这样模型不需要为不重要的参数费心思。比如 max_results 默认 5,大多数情况下够用。

第三,参数描述中给出示例。比如 query 参数的描述可以是:“搜索关键词,建议用简短的名词短语,例如’半导体市场规模’或’AI Agent 框架’“。示例能帮模型理解参数的期望格式。

第四,复杂参数拆成多个简单参数。不要设计一个巨型 JSON 参数,而是拆成多个独立字段。比如不要一个 filters 参数包含行业、地区、时间范围,而是拆成 industry、region、time_range 三个独立参数。

工具返回

工具返回是工具执行后给模型的结果。模型拿到这个结果后,会基于结果继续推理或生成回答。

工具返回的设计原则有三个。

第一,返回内容要精炼。不要把工具的所有原始输出都丢给模型,要做初步的筛选和摘要。比如搜索工具返回 10 条结果,但只需要给模型最相关的 3 条。过多的返回内容浪费 Token,还可能稀释关键信息。

第二,返回格式要结构化。JSON 格式,字段固定,不要返回大段文本让模型自己解析。结构化的返回让模型能快速提取关键信息。

第三,错误信息要明确。工具执行失败时,返回清晰的错误信息,包括错误类型和原因。不要返回一个空结果让模型猜发生了什么。比如”搜索失败:网络超时,请稍后重试”比返回空数组好。

一个容易忽视的问题是返回数据的格式稳定性。同一个工具,同样的参数,每次调用的返回格式应该一致。如果第一次返回 {“title”: ”…”, “content”: ”…”},第二次返回 {“name”: ”…”, “text”: ”…”},模型就会困惑。格式一致性是可靠 Tool Calling 的基础。

工具失败

工具调用不可能每次都成功。网络超时、数据库故障、参数错误、权限不足,各种失败随时可能发生。

工具失败的处理需要分三个层面。

第一个层面是工具内部的容错。工具实现本身应该有重试机制。比如搜索工具遇到网络超时,应该自动重试 2-3 次。数据库查询遇到连接池满,应该等待后重试。工具内部的容错让大部分瞬态故障对 Agent 透明。

第二个层面是返回给模型的错误信息。工具内部重试用完后仍然失败,需要向模型返回错误信息。错误信息要包含:错误类型(网络错误、参数错误、权限错误等)、错误描述(人类可读的说明)、建议操作(模型可以尝试的替代方案)。

比如:{“error”: “network_timeout”, “message”: “搜索服务响应超时”, “suggestion”: “可以尝试减少 max_results 参数或更换搜索关键词”}

第三个层面是 Agent 层面的应对策略。模型收到错误信息后,需要决定下一步怎么做。可选策略包括:换一个工具(搜索失败就用知识库查询)、换参数重试(减少结果数量、简化关键词)、跳过这个步骤(如果这个信息不是必需的)、报告失败(如果关键信息获取不到)。

工具失败的日志记录非常重要。每次失败都要记录:时间、工具名称、参数、错误类型、错误信息、重试次数、最终处理结果。这些日志是后续优化工具和 Agent 的关键数据。

工具权限

不是所有工具都应该被无限制使用。权限控制是 Tool Calling 安全的基石。

权限模型的设计从三个维度出发。

第一个维度是工具级别的权限。哪些 Agent 可以使用哪些工具?比如”行业研究 Agent”可以使用搜索和查询工具,但不能使用”发送邮件”工具。这是最粗粒度的权限控制。

第二个维度是参数级别的权限。同一个工具,不同 Agent 可用的参数范围不同。比如”查询数据库”工具,Agent A 只能查 public schema 的表,Agent B 可以查所有 schema。这是中等粒度的权限控制。

第三个维度是数据级别的权限。同一个工具同一个参数,不同 Agent 能看到的数据范围不同。比如”搜索文档”工具,Agent A 只能搜索公开文档,Agent B 可以搜索内部文档。这是最细粒度的权限控制。

实际项目中,至少要实现工具级别的权限控制。参数级别和数据级别的权限可以根据安全需求逐步加入。

权限控制的核心原则是”最小权限”。只给 Agent 完成任务所必需的权限,不多给一个。这样即使 Agent 被恶意利用(比如被 Prompt Injection 攻击),它能造成的损害也是有限的。

工具白名单

工具白名单是权限控制的实现方式之一。它的思路很简单:为每个 Agent 维护一个工具白名单,只允许调用白名单内的工具。

白名单设计有几个考虑。

第一,白名单应该是配置化的,不是硬编码的。每个 Agent 的白名单存在配置文件或数据库中,需要调整时改配置就行,不用改代码。

第二,白名单应该包含工具版本信息。工具升级后可能行为有变化,白名单要能区分不同版本。

第三,白名单应该支持临时授权。某些特殊情况下,Agent 可能需要临时使用一个不在白名单中的工具。临时授权应该有审批流程和过期时间。

白名单的维护是一个持续的工作。随着 Agent 能力的扩展,白名单需要定期审查和更新。审查的维度包括:白名单中的工具是否都在使用?有没有工具长时间没被调用过?是否需要添加新工具?现有权限是否过大?

工具调用审计

审计是对所有工具调用的完整记录。它回答的问题是:谁在什么时候调用了什么工具、传了什么参数、得到了什么结果。

审计日志至少应该包含以下字段:时间戳、Agent 标识、工具名称、输入参数、输出结果、执行耗时、是否成功、错误信息(如果失败)、Token 消耗。

审计日志有两个核心用途。第一是事后追溯。如果 Agent 产生了错误的输出,通过审计日志可以一步步回放它的工具调用链,找到出错环节。第二是优化分析。统计每个工具的调用频率、成功率、平均耗时,可以识别出哪些工具需要优化。

审计日志的存储要注意隐私和安全。输入参数和输出结果可能包含敏感信息(比如用户数据、企业内部信息),需要做脱敏处理或者加密存储。日志的访问权限也要严格控制,不是所有人都能查看所有日志。

审计还有一个容易被忽视的价值:它是构建 Agent 评估体系的数据基础。通过分析大量历史工具调用数据,可以评估 Agent 的工具选择准确率、参数构造质量、错误处理能力。这些指标是量化 Agent 质量的关键维度。


概念关系图

Tool Calling 完整流程

1. 工具注册阶段
   |
   +-- 定义 Tool Schema
   |   |-- name(工具名称)
   |   |-- description(功能描述)
   |   |-- parameters(输入参数 Schema)
   |   |-- returns(输出格式 Schema)
   |
   +-- 加入工具白名单
   +-- 设置权限级别

2. 工具选择阶段
   |
   用户请求 + 对话上下文 + 工具列表
   |
   v
   模型推理:需要调用工具吗?调哪个?
   |
   +-- 不需要工具 -> 直接生成文本
   +-- 需要工具 -> 选择工具 + 构造参数

3. 工具执行阶段
   |
   +-- 权限检查(工具是否在白名单中?)
   |   |-- 不通过 -> 返回权限错误
   |   |-- 通过 -> 继续
   |
   +-- 参数校验(参数格式是否合法?)
   |   |-- 不合法 -> 返回参数错误
   |   |-- 合法 -> 继续
   |
   +-- 执行工具调用
   |   |-- 成功 -> 返回结果
   |   |-- 失败 -> 重试 / 返回错误信息
   |
   +-- 记录审计日志

4. 结果处理阶段
   |
   +-- 模型接收工具返回
   +-- 基于结果继续推理
   +-- 决定下一步动作
8 个行业研究工具的架构

Agent 工具集
|
+-- 信息获取层
|   |-- search_web:搜索互联网公开信息
|   |-- read_document:读取上传的文档内容
|   |-- query_database:查询行业数据库
|
+-- 分析计算层
|   |-- calculate_roi:计算 AI 方案的投资回报率
|
+-- 内容生成层
|   |-- generate_markdown:生成结构化 Markdown 内容
|
+-- 输出动作层
|   |-- save_report:保存分析报告
|   |-- send_email_draft:生成邮件草稿
|   |-- create_task:创建待办任务

权限分级:
  只读(search_web, read_document, query_database)
  计算(calculate_roi)
  写入(generate_markdown, save_report)
  外部动作(send_email_draft, create_task)

实战分析

实战任务:为 Agent 设计 8 个工具

指南要求为行业研究 Agent 设计 8 个工具,并加入工具权限表。下面逐一展开。

search_web:搜索互联网公开信息

功能:根据关键词搜索互联网上的公开信息,返回搜索结果列表。 输入参数:query(搜索关键词,必填)、max_results(结果数量,可选,默认 5)、language(语言,可选,默认 zh-CN)。 输出:results 数组,每个元素包含 title、snippet、url、source。 权限级别:只读。 设计注意点:搜索结果要做初步过滤,去掉广告和低质量内容。对搜索关键词要做长度限制(不超过 100 字符),避免模型传一大段话当关键词。

read_document:读取上传的文档内容

功能:读取用户上传的文档,提取文本内容。支持 PDF、Word、Markdown、TXT 格式。 输入参数:document_id(文档 ID,必填)、page_range(页码范围,可选)、sections(要提取的章节,可选)。 输出:document_content(文档文本内容)、metadata(文档元数据:标题、作者、创建时间、页数)。 权限级别:只读。 设计注意点:大文档要做分段返回,不要一次返回全部内容。返回时附带元数据,让模型知道文档的来源和结构。

query_database:查询行业数据库

功能:查询预置的行业数据库,获取结构化的行业数据。 输入参数:table_name(表名,必填)、filters(过滤条件对象,可选)、fields(要查询的字段,可选)。 输出:data(查询结果数组)、total_count(总记录数)、query_time(查询耗时)。 权限级别:只读,且有表级别限制(只能查行业相关表)。 设计注意点:这是一个高风险工具,需要严格的 SQL 注入防护。filters 参数不直接拼 SQL,而是用参数化查询。同时要设置查询超时和返回行数上限。

calculate_roi:计算 AI 方案的投资回报率

功能:根据 AI 方案的预估成本和收益,计算 ROI 和回本周期。 输入参数:implementation_cost(实施成本,必填)、annual_savings(年化节省金额,必填)、time_to_implement(实施周期月数,必填)、risk_factor(风险系数,可选,默认 1.0)。 输出:total_cost(总成本)、annual_roi(年化 ROI)、payback_months(回本月数)、three_year_return(三年总回报)、risk_adjusted_roi(风险调整后 ROI)。 权限级别:计算(无副作用,但依赖输入准确性)。 设计注意点:这个工具的计算逻辑应该公开透明,让使用者能验证。输入参数应该有合理的范围检查(比如实施成本不能为负)。

generate_markdown:生成结构化 Markdown 内容

功能:根据结构化数据生成 Markdown 格式的文档章节。 输入参数:section_type(章节类型,必填,枚举值:overview/roles/processes/opportunities/report)、data(结构化数据,必填)、template_id(模板 ID,可选)。 输出:markdown_content(生成的 Markdown 文本)、word_count(字数统计)。 权限级别:写入(生成内容但不修改外部系统)。 设计注意点:章节类型用枚举值而不是自由文本,避免模型传入不支持的类型。生成的内容要做字数限制,防止生成过长内容。

save_report:保存分析报告

功能:将生成的分析报告保存到文件系统或对象存储。 输入参数:report_content(报告内容,必填)、filename(文件名,必填)、format(保存格式,可选,默认 md)。 输出:file_path(文件保存路径)、file_size(文件大小)、saved_at(保存时间)。 权限级别:写入(有副作用,写入文件系统)。 设计注意点:文件名要做安全检查,禁止路径穿越(不能包含 ../ 等)。保存路径要用白名单目录,不允许保存到任意位置。

send_email_draft:生成邮件草稿

功能:生成一封邮件草稿,不自动发送,保存到草稿箱等待人工确认。 输入参数:to(收件人,必填)、subject(主题,必填)、body(正文,必填)、priority(优先级,可选)。 输出:draft_id(草稿 ID)、preview(邮件预览)、status(状态:draft)。 权限级别:外部动作(高风险,需要人工确认后才能真正发送)。 设计注意点:这个工具只生成草稿不发送,发送动作需要另一个更高权限的工具。to 参数要做邮箱格式校验。邮件内容要做敏感信息检查。

create_task:创建待办任务

功能:在任务管理系统中创建一个待办事项。 输入参数:title(任务标题,必填)、description(任务描述,必填)、assignee(负责人,可选)、due_date(截止日期,可选)、priority(优先级,可选)。 输出:task_id(任务 ID)、created_at(创建时间)、status(状态:pending)。 权限级别:外部动作(有副作用,创建外部系统记录)。 设计注意点:负责人和截止日期要有合理范围。任务创建后应该通知相关人员。

工具权限表汇总

工具名称权限级别高风险操作需要人工确认白名单
search_web只读
read_document只读
query_database只读是(限定表范围)
calculate_roi计算
generate_markdown写入
save_report写入是(写文件)可选是(限定目录)
send_email_draft外部动作是(生成外部内容)
create_task外部动作是(创建外部记录)

当日产物说明

《Agent 工具清单》

这个文档列出 8 个工具的完整定义,每个工具一页。包含:名称、功能描述、输入参数(含类型和说明)、输出格式、权限级别、使用注意事项、常见错误和应对策略。

质量标准:一个不了解项目的开发者拿到这个文档,能独立实现每个工具。参数定义无歧义,输出格式完全确定。

《Tool Schema 文件》

这是所有工具的 JSON Schema 定义文件。每个工具的 Schema 包含 name、description、parameters、returns 四个部分。参数类型使用 JSON Schema 标准类型。

质量标准:Schema 文件可以通过 JSON Schema 校验器验证通过。每个参数都有 description 字段,枚举值类型的参数都有 enum 定义。

《工具权限表》

这是所有工具的权限汇总表,包含工具名称、权限级别、是否高风险、是否需要人工确认、白名单范围、审批要求。

质量标准:每个工具的权限级别合理(高风险操作有确认机制),白名单范围明确(不是”全部允许”)。

《工具调用日志》

这是工具调用日志的格式定义,包含:日志字段列表、字段类型、示例数据、存储策略、保留期限、访问权限。

质量标准:日志格式能完整回放任何一次工具调用的全过程。敏感字段已标注脱敏要求。


常见误区与避坑

误区一:工具描述随便写写就行

工具描述是模型选择工具的唯一依据。描述写得不好,模型就选不准工具。花 30 分钟把每个工具的描述写好,能省下后面无数调试时间。描述中特别要写清楚”不适用场景”,这比”适用场景”更能帮模型做出正确选择。

误区二:一个工具做很多事

把搜索、过滤、排序、格式化全塞在一个工具里,参数一大堆,逻辑一团乱。正确做法是一个工具只做一件事。搜索归搜索,过滤归过滤。工具粒度越细,Agent 的组合灵活性越高。就像乐高积木,单个积木越简单,拼出来的造型越丰富。

误区三:不处理工具失败

很多初学者假设工具调用一定成功,不做任何错误处理。结果工具一失败,Agent 就卡住了。每个工具都要定义失败行为:超时怎么办、参数错误怎么办、权限不足怎么办、返回格式异常怎么办。工具失败的处理策略要写入工具描述,让模型知道失败后可以怎么办。

误区四:所有工具都设为无条件可用

这是安全问题的根源。“发送邮件”这种工具如果无条件可用,Agent 就可能在被攻击时向任何人发送邮件。给工具分级、设白名单、加确认机制,虽然增加了设计复杂度,但这是必须付出的代价。

误区五:忽视工具调用的成本

每次工具调用都可能触发 API 调用、数据库查询、文件读写,这些都是有成本的。如果 Agent 在一个任务中调用了 50 次搜索工具,成本可能远超预期。设计时要考虑工具的调用频率上限,对高频调用的工具做缓存。


延伸思考

今天的 Tool Calling 知识是明天 Multi-Agent 架构的基础。在多 Agent 系统中,每个 Agent 有自己的工具集,不同 Agent 的工具可能重叠也可能互补。工具的权限管理在多 Agent 场景下更加复杂,因为要考虑 Agent 之间的协作关系。

Day 26 的 Human-in-the-loop 专题会讨论如何在高风险工具调用前加入人工确认机制。今天提到的 send_email_draft 和 create_task 就是典型的高风险工具,它们的”确认机制”在 Day 26 会详细展开。

Day 27 的安全专题会从工具权限出发,讨论 Prompt Injection 攻击如何诱导 Agent 调用不应该调用的工具,以及怎么设计防护策略。

从与 Week 2 的衔接来看,Day 10 已经学过 Function Calling 的基础概念(工具定义、参数、执行、返回)。今天的深入在于:增加了权限控制、白名单、审计三个安全维度;增加了工具选择策略的分析;增加了失败处理的分层设计。这是从”能调用工具”到”可靠地调用工具”的进化。

从与 Week 3 的衔接来看,RAG 系统中的”检索”操作可以封装成一个工具(query_knowledge_base),让 Agent 在需要时自主检索知识库。这样 RAG 就从”预先检索再生成”变成了”按需检索”,更适合 Agent 场景。


自测问题

  1. Tool Schema 包含哪四个部分?每个部分的作用是什么?

  2. 为什么工具描述中要写”不适用场景”?举例说明缺少这部分描述会导致什么问题。

  3. 工具参数设计的”最少必要参数”原则是什么意思?为什么不要设计多余参数?

  4. 工具返回内容为什么要精炼?返回太多内容会有什么问题?

  5. 工具失败在哪三个层面处理?每个层面举一个具体例子。

  6. 权限控制的三个维度分别是什么?哪个粒度最粗、哪个最细?

  7. 工具白名单的设计要考虑哪些因素?

  8. 审计日志至少应该包含哪些字段?它有什么两个核心用途?

  9. 为什么 send_email_draft 只生成草稿不直接发送?这个设计背后的安全考虑是什么?

  10. 设计一个”查询天气”工具的完整 Schema,包含名称、描述、参数和输出格式。


关键词

  • Tool Schema:工具的完整定义,包含名称、描述、参数和输出格式
  • 工具选择:模型根据用户请求和工具描述判断应该调用哪个工具的决策过程
  • 工具参数:模型传给工具的具体输入数据,用 JSON Schema 定义格式
  • 工具返回:工具执行后返回给模型的结果,需要精炼和结构化
  • 工具失败:工具调用过程中的错误,包括网络超时、参数错误、权限不足等
  • 工具权限:控制 Agent 能使用哪些工具、以什么方式使用的安全机制
  • 工具白名单:限制 Agent 只能调用指定工具列表的安全策略
  • 工具审计:记录所有工具调用的完整日志,用于追溯和分析
  • 最小权限原则:只给 Agent 完成任务所必需的权限,不多给一个
  • 参数校验:在工具执行前检查参数格式和范围是否合法