OpenAI / Anthropic 接口介绍

通过一些例子,介绍 OpenAI chat/response 与 Anthropic messages 协议,不陷于枯燥的接口参数介绍。

在正式介绍 OpenAI Chat / Responses 和 Anthropic Messages 协议之前,有一个概念需要先明确:大模型本身是无状态的。

从模型的视角看,一次调用就是一次独立的输入与输出过程:模型接收当前请求中的上下文,根据概率分布生成结果,然后这次推理就结束了。下一次请求到来时,模型并不会天然记得上一轮发生过什么。因此,无论是 OpenAI 还是 Anthropic,底层的一次次模型调用,本质上都是相互独立、无状态的。

但我们日常使用 ChatGPT、Claude 时,却明显会感觉对话是“有记忆”的。例如:

user:我叫张三。
assistant:你好,张三。
user:我叫什么?
assistant:张三。

模型能够回答“张三”,并不是因为模型在上一次请求结束后保存了某种会话状态,而是因为应用在发起新一轮请求时,把前面的对话历史一起重新提交给了模型。本质上通常是应用层模拟出来的“有状态”。模型调用是无状态的,而多轮对话是在应用层通过重复携带历史上下文,实现出来的“逻辑有状态”。

这种设计也带来了一个直接代价:历史对话会持续占用上下文窗口,逐渐挤占模型可用于新输入、工具结果和输出内容的上下文空间。

理解这一点之后,再去看 OpenAI Chat / Responses 和 Anthropic Messages 协议就会容易很多:这些协议很大一部分工作,其实都围绕着“如何描述当前这一次模型调用所需要的完整上下文”展开。

OpenAI Chat #

endpoint #

POST /v1/chat/completions

example #

关键点:每次请求携带之前对话的所有内容

请求:

{
    "model": "deepseek-v4-pro",
    "messages": [
        {
            "role": "system",
            "content": "You are a helpful assistant."
        },
        {
            "role": "user",
            "content": "我叫张三"
        },
        {
            "role": "assistant",
            "content": "你好,张三!"
        },
        {
            "role": "user",
            "content": "我叫什么?"
        }
    ]
}

响应:

{
    "id": "chatcmpl-d3cd1541-e7f0-95e4-9b9b-53aaef0ad77a",
    "model": "deepseek-v4-pro",
    "choices": [
        {
            "finish_reason": "stop",
            "index": 0,
            "message": {
                "content": "你刚才说你叫张三。😊",
                "role": "assistant",
                "reasoning_content": "我们被问到:“我叫什么?”在前面的对话中,用户说“我叫张三”。所以答案是“张三”。需要直接回复。"
            },
        }
    ],
    "usage": {
        "completion_tokens": 36,
        "prompt_tokens": 24,
        "total_tokens": 60
    }
}

关键点:模型产生调用工具意图,由客户端进行调用获取其他数据。MCP 和 Skill 都建立在 Tool Call 之上,可以自行想象下。

请求:

{
  "model": "deepseek-v4-pro",
  "messages": [
    {
      "role": "user",
      "content": "What is my horoscope? I am an Aquarius."
    }
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "get_horoscope",
        "description": "Get today's horoscope for an astrological sign.",
        "parameters": {
          "type": "object",
          "properties": {
            "sign": {
              "type": "string",
              "description": "An astrological sign like Taurus or Aquarius"
            }
          },
          "required": ["sign"]
        }
      }
    }
  ],
  "tool_choice": "auto"
}

响应:

{
    "id": "chatcmpl-962bc15f-9701-9e3b-a0cf-bddb6afa16cd",
    "model": "deepseek-v4-pro",
    "choices": [
        {
            "finish_reason": "tool_calls",
            "index": 0,
            "message": {
                "content": "",
                "role": "assistant",
                "tool_calls": [
                    {
                        "index": 0,
                        "function": {
                            "arguments": "{\"sign\": \"Aquarius\"}",
                            "name": "get_horoscope"
                        },
                        "id": "call_1aea02ba05b84673a6f81aa9",
                        "type": "function"
                    }
                ],
                "reasoning_content": "The user is asking for their horoscope and they mention they are an Aquarius. I'll use the get_horoscope function to fetch their horoscope."
            }
        }
    ],
    "usage": {
        "completion_tokens": 79,
        "prompt_tokens": 305,
        "total_tokens": 384
    }
}

第二次请求

{
    "model": "deepseek-v4-pro",
    "messages": [
        {
            "role": "user",
            "content": "What is my horoscope? I am an Aquarius."
        },
        {
            "role": "assistant",
            "content": "",
            "tool_calls": [
                {
                    "id": "call_1aea02ba05b84673a6f81aa9",
                    "type": "function",
                    "function": {
                        "name": "get_horoscope",
                        "arguments": "{\"sign\":\"Aquarius\"}"
                    }
                }
            ]
        },
        {
            "role": "tool",
            "tool_call_id": "call_1aea02ba05b84673a6f81aa9",
            "content": "Aquarius: Next Tuesday you will befriend a baby otter."
        }
    ],
    "tools": [
        {
            "type": "function",
            "function": {
                "name": "get_horoscope",
                "description": "Get today's horoscope for an astrological sign.",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "sign": {
                            "type": "string"
                        }
                    },
                    "required": [
                        "sign"
                    ]
                }
            }
        }
    ]
}

第二次响应

{
    "id": "chatcmpl-0e73e44a-a423-9847-959f-8628b068cc81",
    "model": "deepseek-v4-pro",
    "choices": [
        {
            "finish_reason": "stop",
            "index": 0,
            "message": {
                "content": "Here's your horoscope for today, Aquarius! ♒\n\n> **\"Next Tuesday you will befriend a baby otter.\"**\n\nHow delightful! It sounds like an adorable and unexpected encounter is on the horizon for you. Keep your eyes open next Tuesday — perhaps near a river, zoo, or aquarium? Either way, that's one charming friendship in the making! 🦦",
                "role": "assistant",
                "reasoning_content": "The tool returned the horoscope for Aquarius. Let me present this to the user."
            }
        }
    ],
    "usage": {
        "completion_tokens": 98,
        "prompt_tokens": 363,
        "total_tokens": 461
    }
}

关键点:通过json_schema约束模型输出

请求:

{
    "model": "glm-5.2",
    "messages": [
        {
            "role": "user",
            "content": "张三今年 28 岁,邮箱是 zhangsan@example.com。请提取用户信息。"
        }
    ],
    "response_format": {
        "type": "json_schema",
        "json_schema": {
            "name": "user_info",
            "strict": true,
            "schema": {
                "type": "object",
                "properties": {
                    "name": {
                        "type": "string"
                    },
                    "age": {
                        "type": "integer"
                    },
                    "email": {
                        "type": "string"
                    }
                },
                "required": [
                    "name",
                    "age",
                    "email"
                ],
                "additionalProperties": false
            }
        }
    }
}

响应:

{
    "id": "chatcmpl-8071dea7-3ad1-93ef-8600-46159b0a28cc",
    "model": "glm-5.2",
    "choices": [
        {
            "finish_reason": "stop",
            "index": 0,
            "message": {
                "content": "{\n  \"age\": 28,\n  \"email\": \"zhangsan@example.com\",\n  \"name\": \"张三\"\n}",
                "role": "assistant",
                "reasoning_content": "1.  **分析请求:**\n    *   输入文本:“张三今年 28 岁,邮箱是 zhangsan@example.com。请提取用户信息。”(张三今年 28 岁,他的邮箱是 zhangsan@example.com。请提取用户信息。)\n    *   任务:从输入文本中提取用户信息。\n\n2.  **识别实体:**\n    *   姓名:张三(张三)\n    *   年龄:28(28)\n    *   邮箱:zhangsan@example.com\n\n3.  **格式化输出:**\n    *   清晰地呈现提取的信息,可以使用 JSON 格式或结构化列表。结构化列表通常具有很好的可读性,但 JSON 格式在数据提取中非常标准。我们两者都提供,或者只提供一个清晰的列表。采用具有清晰标签的结构化列表:\n        *   姓名:张三\n        *   年龄:28岁\n        *   邮箱:zhangsan@example.com\n\n4.  **起草最终回复:**\n    “提取到的用户信息如下:\n    - 姓名:张三\n    - 年龄:28 岁\n    - 邮箱:zhangsan@example.com”\n    (也可以为了完整性添加一个 JSON 代码块,但我们保持简单直接)。\n\n5.  **对照约束条件审查:**\n    *   我提取所有信息了吗?是的。\n    *   准确吗?是的。\n\n6.  **最终输出生成。**"
            }
        }
    ],
    "usage": {
        "completion_tokens": 344,
        "prompt_tokens": 34,
        "total_tokens": 378
    }
}

随后客户端可以提取结构化信息

user = json.loads(response.choices[0].message.content)
print(user["name"])
print(user["age"])
print(user["email"])

关键点:服务端不会一次性返回完整 JSON,而是通过 SSE text/event-stream 持续发送多个 chunk

请求:

{
    "model": "glm-5.2",
    "stream": true,
    "messages": [
        {
            "role": "user",
            "content": "用一句话介绍北京。"
        }
    ]
}

响应:

{
    "id": "chatcmpl-2c979863-1421-9629-95bb-03aee6314f35",
    "model": "glm-5.2",
    "choices": [
        {
            "index": 0,
            "delta": {
                "reasoning_content": "",
                "content": "",
                "role": "assistant"
            }
        }
    ]
}

{
    "id": "chatcmpl-2c979863-1421-9629-95bb-03aee6314f35",
    "model": "glm-5.2",
    "choices": [
        {
            "index": 0,
            "delta": {
                "reasoning_content": "1",
                "content": ""
            }
        }
    ]
}

{
    "id": "chatcmpl-2c979863-1421-9629-95bb-03aee6314f35",
    "model": "glm-5.2",
    "choices": [
        {
            "index": 0,
            "delta": {
                "reasoning_content": "",
                "content": "历史与蓬勃现代活力完美交融的国际"
            }
        }
    ]
}

{
    "id": "chatcmpl-2c979863-1421-9629-95bb-03aee6314f35",
    "model": "glm-5.2",
    "choices": [
        {
            "finish_reason": "stop",
            "index": 0,
            "delta": {}
        }
    ]
}

{
    "id": "chatcmpl-2c979863-1421-9629-95bb-03aee6314f35",
    "model": "glm-5.2",
    "choices": [
        {
            "index": 0,
            "delta": {}
        }
    ],
    "usage": {
        "completion_tokens": 607,
        "prompt_tokens": 17,
        "total_tokens": 624
    }
}

[DONE]

客户端对应的处理代码:

content = ""
reasoning_content = ""

for chunk in stream:
    delta = chunk.choices[0].delta

    if delta.reasoning_content:
        reasoning_content += delta.reasoning_content

    if delta.content:
        content += delta.content

Anthropic Messages #

endpoint #

POST /v1/messages

example #

OpenAI chat很相似

请求:

{
    "model": "glm-5.2-anthropic",
    "messages": [
        {
            "role": "system",
            "content": "You are a helpful assistant."
        },
        {
            "role": "user",
            "content": "我叫张三"
        },
        {
            "role": "assistant",
            "content": "你好,张三!"
        },
        {
            "role": "user",
            "content": "我叫什么?"
        }
    ]
}

响应:

{
    "id": "chatcmpl-838d4b77-8f0c-9cc4-b479-eae65acfedb3",
    "type": "message",
    "role": "assistant",
    "content": [
        {
            "type": "thinking",
            "text": "",
            "thinking": "1.  **分析用户的输入**:用户正在询问“我叫什么?”(What is my name?)。\n2.  **回顾对话历史**:\n    *   用户:“我叫张三”(我的名字是张三)。\n    *   机器人:“你好,张三!”(你好,张三!)。\n    *   用户:“我叫什么?”(我叫什么?)。\n3.  **识别核心实体**:用户在第一轮对话中已经说明了他们的名字叫“张三”(Zhang San)。\n4.  **构建回复**:清楚地说明用户的名字叫张三,并保持礼貌和乐于助人的语气。\n5.  **起草回复(内心独白/草稿)**:\n    *   *草稿1*:你叫张三。(太生硬)\n    *   *草稿2*:你刚才告诉我,你叫张三。(更好,符合语境)\n    *   *草稿3*:你叫张三呀。(友好,口语化)\n6.  **选择最佳回复**:草稿2或3。让我们采用简单、直接的回答。“你叫张三呀,刚才你自己说的。”或者简单地说“你叫张三。”让我们使其友好些:“你叫张三呀!”\n\n*起草过程中的自我纠正*:用户可能在测试记忆。一个简单直接的“你叫张三。”就很完美。添加一个礼貌的短语:“你叫张三。”\n\n最终选择:“你叫张三。刚才你告诉我了。”(你是张三。你刚才告诉我了。) -> 缩短为“你叫张三呀。”(你是张三。) -> 保持简单:“你叫张三。”"
        },
        {
            "type": "text",
            "text": "你叫张三。刚才你告诉我了。"
        }
    ],
    "model": "glm-5.2-anthropic",
    "stop_reason": "end_turn",
    "stop_sequence": null,
    "usage": {
        "input_tokens": 34,
        "output_tokens": 360,
        "total_tokens": 394
    },
    "error": null
}

第一次请求:

{
    "model": "glm-5.2-anthropic",
    "messages": [
        {
            "role": "user",
            "content": "What is my horoscope? I am an Aquarius."
        }
    ],
    "tools": [
        {
            "name": "get_horoscope",
            "description": "Get today's horoscope for an astrological sign.",
            "input_schema": {
                "type": "object",
                "properties": {
                    "sign": {
                        "type": "string",
                        "description": "An astrological sign like Taurus or Aquarius"
                    }
                },
                "required": [
                    "sign"
                ]
            }
        }
    ],
    "tool_choice": {
        "type": "auto"
    }
}

第一次响应:

{
    "id": "chatcmpl-0092bb86-85eb-9114-ac64-7e63c9ccf444",
    "type": "message",
    "role": "assistant",
    "content": [
        {
            "type": "thinking",
            "text": "",
            "thinking": "The user wants their horoscope and they are an Aquarius. Let me call the get_horoscope function with the sign \"Aquarius\"."
        },
        {
            "type": "text",
            "text": "Let me fetch your horoscope for Aquarius!"
        },
        {
            "type": "tool_use",
            "text": "",
            "id": "call_0cad738fedeb4701b75335ff",
            "name": "get_horoscope",
            "input": {
                "sign": "Aquarius"
            }
        }
    ],
    "model": "glm-5.2-anthropic",
    "stop_reason": "tool_use",
    "stop_sequence": null,
    "usage": {
        "input_tokens": 186,
        "output_tokens": 51,
        "total_tokens": 237
    },
    "error": null
}

第二次请求:

{
    "model": "glm-5.2-anthropic",
    "messages": [
        {
            "role": "user",
            "content": "What is my horoscope? I am an Aquarius."
        },
        {
            "role": "assistant",
            "content": [
                {
                    "type": "thinking",
                    "text": "",
                    "thinking": "The user wants their horoscope and they are an Aquarius. Let me call the get_horoscope function with the sign \"Aquarius\"."
                },
                {
                    "type": "text",
                    "text": "Let me fetch your horoscope for Aquarius!"
                },
                {
                    "type": "tool_use",
                    "text": "",
                    "id": "call_0cad738fedeb4701b75335ff",
                    "name": "get_horoscope",
                    "input": {
                        "sign": "Aquarius"
                    }
                }
            ]
        },
        {
            "role": "user",
            "content": [
                {
                    "type": "tool_result",
                    "tool_use_id": "call_0cad738fedeb4701b75335ff",
                    "content": "Aquarius: Next Tuesday you will befriend a baby otter."
                }
            ]
        }
    ],
    "tools": [
        {
            "name": "get_horoscope",
            "description": "Get today's horoscope for an astrological sign.",
            "input_schema": {
                "type": "object",
                "properties": {
                    "sign": {
                        "type": "string",
                        "description": "An astrological sign like Taurus or Aquarius"
                    }
                },
                "required": [
                    "sign"
                ]
            }
        }
    ]
}

第二次响应:

{
    "id": "chatcmpl-adbfb112-5723-9800-9b6b-c1ced58d6953",
    "type": "message",
    "role": "assistant",
    "content": [
        {
            "type": "thinking",
            "text": "",
            "thinking": "The function returned a horoscope for Aquarius. Let me share this with the user."
        },
        {
            "type": "text",
            "text": "Here is your horoscope for today, Aquarius! 🌟\n\n**Aquarius:** Next Tuesday you will befriend a baby otter.\n\nThat sounds like a wonderful and unexpected encounter! Baby otters are absolutely adorable. Keep an eye out for any opportunities to connect with nature or animals early next week. 🦦✨\n\nIs there anything else you'd like to know?"
        }
    ],
    "model": "glm-5.2-anthropic",
    "stop_reason": "end_turn",
    "stop_sequence": null,
    "usage": {
        "input_tokens": 228,
        "output_tokens": 98,
        "total_tokens": 326
    },
    "error": null
}

请求:

{
    "model": "glm-5.2-anthropic",
    "messages": [
        {
            "role": "user",
            "content": "张三今年 28 岁,邮箱是 zhangsan@example.com。请提取用户信息。"
        }
    ],
    "output_config": {
        "format": {
            "type": "json_schema",
            "name": "user_info",
            "schema": {
                "type": "object",
                "properties": {
                    "name": {
                        "type": "string"
                    },
                    "age": {
                        "type": "integer"
                    },
                    "email": {
                        "type": "string"
                    }
                },
                "required": [
                    "name",
                    "age",
                    "email"
                ],
                "additionalProperties": false
            }
        }
    }
}

响应:

{
    "id": "chatcmpl-9781df6b-f970-9085-89f9-eaa0315df8a0",
    "type": "message",
    "role": "assistant",
    "content": [
        {
            "type": "thinking",
            "text": "",
            "thinking": "1.  **分析请求:**\n    *   输入文本:“张三今年 28 岁,邮箱是 zhangsan@example.com。请提取用户信息。”(张三今年 28 岁,他的邮箱是 zhangsan@example.com。请提取用户信息。)\n    *   任务:提取用户信息(姓名、年龄、邮箱)。\n    *   输出格式:结构化信息(例如:JSON、键值对)。\n\n2.  **识别实体:**\n    *   姓名 (姓名):张三\n    *   年龄 (年龄):28\n    *   邮箱 (邮箱):zhangsan@example.com\n\n3.  **格式化输出:**\n    *   提供清晰、易读的提取信息格式。键值对或 JSON 格式效果最好。\n\n    *草稿 1 (键值对):*\n    姓名:张三\n    年龄:28\n    邮箱:zhangsan@example.com\n\n    *草稿 2 (JSON):*\n    ```json\n    {\n      \"name\": \"张三\",\n      \"age\": 28,\n      \"email\": \"zhangsan@example.com\"\n    }\n    ```\n\n4.  **选择最佳输出:** 提供键值对通常是最直接且易读的,但添加 JSON 格式会显得非常专业。我们同时提供两者,或者只提供结构化的文本列表。为了清晰起见,我将使用简洁的文本列表。\n\n5.  **最终确定回复:**\n    提取到的用户信息如下:\n    - 姓名:张三\n    - 年龄:28岁\n    - 邮箱:zhangsan@example.com\n    (同时也可以选择以 JSON 格式输出,这通常在“提取”类任务中很受欢迎)。我们直接输出结构化数据即可。"
        },
        {
            "type": "text", 
            "text": "{\"name\":\"张三\",\"age\":28,\"email\":\"zhangsan@example.com\"}"
        }
    ],
    "model": "glm-5.2-anthropic",
    "stop_reason": "end_turn",
    "stop_sequence": null,
    "usage": {
        "input_tokens": 32,
        "output_tokens": 467,
        "total_tokens": 499
    },
    "error": null
}

请求:

{
    "model": "qwen3.7-max-anthropic",
    "stream": true,
    "messages": [
        {
            "role": "user",
            "content": "用一句话介绍北京。"
        }
    ]
}

响应:

// message_start
{
    "type": "message_start",
    "message": {
        "id": "chatcmpl-284e68c1-9908-982c-b2bb-366a70fab6cb",
        "type": "message",
        "role": "assistant",
        "content": [],
        "model": "qwen3.7-max",
        "stop_reason": null,
        "stop_sequence": null,
        "usage": {
            "input_tokens": 0
        },
        "error": null
    }
}

// content_block_start - thinking
{
    "type": "content_block_start",
    "index": 0,
    "content_block": {
        "type": "thinking",
        "text": ""
    }
}

// content_block_delta
{
    "type": "content_block_delta",
    "index": 0,
    "delta": {
        "type": "thinking_delta",
        "thinking": "用户"
    }
}

// content_block_stop
{
    "type": "content_block_stop",
    "index": 0
}

// content_block_start - text
{
    "type": "content_block_start",
    "index": 1,
    "content_block": {
        "type": "text",
        "text": ""
    }
}

// content_block_delta
{
    "type": "content_block_delta",
    "index": 1,
    "delta": {
        "type": "text_delta",
        "text": "北京是中国的首都"
    }
}

// content_block_stop
{
    "type": "content_block_stop",
    "index": 1
}

{
    "type": "message_stop"
}

{
    "type": "message_delta",
    "delta": {
        "type": "",
        "stop_reason": "end_turn"
    },
    "usage": {
        "input_tokens": 15,
        "output_tokens": 342,
        "total_tokens": 357
    }
}

OpenAI Responses #