L3 · 专题文章

Function Calling:把模型意图变成可控的程序调用

Function CallingTool UseStructured OutputAgentAPI

Function Calling 不是给模型权限,而是让模型在受控格式里提出动作建议;真正的执行权始终属于应用。

MODEL选择与提议
APP校验与授权
TOOL执行与返回
最重要的边界模型提出调用,后端决定是否执行。

把模型返回的工具名和参数当作不可信输入;校验、鉴权、确认和审计不能交给模型自己完成。

LIVE
CORE 受控调用契约
01 WHAT

是什么

一种让模型按预先声明的工具名称和参数结构表达调用意图的接口契约,而不是让模型直接执行代码。

02 WHY

为什么

把模糊的自然语言需求转换为应用能够校验、授权、记录和执行的结构化动作。

03 HOW

怎么做

先定义一个窄而清晰的工具 Schema,再由后端验证模型返回的参数,确认权限后执行,并把结果送回模型或用户。

04 WHEN

什么时候

需求需要模型在多个动作中做语义选择,参数来自自然语言,而且执行前能够通过明确 Schema 与权限规则约束。不宜时:动作固定、步骤确定或风险很高时,普通表单、按钮、规则引擎或显式工作流通常更简单、更稳定。

FOCUS

先记住这些

只留最重要的判断,细节见下方实践
01 责任边界

模型只负责提议

工具选择和参数都是概率性输出,应用必须允许拒绝、修复或要求用户确认。

02 控制核心

Schema 约束形状,不保证业务正确

类型正确的日期、金额或 ID 仍可能不符合当前用户权限和真实业务状态。

03 独特优势

连接语义与确定性系统

用户可以自然表达意图,真实动作仍由可测试、可审计的程序完成。

04 复杂度

副作用比解析更难

写操作需要审批、幂等、补偿、审计和失败恢复,不能照搬只读查询的处理方式。

ROUTE

学习路径

建议按此顺序逐步深入
01
定义窄工具

一个工具只承担一个清晰动作。

02
分离输入与控制

用户数据是输入,Schema、权限和策略是控制。

03
先校验再执行

结构、业务规则和授权逐层通过。

04
把结果闭环

工具结果返回模型,同时保留日志与失败信息。

PROBLEM / POSITION / INTERFACE

先弄清它为什么存在,以及谁在使用

不从历史开始,从真实工作关系开始。
  1. 01 解决的问题

    当用户不只想得到一段回答,而是希望查询实时数据、写入业务系统、调用 API 或完成某个动作时。

    模型生成的自然语言无法被程序稳定解析,更不能直接获得数据库、支付、文件或外部服务的执行权限。

    成功标准
    模型能提出符合 Schema 的调用请求;应用能验证参数与权限、执行或拒绝,并把可追踪的结果返回给后续流程。
  2. 02 AI 生态位

    位于模型推理与应用执行之间,是把语义判断交给确定性程序的接口层;它连接 LLM、工具注册表、权限系统和业务服务。

    上游
    用户意图、对话上下文、模型能力、工具说明、参数 Schema、身份与当前业务状态。
    下游
    搜索、数据库、内部 API、工作流、浏览器、消息系统,以及需要消费工具结果的模型或页面。
  3. 03 人的生态位

    开发者定义可用工具与安全边界,业务人员决定哪些动作允许自动化,最终用户用自然语言提出目标并在高风险动作前确认。

    适合使用
    需求需要模型在多个动作中做语义选择,参数来自自然语言,而且执行前能够通过明确 Schema 与权限规则约束。
    不必使用
    动作固定、步骤确定或风险很高时,普通表单、按钮、规则引擎或显式工作流通常更简单、更稳定。
  4. 04 独特价值

    它把模型擅长的语义理解与程序擅长的确定性执行分开:既保留自然语言入口,又让每个动作经过可验证的接口边界。

    难点不在“让模型返回 JSON”,而在工具描述、参数歧义、权限、并发、幂等、失败恢复、结果注入和副作用治理。

    复杂度判断
    复杂度不是功能数量,而是控制与验证成本。

INTERFACE FLOW

谁在操作,信息怎样流动

  1. WHO OPERATES

    HUMAN提出目标,配置业务规则,并确认高风险动作

    MODEL判断是否需要工具,选择工具并生成候选参数

    BACKEND校验参数、检查权限、执行调用、记录审计与处理失败

    TOOL读取或改变外部系统,并返回事实结果

  2. INPUT

    用户请求、当前对话与业务状态,以及应用提供给模型的可用工具定义。

  3. CONTROL

    工具白名单、JSON Schema、系统指令、身份权限、审批规则、超时、重试、幂等键和调用预算。

  4. OUTPUT

    模型先输出工具名与结构化参数;后端执行后产生工具结果,可能只读取数据,也可能写库、发消息或改变外部状态。

FLOW

一次可靠工具调用如何完成

01识别目标模型判断用户是在询问信息,还是要求系统采取动作。
02选择工具只从当前会话允许的工具集合中选择,必要时先追问缺失信息。
03生成参数输出工具名和符合声明结构的候选参数,不直接执行。
04验证与授权后端检查 Schema、业务规则、权限、风险和是否需要人工确认。
05执行与隔离在超时、限流、幂等和最小权限保护下调用真实工具。
06适配结果结果被脱敏、裁剪并标注成功或失败,避免把巨大原始数据塞回上下文。
07继续或结束模型基于事实结果回答、调用下一工具,或在失败时请求修正。

MODULES

真正决定好不好用的六个模块

01

工具说明

让模型知道何时用、何时不用;说明含糊会导致选错工具。

02

参数 Schema

约束字段结构和基础取值,减少自由文本解析的不确定性。

03

业务校验

检查日期关系、资源状态、金额范围等 Schema 无法表达的规则。

04

权限与确认

按身份和风险决定只读、写入、拒绝或人工确认。

05

执行控制

处理超时、重试、幂等、限流、并发和补偿。

06

结果适配

把工具事实转换为模型可理解、可追踪且不过度暴露的数据。

COMPARE

什么时候选择 Function Calling

方式适合的问题主要优势主要限制
普通文本回答只需解释、改写或总结最简单,没有执行副作用不能稳定驱动程序动作
固定按钮或表单步骤和字段完全确定可预测、易测试、权限清晰难以处理开放式自然语言意图
Function Calling意图开放,但动作集合可枚举语义选择与确定性执行兼得需要完整校验、权限和失败治理
自由代码生成沙箱中的探索性编程表达能力强执行风险高,不适合直接操作核心业务
JSON / RESPONSE工具定义与候选调用:输入和控制不要混在一起
{
  "tool_definition": {
    "name": "get_order_status",
    "description": "读取当前用户有权查看的订单状态;不能修改订单。",
    "parameters": {
      "type": "object",
      "properties": {
        "order_id": {
          "type": "string",
          "description": "订单公开编号"
        }
      },
      "required": [
        "order_id"
      ],
      "additionalProperties": false
    }
  },
  "business_input": {
    "user_request": "帮我查一下订单 A-2048 到哪了"
  },
  "control_context": {
    "authenticated_user": "current-user",
    "allowed_tools": [
      "get_order_status"
    ],
    "write_access": false
  },
  "model_proposal": {
    "name": "get_order_status",
    "arguments": {
      "order_id": "A-2048"
    }
  }
}
CODE / PYTHON最小但不越权的后端调度器
ALLOWED_TOOLS = {
    "get_order_status": get_order_status,
}

def execute_tool_call(call, user):
    # 1. 模型输出永远是不可信输入
    name = call.get("name")
    arguments = call.get("arguments", {})

    if name not in ALLOWED_TOOLS:
        return {"ok": False, "error": "tool_not_allowed"}

    # 2. Schema 合法不等于业务上有权访问
    order_id = arguments.get("order_id")
    if not isinstance(order_id, str) or not order_id:
        return {"ok": False, "error": "invalid_arguments"}

    if not user.can_read_order(order_id):
        return {"ok": False, "error": "permission_denied"}

    # 3. 真正的工具调用发生在受控代码中
    try:
        result = ALLOWED_TOOLS[name](
            order_id=order_id,
            timeout_seconds=3,
        )
        return {"ok": True, "data": result}
    except TimeoutError:
        return {"ok": False, "error": "tool_timeout"}

示例强调责任边界;生产环境还需 JSON Schema 校验器、持久化审计、分布式幂等和机密管理。

LLM OUTPUT / JSON模型实际返回的是调用提案
{
  "type": "tool_call",
  "id": "call_demo_01",
  "name": "get_order_status",
  "arguments": {
    "order_id": "A-2048"
  }
}

这是脱敏示例。应用仍需检查当前用户是否有权读取该订单,模型返回 completed 不代表工具已经执行。

PRACTICE

从演示走向生产前的检查清单

每个工具只承担一个清晰职责,并写明何时不该调用
根据当前身份动态裁剪工具列表,而不是暴露所有能力
区分结构校验、业务校验、权限校验和人工确认
写操作具备幂等键、审计记录和失败补偿策略
工具设置超时、限流和最大调用次数,避免失控循环
工具结果先脱敏和裁剪,再放回模型上下文
记录模型提案、验证结果、执行结果与最终答复之间的关联
评测不仅看参数正确率,还看拒绝、追问和失败恢复是否合理

FAQ

常见问题

选型、用法与失败,不复述定义。
01到底是谁在调用工具:模型还是后端?

模型生成调用提案;后端读取提案、完成校验并调用真实函数或服务。

如果把模型误认为执行者,就容易跳过权限、审计和失败处理。
02Function Calling 和结构化输出有什么区别?

结构化输出解决“回答长什么样”,Function Calling 进一步表达“希望应用执行哪个动作”;动作是否执行仍由应用决定。

不是每个 JSON 都是工具调用,也不是每个结构化结果都应该产生副作用。
03工具参数已经通过 JSON Schema,为什么还要业务校验?

Schema 检查类型和形状;权限、资源状态、跨字段关系和风险条件属于业务规则。

格式正确的参数仍然可能越权、过期、冲突或造成错误写入。
04工具应该设计得大而全,还是小而明确?

优先使用职责窄、边界清楚的工具,再由应用或 Agent 组合;不要把几十种行为塞进一个万能工具。

工具越宽泛,描述越难写,权限越难切分,模型也越难稳定选对。
05模型参数不完整或含糊时应该怎么办?

不要猜高风险字段;应用可以拒绝候选调用,让模型向用户追问,或提供受控默认值。

自动补全日期、金额、收件人等字段可能把语言歧义变成真实副作用。