GPT-5 函数调用全指南:从结构化到自由文本

2025-11-12 16:53:52
文章摘要
GPT-5 能查天气、调接口、跑程序,还能自己解释为什么这么做。这都是通过五大新功能,结构化工具、自由工具、语法约束、白名单、安全前言来实现。这让GPT-5变成了一个可控、能执行、懂思考的智能助理,正式迈入Agent时代。

如果说GPT-4 还是一个只会说的语言模型,那么GPT-5则已经学会了怎么干活。

它能帮你查天气、执行代码、甚至直接操作数据库,这就是GPT-5的函数调用能力(Function Calling)。

它让模型在理解语义的同时,主动去调用外部函数、接口或数据库,从而获得真实数据或执行操作。

更形象点说就是GPT-4像个知识顾问,只回答问题;GPT-5则是一个能写方案、调接口、跑程序的智能助理。

GPT-5的五中新特性让这一切成为现实:

● 结构化函数工具(JSON Schema),确保输入输出严格规范

● 自由形式工具(Free-Form Tool),允许直接传递原始文本(如 SQL、脚本)

● 语法约束(Lark),保证输出符合特定格式

● 工具白名单(Tool Allowlist),提高调用安全性

● 调用前言(Preamble),让模型解释“为什么要调用这个工具

这五个机制,共同让GPT-5具备了可控执行力和透明推理链,真正迈向 Agent级智能体模型。

来源:https://platform.openai.com/docs/guides/function-calling#function-tool-example

 

JSON Schema

在早期的GPT模型中,我们可以让模型生成JSON参数,但它可能会犯错,字段名拼错、类型不匹配、缺值等。

GPT-5 通过JSON Schema解决了这个问题。

你可以像定义接口文档一样定义一个函数

{
  "type": "function",
  "name": "make_coffee",
  "description": "提供咖啡配方",
  "parameters": {
    "type": "object",
    "properties": {
      "coffee_type": {"type": "string"}
    },
    "required": ["coffee_type"]
  }
}

 

模型会自动理解

1.  这个函数能干什么

2.  它需要哪些参数

3.  参数的类型和描述是什么

于是当用户问怎么做拿铁时,GPT-5 会自动构造

{"coffee_type": "latte"}

然后调用 make_coffee("latte"),再把返回的结果(例如配方)嵌入最终回答中。

这样的模式确保

1.  稳定:参数完全符合结构

2.  安全:输入类型受控

3.  可自动化:结果天然适合程序读取

小结:Function Tools适合配置生成、任务调度、表单填充等场景。

 

Free-Form Tool

有时候我们不想被JSON 束缚,比如你要让模型写一段SQL、生成一份配置、或输出一段Python代码。

这时可以用Free-Form Custom Tool,让模型直接输出原始文本作为工具输入。

例如定义一个meal_planner工具,要求模型只传递食材列表

{
  "type": "custom",
  "name": "meal_planner",
  "description": "根据逗号分隔的食材列表生成菜谱建议"
}

用户输入:“我只有鸡肉、米饭和西兰花”,模型就会这样调用

Input: chicken, rice, broccoli

工具执行后返回

Output: One-pot rice: cook chicken, rice, broccoli together in broth until fluffy.

最终GPT-5再把它润色成食谱步骤。

这种方式的优势是

1.  灵活:不受结构限制,适合DSL、SQL、脚本类输出

2.  快速原型:无需定义复杂Schema

3.  强扩展性:方便对接各种自定义执行环境

简单说:JSON Schema是严谨型,Free-Form Custom Tool是自由型。一个像企业软件接口,另一个像命令行工具。

 

Lark

GPT-5 还支持Lark(上下文无关语法)约束。

你可以定义一套语法规则,让模型输出必须符合结构要求,比如只生成合法SQL或数学表达式。

例如你希望模型只输出数学式,不要废话

"syntax": "lark",
"definition": """
start: expr
?expr: term | expr "+" term | expr "-" term
?term: factor | term "*" factor | term "/" factor
?factor: NUMBER | "(" expr ")"
%import common.NUMBER
%ignore " "
"""

模型接到问题 (12 + 8) * 3减去5除以5等于几?

它会生成

(12 + 8) * 3 - 5 / 5

本地Python执行后返回结果59.0,模型再总结成自然语言答案:答案是59。

这类语法约束非常适合高精度任务,比如数学推理、SQL 生成、配置文件生成、编译规则生成等。

 

Tool Allowlist

Function Calling 的强大之处,也意味着它必须被严格管理。

GPT-5 引入了Tool Allowlist(工具白名单)机制,确保模型只能调用指定工具。

比如你定义了

● firecrawl_search(真实网络搜索)

● dummy_web_search(伪造结果)

通过allowlist限制,GPT-5就只能使用 firecrawl_search。

这样不仅提升了安全性,也保证了可控行为,让模型不会乱调接口。在企业环境中,这一点尤为关键

1.  避免调用未经审计的外部服务

2.  保证工具行为可追踪、可复现

3.  配合必用模式实现强制流程控制

# ========================================
# 🧠 GPT-5 Function Calling: Tool Allowlist 示例
# 限制模型只能调用 firecrawl_search 工具
# ========================================

from openai import OpenAI
from firecrawl import Firecrawl
import json
import os

# 初始化 OpenAI 客户端与 Firecrawl SDK
client = OpenAI()
firecrawl = Firecrawl(api_key=os.environ["FIRECRAWL_API_KEY"])

# --- 定义完整工具集 ---
tools = [
    {
        "type": "function",
        "name": "dummy_web_search",
        "description": "一个伪造的网页搜索,始终返回固定虚假结果。",
        "parameters": {
            "type": "object",
            "properties": {
                "query": {"type": "string"},
                "limit": {"type": "integer"}
            },
            "required": ["query"]
        },
    },
    {
        "type": "function",
        "name": "firecrawl_search",
        "description": "通过 Firecrawl 执行真实的网页搜索。",
        "parameters": {
            "type": "object",
            "properties": {
                "query": {"type": "string"},
                "limit": {"type": "integer"}
            },
            "required": ["query"]
        },
    },
]

# --- Dummy 工具的伪实现 ---
def dummy_web_search(query, limit=3):
    return {
        "query": query,
        "results": [f"[Dummy] Result {i+1} for '{query}'" for i in range(limit)]
    }

# --- 用户输入 ---
messages = [
    {"role": "user", "content": "Find me the latest info about the stock market."}
]

# --- 强制模型仅使用 firecrawl_search ---
resp = client.responses.create(
    model="gpt-5",
    tools=tools,
    input=messages,
    instructions="Only the firecrawl_search tool is allowed. Do not use dummy_web_search.",
    tool_choice={
        "type": "allowed_tools",
        "mode": "required",  # 必须使用 allowlist 中的工具
        "tools": [{"type": "function", "name": "firecrawl_search"}],
    },
)

# --- 执行工具调用逻辑 ---
for item in resp.output:
    if getattr(item, "type", "") in ("function_call", "tool_call"):
        print("\n--- Tool Call ---")
        print("Tool name:", item.name)
        print("Arguments:", item.arguments)

        if item.name == "firecrawl_search":
            args = json.loads(item.arguments)
            results_obj = firecrawl.search(
                query=args["query"],
                limit=args.get("limit", 3),
            )

            # 🔥 转为可序列化的 dict
            if hasattr(results_obj, "to_dict"):
                results = results_obj.to_dict()
            elif hasattr(results_obj, "dict"):
                results = results_obj.dict()
            else:
                results = json.loads(results_obj.json()) if hasattr(results_obj, "json") else results_obj

            print("\n--- Firecrawl Raw Results ---")
            print(json.dumps(results, indent=2)[:500])

            # ✅ 提取可安全总结的部分
            safe_results = results.get("data", results)

            # 将工具结果反馈回模型
            messages += resp.output
            messages.append({
                "type": "function_call_output",
                "call_id": item.call_id,
                "output": json.dumps(safe_results)
            })

            # 最终总结输出
            final = client.responses.create(
                model="gpt-5",
                tools=tools,
                input=messages,
                instructions="Summarize the Firecrawl search results into 3-4 bullet points with clickable [Title](URL) links."
            )

            print("\n--- ✅ Final Natural Language Answer ---")
            print(final.output_text)

        elif item.name == "dummy_web_search":
            args = json.loads(item.arguments)
            results = dummy_web_search(args["query"], args.get("limit", 3))
            print("\n--- Dummy Web Search Results ---")
            print(json.dumps(results, indent=2))

要点说明:

● tool_choice 使用 allowed_tools 限制模型仅能调用指定工具

● mode: "required" 表示必须使用指定工具

● 执行完工具后,通过 function_call_output 把结果反馈给模型

● 再次调用模型生成最终自然语言总结

 

Preambles

GPT-5还加入了一个非常人性化的机制:Preambles(调用前言)。

它让模型在调用工具前,自动输出一句说明,例如

Preamble: 我将调用 medical_advice 工具来提供安全的感冒建议。

然后才会真正执行函数调用。

这一机制带来两大好处

1.  可观测性增强:方便调试和日志审计

2.  信任感提升:用户能看到模型为什么这么做

这种解释性机制,对未来多模型协作与监管合规尤为重要。

# ========================================
# 💬 GPT-5 Function Calling: Preamble 示例
# 让模型在调用工具前输出“Preamble”说明
# ========================================

from openai import OpenAI
import json

client = OpenAI()

# --- 工具的假实现 ---
def medical_advice(symptom: str):
    remedies = {
        "headache": "You can take acetaminophen (paracetamol) or ibuprofen, rest in a quiet room, and stay hydrated.",
        "cough": "Drink warm fluids, use honey in tea, and consider over-the-counter cough syrup.",
        "fever": "Use acetaminophen to reduce fever, stay hydrated, and rest. See a doctor if >39°C.",
    }
    return {"advice": remedies.get(symptom.lower(), "Please consult a healthcare provider for guidance.")}

# --- 定义严格类型的工具 ---
tools = [{
    "type": "function",
    "name": "medical_advice",
    "description": "Provide safe, general over-the-counter advice for common symptoms.",
    "parameters": {
        "type": "object",
        "properties": {"symptom": {"type": "string"}},
        "required": ["symptom"],
        "additionalProperties": False
    },
    "strict": True,
}]

# --- 系统消息:要求模型输出 Preamble ---
messages = [
    {"role": "system", "content": "Before you call a tool, explain why you are calling it in ONE short sentence prefixed with 'Preamble:'."},
    {"role": "user", "content": "What should I take for a headache?"}
]

# 1️⃣ 第一次调用:期望输出 Preamble + 工具调用
resp = client.responses.create(model="gpt-5", input=messages, tools=tools)
print("=== First Response ===")

for item in resp.output:
    t = getattr(item, "type", None)
    if t == "message":
        content = getattr(item, "content", None)
        text = None
        if isinstance(content, list):
            text = "".join([c.text for c in content if hasattr(c, "text")])
        elif content:
            text = str(content)
        if text:
            print(text)

    if t in ("function_call", "tool_call", "custom_tool_call"):
        print("Tool:", getattr(item, "name", None))
        print("Args:", getattr(item, "arguments", None))

# 提取工具调用
tool_call = next(x for x in resp.output if getattr(x, "type", None) in ("function_call","tool_call","custom_tool_call"))
messages += resp.output

# 2️⃣ 执行工具逻辑
args = json.loads(getattr(tool_call, "arguments", "{}"))
symptom = args.get("symptom", "")
tool_result = medical_advice(symptom)

# 3️⃣ 把结果反馈回模型
messages.append({
    "type": "function_call_output",
    "call_id": tool_call.call_id,
    "output": json.dumps(tool_result)
})

# 4️⃣ 最后调用:生成自然语言答案
final = client.responses.create(model="gpt-5", input=messages, tools=tools)
print("\n=== Final Answer ===")
print(final.output_text)

要点说明:

● system 消息中要求模型在调用前输出一句以 "Preamble:" 开头的解释

● 模型随后仍会发出标准的 function_call

● 执行工具后再反馈给模型

● 模型生成清晰、自然的最终回答

● 该机制提高可观察性和信任度,非常适合复杂流程调试

 

总结

在 GPT-5之前,大模型的核心价值是理解与生成,在 GPT-5之后,它的能力已经扩展为 理解 + 生成 + 执行 + 控制。通

这些特性让 GPT-5不只是聊天模型,而是一个通用的智能执行引擎。

无论你是开发智能客服、数据助手、自动化分析系统,还是构建自己的 AI代理平台,GPT-5都已经提供了底层的操作系统级接口。


声明:该内容由作者自行发布,观点内容仅供参考,不代表平台立场;如有侵权,请联系平台删除。