A API Doubao oferece interface compatível com OpenAI, mas "compatível" não significa "copiar e colar". Os três pontos que mais travam são: escrever a URL base como /v1, preencher model com o nome de exibição em vez do ID do modelo ou do ID do endpoint, e ativar o raciocínio profundo já na primeira chamada, truncando a saída. Este artigo percorre curl e Python na ordem, depois explica streaming, chamadas de ferramenta, erros comuns e checklist de migração.
Nota: Doubao, Volcengine Ark, Volcengine e ByteDance são marcas ou nomes de produto da ByteDance e empresas associadas; OpenAI é marca de seu titular. Este artigo é apenas uma explicação técnica de integração e não tem vínculo de afiliação, representação ou endosso com as empresas citadas. Interfaces, modelos e cotas podem mudar; valem a documentação oficial e a página de preços do site.
1. Três confirmações antes de integrar
| Item | Conexão direta ao Volcengine Ark | Gateway unificado |
|---|---|---|
| URL base | https://ark.cn-beijing.volces.com/api/v3 |
https://ai.zhongshangqihang.com/v1 |
| Autenticação | Authorization: Bearer <ARK_API_KEY> |
Authorization: Bearer <chave do gateway> |
| model | ID do modelo ou ID de endpoint ep- |
Conforme a lista de modelos do site |
| Indicado para | Uso exclusivo de Doubao, com cadastro, recarga e nota fiscal próprios | Vários modelos, fatura e cota unificadas |
Chave e URL base devem ser usadas em par. Chave oficial com endereço do gateway, ou chave do gateway com endereço oficial, normalmente retorna 401. Ao trocar de entrada, troque as duas coisas juntas.
2. URL base e caminho: /api/v3, não /v1
O endereço oficial do plano de dados é https://ark.cn-beijing.volces.com/api/v3, com caminho de chat completions /chat/completions, resultando em https://ark.cn-beijing.volces.com/api/v3/chat/completions. Ele não tem o prefixo /v1 comum na OpenAI; acrescentar outro /v1 retorna 404.
A região fica no domínio (cn-beijing). Se o recurso estiver em outra região, use o endereço indicado pelo console e pela documentação. A documentação oficial também alerta que casos como o Coding Plan usam uma URL base diferente; preencher errado pode gerar custo extra, então confira antes de trocar de plano.
3. O que preencher em model: ID do modelo ou ID do endpoint
A documentação oficial apresenta dois modos de chamada:
- Inferência predefinida: preencha diretamente o ID do modelo, por exemplo
doubao-seed-2-1-pro-260915, sem criar um endpoint antes. - Endpoint de inferência personalizado: preencha o ID do endpoint, normalmente começando com
ep-, indicado para implantação fixa, gestão detalhada ou modelos com ajuste fino LoRA.
O ID do modelo inclui a data da versão; não escreva nomes de exibição como "Doubao 2.1 Pro" nem complete sufixos de memória. Ao assinar com Access Key (AK/SK), model deve ser o ID do endpoint; com API Key, pode-se usar o ID do modelo ou do endpoint conforme a documentação. Se retornar InvalidEndpointOrModel.ModelIDAccessDisabled, a conta atual não permite chamada direta por ID de modelo e é preciso criar um endpoint.
4. Requisição mínima com curl
Desligue primeiro o raciocínio profundo para reduzir variáveis. Exemplo com a entrada oficial:
export ARK_API_KEY="sua API Key do Volcengine Ark"
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": "Responda apenas OK"}],
"thinking": {"type": "disabled"},
"max_tokens": 32
}'
Se retornar 200 e choices[0].message.content tiver conteúdo, os três itens — chave, endereço e modelo — estão corretos. Se retornar 404, verifique primeiro se model tem o sufixo de versão e se é necessário usar um endpoint ep-; se retornar 401, verifique a chave e o cabeçalho antes de suspeitar do saldo.
5. Chamada em Python com o SDK da OpenAI
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": "Você é um assistente técnico. Responda de forma concisa."},
{"role": "user", "content": "Explique em três frases o que é um endpoint de inferência."},
],
max_tokens=512,
extra_body={"thinking": {"type": "disabled"}},
)
print(resp.choices[0].message.content)
print(resp.usage)
O SDK da OpenAI não reconhece o campo thinking, por isso ele é enviado via extra_body; se o gateway ou o modelo não suportar, basta remover o campo. Depois de funcionar, observe primeiro os tokens de entrada e saída em usage e só então defina timeout, concorrência e orçamento.
6. Streaming: delta, reasoning_content e [DONE]
Com stream=True, o servidor retorna em blocos via SSE, com objeto chat.completion.chunk. O texto fica em choices[0].delta.content, o raciocínio em delta.reasoning_content, e finish_reason aparece apenas no último chunk; ao final, é enviado data: [DONE].
stream = client.chat.completions.create(
model=os.environ["ARK_MODEL_ID"],
messages=[{"role": "user", "content": "Escreva um exemplo em Python para ler um CSV"}],
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="")
Blocos de raciocínio podem vir com content vazio; não presuma que todo chunk tem texto. max_tokens baixo demais trunca a saída. Em caso de queda de rede, o conteúdo já recebido permanece; o cliente deve salvar o resultado parcial e decidir se tenta novamente.
7. Chamadas de ferramenta: tools, tool_calls e ordem de retorno
A sintaxe de tools é próxima da OpenAI: cada ferramenta contém type: "function" e function, com name, description e parameters obrigatórios; parameters usa JSON Schema. Quando o modelo decide chamar, o finish_reason da resposta é tool_calls, e message.tool_calls[i].function.arguments é uma string JSON que precisa ser interpretada.
A ordem de retorno não pode errar: primeiro adicione à conversa a mensagem assistant com tool_calls, depois acrescente para cada chamada uma mensagem role: "tool" com o tool_call_id e o resultado correspondentes, e só então chame o modelo de novo, até que finish_reason deixe de ser tool_calls. parallel_tool_calls é true por padrão; definir false limita a uma ferramenta por vez. Em streaming, os incrementos de chamada de ferramenta trazem index; acumule arguments conforme esse índice.
8. Erros comuns: como localizar 401, 404 e 429
| HTTP | Código típico | Significado | Tratamento |
|---|---|---|---|
| 401 | AuthenticationError |
Chave ausente, inválida, ou cabeçalho sem Bearer |
Copiar a chave novamente e verificar Authorization |
| 404 | InvalidEndpointOrModel.NotFound |
Modelo ou endpoint inexistente, ou sem permissão no workspace | Copiar o ID completo do console e conferir região e workspace |
| 404 | ModelNotOpen |
Modelo ainda não ativado na conta | Ativar o modelo no console do Ark |
| 404 | InvalidEndpointOrModel.ModelIDAccessDisabled |
Conta não permite chamada direta por ID de modelo | Criar endpoint personalizado e usar o ID ep- |
| 400 | InvalidParameter, MissingParameter |
Parâmetro ausente, tipo errado ou modelo sem suporte ao parâmetro | Ajustar campos conforme a documentação e rodar a requisição mínima |
| 403 | AccessDenied, AccountOverdueError |
Sem permissão ou saldo negativo | Verificar chave e permissões do workspace e regularizar o débito |
| 429 | RateLimitExceeded.* |
RPM/TPM acima do limite ou pico de tráfego | Reduzir concorrência, backoff exponencial e, se necessário, pedir aumento |
| 500/503 | InternalServiceError, ServerOverloaded |
Anomalia ou sobrecarga no upstream | Aguardar brevemente e retentar; preparar canal alternativo |
9. Checklist de migração
Ao passar do acesso oficial para um gateway, ou de um gateway para outro, verifique na seguinte ordem:
- Fixe um lote de prompts reais e registre a saída, o tempo e o
usageatuais como linha de base. - Troque
base_urleapi_keyao mesmo tempo; não altere apenas um deles. - Confirme o mapeamento de nomes de modelo na página de preços; não reutilize nomes antigos.
- Valide o streaming: tempo até o primeiro token,
delta.content,finish_reasone[DONE]. - Valide chamadas de ferramenta:
tools,tool_calls, retorno detool_call_ide ordem dos turnos. - Valide parâmetros estendidos como
thinkingereasoning_effort, inclusive se os padrões são diferentes. - Configure timeouts de conexão e leitura; retente apenas 429, 5xx e timeout; assegure idempotência em operações de escrita.
- Desvie primeiro 5% a 10% do tráfego, observe de 3 a 7 dias e mantenha o acesso oficial como canal de rollback.
Se precisar de entrada unificada, crie uma chave de teste na página de cadastro; cobertura de modelos e critério de cobrança estão na página de preços. O ZSQH AI Gateway é um serviço independente de terceiros e não tem vínculo de afiliação com a ByteDance.
Leituras relacionadas:
- Preços e cobrança do Doubao API
- Integração da GLM API: URL base, modelos e primeira chamada
- Modelos e preços
Perguntas frequentes
A URL base deve incluir /v1?
Não. A entrada compatível com OpenAI do Volcengine Ark é https://ark.cn-beijing.volces.com/api/v3, com caminho de chat /chat/completions; acrescentar /v1 retorna 404. Endereços de gateway normalmente terminam em /v1, e os dois não devem ser misturados.
Em model, uso o ID do modelo ou o ID de endpoint ep-?
Para inferência predefinida, use o ID do modelo; para endpoint personalizado, use o ID ep-; ao assinar com Access Key, é obrigatório usar o ID do endpoint. Se retornar ModelIDAccessDisabled, é preciso criar um endpoint, não trocar a chave.
A primeira chamada deve ativar o raciocínio profundo?
Recomenda-se desligar e confirmar o caminho com a requisição mais curta. Alguns modelos Doubao ativam o raciocínio profundo por padrão; com ele ligado, a saída primeiro produz raciocínio, e um max_tokens pequeno tende a truncar o texto final. Ative conforme a tarefa depois de validar o caminho.
Por que o streaming não retorna conteúdo?
Confirme primeiro stream=True e verifique se você lê apenas delta.content, ignorando blocos vazios; o raciocínio fica em delta.reasoning_content. Verifique também max_tokens, buffer de rede e se o proxy está acumulando o SSE em uma resposta única.
Por que é preciso devolver os tool_calls do assistant?
O protocolo exige que a mensagem assistant carregue tool_calls, e que cada mensagem role: "tool" corresponda ao resultado por tool_call_id. Pular essa etapa pode fazer o modelo repetir a ferramenta ou retornar 400. Em streaming, acumule os argumentos por index.
