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:

  1. Fixe um lote de prompts reais e registre a saída, o tempo e o usage atuais como linha de base.
  2. Troque base_url e api_key ao mesmo tempo; não altere apenas um deles.
  3. Confirme o mapeamento de nomes de modelo na página de preços; não reutilize nomes antigos.
  4. Valide o streaming: tempo até o primeiro token, delta.content, finish_reason e [DONE].
  5. Valide chamadas de ferramenta: tools, tool_calls, retorno de tool_call_id e ordem dos turnos.
  6. Valide parâmetros estendidos como thinking e reasoning_effort, inclusive se os padrões são diferentes.
  7. Configure timeouts de conexão e leitura; retente apenas 429, 5xx e timeout; assegure idempotência em operações de escrita.
  8. 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:

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.