为了让你一目了然,我把三种主流流派(OpenAI天花板、国产平替、Claude特立独行)最核心的 Request(请求体) 和 Response(响应体) 的 JSON 结构扒出来。
我们统一设定一个任务:让模型从“张三今年25岁”这句话中,提取出姓名和年龄。
场景一:OpenAI(Structured Outputs 严格模式)
这是目前最稳的,它在请求里直接塞入了一张完整的“图纸”(JSON Schema),响应直接就是纯 JSON。 【Request 请求示例】
{
"model": "gpt-4o-2024-08-06",
"messages": [
{"role": "user", "content": "张三今年25岁"}
],
"response_format": {
"type": "json_schema",
"json_schema": {
"name": "user_info",
"strict": true,
"schema": {
"type": "object",
"properties": {
"name": { "type": "string" },
"age": { "type": "integer" }
},
"required": ["name", "age"],
"additionalProperties": false
}
}
}
}
💡 关键点:strict: true 和 additionalProperties: false 是灵魂,代表“必须且只能”按这个格式来。 【Response 响应示例】
{
"id": "chatcmpl-xxx",
"choices": [
{
"message": {
"role": "assistant",
"content": "{"name":"张三","age":25}"
}
}
]
}
💡 关键点:content 里面干干净净,就是一串合法的 JSON 字符串,你直接 json.loads() 就行,绝对报错不了。
场景二:国产模型(以 阿里 Qwen / 智谱 GLM 为例)
国产模型大部分走的是 OpenAI 早期的“简易开关”路线。它不让你传复杂的 Schema 图纸,只给你一个开关,具体长什么样靠你在 Prompt 里“嘴遁”。 【Request 请求示例】
{
"model": "qwen-plus",
"messages": [
{
"role": "system",
"content": "你是一个数据提取助手。请务必提取用户的name(字符串)和age(整数),并以纯JSON格式返回,不要任何多余文字。格式如:{"name":"xx","age":xx}"
},
{"role": "user", "content": "张三今年25岁"}
],
"response_format": { "type": "json_object" }
}
💡 关键点:只有一句 "type": "json_object"。注意阿里要求 Prompt 里必须出现“JSON”字样。 【Response 响应示例】
{
"id": "chatcmpl-xxx",
"choices": [
{
"message": {
"role": "assistant",
"content": "{"name":"张三","age":25}"
}
}
]
}
💡 关键点:返回格式和 OpenAI 一样,content 是纯 JSON 字符串。区别在于,它只保证语法合法(比如不会少个括号),但不保证字段一定叫 name/age(如果你 Prompt 写得不清楚,它可能返回 {"姓名":"张三","年龄":25}),所以需要配合好 Prompt。
场景三:Anthropic Claude(Tool Use 借尸还魂)
Claude 没有上面的 response_format 开关,它的做法是:假装系统里挂了一个名叫“提取工具”的函数,逼着 Claude 按照这个函数的参数格式来输出。 【Request 请求示例】
{
"model": "claude-sonnet-4-20250514",
"messages": [
{"role": "user", "content": "张三今年25岁"}
],
"tools": [
{
"name": "extract_user_info",
"description": "提取用户信息",
"strict": true,
"input_schema": {
"type": "object",
"properties": {
"name": { "type": "string", "description": "姓名" },
"age": { "type": "integer", "description": "年龄" }
},
"required": ["name", "age"],
"additionalProperties": false
}
}
]
}
💡 关键点:没有 response_format,而是把你的 JSON 结构定义在了 tools[0].input_schema 里。 【Response 响应示例】
{
"id": "msg_xxx",
"content": [
{
"type": "text",
"text": "好的,我来为您提取信息。"
},
{
"type": "tool_use",
"id": "toolu_xxx",
"name": "extract_user_info",
"input": {
"name": "张三",
"age": 25
}
}
]
}
💡 关键点:Claude 的响应结构变了!它返回的是一个 content 数组。你要找里面 type 为 "tool_use" 的那个块,然后从它的 input 字段里把 JSON 拿出来。这个 input 是被 Claude 底层严格约束过的,100% 符合你写的 input_schema。
🌟 总结对照表(写代码时怎么取值)
| 厂商流派 | 开启方式的灵魂参数 | 返回 JSON 藏在哪里? | 你的 Python 取值代码 |
|---|---|---|---|
| OpenAI (严格) | response_format.json_schema.strict: true | message.content | data = json.loads(response.choices[0].message.content) |
| 国产 Qwen/GLM | response_format.type: "json_object" | message.content | data = json.loads(response.choices[0].message.content) |
| Claude | tools[0].strict: true + input_schema | content[?].input (需遍历找 tool_use) | data = next(block.input for block in response.content if block.type == "tool_use") |
有了这三个对照,你在写代码封装统一调用层的时候,就知道针对不同模型该怎么发请求、怎么抠数据了。