lolspider / notes
Clipping · 6 分钟

DeepSeek 刚支持了 OpenAI Responses API,但你可能还不知道它是什么

2026 年 7 月 31 日,DeepSeek 悄悄发布了 V4-Flash-0731,附带一行更新日志:原生支持 OpenAI Responses API。消息不大,但背后藏着一条值得认真讲的故事线。一套 API 格式,是怎么从聊天框时代的产物,一步一步演化成今天 agent...

x.com @saladdayyy

📝 摘要

本文以时间线叙事,梳理了 LLM API 格式从 Chat Completions 到 Responses API 的演化历程。2022 年 Chat Completions 以无状态消息数组格式诞生,2023-2024 年各家厂商格式分叉但 Chat Completions 仍成为事实标准,2025 年 Responses API 引入服务端有状态、response_id 分支对话和内置云端工具,2026 年 7 月 DeepSeek V4-Flash-0731 原生支持 Responses API,重演了 Chat Completions 时代的兼容路径。

核心概念: LLM API 正从无状态聊天协议向有状态 Agent 协议演化,Responses API 的服务端状态管理解决了 Agent 时代对话缓存和分支的痛点,而 DeepSeek 的兼容策略再次验证了"API 格式兼容=降低迁移成本"的产业逻辑。

关键技术栈 / 关键要点:

  1. Chat Completions API — 无状态消息数组格式,每次请求携带完整对话历史,是行业事实标准
  2. 各家格式分叉 — Anthropic 将 system 提为顶层字段,Gemini 全面重命名字段(messages→contents, content→parts),但均未撼动 Chat Completions 的兼容生态
  3. Prompt Cache 的脆弱性 — 字段序列化顺序不同、时间戳/日期注入等导致 byte 级前缀缓存 miss,是 Agent 场景的核心痛点
  4. Responses API 服务端有状态 — 用 response_id 替代全量历史传输,缓存由服务端内部格式保证,序列化稳定性不再是开发者的责任
  5. response_id 分支对话 — 每个 response_id 是永久检查点,可从任意点分叉出新对话线,原生支持多分支探索
  6. 内置云端工具 — Responses API 附带 web_search、file_search、code_interpreter、computer_use、MCP 等工具,执行循环在 OpenAI 服务器上运行,标志着模型与 Agent 边界模糊化
  7. DeepSeek V4-Flash-0731 — 原生支持 Responses API,只需改 base_url 即可切换,与三年前 Chat Completions 的兼容路径完全一致

关键结论 / 实践建议: Responses API 的服务端状态和内置工具是 OpenAI 向 PaaS 方向走的信号——更深的锁定、新的计费单元,但简单场景省去胶水代码。开发者需权衡:简单场景用内置工具快速交付,需要定制或审计的场景仍需自行实现工具调用。


2026 年 7 月 31 日,DeepSeek 悄悄发布了 V4-Flash-0731,附带一行更新日志:原生支持 OpenAI Responses API。

消息不大,但背后藏着一条值得认真讲的故事线。一套 API 格式,是怎么从聊天框时代的产物,一步一步演化成今天 agent 时代的行业语言的。

第一幕:聊天框时代(2022)

2022 年 11 月,ChatGPT 以一个聊天框的形态亮相。你输入,它回答,就这样。

2023 年 3 月,OpenAI 开放 API,格式叫 Chat Completions。它的数据结构几乎就是聊天界面的直接映射:一个消息数组,每条消息有角色,有内容:

plaintext
{
  "model": "gpt-4",
  "messages": [
    { "role": "system",    "content": "你是一个助手" },
    { "role": "user",      "content": "帮我解释一下递归" },
    { "role": "assistant", "content": "递归是函数调用自身的编程技巧……" },
    { "role": "user",      "content": "能给个例子吗?" }
  ]
}

三种角色:system 定义行为,user 是你,assistant 是模型。简单、直觉,和聊天框一一对应。

这个格式有一个关键特征:无状态。每次请求,你都要把完整的对话历史带上。服务器不记得你上次说了什么,它只看你这次发来的消息数组。

第二幕:竞争者涌入,格式开始分叉(2023—2024)

Anthropic、Google、DeepSeek 相继推出自己的模型和 API,但格式各不相同。

**各家同时在做chat,如果兼容对手的格式等于帮对手降低用户的迁移成本,**今天用你,明天一行代码就能换走,何必。所以每家都选择了自己设计,在接口层留一点摩擦。

1. Anthropic Messages API

Anthropic 的格式和 Chat Completions 最像,核心差异只有一个:system 不是消息数组里的一条,而是顶层的独立字段。

plaintext
// OpenAI:system 混在 messages 数组里
{
  "model": "gpt-4o",
  "messages": [
    { "role": "system",    "content": "你是一个助手" },
    { "role": "user",      "content": "帮我解释一下递归" },
    { "role": "assistant", "content": "递归是函数调用自身的编程技巧……" }
  ]
}

// Anthropic:system 单独提出来
{
  "model": "claude-opus-5",
  "system": "你是一个助手",
  "messages": [
    { "role": "user",      "content": "帮我解释一下递归" },
    { "role": "assistant", "content": "递归是函数调用自身的编程技巧……" }
  ]
}

其他地方两家都有,只是字段命名不同,概念是一样的。

2. Google Gemini API

Gemini 的格式差异最大,字段名几乎全换了一套:

plaintext
{
  "contents": [
    { "role": "user",  "parts": [{ "text": "帮我解释一下递归" }] },
    { "role": "model", "parts": [{ "text": "递归是函数调用自身的编程技巧……" }] },
    { "role": "user",  "parts": [{ "text": "能给个例子吗?" }] }
  ],
  "system_instruction": {
    "parts": [{ "text": "你是一个助手" }]
  }
}

messages → contents,content → parts,assistant → model,system 变成独立的 system_instruction。同样的概念,完全不同的语言。

这段时间里发生了一件有意思的事:Chat Completions 的格式,因为种种原因成了行业的事实标准。

Groq、Together AI、Mistral、Ollama、Azure OpenAI,全都实现了完全相同的 Chat Completions 接口。换供应商只需要改一个 base_url:

plaintext
# 从 OpenAI 切到 DeepSeek,只改这一行
client = OpenAI(
    api_key="your-deepseek-key",
    base_url="https://api.deepseek.com"  # ← 只改这里
)

第三幕:Agent 时代,旧格式开始显老(2025)

开发者开始用这些 API 造更复杂的东西:不只是聊天,而是能调工具、能执行任务、能自主决策的 agent。

这时候 Chat Completions 的设计局限暴露出来了。

核心问题:状态要自己管,缓存很难打稳。

每次请求都要把完整对话历史序列化成 JSON 发过去。

Prompt cache 的命中条件是:这次发的 token 前缀和上次完全一致,byte 级别。听起来简单,实际上很脆:

plaintext
// 这次序列化出来的
{"role": "user", "content": "你好"}

// 下次序列化出来的(字段顺序换了,不同的 serializer 行为不同)
{"content": "你好", "role": "user"}

内容一样,token 序列不同,缓存 miss。工具结果里带了时间戳、system prompt 里拼了当前日期,一样 miss。

2025 年 3 月,Responses API 发布。

Responses API 在结构上做了两件根本性的改变。

改变一:服务端有状态。

不再需要每次带全量历史。第一次调用拿到 response_id,之后只传这个 ID:

plaintext
// 第一次调用
{ "model": "gpt-4o", "input": "帮我写一个快速排序" }

// 返回
{ "id": "resp_001", "output": [{ "type": "message", "content": [{ "text": "好的,这是快速排序……" }] }] }

// 第二次,只传 id
{ "model": "gpt-4o", "previous_response_id": "resp_001", "input": "加上注释" }

// 返回
{ "id": "resp_002", "output": [...] }

历史由 OpenAI 服务器维护,payload 从"整份对话"变成"一句新消息"。缓存 key 由 OpenAI 的内部格式生成,序列化稳定性不再是你的问题。

想从中间某个点分叉,直接用那个点的 response_id 就行。

比如你从 resp_001(快速排序)已经聊到了 resp_003(Python 版本加注释),但突然想回到 resp_001 再开一条"改成 Rust 版本"的线:

plaintext
resp_001(快速排序)
    └── resp_002(加注释)
           └── resp_003(Python 版本)   ← 当前主线
    └── resp_004(Rust 版本)            ← 从 resp_001 分叉出去

// 不传最新的 resp_003,而是传你想分叉的那个点
{
  "model": "gpt-4o",
  "previous_response_id": "resp_001",
  "input": "改成 Rust 版本"
}

每个 response_id 都是一个永久的检查点,随时可以从任何一个点往前走,原生支持分支。

还支持了个内置工具,顺带讲一下

Responses API 还带了几个云端执行的工具(openai提供了web_search、file_search、code_interpreter、computer_use、mcp),声明一下就能用,工具的执行循环在 OpenAI 服务器上跑,不经过你的代码。

像是OpenAI 在往 PaaS 方向走的信号。传统上,模型只做推理,工具执行、循环控制是你本地 harness 的事;内置工具把这部分吸进了云端,模型不再只是"告诉你它要调工具",而是自己把活干完再把结果交给你。

model 和 agent 的边界开始模糊。

对 OpenAI 来说,这意味着更深的锁定(循环跑在它的基础设施上),以及新的计费单元(每次工具调用都可以收费)。

对开发者来说,简单场景省了很多胶水代码,但中间过程你看不到也插不了手,需要定制或审计的场景,还是得自己实现工具调用

第四幕:DeepSeek 重演历史(2026 年 7 月 31 日)

回到开头那条更新日志。

DeepSeek V4-Flash-0731 加入了 Responses API 的原生支持。这意味着什么?

三年前,开发者发现把 base_url 改成 DeepSeek 的地址,Chat Completions 的代码就能直接跑。现在,同样的事情发生在 Responses API 上:

plaintext
client = OpenAI(
    api_key="your-deepseek-key",
    base_url="https://api.deepseek.com"
)

response = client.responses.create(
    model="deepseek-v4-flash",
    previous_response_id="resp_abc123",  # 有状态对话
    tools=[{ "type": "web_search_preview" }],
    input="继续"
)