PAYATHON 2026

通过程序调用 ChatGPT 时,如何在不重复提交历史消息的情况下继续对话?

技术老齐

能否省去客户端每轮重传全部历史,取决于使用的 API:

  • 题目中的 client.chat.completions.create() 需要你管理历史消息,每轮提交回答当前问题所需的上下文。
  • 使用 Responses API 时,可以通过 previous_response_id 续接上一轮,也可以通过 Conversations API 保存会话,后续只发送会话标识和新消息。官方会话状态文档

省去历史消息的重复传输后,服务端仍需将相关历史作为上下文,参与计算和计费。能否减少重复计算,取决于缓存是否命中。

为什么复用同一个 client 不能自动续聊?

OpenAI() 创建的是 API 客户端。调用 chat.completions.create() 时,复用同一个客户端、API Key 或 HTTP 连接,都不会自动关联前后两次对话。

如果第二次请求只包含:

messages=[
    {"role": "user", "content": "Can you elaborate more on that?"}
]

模型没有收到上一轮内容,就无法可靠判断 that 指什么。使用 Chat Completions 进行多轮对话,需要通过 messages 提供上下文。官方会话状态说明

题目中的环境变量注释也需要更正:Python SDK 默认读取的是 OPENAI_API_KEY,而非 OPEN_AI_KEY。官方 Python SDK 说明

继续使用 Chat Completions:维护 messages

每轮将模型回复保存为 assistant 消息,再追加新的 user 消息,就不用每次手写整个列表。

下面的示例从环境变量 OPENAI_MODEL 读取模型名称。请将它设为你有权限使用、且支持对应接口的模型。

import os
from openai import OpenAI

client = OpenAI()
model = os.environ["OPENAI_MODEL"]

messages = [
    {
        "role": "system",
        "content": (
            "You are a poetic assistant, skilled in explaining "
            "complex programming concepts with creative flair."
        ),
    }
]

def ask(prompt: str) -> str:
    request_messages = messages + [
        {"role": "user", "content": prompt}
    ]

    completion = client.chat.completions.create(
        model=model,
        messages=request_messages,
    )

    answer = completion.choices[0].message.content or ""
    messages.extend([
        {"role": "user", "content": prompt},
        {"role": "assistant", "content": answer},
    ])
    return answer

print(ask("Compose a poem that explains recursion in programming."))
print(ask("What happens if recursion has no valid base case?"))

这个示例适用于普通文本对话。如果使用工具调用,还需要保存相应的工具调用和结果,只保留回复文字不够。

不想重传历史:使用 previous_response_id

如果可以改用 Responses API,可以通过 previous_response_id 续聊:第一轮保存响应,第二轮传入它的 ID 和新问题。

import os
from openai import OpenAI

client = OpenAI()
model = os.environ["OPENAI_MODEL"]

instructions = (
    "You are a poetic assistant, skilled in explaining "
    "complex programming concepts with creative flair."
)

first = client.responses.create(
    model=model,
    instructions=instructions,
    input="Compose a poem that explains recursion in programming.",
    store=True,
)
print(first.output_text)

second = client.responses.create(
    model=model,
    instructions=instructions,
    previous_response_id=first.id,
    input="What happens if recursion has no valid base case?",
    store=True,
)
print(second.output_text)

第三轮使用 previous_response_id=second.id,之后每轮保存最新的响应 ID,供下一轮使用。服务端会据此关联之前的上下文。官方续聊示例

示例每轮都传入 instructions,因为通过 previous_response_id 续聊时,上一轮的 instructions 不会自动沿用。需要持续生效的要求,应在当前请求中再次提供。官方文本生成文档

previous_response_id 接收的是 Responses API 返回的 response.id,不能直接填入 Chat Completions 返回的 completion.id。

需要持久化会话:使用 Conversations API

如果希望为一段对话保留固定的会话 ID,可以创建 Conversation,让后续 Responses 请求引用它。下面沿用上一个示例中的 client、model 和 instructions:

conversation = client.conversations.create()

for prompt in [
    "Compose a poem that explains recursion in programming.",
    "What happens if recursion has no valid base case?",
]:
    response = client.responses.create(
        model=model,
        conversation=conversation.id,
        instructions=instructions,
        input=[{"role": "user", "content": prompt}],
    )
    print(response.output_text)

应用保存 conversation.id 后,可以在后续请求中继续使用。Conversation 在服务端保存消息等会话条目,适合需要跨进程或跨设备恢复的对话。官方 Conversations 用法

历史消息是否每轮都要重新计算、重新收费?

历史消息仍是后续回答的输入上下文。即使使用 previous_response_id,链中此前的输入 token 仍会作为输入 token 计费,后续请求并非只收取新增问题的费用。官方计费说明

支持 Prompt Caching 的模型可以复用相同提示词前缀的 KV cache,减少重复的前缀处理。因此,每次重传历史,也未必需要从头计算全部内容。

缓存能否命中,取决于模型、前缀匹配、长度和缓存有效期等条件。命中后按对应模型的缓存价格计费,保存会话本身不保证缓存命中。可以查看响应 usage 中的 cached_tokens 等字段,确认实际缓存情况。官方 Prompt Caching 文档

使用时还要注意什么?

  • 即使历史保存在服务端,也受模型上下文窗口限制。长对话需要适时摘要、压缩,或只保留与当前问题相关的内容。
  • previous_response_id 依赖可用的历史响应。官方文档说明,Response 对象默认保存 30 天,因此普通响应 ID 不适合作为永久会话标识。Conversation 及其条目不受同样的 30 天 TTL 限制。官方保存期限说明
  • Responses 和 Conversations 示例需要 Python SDK、模型及服务端都支持相应接口。第三方兼容服务是否支持这些功能,应查阅对应服务的文档。

备注:内容仅供参考。