Tool Calling 工程化:不只是函数调用
这篇文章解决什么问题
很多人把 Tool Calling 理解成:
模型选择函数 → 传参数 → 执行函数 → 返回结果
但在真实 Agent 系统中,Tool Calling 不是简单函数调用,而是完整的工程机制。模型输出的参数可能有格式错误,工具调用可能涉及敏感操作,执行可能失败,结果需要结构化记录——这些都不是"调用一个函数"能解决的。
这篇文章要回答的核心问题是:Tool Calling 在工程上需要考虑什么?为什么不能只靠模型来决定工具调用?
核心观点:Tool Calling = 工具定义 + 参数 Schema + 权限上下文 + 调用执行 + 错误处理 + Trace 记录 + 安全审计。
为什么 Tool Calling 重要
LLM 只能生成文本。它可以说"我帮你搜索一下",但不能真正执行搜索。工具让 Agent 从"能说"变成"能做":
- 查询数据库。Agent 可以真正读取业务数据。
- 搜索知识库。Agent 可以检索相关文档和知识。
- 访问 API。Agent 可以调用外部服务获取实时信息。
- 操作文件。Agent 可以读写、创建、修改文件。
- 执行代码。Agent 可以运行代码片段完成计算或数据处理。
- 创建任务。Agent 可以在项目管理工具中创建工单。
- 发送通知。Agent 可以通过邮件、消息渠道发送信息。
- 调用业务系统。Agent 可以执行业务操作,如下单、审批。
没有工具调用,Agent 只能回答问题;有工具调用,Agent 才能执行任务。Tool Calling 是 Agent 从"对话助手"升级为"任务执行者"的关键能力。
Tool Calling 的工程链路
一条完整的 Tool Calling 工程链路:
工具注册 → Schema 定义 → 模型选择工具 → 参数生成 → 参数校验 → 权限检查 → 工具执行 → 结果结构化 → Trace 记录 → 错误处理 → 返回上下文
每一步的作用:
- 工具注册:把可用工具注册到工具注册中心,定义工具名称、描述和能力边界。
- Schema 定义:为每个工具定义输入输出 Schema,明确参数类型、必填字段和约束条件。
- 模型选择工具:模型根据任务上下文,决定调用哪个工具。
- 参数生成:模型生成工具调用的参数,可能是 JSON、字符串或其他结构。
- 参数校验:Runtime 校验参数是否符合 Schema——类型是否正确、必填字段是否齐全、值域是否合理。
- 权限检查:检查当前用户是否有权限调用这个工具、访问目标资源。
- 工具执行:把校验通过的参数传给工具,执行具体操作。
- 结果结构化:把工具返回的结果转换成标准格式,包含状态、数据、错误信息等。
- Trace 记录:记录这次工具调用的完整信息——工具名、参数、结果、耗时、状态。
- 错误处理:如果工具调用失败,根据错误类型决定重试、降级还是终止。
- 返回上下文:把工具结果返回给 Runtime,用于更新状态和下一次模型调用。
这条链路中,模型只参与"选择工具"和"生成参数"两步,其他都是工程系统的职责。
工具 Schema 设计
工具 Schema 是 Tool Calling 的基础。一个清晰的 Schema 让模型知道工具能做什么、需要什么参数、会返回什么结果。
工具 Schema 至少要定义:
| 字段 | 作用 |
|---|---|
tool_name | 工具的唯一标识,模型用它来引用工具 |
description | 工具的功能描述,帮助模型判断什么时候该用这个工具 |
input_schema | 输入参数的 JSON Schema,定义参数类型、必填字段和约束 |
output_schema | 输出结果的结构定义,让 Runtime 知道如何解析结果 |
required_fields | 必填参数列表,参数校验时使用 |
permission_level | 权限等级,如 read、write、admin,用于权限控制 |
timeout | 超时时间,防止工具调用无限等待 |
retry_policy | 重试策略,失败后重试几次、间隔多久 |
risk_level | 风险等级,如 low、medium、high,决定是否需要审批 |
Schema 设计得好,模型就能准确理解工具;Schema 设计得差,模型可能误用工具或传错参数。
参数校验
模型生成的参数不能直接执行。模型是概率性的,它可能输出格式错误、类型不匹配、缺少必填字段甚至包含危险输入。
必须校验的内容:
- 类型是否正确。 期望数字,模型可能输出字符串;期望数组,模型可能输出对象。
- 必填字段是否存在。 Schema 定义了 required 字段,但模型可能遗漏。
- 值域是否合理。 期望 1-100 的数字,模型可能输出负数或超大值。
- 是否包含危险输入。 SQL 注入、路径遍历、命令注入等安全风险。
- 是否越权访问资源。 参数中指定的资源 ID 是否属于当前用户。
- 是否符合业务约束。 比如删除操作需要确认参数,发送邮件需要有效邮箱地址。
举例来说:删除文件工具需要校验文件路径是否在允许的目录内;发送邮件工具需要校验邮箱格式是否正确;调用支付工具需要校验金额是否在授权范围内。这些都不能只靠模型判断——模型可能幻觉出一个不存在的文件路径,或者生成一个恶意的邮箱地址。
权限上下文
工具调用必须知道"谁在调用"和"在什么上下文中调用"。权限上下文至少包括:
- 当前用户是谁。 用户身份决定了能访问哪些资源。
- 所属租户是什么。 多租户系统中,工具只能访问当前租户的数据。
- 用户角色是什么。 管理员和普通用户能调用的工具不同。
- 是否有访问目标资源的权限。 用户可能有工具调用权限,但没有目标数据的访问权限。
- 当前任务是否允许调用该工具。 某些任务类型可能限制可用工具范围。
- 是否需要人工审批。 高风险操作可能需要人工确认后才能执行。
权限检查应该在工具执行之前,不能依赖模型来判断权限——模型不知道用户的权限边界。
高风险工具审批
不是所有工具调用都应该自动执行。高风险工具包括:
- 删除数据。误删可能导致数据丢失。
- 修改权限。可能影响系统安全。
- 发送外部消息。可能造成信息泄露或骚扰。
- 执行代码。可能有安全风险。
- 操作生产环境。直接影响线上服务。
- 调用付费 API。产生额外成本。
- 访问敏感信息。涉及个人隐私或商业机密。
这些工具应当有审批机制:
- 二次确认。 执行前要求用户确认。
- 沙箱执行。 在隔离环境中执行,限制影响范围。
- 只读模式。 对于查询类操作,限制为只读。
- 人工审批。 高风险操作需要管理员审批。
审批机制不能只靠 Prompt 约束——模型可能被绕过。需要在 Runtime 层面实现强制检查。
工具结果结构化
工具返回结果不应该只是一段文本。结构化的结果让 Runtime 能更好地理解和处理。
更好的结构包括:
status:执行状态,如 success、failed、timeout、permission_denied。data:返回的数据内容。error_code:错误码,用于程序化处理。error_message:错误描述,用于日志和调试。latency_ms:执行耗时,用于性能监控。source:数据来源,用于引用和溯源。metadata:额外元信息,如数据版本、缓存命中等。
有了结构化结果,Runtime 可以根据 status 决定下一步:成功则继续,失败则重试或降级,权限不足则提示用户。
错误处理
工具调用可能以各种方式失败:
- 参数不合法。模型生成的参数有格式错误。
- 权限不足。用户没有权限调用这个工具。
- 超时。工具执行时间超过限制。
- 外部服务失败。依赖的 API、数据库、搜索引擎不可用。
- 返回格式异常。工具返回了预期之外的格式。
- 结果为空。查询没有找到匹配结果。
- 触发安全策略。参数包含危险输入,被安全策略拦截。
Runtime 需要根据错误类型决定处理策略:
- 重试。 临时性错误(如超时、服务不可用)可以重试。
- 换工具。 当前工具不可用,尝试替代工具。
- 降级。 无法获取完整结果,返回部分结果或默认值。
- 询问用户。 缺少必要信息,追问用户补充。
- 人工接管。 无法自动处理,交给人工处理。
- 结束任务。 无法继续,终止任务并返回错误信息。
错误处理不能只靠模型重新回答——模型不知道工具为什么失败,也无法做出工程决策。
Tool Calling 与 Trace
每次工具调用都应记录到 Trace 中,用于调试、审计和评估:
run_id:任务执行的唯一标识。step_id:当前步骤的唯一标识。tool_call_id:这次工具调用的唯一标识。tool_name:调用的工具名称。arguments_summary:参数摘要(敏感参数需脱敏)。result_summary:结果摘要。status:执行状态。latency_ms:执行耗时。error_message:错误信息(如果失败)。created_at:调用时间。
重要:敏感参数需要脱敏。 API Key、Token、密码、个人隐私信息不能明文保存。Trace 的目的是调试和审计,不是泄露敏感信息。
Tool Calling 与 MCP
MCP(Model Context Protocol)可以理解为一种标准化工具接入方式。它让 Agent 不需要为每个外部系统写一套临时集成,而是通过标准协议暴露 Tools、Resources、Prompts。
需要区分两个概念:
- Tool Calling 是能力调用机制。 它解决的是"模型如何调用工具、参数如何传递、结果如何返回"的问题。
- MCP 是工具接入协议之一。 它解决的是"工具如何注册、如何发现、如何标准化调用"的问题。
二者不是同一层概念。Tool Calling 是 Agent 的核心能力,MCP 是实现工具接入的一种标准。你可以用 MCP 来接入工具,也可以自己实现工具注册和调用——Tool Calling 的工程化要求不会因为接入方式不同而改变。
最小工具调用伪代码
下面是一个最小工具调用的伪代码:
def execute_tool_call(run_id, step_id, tool_name, arguments, user_context):
# 1. 查找工具
tool = tool_registry.get(tool_name)
# 2. 参数校验
validate_arguments(tool.input_schema, arguments)
# 3. 权限检查
check_permission(tool.permission_level, user_context)
# 4. 执行工具并记录
try:
result = tool.execute(arguments)
record_tool_call(
run_id=run_id,
step_id=step_id,
tool_name=tool_name,
arguments_summary=safe_summary(arguments),
result_summary=safe_summary(result),
status="success",
)
return result
except Exception as e:
# 5. 失败时记录错误
record_tool_call(
run_id=run_id,
step_id=step_id,
tool_name=tool_name,
arguments_summary=safe_summary(arguments),
result_summary=None,
status="failed",
error_message=str(e),
)
raise每一步的作用:
- 查找工具:从工具注册中心获取工具定义。
- 参数校验:检查参数是否符合 Schema 定义。
- 权限检查:检查当前用户是否有权限调用这个工具。
- 执行工具并记录:调用工具,记录成功结果到 Trace。
- 失败时记录错误:捕获异常,记录失败信息到 Trace,然后抛出异常让 Runtime 处理。
注意 safe_summary 函数——它对敏感参数进行脱敏,确保 Trace 中不包含 API Key、Token 等敏感信息。
常见误区
把工具调用当成普通函数调用。 工具调用涉及权限控制、参数校验、错误处理和 Trace 记录,比普通函数调用复杂得多。
不做参数校验。 模型输出的参数不可信,必须校验类型、必填字段、值域和安全风险。
不做权限控制。 Agent 可能调用高风险工具,必须在执行前检查权限。
高风险工具没有审批。 删除数据、发送消息、执行代码等操作需要二次确认或人工审批。
工具结果没有结构化。 只返回文本,Runtime 无法根据状态做决策。
工具失败只让模型重新回答。 模型不知道工具为什么失败,需要 Runtime 根据错误类型做工程决策。
不记录 tool_call。 没有 Trace,无法调试问题、审计操作、评估效果。
敏感参数明文入库。 API Key、Token、密码等敏感信息必须脱敏后记录。
没有超时和重试策略。 工具调用可能无限等待或反复失败,需要设置超时和重试上限。
对个人项目的启发
项目 A(RAG 工单系统):
RAG 查询可以标准化成 Tool。定义一个 search_knowledge_base 工具,输入是查询文本和过滤条件,输出是文档列表和相关度分数。文档上传、检索、Rerank、评测触发都可以工具化——每个操作都有清晰的 Schema、权限要求和错误处理。工具调用结果应记录到 Trace,这样你可以知道每次查询用了什么工具、传了什么参数、返回了什么结果,为后续优化提供依据。
项目 B(多 Agent 运营中台 Copilot):
多 Agent Copilot 更需要工具权限控制。不同 Agent 应该只能访问自己的工具集合——数据查询 Agent 不能调用发送消息工具,内容生成 Agent 不能调用删除数据工具。高风险工具需要审批和审计——谁调用了什么工具、传了什么参数、产生了什么结果,都要可追溯。工具调用结果要参与 Evaluation——工具是否被正确使用、结果是否被正确处理,都是评估 Agent 质量的维度。
面试表达
我不会把 Tool Calling 理解成简单函数调用。在面试中,我会这样表达:
Tool Calling 在工程上需要考虑六个层面:工具 Schema 定义、参数校验、权限控制、错误处理、Trace 记录和安全审计。模型只负责选择工具和生成参数,其他都是工程系统的职责。参数校验确保模型输出的参数合法,权限控制确保用户有权调用工具,错误处理确保失败时有恢复策略,Trace 记录确保每次调用可追溯,安全审计确保敏感操作有审批。
在生产级 Agent 中,工具调用是安全风险最高的部分之一。Agent 可能调用删除数据、发送消息、执行代码等高风险操作,不能只靠 Prompt 约束——模型可能被绕过或产生幻觉。必须在 Runtime 层面实现强制的权限检查和审批机制。同时,工具调用记录也是 Evaluation 和问题排查的重要依据——知道 Agent 调用了什么工具、传了什么参数、得到了什么结果,才能评估 Agent 的行为是否合理。
如果面试官追问 Tool Calling 和 MCP 的关系,我会说:Tool Calling 是能力调用机制,解决"模型如何调用工具"的问题;MCP 是工具接入协议,解决"工具如何标准化注册和发现"的问题。二者不是同一层概念,MCP 是实现 Tool Calling 的一种方式,但不是唯一方式。
后续 TODO
- 补充工具注册中心设计,展示如何管理和发现可用工具。
- 补充工具权限矩阵,展示不同角色对不同工具的访问权限。
- 补充 MCP Server 工具接入示例,展示如何通过 MCP 标准化接入外部工具。
- 补充 Tool Calling 与 Agent Trace 的联动,展示工具调用记录如何用于调试和评估。