OpenAI Responses API 进阶用法:从入门到精通

research进阶10 分钟阅读2026/9/24

OpenAI Responses API 进阶用法:从入门到精通

我为什么要折腾这个 API

上个月我在重构一个客服工单系统,原来用的是 Chat Completions API。痛点很明显:模型需要调用我们内部的工单查询工具,每次工具调用结果回来后,我得手动把完整的消息历史拼回去再发一次请求。消息越拼越长,token 费用蹭蹭涨,代码里全是状态管理的胶水代码。

后来我把服务迁到了 OpenAI 的 Responses API(2025 年 3 月发布的),核心原因就一个:服务端状态管理。模型的多轮工具调用状态可以存在 OpenAI 那边,我只需要传一个 previous_response_id 就能续上对话。下面是我踩完坑之后总结的完整实践。

基础:一次最简单的调用

先看最小的可用例子:

from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model="gpt-4.1",
    input="用一句话解释什么是幂等性"
)

print(response.output_text)

注意 response.output_text 这个便捷属性,它把 output 数组里所有文本自动拼接好了。在 Chat Completions 里你得写 response.choices[0].message.content,初学者经常在这里写错索引。

进阶一:服务端状态,省掉 80% 的胶水代码

这是我最看重的特性。多轮对话不用再传完整历史:

# 第一轮
r1 = client.responses.create(
    model="gpt-4.1",
    input="我的订单号是 A10293,查询一下物流状态"
)

# 第二轮:只传新内容和上一轮的 ID
r2 = client.responses.create(
    model="gpt-4.1",
    previous_response_id=r1.id,
    input="如果超时了该怎么申请赔偿?"
)

print(r2.output_text)

默认状态下对话会保留 30 天。第一个坑:如果涉及用户隐私数据,记得关掉存储:

response = client.responses.create(
    model="gpt-4.1",
    input="...",
    store=False  # 不落盘,也就不能用 previous_response_id 了
)

进阶二:内置工具,不用自己写 glue code

Responses API 把联网搜索、文件搜索、代码解释器做成了原生参数。我的工单系统里查物流就用了 web search:

response = client.responses.create(
    model="gpt-4.1",
    tools=[{
        "type": "web_search_preview",
        "search_context_size": "medium"  # low / medium / high
    }],
    input="查一下 2024 年欧盟 AI Act 对客服聊天机器人的合规要求"
)

# 引用来源
for item in response.output:
    if item.type == "message":
        for annotation in item.content[0].annotations:
            print(annotation.url_citation.url)

第二个坑:web search 的 token 消耗比想象中大,search_context_size 设成 high 一次请求能吃掉上万 token。我的做法是日常查询用 low,需要深度资料时才切 medium。

文件搜索也一样简单,先建 vector store,上传文档,然后:

response = client.responses.create(
    model="gpt-4.1",
    tools=[{
        "type": "file_search",
        "vector_store_ids": ["vs_abc123"],
        "max_num_results": 5
    }],
    input="退货政策里关于定制商品是怎么规定的?"
)

进阶三:结构化输出,直接吐 JSON

工单自动分类这个场景,我需要模型输出严格的 JSON。用 text.format 而不是老的 response_format:

import json

response = client.responses.create(
    model="gpt-4.1",
    input="用户反馈:『APP 打开就闪退,小米 14,系统最新版』",
    text={
        "format": {
            "type": "json_schema",
            "name": "ticket_classification",
            "schema": {
                "type": "object",
                "properties": {
                    "category": {"type": "string",
                                 "enum": ["bug", "billing", "feature_request"]},
                    "severity": {"type": "string",
                                 "enum": ["low", "medium", "high"]},
                    "device_info": {"type": "string"}
                },
                "required": ["category", "severity"],
                "additionalProperties": False
            },
            "strict": True
        }
    }
)

data = json.loads(response.output_text)

strict: true 配合 additionalProperties: False 是关键,不然校验经常失败。这是我从错误里学到的——第一次没加 additionalProperties: False,strict 模式直接拒绝了请求。

进阶四:多模态输入

图片直接放在 input 数组里,这个设计比 Chat Completions 的 content blocks 更直观:

response = client.responses.create(
    model="gpt-4.1",
    input=[
        {
            "role": "user",
            "content": [
                {"type": "input_text", "text": "这张报错截图是什么问题?"},
                {
                    "type": "input_image",
                    "image_url": "https://example.com/screenshot.png",
                    "detail": "high"  # 低分辨率够用就设 low,省 token
                }
            ]
        }
    ]
)

流式输出 + 事件类型

流式这块 Responses API 的事件粒度比 Chat Completions 细很多:

stream = client.responses.create(
    model="gpt-4.1",
    input="写一段产品更新公告",
    stream=True
)

for event in stream:
    if event.type == "response.output_text.delta":
        print(event.delta, end="", flush=True)
    elif event.type == "response.completed":
        print(f"\n[完成] 用量: {event.response.usage.total_tokens}")

我一开始只处理了 delta 事件,结果漏了工具调用相关的事件类型,前端展示工具执行状态时全是空白。后来老老实实根据 event.type 分支处理才正常。

实用技巧汇总

  1. reasoning 模型要用 max_output_tokens,不是 max_tokens。参数名变了,老代码迁移时最容易在这里报错。
  2. 计时和用量都直接挂在 response.usage 上,做成本监控很方便,不用自己算。
  3. instructions 参数代替 system message,语义更清晰,而且可以配合 previous_response_id 在多轮中保持不变的系统指令。
  4. store + previous_response_id 有 30 天限制,长期对话记得定期把关键内容抽出来存自己的数据库。

诚实的局限性评估

用了两个月,说点实在的:

  • web search 和 file search 只有在支持的地区/账号下可用,而且价格比纯推理贵不少,高流量场景要先算账。
  • previous_response_id 是线性的,不能做树状分支对话。如果需要“回到三步前重试”,还是得自己管理完整历史。
  • 状态存放在 OpenAI 那边,对数据合规敏感的企业这是个需要过安全评审的点,store=False 能缓解但会失去状态管理的好处。
  • SDK 的类型提示有些地方还没跟上新 API,response.output 里的 item 类型要靠运行时判断。

总体来说,如果你在做多轮工具调用、RAG 或需要结构化输出的应用,Responses API 值得迁移——我把工单系统的工具调用代码从 300 行减到了不到 100 行。但如果只是简单的单轮问答,Chat Completions 也够用,不必为了迁移而迁移。

相关 Agent

D

DeepSeek V4

DeepSeek最新开源MoE大模型,671B参数,推理和编程能力顶尖,成本极低

了解更多 →