For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
主导航

程序化工具调用

让模型编写并运行 JavaScript 来编排工具调用。

程序化工具调用让模型能够编写并运行 JavaScript,协调其工具的调用。程序可以并行调用工具、使用循环和条件判断,并将中间结果保留在托管运行时中。当任务需要执行一系列相互关联的工具调用,或需要先处理大量工具输出再返回结果时,这项功能很有用。

在 Responses API 中,您的应用决定是否启用程序化工具调用,以及模型可以直接调用哪些符合条件的工具、可以通过程序调用哪些工具,或可以通过这两种方式调用哪些工具。由客户端负责的工具调用仍由您的应用执行。Agents API 默认启用程序化工具调用,并为您管理智能体循环。

启用程序化工具调用前,请查看模型页面

了解运行时环境

OpenAI 在全新、隔离的 V8 运行时中运行每个生成的程序。该运行时支持使用顶层 await 的 JavaScript,但不提供 Node.js、软件包安装、直接网络访问、通用文件系统、子进程执行或控制台,也不会在程序的多次执行之间持久保留 JavaScript 状态。程序只能通过请求中启用的工具与外部系统交互,并可通过 text(...)image(...) 输出内容。

对于 Responses API 请求,程序化工具调用支持零数据保留(ZDR)工作流,无需持久化的代码执行容器。必须为组织或项目启用 ZDR;设置 store: false 可让程序以无状态方式继续执行,但这一设置本身不会启用 ZDR。是否符合 ZDR 条件以及数据保留情况取决于完整请求,包括所用模型、工具和第三方服务;请参阅数据控制

选择何时使用程序化工具调用

当某个阶段的控制流可预测,且代码可以返回更精简的结构化结果时,请使用程序化工具调用。如果一次调用就足够、每个结果都需要模型重新判断,或者任务需要审批或保留引用或原生产物,请使用直接工具调用。

任务特点推荐模式
单次查询或操作使用直接工具调用。
多个结果,可通过代码进行筛选、关联、排序、去重、聚合或验证当程序可以返回更精简的结构化结果时,使用程序化工具调用。
存在依赖关系且数据流可预测的调用当代码可以推导后续调用的参数,且限制和失败时的行为都已明确时,使用程序化工具调用。
自适应搜索或语义评估当每个结果都应影响模型的下一步决策时,使用直接工具调用。
写入操作或对审批有严格要求的操作默认使用直接工具调用,以保持清晰的授权边界。
最终引用或原生产物的验证使用直接工具调用,除非程序能够保留原生输出并验证每个必需项。

配置程序化工具调用

使用 Responses API 时,请将 programmatic_tool_calling 托管工具添加到请求中,然后为程序可调用的每个符合条件的工具设置 allowed_callers

启用程序化工具调用
[
  {
    "type": "function",
    "name": "get_inventory",
    "description": "Return an object with sku (string) and available_units (number).",
    "parameters": {
      "type": "object",
      "properties": {
        "sku": { "type": "string" }
      },
      "required": ["sku"],
      "additionalProperties": false
    },
    "output_schema": {
      "type": "object",
      "properties": {
        "sku": { "type": "string" },
        "available_units": { "type": "number" }
      },
      "required": ["sku", "available_units"],
      "additionalProperties": false
    },
    "allowed_callers": ["programmatic"]
  },
  {
    "type": "programmatic_tool_calling"
  }
]

allowed_callers 控制模型可以通过哪些方式调用工具:

行为
省略或设为 ["direct"]模型可以直接调用该工具。
["programmatic"]只有 program 项中的代码可以调用该工具。
["direct", "programmatic"]模型可以直接调用该工具,也可以通过程序调用。

parameters 描述函数参数。当函数返回可预测的结构化数据时,output_schema 描述编码在其 function_call_output.output 字符串中的 JSON 对象。请同时定义这两项,以便生成的 JavaScript 能够可靠地使用返回的字段。

支持的工具

以下工具类型支持 allowed_callers: ["programmatic"]

  • functioncustom
  • mcp
  • apply_patch
  • 本地和托管式 shell
  • code_interpreter

对于 MCP 工具,工具的 require_approval 策略可以暂停程序,直到您批准该调用。

对于 OpenAI 托管的工具,请先查看该工具的数据保留和安全指南,再在程序中启用它。

工具搜索作为顶层 Responses API 工具运行,不在生成的 JavaScript 内部运行。设置了 defer_loading: true 的函数工具、自定义工具和 MCP 工具最初无法供程序使用。模型加载匹配的工具后,如果该工具的 allowed_callers 包含 "programmatic",后续程序就可以通过 tools.* 调用它。已经运行的程序无法调用工具搜索,因此模型必须先加载延迟加载的工具,再启动需要这些工具的程序。

在两种模式都可用时引导调用路径选择

当您的应用允许模型直接调用函数或通过程序调用函数时,请为每种调用路径指定相应的工作流阶段。“高效使用程序化工具调用”这类笼统指令无法明确预期的使用边界。例如:

<tool_orchestration>
Use Programmatic Tool Calling for [bounded stage] using only [eligible tools].
Run independent calls concurrently when safe. Use only documented tool input
and output fields.

Process and reduce the intermediate results, then emit exactly [program result shape],
including the evidence needed for the final answer.

Stop when [condition] is met. Retry transient failures at most [R] times.
Do not repeat completed calls or perform side-effecting actions. If a required
result is still missing, return a clear structured failure.

Use direct tool calls for [semantic judgment, approval, or final validation].
</tool_orchestration>

以下示例展示了如何使用此模板:

<tool_orchestration>
Use Programmatic Tool Calling to compare inventory with demand for sku_123
using only get_inventory and get_demand. Run both calls concurrently. Use
only documented tool input and output fields.

Process and reduce the intermediate results, then emit exactly one JSON object
with sku, available_units, requested_units, and shortage_units, where
shortage_units is max(requested_units - available_units, 0). Include
available_units and requested_units as evidence for the calculation.

Stop when both tool results contain the required fields. Retry transient
failures at most 1 time. Do not repeat completed calls or perform
side-effecting actions. If a required result is still missing, return a clear
structured failure.

Use direct tool calls only for approval before any inventory-changing action.
</tool_orchestration>

对于需要同时使用两种模式的工作流,请定义一个交接点,避免来回切换调用路径或重复执行工作。如果存在安全的回退方案,请统一定义,并限制其重试次数。

了解程序响应项

每次 API 调用仍返回标准的 Responses API 对象。程序化工具调用不会引入单独的响应封装结构。当模型使用程序化工具调用时,响应的 output 数组可以包含:

  • 一个 program 项,其中包含生成的 JavaScript、一个 call_id,以及一个用于恢复或重放程序的不透明 fingerprint
  • 一个由程序发起的 function_call 项。它有自己的 call_id,您的应用使用该值返回函数结果。它的 caller.caller_id 与程序的 call_id 一致。
  • 一个 program_output 项,其中包含程序的最终结果和状态。它的 call_id 与程序的 call_id 一致,其 statuscompletedincomplete

这些是 response.output 中彼此独立的顶层项;caller 字段记录它们的执行关系。

例如,程序可以在您的应用运行 get_inventoryget_demand 时暂停:

程序及嵌套函数调用
[
  {
    "type": "program",
    "id": "prog_123",
    "call_id": "call_prog_123",
    "code": "const [stock, demand] = await Promise.all([tools.get_inventory({ sku: 'sku_123' }), tools.get_demand({ sku: 'sku_123' })]); text(JSON.stringify({ sku: stock.sku, available_units: stock.available_units, requested_units: demand.requested_units, shortage_units: Math.max(demand.requested_units - stock.available_units, 0) }));",
    "fingerprint": "opaque_replay_state"
  },
  {
    "type": "function_call",
    "id": "fc_123",
    "call_id": "call_inventory_123",
    "name": "get_inventory",
    "arguments": "{\"sku\":\"sku_123\"}",
    "caller": {
      "type": "program",
      "caller_id": "call_prog_123"
    }
  },
  {
    "type": "function_call",
    "id": "fc_456",
    "call_id": "call_demand_123",
    "name": "get_demand",
    "arguments": "{\"sku\":\"sku_123\"}",
    "caller": {
      "type": "program",
      "caller_id": "call_prog_123"
    }
  }
]

这些示例仅展示 response.output 中的相关项,省略了外层的标准 Responses 对象。您的应用返回嵌套函数调用的结果后,后续响应中可以包含完整的 program_output 项:

程序输出
{
  "type": "program_output",
  "id": "prog_out_123",
  "call_id": "call_prog_123",
  "result": "{\"sku\":\"sku_123\",\"available_units\":42,\"requested_units\":31,\"shortage_units\":0}",
  "status": "completed"
}

program_output.result 中的 JSON 字符串遵循您在指令中定义的程序结果结构。外层的 program_output 项遵循上文所示的 API 约定。这是两套独立的约定。最终的 message 可能与程序输出一起返回,也可能在后续响应中返回,因此请继续,直到收到该消息。

OpenAI 在托管运行时中运行模型生成的 JavaScript。您的应用程序执行返回的、由客户端负责的函数调用,而不执行生成的 JavaScript。

function_call_output 的形式返回函数结果。从函数调用中原样复制 caller,不要修改。服务使用该值恢复对应程序的执行。

在客户端负责的函数调用后继续执行

程序在执行到由客户端负责的工具时可能会多次暂停。请继续,直到响应中包含最终的助手消息:

  1. 发送请求,其中包含托管工具和允许程序化调用的函数。
  2. 执行返回的每个由客户端负责的函数调用。
  3. 返回每个函数结果,并附上原始的 call_idcaller
  4. 先处理不完整的响应,再继续。
  5. 如果响应中既没有待处理的 function_call 项,也没有最终的 message 项,请从该响应继续。使用 store: false 时,重放其输出项;对于已存储的响应,请使用 previous_response_id
  6. 当响应中包含最终的 message 项时停止。读取 response.output_text 或消息中的拒绝内容。

以下示例使用 store: false,保留每个响应项,并将每个函数结果返回给程序:

运行程序化工具调用循环
import json

from openai import OpenAI

client = OpenAI()
model = "gpt-6-astra"


def get_inventory(sku):
    return {"sku": sku, "available_units": 42}


def get_demand(sku):
    return {"sku": sku, "requested_units": 31}


implementations = {
    "get_inventory": get_inventory,
    "get_demand": get_demand,
}

tools = [
    {
        "type": "function",
        "name": "get_inventory",
        "description": "Return an object with sku (string) and available_units (number).",
        "parameters": {
            "type": "object",
            "properties": {"sku": {"type": "string"}},
            "required": ["sku"],
            "additionalProperties": False,
        },
        "output_schema": {
            "type": "object",
            "properties": {
                "sku": {"type": "string"},
                "available_units": {"type": "number"},
            },
            "required": ["sku", "available_units"],
            "additionalProperties": False,
        },
        "allowed_callers": ["programmatic"],
    },
    {
        "type": "function",
        "name": "get_demand",
        "description": "Return an object with sku (string) and requested_units (number).",
        "parameters": {
            "type": "object",
            "properties": {"sku": {"type": "string"}},
            "required": ["sku"],
            "additionalProperties": False,
        },
        "output_schema": {
            "type": "object",
            "properties": {
                "sku": {"type": "string"},
                "requested_units": {"type": "number"},
            },
            "required": ["sku", "requested_units"],
            "additionalProperties": False,
        },
        "allowed_callers": ["programmatic"],
    },
    {"type": "programmatic_tool_calling"},
]

input_items = [
    {
        "role": "user",
        "content": "Compare inventory with demand for sku_123.",
    }
]

while True:
    response = client.responses.create(
        model=model,
        store=False,
        input=input_items,
        tools=tools,
    )

    if response.status != "completed":
        raise RuntimeError(f"Response ended with status {response.status}")

    # Preserve every output item, including program and reasoning items.
    input_items.extend(item.model_dump(exclude_none=True) for item in response.output)

    calls = [item for item in response.output if item.type == "function_call"]
    if not calls:
        message = next(
            (item for item in response.output if item.type == "message"), None
        )
        if message:
            refusal = next(
                (part.refusal for part in message.content if part.type == "refusal"),
                "",
            )
            print(response.output_text or refusal)
            break
        continue

    for call in calls:
        run = implementations.get(call.name)
        if run is None:
            raise ValueError(f"Unknown tool: {call.name}")

        result = run(**json.loads(call.arguments))
        input_items.append(
            {
                "type": "function_call_output",
                "call_id": call.call_id,
                "output": json.dumps(result),
                # Preserve caller so the runtime can resume the correct program.
                "caller": call.caller.model_dump() if call.caller else None,
            }
        )

存储响应后,您可以通过 previous_response_id 继续,而无需重新发送此前的所有响应项。将新的 function_call_output 项作为下一次输入发送。使用 store: false 时,请按顺序重放完整序列,包括每个 program 项、推理项、函数调用项、函数调用输出项和 program_output 项。

对于无状态的推理模型请求,请重放返回的每个推理项。每个项默认包含 encrypted_content。有关通用的无状态处理模式,请参阅对话状态

为程序设计工具

  • 返回结构化且精简的数据,让 JavaScript 无需解析自然语言文本即可检查数据。
  • 使用 output_schema 定义每个工具预期返回的字段及其类型,并记录工具出错时的行为。如果无法预先确定返回结构,请让模型直接调用该工具,以便模型检查结果。
  • 明确定义程序结果的结构和所需证据。当程序无法生成有效结果时,返回清晰的结构化失败信息。
  • 尽可能让函数调用具有幂等性。重试或重放不应重复触发不安全的副作用。
  • 在您的应用程序中检查每次调用的参数和权限,即使调用来自托管程序也不例外。
  • 为工具提供具体的名称和描述,以便模型正确组合使用这些工具。
  • 无论调用方是谁,执行影响重大的操作前都必须经过应用程序层面的审批。

评估程序化工具调用

程序化工具调用可以减少添加到模型上下文中的中间工具输出量,但效果取决于任务和工具响应。先以直接工具调用作为基准,再在具有代表性的任务上比较这两种方式。

衡量效率之前,先定义最终答案的质量标准和所需证据。在评估 Token 用量和工具调用的同时,也要评估正确性、完整性和证据覆盖情况,并明确说明任何可接受的质量取舍。

衡量以下方面:

  • 最终答案的正确性、完整性和证据覆盖情况。
  • 输入 Token 数和总 Token 数、端到端延迟及成本。
  • 模型交互轮次、工具调用、重试和恢复行为。
  • 安全方面的实际表现,尤其是副作用和审批要求方面。
  • 实际采用的调用方式是否符合预期的工作流程阶段。

Agents API

Agents API 中,程序化工具调用在 OpenAI 管理的智能体执行框架中运行,并且默认启用。该框架为智能体提供 exec 工具,并让生成的 JavaScript 能够调用智能体已有的工具。您无需将这些工具封装为命令行程序,也无需将其安装到沙盒中。

要禁用程序化工具调用,请在 agent.tools 中加入以下条目:

{
  "type": "programmatic_tool_calling",
  "enabled": false
}

省略该条目或其中的 enabled 字段,程序化工具调用仍会保持启用。仅包含类型的条目 { "type": "programmatic_tool_calling" } 也会使其保持启用。上文的 allowed_callers 配置和 Responses 续接循环适用于 Responses API 集成。

程序化工具调用也适用于将 environment.type 设置为 none 的纯对话会话。Bash、执行器 MCP 以及其他在沙盒中运行的工具仍需要执行环境

在 JavaScript 中编排工具调用不会改变工具的运行位置。Shell 调用在沙盒中执行命令;JavaScript 运行时本身不会启动系统进程。执行器 MCP 仍使用沙盒,函数工具仍调用您的应用服务器。智能体会先处理这些工具的结果,再决定将哪些内容纳入模型上下文。

请根据上文关于调用方式的指导,确定哪些工作流程阶段应使用代码。有关 Agents API 的配置和调用处理,请参阅函数MCP 连接

  • 使用函数调用定义由客户端负责的函数。
  • 使用工具搜索,将大型工具定义的加载推迟到模型需要时。
  • 使用对话状态继续已存储或无状态的 Responses API 请求。
  • 选择存储模式前,请查看数据控制