为什么 AI 能调用工具

“AI 能调用工具”这句话很容易让人产生误会。

它听起来像模型突然拥有了手,可以自己打开浏览器、查数据库、发邮件、改文件。真实工程里不是这样。模型本身仍然只是根据输入生成输出。所谓工具调用,是应用把工具说明放进上下文,模型生成调用意图,应用执行工具,再把结果作为新的上下文交回模型。

换句话说,AI 能调用工具,不是因为模型突破了沙盒,而是因为软件系统给它设计了一套受控的协作流程。

模型本身只会生成 token

LLM 的基础能力是根据上下文预测并生成下一个 token。无论它是在写文章、解释代码、输出 JSON,还是“调用工具”,底层都仍然是在生成内容。

区别在于,工具调用场景里,模型生成的不是给人直接阅读的自然语言,而是一段机器可解析的结构化指令。例如:

{
  "tool": "readProjectFile",
  "arguments": {
    "path": "src/App.tsx"
  }
}

应用看到这段结构化输出后,决定是否真的读取文件。

所以“AI 调用工具”的准确说法应该是:

模型生成工具调用请求,应用负责执行请求。

这个边界非常重要。只要边界清楚,工具调用就可以被权限系统、日志系统、审批流程、超时控制和错误处理接住。

工具调用为什么可行

工具调用可行,是因为模型擅长在自然语言和结构化表示之间转换。

用户说:

帮我查一下这个客户最近三个月有没有逾期订单。

模型可以理解出几个隐含信息:

  • 需要查客户相关数据。
  • 需要时间范围:最近三个月。
  • 需要订单状态或支付状态。
  • 需要返回判断和证据。

如果应用提供了 searchOrdersgetCustomerProfilecalculateOverdueRisk 等工具,模型就可以选择合适工具,并填出参数。

这不是模型知道你数据库里有什么,而是工具定义告诉了它可以问什么。工具定义越清晰,模型越容易把自然语言映射到正确接口。

工具定义是给模型看的 API 文档

前端工程师很熟悉 OpenAPI、TypeScript 类型、组件 props。工具定义也类似,只是读者从人类工程师变成了模型。

一个工具定义通常包含:

  • 工具名称。
  • 工具描述。
  • 参数 schema。
  • 必填字段。
  • 字段描述。
  • 返回结果格式。
  • 何时使用这个工具的说明。

模型通过这些信息判断:当前任务是否需要工具,需要哪个工具,参数应该是什么。

如果工具描述含糊,模型就会误选。如果参数太自由,模型就会生成难以验证的内容。如果多个工具边界重叠,模型就可能来回试错。

工具定义不是装饰,而是模型和应用之间的接口契约。

一次工具调用的完整闭环

工具调用不是单次请求结束,而是一个闭环。

这里有两个关键点。

第一,工具结果必须回到上下文。模型只有看到工具返回的内容,才能基于真实数据继续回答。

第二,工具执行不一定只发生一次。复杂任务可能需要多轮:先查客户,再查订单,再计算风险,再生成摘要。

工程流程

把工具调用落到真实应用里,可以按一条稳定流水线设计。

第一,应用根据当前用户、页面和业务场景选择可用工具。不要把系统里所有能力都暴露给本次请求。

第二,应用把用户消息、必要上下文和工具定义发送给模型。工具定义应该包含名称、描述、参数 schema 和必要约束。

第三,模型返回工具调用请求。应用解析请求后,只把它当作外部输入处理,而不是当作可信指令。

第四,应用执行校验和授权。包括 schema 校验、资源权限、调用次数、成本预算、超时限制和风险等级判断。

第五,应用执行工具并得到结构化结果。结果可以来自内部 API、数据库、浏览器、文件系统或第三方服务。

第六,应用把工具结果追加回模型上下文,或者直接把结构化结果交给前端渲染。需要模型继续组织语言时,再让模型基于结果生成最终回答。

第七,应用记录调用链路。至少应该记录工具名、参数摘要、执行状态、耗时、错误码和用户确认信息。

前端在工具调用里负责什么

很多工具调用发生在后端,但前端并不是旁观者。

前端首先负责表达用户意图。一个输入框、一个选中的文件、一段页面选区、当前路由、用户选择的项目空间,都可能成为模型判断工具调用的上下文。

前端还要展示过程状态。传统请求通常只有 loading、success、error。工具调用会更细:

  • 模型正在判断是否需要工具。
  • 工具调用已生成,等待执行。
  • 工具执行中。
  • 工具返回空结果。
  • 工具失败,可重试。
  • 工具需要用户确认。
  • 多个工具按顺序执行。

如果 UI 只显示一个旋转 loading,用户会不知道系统到底在做什么。

前端还要设计确认流程。涉及写操作、付款、删除、发送消息、修改权限时,模型最多只能提出建议,真正执行前应该让用户确认。

最后,前端要区分“模型回答”和“工具数据”。例如订单列表、航班信息、价格结果,最好直接用结构化数据渲染;模型文本可以作为解释,而不是唯一事实来源。

应用为什么必须做编排

工具调用最容易出问题的地方,是把模型当成编排器的全部。

模型可以规划下一步,但应用必须掌握控制权。原因很简单:只有应用知道权限、成本、并发、网络状态、审计要求和业务风险。

应用编排层通常要处理:

  • 工具白名单:这个用户当前能用哪些工具。
  • 参数校验:参数是否符合 schema 和业务规则。
  • 权限控制:用户是否能访问对应资源。
  • 预算限制:最多调用几次工具,最长执行多久。
  • 错误规范:失败时返回什么结构。
  • 可观测性:记录调用链路、耗时、参数摘要和结果摘要。
  • 人工确认:高风险动作是否需要用户批准。

没有这一层,工具调用会变成“模型想做什么就做什么”,这在真实产品里不可接受。

工具调用和结构化输出的关系

结构化输出是工具调用的基础能力之一。

当模型返回普通文本时,应用很难稳定知道它想做什么:

我需要查询一下订单系统。

当模型返回结构化工具调用时,应用可以确定下一步:

{
  "tool": "searchOrders",
  "arguments": {
    "customerId": "cus_123",
    "months": 3,
    "status": "overdue"
  }
}

所以工具调用本质上依赖一个约定:模型输出必须变成程序可以解析和验证的数据。

这也是为什么结构化输出与工具调用主题把结构化输出、Function Calling、Tool Calling 和 MCP 放在一起。它们不是孤立概念,而是同一条工程链路上的不同层。

常见坑

第一个坑:以为模型真的知道工具执行结果。

模型只有在应用把工具结果追加回上下文后,才知道执行结果。工具在后台执行了但结果没回填,模型就只能猜。

第二个坑:把工具描述写给人看,不写给模型看。

模型需要明确的使用条件、参数含义和边界。内部缩写、业务黑话和过短描述都会降低选择准确率。

第三个坑:让工具返回超长文本。

工具返回几十 KB 的日志、HTML 或数据库原始记录,会浪费 token,也会干扰模型。更好的方式是返回结构化摘要和必要字段,原始数据留给应用存储。

第四个坑:没有限制调用次数。

模型可能在不确定时连续调用工具,造成成本和延迟失控。应用应设置最大调用轮数、超时和预算。

第五个坑:把写操作做成自动执行。

创建订单、发送邮件、删除文件、修改权限都属于高风险动作。模型可以准备参数,但执行前应该有确认、权限校验和审计。

第六个坑:前端没有处理工具失败。

工具失败不是普通接口失败那么简单。模型可能需要基于失败原因继续解释,用户可能需要修改输入,系统可能允许重试或换工具。UI 需要给这些状态留位置。

小结

AI 能调用工具,是因为应用把工具能力暴露成模型可理解的结构化接口,并在模型生成调用请求后负责真实执行。

模型不是执行环境,应用才是执行环境。模型做的是意图理解、工具选择和参数生成;应用做的是编排、验证、权限、执行和回填;前端做的是输入上下文、过程状态、确认交互和结构化结果展示。

理解这个分工后,工具调用就不再神秘。它是一套把自然语言、结构化数据和确定性系统连接起来的工程协议。