豆包 API 提供 OpenAI 兼容接口,但“兼容”不等于“照抄”。最容易卡住的三处是:Base URL 写成了 /v1、model 填了展示名而不是模型 ID 或接入点 ID、第一次调用就开了深度思考导致输出被截断。本文按顺序跑通 curl 和 Python,再讲流式输出、工具调用、常见报错和迁移检查清单。

说明:豆包、火山方舟、火山引擎、字节跳动是字节跳动及其关联公司的商标或产品名称;OpenAI 是其权利人的商标。本文仅作技术接入说明,与上述公司无隶属、代理或背书关系。接口、模型与配额可能调整,请以官方文档和站内模型价格为准。

一、接入前确认三件事

项目 火山方舟直连 统一网关
Base URL https://ark.cn-beijing.volces.com/api/v3 https://ai.zhongshangqihang.com/v1
鉴权 Authorization: Bearer <ARK_API_KEY> Authorization: Bearer <网关 Key>
model 模型 ID 或 ep- 接入点 ID 以站内模型列表为准
适合 只用豆包、自行实名充值开票 多模型、统一账单与额度

Key 与 Base URL 必须成对使用。官方 Key 配网关地址,或网关 Key 配官方地址,通常直接返回 401。切换入口时,两样一起换。

二、Base URL 与请求路径:/api/v3 不是 /v1

官方数据面地址是 https://ark.cn-beijing.volces.com/api/v3,聊天补全路径为 /chat/completions,完整地址是 https://ark.cn-beijing.volces.com/api/v3/chat/completions。它没有 OpenAI 常见的 /v1 前缀,再拼一个 /v1 会得到 404。

区域写在域名里(cn-beijing)。如果资源在其他区域,以控制台和文档给出的地址为准。官方文档还提醒,Coding Plan 等情况使用不同的 Base URL,填错可能产生额外费用,切换套餐前先核对。

三、model 填什么:模型 ID 还是接入点 ID

官方文档给出两种调用方式:

  • 预置推理:直接填模型 ID,例如 doubao-seed-2-1-pro-260915,不需要提前创建接入点。
  • 自定义推理接入点:填接入点 ID,通常以 ep- 开头,适合固定部署、精细管理或 LoRA 微调模型。

模型 ID 带版本日期,不要写成“豆包 2.1 Pro”这类展示名,也不要凭记忆补后缀。使用 Access Key(AK/SK)签名时,model 必须填接入点 ID;使用 API Key 时可按文档填模型 ID 或接入点 ID。若返回 InvalidEndpointOrModel.ModelIDAccessDisabled,说明当前账号不支持模型 ID 直调,需要创建接入点。

四、curl 跑通最小请求

先关闭深度思考,减少变量。官方入口示例:

export ARK_API_KEY="你的火山方舟 API Key"
export ARK_MODEL_ID="doubao-seed-2-1-pro-260915"

curl "https://ark.cn-beijing.volces.com/api/v3/chat/completions" \
  -H "Authorization: Bearer $ARK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "'"$ARK_MODEL_ID"'",
    "messages": [{"role": "user", "content": "只回复 OK"}],
    "thinking": {"type": "disabled"},
    "max_tokens": 32
  }'

返回 200,且 choices[0].message.content 有内容,说明 Key、地址、模型三项都对。若 404,先检查 model 是否带了版本后缀、是否需要改用 ep- 接入点;若 401,先检查 Key 和请求头,不要先怀疑余额。

五、Python 用 OpenAI SDK 调用

import os
from openai import OpenAI

client = OpenAI(
    api_key=os.environ["ARK_API_KEY"],
    base_url="https://ark.cn-beijing.volces.com/api/v3",
)

resp = client.chat.completions.create(
    model=os.environ.get("ARK_MODEL_ID", "doubao-seed-2-1-pro-260915"),
    messages=[
        {"role": "system", "content": "你是中文技术助手,回答简洁。"},
        {"role": "user", "content": "用三句话解释什么是推理接入点。"},
    ],
    max_tokens=512,
    extra_body={"thinking": {"type": "disabled"}},
)

print(resp.choices[0].message.content)
print(resp.usage)

OpenAI SDK 不认识 thinking 字段,所以通过 extra_body 传入;若网关或模型不支持,去掉该字段即可。跑通后先看 usage 的输入、输出 Token,再决定超时、并发和成本预算。

六、流式输出:delta、reasoning_content 与 [DONE]

设置 stream=True 后,服务端通过 SSE 逐块返回,响应对象为 chat.completion.chunk,正文在 choices[0].delta.content,思考内容在 delta.reasoning_content,finish_reason 只在最后一个 chunk 出现,结束时发送 data: [DONE]。

stream = client.chat.completions.create(
    model=os.environ["ARK_MODEL_ID"],
    messages=[{"role": "user", "content": "写一个读取 CSV 的 Python 示例"}],
    stream=True,
    stream_options={"include_usage": True},
)
for chunk in stream:
    if chunk.choices and chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="")

思考块可能带有空 content,不要假设每个 chunk 都有正文;max_tokens 太小会截断输出;网络中断时已收到的内容仍在,客户端要能保存部分结果并决定是否重试。

七、工具调用:tools、tool_calls 与回传顺序

tools 的写法与 OpenAI 接近:每个工具包含 type: "function" 和 function,其中 name、description、parameters 必填,parameters 使用 JSON Schema。模型决定调用时,响应的 finish_reason 为 tool_calls,message.tool_calls[i].function.arguments 是 JSON 字符串,需要自行解析。

回传顺序不能错:先把带 tool_calls 的 assistant 消息加入对话,再为每个调用追加一条 role: "tool" 消息,带上对应的 tool_call_id 和结果,然后再次请求模型,直到 finish_reason 不再是 tool_calls。parallel_tool_calls 默认为 true,可限制为一次只调一个工具。流式场景下,工具调用增量带有 index,要按 index 累积 arguments。

八、常见错误:401、404、429 怎么定位

HTTP 典型错误码 含义 处理
401 AuthenticationError Key 缺失、无效,或请求头没有 Bearer 重新复制 Key,检查 Authorization 头
404 InvalidEndpointOrModel.NotFound 模型或接入点不存在,或当前空间无权限 从控制台复制完整 ID,核对区域与空间
404 ModelNotOpen 模型尚未在账号内开通 在方舟控制台开通对应模型
404 InvalidEndpointOrModel.ModelIDAccessDisabled 账号不支持模型 ID 直调 创建自定义接入点,改用 ep- ID
400 InvalidParameter、MissingParameter 参数缺失、类型错误或模型不支持该参数 对照文档删改字段,先跑最小请求
403 AccessDenied、AccountOverdueError 无权限,或账户余额为负 检查 Key 与空间权限,处理欠费
429 RateLimitExceeded.* RPM/TPM 超限或流量脉冲 降并发、指数退避,必要时申请提额
500/503 InternalServiceError、ServerOverloaded 上游异常或过载 短暂等待后重试,准备备用通道

九、迁移检查清单

从官方直连切到网关,或从一个网关换到另一个,按下面顺序检查:

  1. 固定一批真实提示词,记录当前输出、耗时和 usage,作为对比基线。
  2. 同时更换 base_url 和 api_key,不要只改其中一个。
  3. 按站内模型价格重新确认模型名映射,不要沿用旧名称。
  4. 验证流式输出:首 Token 时间、delta.content、finish_reason 和 [DONE]。
  5. 验证工具调用:tools、tool_calls、tool_call_id 回传和多轮顺序。
  6. 验证 thinking、reasoning_effort 等扩展参数是否支持,默认值是否不同。
  7. 设置连接与读取超时,只对 429、5xx 和超时重试,写操作保证幂等。
  8. 先切 5%–10% 流量观察 3–7 天,保留官方直连作为回滚通道。

需要统一入口时,可在注册页创建测试 Key;模型覆盖与计费口径见模型价格。中商起航 AI 网关是独立第三方服务,与字节跳动无隶属关系。

相关阅读:

常见问题

Base URL 要不要加 /v1?

不要。火山方舟的 OpenAI 兼容入口是 https://ark.cn-beijing.volces.com/api/v3,聊天路径为 /chat/completions,再拼 /v1 会得到 404。网关地址通常以 /v1 结尾,两者不要混用。

model 填模型 ID 还是 ep- 开头的接入点 ID?

预置推理填模型 ID;自定义接入点填 ep- ID;使用 Access Key 签名时必须填接入点 ID。若返回 ModelIDAccessDisabled,说明需要创建接入点,而不是改 Key。

第一次调用要不要开深度思考?

建议先关闭,用最短请求确认链路通。部分豆包模型默认启用深度思考,开启后输出会先产生思考内容,max_tokens 太小容易截断正文。确认链路后再按任务开启。

流式输出为什么没有内容?

先确认 stream=True,再检查是否只读了 delta.content 而忽略空块;思考内容在 delta.reasoning_content。还要检查 max_tokens、网络缓冲和代理是否把 SSE 攒成一次性响应。

工具调用为什么要回传 assistant 的 tool_calls?

协议要求 assistant 消息携带 tool_calls,随后每条 role: "tool" 消息用 tool_call_id 对应结果。跳过这一步,模型可能重复调用工具或直接报 400。流式调用还要按 index 累积参数。