豆包 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 |
上游异常或过载 | 短暂等待后重试,准备备用通道 |
九、迁移检查清单
从官方直连切到网关,或从一个网关换到另一个,按下面顺序检查:
- 固定一批真实提示词,记录当前输出、耗时和
usage,作为对比基线。 - 同时更换
base_url和api_key,不要只改其中一个。 - 按站内模型价格重新确认模型名映射,不要沿用旧名称。
- 验证流式输出:首 Token 时间、
delta.content、finish_reason和[DONE]。 - 验证工具调用:
tools、tool_calls、tool_call_id回传和多轮顺序。 - 验证
thinking、reasoning_effort等扩展参数是否支持,默认值是否不同。 - 设置连接与读取超时,只对 429、5xx 和超时重试,写操作保证幂等。
- 先切 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 累积参数。
