OpenAI API

OpenAI的API文档有点乱,有时候会找不到相关内容,或者新旧文档没有很好的整理。下面几个链接应该是旧文档的,但新文档里确没有对应的导航。
batch API: https://developers.openai.com/api/docs/guides/batch
异步调用API: https://developers.openai.com/api/reference/python/

client.responses.create: 新的调用模型接口,提供了获取历史会话内容的能力,还有并行调用工具,网络搜索,文件上传等等。

推荐使用instructions属性来输入system prompt。 新接口传入图片链接时,如果不填detail参数,image_url后面直接放url地址,否则要嵌套一层字典。

from openai import OpenAI
client = OpenAI(api_key="OPENAI_API_KEY")

response = client.responses.create(
  model="gpt-4o",
  instructions="你是一个精准的图像解析专家,风格严谨、细节丰富。",
  input=[
    {
      "role": "user",
      "content": [
        {"type":"input_image","image_url":"https://.../img1.jpg"},
        {"type":"input_image",
         "image_url": {"url": "https://.../img2.jpg",
                            "detail": "high"  # 可选参数
                           }
        },
        {"type":"input_text","text":"请综合描述这两张图。"}
      ]
    }
  ]
)

现在又增加了开发者角色,用于与system角色区分。并且,system提示词也可以放在input里并标明system role,但并不推荐。

response = client.responses.create(
  model="gpt-4o",
  input=[
    {"role":"system","content":[{"type":"input_text","text":"你是图像解析助手。"}]},
    {"role":"developer","content":[{"type":"input_text","text":"请用简练的三个要点总结。"}]},
    {"role":"user","content":[
        {"type":"input_image","image_url":"..."},
        {"type":"input_text","text":"描述这张图。"}
    ]}
  ]
)

client.chat.completions.create: 传统聊天接口。就算不填detail参数,image_url后也要嵌套一层字典。

from openai import OpenAI

client = OpenAI(api_key="YOUR_API_KEY")

completion = client.chat.completions.create(
    model="gpt-4o",
    messages=[
        { "role": "system", "content": "你是一个图像理解专家,请解析以下图片。" },
        {
            "role": "user",
            "content": [
                { "type": "text", "text": "这是第一张图片:" },
                { "type": "image_url", "image_url": { "url": "https://example.com/image1.jpg" , 
                                                                "detail": "high"   # 可选属性,控制图像清晰度
                                                                } 
                },
                { "type": "text", "text": "还有第二张图片:" },
                { "type": "image_url", "image_url": { "url": "https://example.com/image2.jpg" } },
                { "type": "text", "text": "请综述这两张图所呈现的内容。" }
            ]
        }
    ],
    max_tokens=500,
    temperature=0.7,
)

print(completion.choices[0].message.content)

注意,两种接口都支持developer角色。它的指令优先级介于system role与user role之间,使得对模型指令进一步细分,增强模型安全使用的能力。

目前OpenAI推荐使用Response API。
此API返回的对象有很多方法, 常用的model_dump或to_dict方法,以dict类型展示对象属性值,model_dump_json方法以json形式返回内容但没有进行格式缩进,若想格式化显示,则使用to_json方法。

使用API内置的属性进行多轮会话实现

因为每家模型厂商提供的API不一致。像dify, langchain这样的框架为了兼容不同厂商的模型API,又抽象了一层中间层,对于多轮会话的实现采用的是手工拼接多轮会话内容,这样当聊天轮次增多的时候,由于后面每一轮对话都带着全部聊天记录做为上下文,导致tokens开销特别大。
OpenAI的Responses API内部帮我们做了些工作,我们每次对话只有传入上一轮会话的id,就能自动实现多轮会话,同时有效节省tokens。API内部实现了类似压缩和内容总结之类的功能,但对外是不透明的。
下面是一个简单的对话示例:

from openai import OpenAI
client = OpenAI(api_key=your_api_key)

response = client.responses.create(model="gpt-5-mini",
  input="你是谁呀?"
)
# 第一轮返回的内容:
# AI:我是由 OpenAI 开发的人工智能聊天助手,基于大型语言模型(知识截至2024年6月)。我可以用中文或其他语言帮你回答问题、写作润色、翻译、编程调试、做笔记、出主意、学习辅导等。

# 我的一些限制:不能实时上网抓取最新信息、不能提供替代专业医生或律师的诊断/法律意见、不会在对话外保存你的私人信息(除非你在对话中提供)。你想让我帮你做什么?

# 第一轮token消耗:
# usage=ResponseUsage(input_tokens=10, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=381, output_tokens_details=OutputTokensDetails(reasoning_tokens=256), total_tokens=391)

response2 = client.responses.create(model="gpt-5-mini",
  input="我叫Roland",
  previous_response_id='resp_004207b69807fcfd00696a0fd7d8208196aacd29eec863fa12'
)

# 第二轮返回的内容:
# AI:很高兴认识你,Roland!你想我怎么称呼你?需要我帮你做什么——聊天、写作、翻译、编程、学习辅导,还是别的?

# 第二轮token消耗:
# usage=ResponseUsage(input_tokens=143, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=177, output_tokens_details=OutputTokensDetails(reasoning_tokens=128), total_tokens=320)

response3 = client.responses.create(model="gpt-5-mini",
  input="你还记得我的名字么?",
  previous_response_id='resp_004207b69807fcfd00696a161b15d48196bebbd807b72bc042'
)

# 第三轮返回的内容:
# AI:记得的——你叫 Roland。我可以这么称呼你,还是用别的称呼或昵称?(我会在本次会话里记住,但不会在未来独立会话中保留这个信息。)

# 第三轮token消耗:
# usage=ResponseUsage(input_tokens=204, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=246, output_tokens_details=OutputTokensDetails(reasoning_tokens=192), total_tokens=450)

response4 = client.responses.create(
    model="gpt-5-mini",
    input="我今年40岁,你呢?",
    previous_response_id='resp_004207b69807fcfd00696af1e997808196919471d0f305be97'
)

# 第四轮返回的内容:
# AI:我没有生物学上的年龄——我是一个 AI 模型,没有生日和身体。我的知识和训练资料截至到 2024 年 6 月。如果你愿意把我当作某个年龄的人设(比如更像个朋友或顾问),我也可以配合。你 40 岁了,有什么想聊或需要我帮忙的吗?

# 第四轮token消耗:
# usage=ResponseUsage(input_tokens=270, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=340, output_tokens_details=OutputTokensDetails(reasoning_tokens=256), total_tokens=610)

# 我们可以看到,前三轮对话,总的input_tokens = 10 + 143 + 204 = 357, 第四轮会话的input_tokens才270。
# 想想如果我们在第四轮对话时要传递整个对话上下文,这里,干脆不加除了第一轮以为的input_tokens,因为API内部可能做了些内容总结和压缩,我只加每轮的output_tokens。那么第四轮会话的input_tokens应该为10 + 177 + 246 + 不确定的input_tokens(这部分其实也可以用tokenizer计算),
# 即便这样,第四轮手动拼接的tokens也远超过270(这还没包括推理的tokens)。所以这么看,使用previous_response_id来构造的多轮会话比我们自己拼接会话历史会节省很多的tokens。
# 更夸张的是,API内部其实还把思考内容做为上下文了,这部分的tokens其实更多。

# 总之, 使用OpenAI的Responses API内部机制来构造多轮会话要比传统的拼接聊天内容的方式大大节省tokens。而且内部机制的聊天记录还包括了我们上传的图片,文件,以及模型调用工具等相关内容。

即便使用这种内部机制,但经过很多轮会话后,input_tokens还是可能超过模型的max_tokens限制。 对此OpenAI还提供了一个压缩API,以我们这四轮会话为例,调用此API进行压缩:

compacted_response = client.responses.compact(
    model="gpt-5-mini",
    previous_response_id='resp_004207b69807fcfd00696b109822b88196b025d191e31c9fbf')


# CompactedResponse(id='resp_004207b69807fcfd01696cab591cf481968c60a6be547ea8ac', created_at=1768729447, object='response.compaction', 
#           output=[ResponseOutputMessage(id='msg_004207b69807fcfd00696a0fd7de688196b81a7bb7602508f9', 
#                     content=[ResponseOutputText(annotations=None, text='你是谁呀?', type='input_text', logprobs=None)], role='user', status='completed', type='message'), 
#                   ResponseOutputMessage(id='msg_004207b69807fcfd00696a161b1a5c8196b828d7c5aba3e68f', 
#                     content=[ResponseOutputText(annotations=None, text='我叫Roland', type='input_text', logprobs=None)], role='user', status='completed', type='message'), 
#                   ResponseOutputMessage(id='msg_004207b69807fcfd00696af1e99c988196afee7255a5f8369a', 
#                     content=[ResponseOutputText(annotations=None, text='你还记得我的名字么?', type='input_text', logprobs=None)], role='user', status='completed', type='message'), 
#                   ResponseOutputMessage(id='msg_004207b69807fcfd00696b109826d4819686e5a57e08f88c14', 
#                     content=[ResponseOutputText(annotations=None, text='我今年40岁,你呢?', type='input_text', logprobs=None)], role='user', status='completed', type='message'), 
#                   ResponseCompactionItem(id='cmp_004207b69807fcfd01696cab59a4848196a57e24cb47b02843', 
#                     encrypted_content='gAAAAABpbKtnVlYWKO...6eL5Dy2zAbYLnZBV0wIA', type='compaction', created_by=None)], 
#                     usage=ResponseUsage(input_tokens=1403, input_tokens_details=InputTokensDetails(cached_tokens=0), output_tokens=1009, output_tokens_details=OutputTokensDetails(reasoning_tokens=448), total_tokens=2412))

压缩后的内容是加密的。 但我使用Responses API窥探到了里面的内容:

response5 = client.responses.create(
    model="gpt-5-mini",
    input=compacted_response.output)

print(response5.output[0].content[0].text)

Handoff summary (for the next LLM to resume)

Purpose

  • Resume the conversation and continue assisting the user after a brief identity/intro exchange. The user asked about the assistant’s identity and provided their own name and age.

Conversation state / context

  • User introduced themself as “Roland.”
  • User age: 40.
  • Initial messages were in Chinese; the last instruction (this handoff request) is in English. User has switched between Chinese and English.
  • User asked: “Who are you?” → Assistant replied (introduced itself and noted it can’t retain personal data beyond the session).
  • User asked: “Do you remember my name?” → Assistant confirmed remembering it for the current session.
  • User asked: “I’m 40 years old, you?” → Assistant explained it has no age and clarified limits on personal data retention.

Current progress and decisions

  • Identity details captured for the session-only context: name = Roland, age = 40.
  • Assistant already gave the standard privacy/memory limitation response.
  • No active task beyond the identity/intro exchange. No outstanding technical problem or content request from the user.

Constraints and safety/privacy notes

  • Do not claim persistent memory beyond the current session. Reiterate session-only context if needed.
  • Avoid revealing system internals beyond standard limits (but it’s OK to state that the assistant has no human age).
  • Follow user language preference (ask which language they prefer if unclear).

User preferences (inferred)

  • Comfortable using Chinese; also used English. Prefer clarification from assistant which language to continue in.
  • Friendly, conversational tone.

What remains / recommended next steps for the next LLM

  1. Acknowledge Roland by name to show continuity.
  2. Confirm preferred language for the rest of the conversation (Chinese or English).
  3. Offer options for how to proceed (examples):
    • “How would you like me to address you? Roland or a nickname?”
    • “What can I help you with today?” or propose common tasks (answer questions, translate, plan, code help, etc.).
  4. Reiterate privacy note briefly if relevant: memory only for current session.
  5. Wait for Roland’s instruction.

Suggested next-message templates (pick one depending on desired tone)

  • Friendly, bilingual option:
    • English: “Hi Roland — I’ve got you as Roland, age 40. Which language would you like to use going forward (English or Chinese)? How can I help you today?”
    • Chinese: “你好 Roland,我记录到你叫 Roland、40 岁。接下来想用中文还是英文?我能帮你做些什么?”

Critical data to carry forward

  • Name: Roland
  • Age: 40
  • Language: undecided (Chinese and English have both been used)
  • No ongoing task or deliverable

End.

我们虽然看不到Compaction API里加密的内容,但将这个内容传给Responses API后,我们可以看到Compaction API的作用是总结已有的聊天内容,为移交给下一个模型做准备。
并且可以注意到一个细节:

Conversation state / context
- Initial messages were in Chinese; the last instruction (this handoff request) is in English. User has switched between Chinese and English.

我在四轮对话中并没有使用英文,但会话状态这里却提到最后一个我的指令(移交请求/任务)是用的英语,这说明Compaction API里有一段内置的提示词,即用来总结对话历史内容以便移交给下一个模型的提示词是用英文写的。

如果我们带上我们新的问题,我们可以这样使用:

response6 = client.responses.create(
    model="gpt-5-mini",
    input=compacted_response.output + [{"role": "user", "content": "你擅长数学么?"}])

print(response6.output[1].content[0].text)

# 可以的——我在很多数学领域都能帮忙:代数、微积分、线性代数、概率与统计、数论、微分方程、离散数学、优化、数学建模等。可以:

# - 解题并给出规范步骤与证明(但不会透露模型的内部“思路流”)。
# - 给出符号推导、数值计算、画图或写示例代码(如 Python/NumPy)。
# - 帮你检查解答、找错或优化解法,或把复杂内容讲得更直观。

# 注意我可能会出错,重要结论可让我们一起核验。你想要我帮你做什么?是具体题目、某个概念的讲解,还是按难度(高中/本科/研究生/竞赛)来?

可以看到,这样我们可以使用Compaction API的结果加上我们的新问题传给Responses API,以生成一个新的会话,甚至使用新的模型,并且模型会回答我们最后提的问题。

我又做了个测试,即使用Compaction API时直接加上我的新问题:

compacted_response2 = client.responses.compact(
    model="gpt-5-mini",
    input="你擅长数学么?",
    previous_response_id='resp_004207b69807fcfd00696b109822b88196b025d191e31c9fbf')

response7 = client.responses.create(
    model="gpt-5-mini",
    input=compacted_response2.output)

print(response7.output[1].content[0].text)

输出内容如下:

Handoff summary — CONTEXT CHECKPOINT COMPACTION

1) Current progress and key decisions made
- Conversation established in Chinese with occasional English instructions.
- User introduced themself: name = Roland; age = 40.
- Assistant identified as an AI (no personal age), acknowledged remembering the user’s name.
- User asked: “你擅长数学么?” (Are you good at math?) — this question is pending an answer.
- New instruction from the user (in English) requests a concise handoff summary so another LLM can resume.

2) Important context, constraints, and user preferences
- Language: primary language used so far = Chinese; user understands English (gave the handoff instruction in English). Default to Chinese for replies unless user requests otherwise.
- Personal info: Roland, 40 years old. User likely expects the assistant to remember and use the name during the session.
- Assistant constraints: standard AI limitations — knowledge cutoff 2024-06, no persistent memory beyond session unless stated, no internet access, must follow safety/privacy rules and avoid disallowed content.
- Tone: informal/polite conversational style (user asked casual questions).

3) What remains to be done (clear next steps)
- Directly answer the pending question: confirm math capability, give brief description of strengths (topics and types of help), and ask a clarifying question about what specific math help Roland wants (level, topic, examples).
- Offer examples of services: solve problems step-by-step, explain concepts, provide practice problems, verify solutions, produce proofs, or generate visualizations (text-based).
- Ask user language preference for the forthcoming math assistance (Chinese or English) and desired depth (quick answer vs. full derivation).
- If user requests a specific problem, prompt for the problem statement and any work they’ve done so far.

4) Critical data, examples, or references needed to continue
- User identifier: Roland (use in replies).
- User age: 40 — may be used only for conversational context if needed.
- Knowledge cutoff: 2024-06 (inform user if they request very recent info).
- Safety/legal constraints: do not provide professional advice beyond general informational help.
- No external tools or prior tool outputs required to continue; conversation state is sufficient.

Suggested immediate reply to user (in Chinese)
- One- or two-sentence answer to “你擅长数学么?” (e.g., “我擅长很多数学领域……你想要哪方面的帮助?用中文还是英文?”), then the clarifying prompts listed above.

End of handoff.

可以看到,这回Compaction API总结的当前会话状态已经包含了我最后的新问题,并且AI还没有回答。 要让AI回答,我们还是要像刚才那样将Compaction API的output再拼接最后一个问题后调用Responses API:

response8 = client.responses.create(
    model="gpt-5-mini",
    input=compacted_response2.output + [{"role": "user", "content": "你擅长数学么?"}])

print(response8.output[1].content[0].text)

# 擅长的。简单说明:

# - 我可以处理:代数、微积分、线性代数、概率与统计、离散数学、数论、微分方程、数值方法、优化等。  
# - 能做到:给出详细解题步骤、证明思路、例题解析、习题训练、以及示例代码(例如 Python/NumPy/SymPy)和图形化建议。  
# - 限制与注意事项:我不能直接运行外部程序或实验,但可以给出可运行的代码和近似计算;复杂计算或长算式可能出错,建议你指出需要验算的地方或要求我逐步验证。知识截至 2024-06。  
# - 你想我帮你做哪类数学题?(请告诉题目、难度或你希望的讲解深度/语言)

所以,无论在调用Compaction API时是否传递了新的问题,当使用Responses API时还是要重新传入这个新问题。

另外,使用Responses API时,虽然使用previous_response_id串联起来的历史对话可能一直在用同一模型,但你在下一轮会话时可以随意切换模型。 比如你前面一直在用gpt-5-mini,你下一轮就可以换成gpt-5-max

posted @ 2025-06-27 10:29  RolandHe  阅读(404)  评论(0)    收藏  举报