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 场景。
自测问题
-
Tool Schema 包含哪四个部分?每个部分的作用是什么?
-
为什么工具描述中要写”不适用场景”?举例说明缺少这部分描述会导致什么问题。
-
工具参数设计的”最少必要参数”原则是什么意思?为什么不要设计多余参数?
-
工具返回内容为什么要精炼?返回太多内容会有什么问题?
-
工具失败在哪三个层面处理?每个层面举一个具体例子。
-
权限控制的三个维度分别是什么?哪个粒度最粗、哪个最细?
-
工具白名单的设计要考虑哪些因素?
-
审计日志至少应该包含哪些字段?它有什么两个核心用途?
-
为什么 send_email_draft 只生成草稿不直接发送?这个设计背后的安全考虑是什么?
-
设计一个”查询天气”工具的完整 Schema,包含名称、描述、参数和输出格式。
关键词
- Tool Schema:工具的完整定义,包含名称、描述、参数和输出格式
- 工具选择:模型根据用户请求和工具描述判断应该调用哪个工具的决策过程
- 工具参数:模型传给工具的具体输入数据,用 JSON Schema 定义格式
- 工具返回:工具执行后返回给模型的结果,需要精炼和结构化
- 工具失败:工具调用过程中的错误,包括网络超时、参数错误、权限不足等
- 工具权限:控制 Agent 能使用哪些工具、以什么方式使用的安全机制
- 工具白名单:限制 Agent 只能调用指定工具列表的安全策略
- 工具审计:记录所有工具调用的完整日志,用于追溯和分析
- 最小权限原则:只给 Agent 完成任务所必需的权限,不多给一个
- 参数校验:在工具执行前检查参数格式和范围是否合法