当前位置:首页 > 文章列表 > 科技周边 > 人工智能 > OpenAI Responses API 怎样迁移旧工具调用代码

OpenAI Responses API 怎样迁移旧工具调用代码

来源:17golang原创 2026-10-23 05:38:47 0浏览 收藏

把旧工具调用代码迁移到 OpenAI Responses API,不能只把接口名称换掉。至少要一起改动工具声明、响应读取和工具结果回传:声明使用扁平 function 结构,从 response.output 找 function_call,再用对应的 call_id 回传 function_call_output。最终文本则从 output_text 读取。

官方地址:https://developers.openai.com/api/docs/guides/migrate-to-responses

这里的“旧代码”指 Chat Completions 自定义函数调用,不包含 Assistants,也不涉及流式事件处理。下面我会用一个只读的收纳盒位置查询小项目串起迁移点。示例没有在本文制作过程中请求真实 API,配图均为静态说明图,不代表运行结果。

先把迁移目标缩到一个只读工具

如果我要迁移一段已经能工作的旧代码,我会先保留原业务函数,只替换模型交互层。本例只有 lookup_box:输入 item_name,返回 found 和 location。数据来自程序内字典;它不具备写数据库、运行脚本或访问任意地址的能力。

旧代码关注点Responses 写法
messages 请求历史input 输入项;本例配合 previous_response_id
tools 内嵌 function 对象type、name、parameters、strict 同层
message.tool_callsresponse.output 中的 function_call 项
工具返回消息function_call_output,携带对应 call_id
choices 中的最终文本response.output_text 便捷属性

output 是带类型的输出项集合,不保证第一个元素就是工具调用,更不能把所有输出项都当成文本。工具声明只是让模型知道可选能力,真正的业务函数仍由应用执行。

Responses 工具声明和输出项的静态字段关系说明图
说明图:声明边界与输出边界分开看;name 关联工具,call_id 关联一次具体调用,图中不表示请求执行顺序。

准备客户端与严格的函数声明

在服务端安装当前 OpenAI Python SDK,并通过部署环境设置 OPENAI_API_KEY。OPENAI_MODEL 由你填写为账户可用、支持 Responses 自定义函数调用的模型。不要把密钥放进文章示例、浏览器代码或提交记录。下面的代码使用当前 SDK 的 Responses 客户端写法,不固定模型名称与 SDK 版本。

strict 显式设为 True,所有字段列入 required,且 additionalProperties 为 False。这里没有可选字段;复杂业务若需要可选值,应按官方严格模式支持的 schema 表达。即使启用了严格模式,服务器仍须检查工具白名单、参数范围和业务授权。

用 call_id 把工具结果接回响应链

这份完整示例采用服务器保存响应链的方式,显式 store=True,并通过 previous_response_id 指向上一次 response.id。部署前须确认这种保存方式符合项目的数据处理要求;不能把它直接照搬到要求不保存响应的环境。每次请求都会重发 instructions 和 tools:previous_response_id 不是应用配置的自动继承开关,尤其上一条 instructions 不会自动带入下一条请求。

import json
import os
from openai import OpenAI

# 只读示例数据,不连接仓库系统,也不执行模型给出的代码。
BOXES = {"收纳盒A": "二号架上层", "收纳盒B": "三号架下层"}
TOOLS = [{
    "type": "function",
    "name": "lookup_box",
    "description": "查询收纳盒的登记位置",
    "strict": True,
    "parameters": {
        "type": "object",
        "properties": {"item_name": {"type": "string"}},
        "required": ["item_name"],
        "additionalProperties": False,
    },
}]
INSTRUCTIONS = "使用 lookup_box 查询位置;找不到时明确说未登记,不得编造位置。"

def execute_tool(call):
    # 服务端白名单和参数检查不能用模型声明代替。
    if call.name != "lookup_box":
        raise ValueError("收到未允许的工具名称")
    args = json.loads(call.arguments)
    if not isinstance(args, dict) or set(args) != {"item_name"}:
        raise ValueError("工具参数结构不符")
    name = args["item_name"]
    if not isinstance(name, str) or not name.strip() or len(name) > 80:
        raise ValueError("物品名称无效")
    location = BOXES.get(name)
    return {"found": location is not None, "location": location}

def answer(client, model, question):
    current_input = question
    previous_id = None
    # 上限是模型请求次数,不是无限递归调用工具。
    for _ in range(4):
        request = {
            "model": model, "instructions": INSTRUCTIONS,
            "tools": TOOLS, "input": current_input, "store": True,
        }
        if previous_id is not None:
            request["previous_response_id"] = previous_id
        response = client.responses.create(**request)
        if response.status != "completed":
            raise RuntimeError("响应未完成,请交由应用处理失败状态")
        calls = [item for item in response.output if item.type == "function_call"]
        if not calls:
            text = response.output_text.strip()
            if not text:
                raise RuntimeError("没有工具调用,也没有可用回答")
            return text
        results = []
        # 一次响应可能带多个调用,逐个保留各自的 call_id。
        for call in calls:
            result = execute_tool(call)
            results.append({
                "type": "function_call_output",
                "call_id": call.call_id,
                "output": json.dumps(result, ensure_ascii=False),
            })
        current_input = results
        previous_id = response.id
    raise RuntimeError("达到请求轮数上限,尚未取得最终回答")

if __name__ == "__main__":
    # 密钥交给 SDK 从环境读取,模型由部署方选择支持此接口的型号。
    model = os.environ.get("OPENAI_MODEL")
    if not model:
        raise SystemExit("请先配置 OPENAI_MODEL 和 OPENAI_API_KEY")
    try:
        with OpenAI(timeout=30.0, max_retries=0) as client:
            print(answer(client, model, "收纳盒A放在哪里?"))
    except Exception:
        # 示例不打印异常原文,避免把请求或敏感配置带进日志。
        raise SystemExit("调用未完成,请在受控日志中排查配置、网络或工具参数")

代码里的两个 ID 分工不同:response.id 标识整次响应,用于续接上下文;call.call_id 标识该响应中的一次工具调用,用于绑定结果。不要拿输出项自身的 id 或 response.id 填进 function_call_output.call_id。

遇到多个 function_call,代码逐个处理并一次回传结果列表。未知工具、JSON 解析失败和不合规参数会停止本轮,由应用接管,而不是伪造成功结果。业务“未登记”则是合法查询结果,found=False、location=None 会被序列化为 JSON 后传回模型。

响应链标识与工具结果标识的静态边界说明图
说明图:响应关联组解释 previous_response_id 与 response.id;工具关联组解释 call_id 与 function_call_output。instructions 和 tools 属于本次请求配置。

迁移后先看这几个失败出口

读者试运行时,先查询字典中存在的收纳盒,再查询一个未登记名称,观察业务返回能否与最终文字对应。这里描述的是建议试验,不是本文实际执行记录;模型是否调用工具及怎样表述回答,仍以你的真实请求为准。最终回答是自然语言,不应代替工具原始结果作为库存或位置的权威数据。

本例只接收 completed 响应,对未完成状态直接报错;生产代码可按官方响应状态分别处理。即使响应完成,也要先看有没有工具调用,再判断最终文本。四次请求仍未结束时应停止并交由业务处理,不要无限追加请求。timeout 限制单次请求等待,max_retries=0 避免 SDK 自动重试;它们不构成业务上的幂等保证。

如果旧系统使用流式输出,还要单独迁移事件解析、参数增量拼接和完成事件处理,不能把旧事件处理器原封不动接到这个非流式例子上。若选择不保存响应、手动维护 input,就必须按官方说明保留需要的输出项,包括适用模型的 reasoning 项,不能只摘取 function_call 而丢掉其他上下文。

常见问题与集成判断

只替换 messages 为 input 行不行?不行。工具 schema 和返回项结构也变了,旧的 message.tool_calls 路径不会自动变成 Responses 的读取方式。

output_text 为空就说明失败吗?不一定。模型可能正在请求工具,先判断 function_call。没有工具调用、也没有可用文本时,应用再按明确的异常出口处理。

什么时候可以接入写入型工具?先让只读示例的参数、调用关联和失败出口跑通,再额外设计授权、人工确认及业务幂等。本文工具循环本身不提供这些能力。

迁移的核心不是换一个接口名,而是把“声明了什么能力”“模型请求了哪次调用”“应用回传哪次结果”这三层分开。先保住 call_id 和上下文,再扩展复杂工具,代码会更容易解释和维护。

官方参考:https://developers.openai.com/api/docs/guides/function-calling;迁移指南地址见文首。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
OpenAI Agents API 怎样把工具权限限制在沙箱OpenAI Agents API 怎样把工具权限限制在沙箱
上一篇
OpenAI Agents API 怎样把工具权限限制在沙箱
AI 应用怎样让结构化输出失败时安全回退
下一篇
AI 应用怎样让结构化输出失败时安全回退
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    604次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    644次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    674次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    639次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    460次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码