API 接入Responses
Responses API
OpenAI Responses 原生请求格式
接口
POST
https://www.yunqiai.chat/v1/responses所有请求使用 HTTPS,并通过请求头携带访问密钥。
请求参数
| 参数 | 类型 | 说明 |
|---|---|---|
model必填 | string | 要调用的模型 ID |
input必填 | string | array | 文本、消息或多模态输入项 |
max_output_tokens | integer | 取值从 16 起;最大输出量随模型变化 |
stream | boolean | 是否以 SSE 流式返回,默认 false |
temperature | number | 0–2;推理模型建议省略并使用模型默认值 |
top_p | number | 0–1;与 temperature 通常只设置一个 |
tools | array | 客户端执行的 function 工具定义;图片生成使用 Images 端点 |
metadata | object | 可选业务元数据 |
请求示例
curl --request POST \
--url https://www.yunqiai.chat/v1/responses \
--header "Authorization: Bearer YOUR_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"model": "gpt-5.6-sol",
"input": "用三句话解释量子计算",
"max_output_tokens": 1024,
"stream": false
}'工具调用闭环
先随请求发送工具定义;模型返回调用参数后,由客户端校验参数并执行本地函数,再用同一个调用 ID 回传结果。Responses 多轮由客户端保存原始 input 与完整 output[](含 reasoning 项),追加全部 function_call_output;继续发送 tools,不依赖 previous_response_id。
{
"model": "gpt-5.6-sol",
"input": "上海现在几点?",
"tools": [
{
"type": "function",
"name": "get_time",
"description": "返回指定时区的当前时间",
"parameters": {
"type": "object",
"properties": {
"timezone": {
"type": "string",
"description": "IANA 时区,例如 Asia/Shanghai"
}
},
"required": [
"timezone"
],
"additionalProperties": false
},
"strict": true
}
]
}{
"type": "function_call",
"id": "fc_01JY7X",
"call_id": "call_01JY7X",
"name": "get_time",
"arguments": "{\"timezone\":\"Asia/Shanghai\"}",
"status": "completed"
}| 协议 | 模型返回 | 客户端回传 |
|---|---|---|
| Responses | function_call · call_id · arguments | function_call_output · call_id |
{
"model": "gpt-5.6-sol",
"input": [
{
"role": "user",
"content": "上海现在几点?"
},
{
"type": "function_call",
"id": "fc_01JY7X",
"call_id": "call_01JY7X",
"name": "get_time",
"arguments": "{\"timezone\":\"Asia/Shanghai\"}"
},
{
"type": "function_call_output",
"call_id": "call_01JY7X",
"output": "{\"time\":\"14:30\"}"
}
]
}上述 JSON 展示消息结构。完整 Agent 文档第 13 节提供可运行的多轮 Python 示例,保留所有响应项并校验全部工具调用。
Base64 图片输入
Responses 使用完整 Data URL;不要只传裸 Base64 字符串。
{
"model": "gpt-5.6-luna",
"input": [
{
"role": "user",
"content": [
{
"type": "input_text",
"text": "描述这张图片"
},
{
"type": "input_image",
"image_url": "data:image/png;base64,BASE64_IMAGE_DATA",
"detail": "low"
}
]
}
],
"max_output_tokens": 256
}文件输入
把文件作为 input_file 内容块传入,并在同一条消息中加入 input_text 说明任务。下面示例直接读取本地 PDF、编码为 Data URL,并遍历原始 output[] 打印结果。
import base64
import os
from pathlib import Path
import requests
file_path = Path("report.pdf")
file_data = base64.b64encode(file_path.read_bytes()).decode("ascii")
payload = {
"model": "gpt-5.5",
"input": [{
"role": "user",
"content": [
{
"type": "input_file",
"filename": file_path.name,
"file_data": f"data:application/pdf;base64,{file_data}",
},
{"type": "input_text", "text": "提取报告中的结论与关键数字"},
],
}],
}
response = requests.post(
"https://www.yunqiai.chat/v1/responses",
headers={
"Authorization": f"Bearer {os.environ['YUNQIAI_API_KEY']}",
"Content-Type": "application/json",
},
json=payload,
timeout=(10, 180),
)
response.encoding = "utf-8"
if not response.ok:
request_id = response.headers.get("x-request-id") or response.headers.get("request-id")
raise RuntimeError(f"HTTP {response.status_code}; request_id={request_id}; body={response.text[:2000]}")
result = response.json()
if result.get("error") or result.get("status") != "completed":
raise RuntimeError(f"File response not completed: {result.get('id')}; {result.get('status')}; {result.get('error')}; {result.get('incomplete_details')}")
texts = [
part["text"]
for item in result.get("output", [])
if item.get("type") == "message"
for part in item.get("content", [])
if part.get("type") == "output_text"
]
if not any(text.strip() for text in texts):
raise RuntimeError(f"No file answer in response: {result.get('id')}")
print("\n".join(texts))| 来源 | input_file 字段 | 适用条件 |
|---|---|---|
| 内联 Data URL | file_data + filename | 本地文件可直接编码后发送 |
| 公开 URL | file_url | 文件地址可由服务端直接访问 |
| 文件类型 | 处理方式 | 建议 |
|---|---|---|
| 结合提取文本与页面图像理解内容 | 扫描件、图表较多的文档优先使用 PDF | |
| DOCX、PPTX | 客户端先转换为 PDF,再使用 input_file | 需要版式与图表时保留页面内容 |
| TXT、CSV、XLSX | 客户端提取文本或所需行列,放入 input_text | 明确列名与单位;不把这些文件直接套入 PDF 字段 |
流式输出
客户端以 SSE 逐行读取 data: 事件。不要按网络数据块直接解码 JSON;同一事件可能被拆成多个传输片段。
| 阶段 | 事件 | 读取内容 |
|---|---|---|
| 开始 | response.created | 响应 ID 与初始状态 |
| 文本增量 | response.output_text.delta | delta |
| 工具调用 | response.output_item.added / response.function_call_arguments.delta | 保存 name 与 call_id,按 item_id 拼接参数;完成后再解析 JSON |
| 完成 | response.completed | 最终响应与用量 |
| 失败或不完整 | response.failed / response.incomplete / error | 不要把已收到的增量标记为完整结果 |
import json
import os
import requests
def iter_sse_data(response):
data_lines = []
for line in response.iter_lines(decode_unicode=True):
if line == "":
if data_lines:
data = "\n".join(data_lines)
data_lines = []
if data:
yield data
continue
if line.startswith(":"):
continue
field, _, value = line.partition(":")
if value.startswith(" "):
value = value[1:]
if field == "data":
data_lines.append(value)
# A frame without its terminating blank line is incomplete.
payload = {
"model": "gpt-5.6-sol",
"input": "用三句话解释量子计算",
"stream": True,
}
with requests.post(
"https://www.yunqiai.chat/v1/responses",
headers={
"Authorization": f"Bearer {os.environ['YUNQIAI_API_KEY']}",
"Content-Type": "application/json",
},
json=payload,
stream=True,
timeout=(10, 180),
) as response:
response.encoding = "utf-8"
if not response.ok:
request_id = response.headers.get("x-request-id") or response.headers.get("request-id")
raise RuntimeError(f"HTTP {response.status_code}; request_id={request_id}; body={response.text[:2000]}")
completed = False
tool_calls = {}
for data in iter_sse_data(response):
if data == "[DONE]":
break
event = json.loads(data)
event_type = event.get("type")
if event_type == "response.output_text.delta":
print(event.get("delta", ""), end="", flush=True)
elif event_type == "response.output_item.added":
item = event.get("item") or {}
if item.get("type") == "function_call":
item_id = item.get("id", str(event.get("output_index", 0)))
tool_calls[item_id] = {
"call_id": item.get("call_id", ""),
"name": item.get("name", ""),
"arguments": item.get("arguments", ""),
}
elif event_type == "response.function_call_arguments.delta":
item_id = event.get("item_id", str(event.get("output_index", 0)))
current = tool_calls.setdefault(item_id, {"call_id": "", "name": "", "arguments": ""})
current["arguments"] += event.get("delta", "")
elif event_type == "response.function_call_arguments.done":
item_id = event.get("item_id", str(event.get("output_index", 0)))
current = tool_calls.setdefault(item_id, {"call_id": "", "name": "", "arguments": ""})
current["arguments"] = event.get("arguments", current["arguments"])
elif event_type == "response.output_item.done":
item = event.get("item") or {}
if item.get("type") == "function_call":
item_id = item.get("id", str(event.get("output_index", 0)))
current = tool_calls.setdefault(item_id, {"call_id": "", "name": "", "arguments": ""})
current["call_id"] = item.get("call_id", current["call_id"])
current["name"] = item.get("name", current["name"])
current["arguments"] = item.get("arguments", current["arguments"])
elif event_type == "response.completed":
for item in (event.get("response") or {}).get("output", []):
if item.get("type") != "function_call":
continue
item_id = item.get("id", str(item.get("output_index", 0)))
current = tool_calls.setdefault(item_id, {"call_id": "", "name": "", "arguments": ""})
current.update({
"call_id": item.get("call_id", current["call_id"]),
"name": item.get("name", current["name"]),
"arguments": item.get("arguments", current["arguments"]),
})
completed = True
elif event_type in {"response.failed", "response.incomplete"}:
final_response = event.get("response", {})
error = final_response.get("error") or {}
detail = error.get("message") or final_response.get("incomplete_details") or event_type
raise RuntimeError(str(detail))
elif event_type == "error":
error = event.get("error") or event
code = error.get("code") or error.get("type") or "stream_error"
raise RuntimeError(f"{code}: {error.get('message', 'unknown error')}")
if not completed:
raise RuntimeError("stream ended before response.completed")
print()
for call in tool_calls.values():
arguments = json.loads(call["arguments"] or "{}")
print("tool", call["call_id"], call["name"], arguments)错误、重试与超时
| 情况 | 客户端处理 |
|---|---|
| 连接超时 | 单独设置连接超时,例如 10 秒;确认网络与 Base URL |
| 读取超时 | 文本请求可从 180 秒起设置;图像等耗时任务可设为 300 秒 |
| 408、5xx、网络超时 | POST 结果可能未知;记录请求 ID,不自动重放。已取得 MJ id 时只重试 GET 查询 |
| 429 | 先区分限流与额度不足;限流按 Retry-After 等待,额度不足先处理额度 |
| 400、401、403、404 | 不要自动重试;先检查错误正文、鉴权头、接口路径、模型与参数 |
{
"error": {
"message": "请求参数无效",
"type": "invalid_request_error",
"param": "model",
"code": "invalid_model"
}
}| 协议 | 错误正文中的请求 ID | 响应头中的请求 ID |
|---|---|---|
| Responses | 无固定顶层字段 | x-request-id |
import os
import requests
url = "https://www.yunqiai.chat/v1/responses"
headers = {"Authorization": f"Bearer {os.environ['YUNQIAI_API_KEY']}"}
payload = {"model": "gpt-5.6-sol", "input": "你好", "max_output_tokens": 1024}
# A POST may have reached the server even when the client times out.
# Do not automatically replay it or switch to a different model.
response = requests.post(url, headers=headers, json=payload, timeout=(10, 180))
request_id = response.headers.get("x-request-id") or response.headers.get("request-id")
if not response.ok:
raise RuntimeError(
f"HTTP {response.status_code}; request_id={request_id or 'unavailable'}; "
"check the redacted error response before starting a new request"
)
print(response.json())响应
{
"id": "resp_01JY7X",
"object": "response",
"status": "completed",
"model": "gpt-5.6-sol",
"output": [
{
"id": "msg_01JY7X",
"type": "message",
"status": "completed",
"role": "assistant",
"content": [
{
"type": "output_text",
"annotations": [],
"text": "量子计算利用量子态处理信息……"
}
]
}
],
"usage": {
"input_tokens": 16,
"output_tokens": 48,
"total_tokens": 64
}
}协议参考
本页示例按 YunQi AI 的模型 ID、接口路径和参数格式编写;以下链接提供对应协议的字段说明。