Skip to content

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 记录 → 错误处理 → 返回上下文

每一步的作用:

  1. 工具注册:把可用工具注册到工具注册中心,定义工具名称、描述和能力边界。
  2. Schema 定义:为每个工具定义输入输出 Schema,明确参数类型、必填字段和约束条件。
  3. 模型选择工具:模型根据任务上下文,决定调用哪个工具。
  4. 参数生成:模型生成工具调用的参数,可能是 JSON、字符串或其他结构。
  5. 参数校验:Runtime 校验参数是否符合 Schema——类型是否正确、必填字段是否齐全、值域是否合理。
  6. 权限检查:检查当前用户是否有权限调用这个工具、访问目标资源。
  7. 工具执行:把校验通过的参数传给工具,执行具体操作。
  8. 结果结构化:把工具返回的结果转换成标准格式,包含状态、数据、错误信息等。
  9. Trace 记录:记录这次工具调用的完整信息——工具名、参数、结果、耗时、状态。
  10. 错误处理:如果工具调用失败,根据错误类型决定重试、降级还是终止。
  11. 返回上下文:把工具结果返回给 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 的工程化要求不会因为接入方式不同而改变。


最小工具调用伪代码 ​

下面是一个最小工具调用的伪代码:

python
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

每一步的作用:

  1. 查找工具:从工具注册中心获取工具定义。
  2. 参数校验:检查参数是否符合 Schema 定义。
  3. 权限检查:检查当前用户是否有权限调用这个工具。
  4. 执行工具并记录:调用工具,记录成功结果到 Trace。
  5. 失败时记录错误:捕获异常,记录失败信息到 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 的联动,展示工具调用记录如何用于调试和评估。