为什么 API 响应中会有 choices 字段?

快速答案

choicesOpenAI API 标准格式的一部分,用于表示模型可能生成多个候选回复。


深入理解

1. OpenAI API 的设计

OpenAI 在设计 Chat Completion API 时,考虑了以下场景:

用户的一个请求 → 模型可能生成多个候选回复

因此,响应格式设计为:

{
  "choices": [
    {"message": {"content": "..."}},
    {"message": {"content": "..."}},
    {"message": {"content": "..."}}
  ]
}

参数n 参数控制生成多少个候选

response = client.chat.completions.create(
    model="gpt-4",
    messages=[...],
    n=3  # 生成 3 个候选回复
)

# 响应中会有 3 个 choices

2. 为什么需要多个候选?

场景 1:创意写作
# 生成多个故事开头
response = client.chat.completions.create(
    model="gpt-4",
    messages=[{"role": "user", "content": "写一个故事开头"}],
    n=3  # 生成 3 个不同的开头
)

# 用户可以选择最喜欢的
for i, choice in enumerate(response.choices):
    print(f"选项 {i+1}: {choice.message.content}")
场景 2:多路径推理
# 生成多个解决方案
response = client.chat.completions.create(
    model="gpt-4",
    messages=[{"role": "user", "content": "解决这个数学问题"}],
    n=5  # 生成 5 个不同的解法
)

# 分析多个解法
for i, choice in enumerate(response.choices):
    print(f"解法 {i+1}: {choice.message.content}")
场景 3:投票机制
# 生成多个回复,然后投票选择最好的
responses = []
for _ in range(5):
    response = client.chat.completions.create(
        model="gpt-4",
        messages=[...],
        n=1
    )
    responses.append(response.choices[0].message.content)

# 选择最佳回复
best_response = select_best(responses)

3. 标准结构详解

完整的 OpenAI 响应格式
{
  "id": "chatcmpl-8Hs...",
  "object": "chat.completion",
  "created": 1699564200,
  "model": "gpt-4",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "The answer is..."
      },
      "finish_reason": "stop"
    },
    {
      "index": 1,
      "message": {
        "role": "assistant",
        "content": "Another answer..."
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 10,
    "completion_tokens": 20,
    "total_tokens": 30
  }
}

字段说明

  • choices:候选回复数组
  • index:候选的索引(0, 1, 2, …)
  • message:实际的回复内容
  • finish_reason:生成停止的原因(“stop”、“length”、"tool_calls"等)

4. 为什么 Deepseek 也用 choices

Deepseek 兼容 OpenAI API,所以采用了相同的格式:

from openai import OpenAI

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

# 调用 Deepseek
response = client.chat.completions.create(
    model="deepseek-reasoner",
    messages=[{"role": "user", "content": "..."}],
    n=2  # 也支持多个候选
)

# 响应格式完全相同
for choice in response.choices:
    print(choice.message.content)

5. 其他厂商的格式对比

Gemini(Google)
{
  "candidates": [
    {
      "content": {
        "parts": [{"text": "..."}]
      }
    }
  ]
}

说明:用 candidates 代替 choices


Claude(Anthropic)
{
  "content": [
    {"type": "text", "text": "..."}
  ]
}

说明:直接返回内容,没有 choices


豆包(VolcEngine)
{
  "choices": [
    {
      "message": {
        "content": "...",
        "thinking": "..."
      }
    }
  ]
}

说明:也用 choices,兼容 OpenAI 格式


6. 实际使用中的处理

方式 1:获取第一个候选(最常见)
response = client.chat.completions.create(
    model="deepseek-reasoner",
    messages=[...],
    n=1  # 只生成 1 个候选
)

# 获取第一个(也是唯一的)候选
message = response.choices[0].message
print(message.content)
方式 2:获取所有候选
response = client.chat.completions.create(
    model="deepseek-reasoner",
    messages=[...],
    n=3  # 生成 3 个候选
)

# 遍历所有候选
for i, choice in enumerate(response.choices):
    print(f"候选 {i+1}: {choice.message.content}")
方式 3:处理 finish_reason
response = client.chat.completions.create(
    model="deepseek-reasoner",
    messages=[...],
)

choice = response.choices[0]

if choice.finish_reason == "stop":
    print("正常完成")
elif choice.finish_reason == "length":
    print("达到最大长度限制")
elif choice.finish_reason == "tool_calls":
    print("调用了工具")

7. 为什么通常只用第一个?

在实际应用中,大多数情况下 n=1(默认值),所以只有一个候选:

# 默认 n=1
response = client.chat.completions.create(
    model="deepseek-reasoner",
    messages=[...],
    # n 默认为 1
)

# 响应中只有一个 choice
print(len(response.choices))  # 输出: 1
print(response.choices[0].message.content)

8. 完整示例

示例 1:单个候选(常见)
from openai import OpenAI

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

response = client.chat.completions.create(
    model="deepseek-reasoner",
    messages=[
        {"role": "user", "content": "What is 2+2?"}
    ],
    n=1  # 默认值
)

# 访问响应
choice = response.choices[0]
print(f"Content: {choice.message.content}")
print(f"Finish reason: {choice.finish_reason}")

输出

Content: The answer is 4
Finish reason: stop

示例 2:多个候选
response = client.chat.completions.create(
    model="deepseek-reasoner",
    messages=[
        {"role": "user", "content": "给我 3 个创意想法"}
    ],
    n=3  # 生成 3 个候选
)

# 处理多个候选
for i, choice in enumerate(response.choices):
    print(f"想法 {i+1}:")
    print(choice.message.content)
    print("---")

输出

想法 1:
使用 AI 生成个性化学习计划...
---
想法 2:
开发一个社区知识共享平台...
---
想法 3:
创建虚拟现实教学体验...
---

示例 3:处理 thinking 和 choices
response = client.chat.completions.create(
    model="deepseek-reasoner",
    messages=[
        {"role": "user", "content": "解决这个难题"}
    ],
    n=2  # 生成 2 个候选
)

# 处理多个候选,每个都可能有 thinking
for i, choice in enumerate(response.choices):
    message = choice.message
    
    print(f"候选 {i+1}:")
    
    # 访问 thinking(如果有)
    if hasattr(message, 'reasoning_content'):
        print(f"  思考: {message.reasoning_content[:100]}...")
    
    # 访问答案
    print(f"  答案: {message.content}")
    print(f"  完成原因: {choice.finish_reason}")
    print("---")

9. 成本影响

⚠️ 重要:生成多个候选会增加成本!

# n=1: 生成 1 个回复
response1 = client.chat.completions.create(
    model="deepseek-reasoner",
    messages=[...],
    n=1
)
# 成本: 基础成本

# n=3: 生成 3 个回复
response3 = client.chat.completions.create(
    model="deepseek-reasoner",
    messages=[...],
    n=3
)
# 成本: 基础成本 × 3

10. 最佳实践

推荐

  • 默认使用 n=1
  • 只在需要多个候选时设置 n > 1
  • 检查 finish_reason 了解生成状态
  • 使用 response.choices[0] 获取主要回复

避免

  • 不必要地设置 n > 1(增加成本)
  • 忽略 finish_reason
  • 假设总是有多个候选

总结

为什么有 choices

  1. OpenAI API 标准:设计用于支持多个候选回复
  2. 灵活性:允许生成多个不同的回复供选择
  3. 兼容性:Deepseek、豆包等兼容 OpenAI API 的厂商都采用这个格式

结构

请求 (n=1/2/3/...)
    ↓
模型生成 n 个候选
    ↓
响应中的 choices 数组包含所有候选
    ↓
通常只使用 choices[0]

访问方式

# 获取第一个候选
response.choices[0].message.content

# 获取所有候选
for choice in response.choices:
    print(choice.message.content)

参考资源

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐