OpenAI Function Calling 完全指南:让大模型安全调用你的业务系统

Function Calling 让大模型从"只会生成文字"升级为"能够连接真实数据和业务动作"的系统组件。它不是让模型直接运行代码,而是让模型以结构化方式提出调用请求,再由应用程序完成校验、执行和结果回传。

一、什么是 OpenAI Function Calling?

普通大模型只能根据已有上下文生成文本。用户问"今天上海天气如何",模型无法凭空知道实时天气;用户说"帮我查询订单 10086",模型也不能直接访问企业数据库。Function Calling(也称 Tool Calling)解决的正是模型与外部世界之间的连接问题。

开发者先向模型声明一组可用工具,包括工具名称、用途和参数结构。模型分析用户意图后,可以返回一个结构化的函数调用请求,例如调用 get_order_status,并给出参数 {"order_id":"10086"}。应用程序收到请求后,自行检查权限、执行函数,再把结果交回模型,由模型生成适合用户阅读的最终答案。

根据 OpenAI 官方 Function Calling 文档,工具可以连接实时数据、内部系统或具体业务动作,例如天气查询、账户信息读取和退款处理。这里必须强调:模型只提出调用请求,不会替你执行函数。代码执行、身份认证、参数校验、事务控制和审计仍然由你的应用负责。

二、为什么不能只靠提示词?

有人会要求模型"请严格输出 JSON",然后直接解析生成结果。这种方法适合简单演示,但很难支撑生产系统。模型可能添加解释文字、遗漏字段、拼错枚举值,甚至虚构一个根本不存在的操作。Function Calling 则把工具定义和 JSON Schema 一起传入模型,让工具选择与参数生成成为标准化的 API 输出。

它主要带来四项价值:

  • **连接实时信息:**访问天气、库存、价格、物流和数据库等训练数据之外的信息。
  • **调用业务能力:**创建工单、发送通知、预订会议或触发审批流程。
  • **获得结构化参数:**使用 JSON Schema 约束参数名称、类型、必填项和可选值。
  • **分离决策与执行:**模型负责理解自然语言,应用负责安全、确定地执行代码。

因此,Function Calling 并不是一个"远程函数执行器",而是一套模型与应用之间的结构化协作协议。

三、一次完整调用经历了什么?

Function Calling 的六步工作流:用户请求、模型判断、生成参数、应用执行、返回结果、生成答案。

  1. 用户用自然语言提出问题或任务。
  2. 应用把用户输入和可用工具定义发送给模型。
  3. 模型判断是否需要工具;需要时返回函数名、调用 ID 和 JSON 参数。
  4. 应用解析参数,完成权限检查和业务校验,然后执行对应函数。
  5. 应用使用原始调用 ID,把工具结果作为 function_call_output 返回模型。
  6. 模型结合用户问题与工具结果,生成最终回答;必要时还可能继续调用其他工具。

这是一段循环,而不是固定的"一问一答"。复杂任务可能先查询用户、再查询订单、最后创建售后单。编排层必须持续处理模型输出,直到模型不再请求工具,或者达到预设的最大调用次数。

四、使用 Responses API 实现第一个函数调用

下面用 Python 演示一个订单查询工具。示例以 Responses API 为主线,省略了 API Key 配置、数据库连接和 Web 框架代码。

python 复制代码
from openai import OpenAI

client = OpenAI()

tools = [
    {
        "type": "function",
        "name": "get_order_status",
        "description": "根据订单编号查询当前登录用户有权查看的订单状态。",
        "parameters": {
            "type": "object",
            "properties": {
                "order_id": {
                    "type": "string",
                    "description": "订单编号,例如 10086"
                }
            },
            "required": ["order_id"],
            "additionalProperties": False
        },
        "strict": True
    }
]

response = client.responses.create(
    model="gpt-5.4-mini",
    input="帮我查询订单 10086 的状态",
    tools=tools
)

tool_outputs = []

for item in response.output:
    if item.type != "function_call":
        continue

    arguments = json.loads(item.arguments)

    if item.name == "get_order_status":
        result = {
            "order_id": arguments["order_id"],
            "status": "已发货",
            "tracking_number": "SF1234567890"
        }
    else:
        result = {"error": "unknown_tool"}

    tool_outputs.append({
        "type": "function_call_output",
        "call_id": item.call_id,
        "output": json.dumps(result, ensure_ascii=False)
    })

final_response = client.responses.create(
    model="gpt-5.4-mini",
    previous_response_id=response.id,
    input=tool_outputs,
    tools=tools
)

print(final_response.output_text)</code></pre>

代码中最重要的不是模拟的订单结果,而是两个关联点:第一次响应返回的 call_id 必须原样带回;第二次请求通过 previous_response_id 延续上下文。工具输出最好使用紧凑、稳定、可序列化的 JSON,而不是把数据库对象或整段日志直接交给模型。

五、工具 Schema 决定调用质量

模型是否能选对工具、填对参数,很大程度上取决于工具定义,而不只是系统提示词。一个名为 process、描述为"处理数据"的工具几乎没有决策价值;get_order_status 配合明确的使用条件,则容易被模型正确理解。

设计工具时可以遵循以下原则:

  • 工具名称使用动词加对象,例如 search_productscreate_ticket
  • 描述说明"做什么、何时使用、有什么副作用",避免模糊宣传语。
  • 参数保持少而清晰,不要要求模型填写可由登录态或服务端推导的数据。
  • 固定选项使用 enum,日期、金额和标识符明确格式与单位。
  • 开启 strict,并使用严格 JSON Schema 降低参数漂移。

在严格模式下,应把对象允许的字段写进 properties,将必须出现的字段列入 required,并设置 additionalProperties: false。如果某个字段允许为空,可在 Schema 中把它定义为对应类型与 null 的联合类型,而不是放任模型省略任意字段。

六、控制模型何时调用工具

默认情况下,模型会根据上下文自行决定是否调用工具。实际业务还可以通过 tool_choice 调整策略:

  • **自动选择:**模型可以直接回答,也可以调用一个或多个工具,适合普通助手。
  • **禁止调用:**只允许模型生成文本,适合降级模式或纯内容任务。
  • **必须调用:**要求模型至少选择一个工具,适合所有答案都必须来自业务系统的场景。
  • **指定函数:**强制调用某个函数,适合已由上游流程确定动作的场景。

模型还可能在一次响应中返回多个调用。例如用户问"比较北京和上海的天气",它可能并行调用两次天气工具。应用必须遍历全部 function_call,不能假设响应里永远只有一个。若工具存在顺序依赖或写入副作用,可关闭并行工具调用,或者由业务编排层明确控制执行顺序。

七、错误处理不能交给模型猜

生产环境中的工具会超时、限流,也会遭遇权限不足、资源不存在和业务规则冲突。工具层应返回结构化错误,而不是把异常堆栈直接暴露给模型:

bash 复制代码
{
  "ok": false,
  "error": {
    "code": "ORDER_NOT_FOUND",
    "message": "未找到订单,或当前用户无权查看",
    "retryable": false
  }
}

编排层应区分可重试与不可重试错误。网络抖动和部分服务端错误可以指数退避后重试;参数错误和权限错误通常不应原样重试;支付、发券和发送消息等操作只有在提供幂等键后才能安全重试。为了防止模型陷入循环,还应设置单次任务最大工具调用数、整体超时和熔断策略。

八、安全边界:模型输出永远不是授权

Function Calling 最大的误区,是把模型生成的参数视为可信输入。事实上,用户输入、网页内容和工具结果都可能诱导模型执行越权操作。模型说"请退款"并不等于用户具备退款权限,模型生成 tenant_id=1 也不代表它可以访问该租户。

生产系统至少需要以下防线:

  • 从服务端会话获取用户和租户身份,不接受模型伪造身份字段。
  • 在真正的 Service 或 API 层再次执行权限和数据范围检查。
  • 退款、删除、转账等高风险动作要求人工确认,并明确展示对象与影响。
  • 对字符串长度、金额范围、日期和枚举执行确定性校验。
  • 工具采用最小权限,只返回完成任务所需的字段,并对敏感数据脱敏。
  • 记录调用 ID、工具名、脱敏参数、结果、耗时和操作者,形成审计链路。

一个好的工具不是"执行任意 SQL"或"调用任意 URL",而是边界清晰的业务能力,例如"查询我的订单"或"创建待审批退款申请"。能力越具体,测试、安全和治理就越容易。

九、Function Calling 与 Structured Outputs 有什么区别?

两者都能利用 JSON Schema,但目标不同。Function Calling 用于连接外部能力:模型返回函数名和参数,应用执行工具。Structured Outputs 用于约束模型最终生成的内容,例如要求它输出一份包含标题、风险等级和改进建议的 JSON 报告。

能力 主要用途 是否执行外部动作
Function Calling 选择并调用应用提供的工具 由应用程序执行
Structured Outputs 约束模型最终输出的数据结构 通常不执行

在真实 Agent 中,两者经常组合使用:先通过 Function Calling 查询业务数据或执行动作,再使用 Structured Outputs 生成稳定的结果对象,供前端渲染或下游流程消费。

十、上线前检查清单

  • 每个工具是否拥有准确、可区分的名称和描述?
  • Schema 是否限制了类型、枚举、长度、必填字段和额外字段?
  • 是否处理了多个并行工具调用,而非只读取第一项?
  • 登录身份、租户和权限是否全部由服务端可信上下文提供?
  • 写操作是否支持幂等、确认、超时和审计?
  • 工具错误是否结构化,并区分是否可以重试?
  • 是否设置了最大调用次数,防止无限循环和成本失控?
  • 是否准备真实测试集,统计工具选择和参数生成的成功率?

结语

OpenAI Function Calling 的核心并不复杂:向模型声明工具,接收结构化调用请求,安全执行函数,再把结果返回模型。但从"能够调用"走到"可靠调用",还需要良好的 Schema、清晰的工具边界、完整的错误模型、严格的权限校验和可追踪的编排循环。

如果把大模型比作负责理解与决策的大脑,那么 Function Calling 就是神经系统,而业务 API 才是真正执行动作的双手。模型可以提出建议和参数,但最终权限必须掌握在确定性的应用代码中。守住这条边界,Function Calling 才能成为构建客服助手、数据分析 Agent、运维助手和企业自动化系统的可靠基础。

相关推荐
俊昭喜喜里40 分钟前
C#中的func<>
java·前端·c#
BillKu1 小时前
vue3 字符串排序 localeCompare,确保排序为 “B01“, “B10“, “B2“
前端·javascript·vue.js
一鸣AI编程1 小时前
功能用例接入 TestStar AI UI:一份可执行的接入清单
前端
晴天161 小时前
打造自己的 npm 包实战指南
前端·npm·node.js
然我1 小时前
从 Service 到生命周期:Agent Runtime 的插件内核
前端·javascript·agent
2401_868534781 小时前
网规备考_5.5 IDS与IPS的原理及应用
网络·安全
Moriarty1236662 小时前
【前端技巧】实现卡片页面容器全屏
前端·css
mmsx2 小时前
osmdroid 地图实战 02|在线底图接入:从 URL 模板到生产级瓦片源工厂(谷歌 + 天地图双案例)
前端·前端框架
用户307140958482 小时前
零依赖实现「形切形」:我用 6200 行原生 Canvas 写了个网页设计工具
前端·ai编程·canvas