더우바오 API는 OpenAI 호환 인터페이스를 제공하지만 "호환"이 "그대로 복사"를 뜻하지는 않습니다. 가장 막히기 쉬운 세 곳은 Base URL을 /v1로 적는 것, model에 모델 ID나 엔드포인트 ID 대신 표시 이름을 넣는 것, 첫 호출부터 깊은 사고를 켜서 출력이 잘리는 것입니다. 이 글은 curl과 Python을 순서대로 실행한 뒤 스트리밍 출력, 도구 호출, 자주 발생하는 오류, 마이그레이션 체크리스트를 설명합니다.
안내: 더우바오, 화산팡저우, 화산엔진, 바이트댄스는 바이트댄스 및 그 계열사의 상표 또는 제품명이며 OpenAI는 해당 권리자의 상표입니다. 이 글은 기술 연동 설명만 제공하며 위 회사들과 소속, 대리, 보증 관계가 없습니다. 인터페이스, 모델, 할당량은 조정될 수 있으므로 공식 문서와 사이트 내 모델 가격을 기준으로 하세요.
1. 연동 전에 확인할 세 가지
| 항목 | 화산팡저우 직접 연결 | 통합 게이트웨이 |
|---|---|---|
| Base URL | https://ark.cn-beijing.volces.com/api/v3 |
https://ai.zhongshangqihang.com/v1 |
| 인증 | Authorization: Bearer <ARK_API_KEY> |
Authorization: Bearer <게이트웨이 키> |
| model | 모델 ID 또는 ep- 엔드포인트 ID |
사이트 내 모델 목록 기준 |
| 적합한 경우 | 더우바오만 사용, 직접 실명 충전·발행 | 멀티모델, 통합 청구와 할당량 |
키와 Base URL은 반드시 쌍으로 사용해야 합니다. 공식 키에 게이트웨이 주소를 쓰거나 게이트웨이 키에 공식 주소를 쓰면 보통 바로 401이 반환됩니다. 진입점을 바꿀 때는 둘을 함께 교체하세요.
2. 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을 사용하며 잘못 적으면 추가 비용이 발생할 수 있다고 안내하므로 요금제를 바꾸기 전에 확인하세요.
3. model에 무엇을 넣나: 모델 ID인가 엔드포인트 ID인가
공식 문서는 두 가지 호출 방식을 제시합니다.
- 사전 설정 추론: 모델 ID를 직접 넣습니다. 예:
doubao-seed-2-1-pro-260915. 엔드포인트를 미리 만들 필요가 없습니다. - 사용자 정의 추론 엔드포인트: 엔드포인트 ID를 넣습니다. 보통
ep-로 시작하며 고정 배포, 세밀한 관리, LoRA 미세 조정 모델에 적합합니다.
모델 ID에는 버전 날짜가 붙으므로 "더우바오 2.1 Pro" 같은 표시 이름을 쓰거나 기억에 의존해 접미사를 붙이지 마세요. Access Key(AK/SK) 서명을 쓸 때는 model에 반드시 엔드포인트 ID를 넣어야 하고, API 키를 쓸 때는 문서에 따라 모델 ID나 엔드포인트 ID를 넣을 수 있습니다. InvalidEndpointOrModel.ModelIDAccessDisabled가 반환되면 현재 계정이 모델 ID 직접 호출을 지원하지 않으므로 엔드포인트를 만들어야 합니다.
4. curl로 최소 요청 실행
먼저 깊은 사고를 꺼서 변수를 줄이세요. 공식 진입점 예시입니다.
export ARK_API_KEY="여기의 화산팡저우 API 키"
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에 내용이 있으면 키, 주소, 모델 세 가지가 모두 맞은 것입니다. 404라면 model에 버전 접미사가 붙었는지, ep- 엔드포인트로 바꿔야 하는지 먼저 확인하세요. 401이라면 키와 요청 헤더를 먼저 보고 잔액을 의심하지 마세요.
5. 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의 입력·출력 토큰을 먼저 보고 타임아웃, 동시성, 비용 예산을 정하세요.
6. 스트리밍 출력: 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가 너무 작으면 출력이 잘립니다. 네트워크가 끊겨도 이미 받은 내용은 남아 있으므로 클라이언트가 부분 결과를 저장하고 재시도 여부를 판단할 수 있어야 합니다.
7. 도구 호출: 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 메시지를 대화에 추가하고, 각 호출마다 해당 tool_call_id와 결과를 담은 role: "tool" 메시지를 덧붙인 뒤 다시 모델에 요청합니다. finish_reason이 더 이상 tool_calls가 아닐 때까지 반복합니다. parallel_tool_calls는 기본값이 true이며 false로 설정하면 한 번에 하나의 도구만 호출합니다. 스트리밍에서는 도구 호출 증분에 index가 붙으므로 index별로 arguments를 누적해야 합니다.
8. 자주 발생하는 오류: 401, 404, 429 원인 찾기
| HTTP | 대표 오류 코드 | 의미 | 처리 |
|---|---|---|---|
| 401 | AuthenticationError |
키 누락, 무효, 요청 헤더에 Bearer 없음 |
키를 다시 복사하고 Authorization 헤더 확인 |
| 404 | InvalidEndpointOrModel.NotFound |
모델 또는 엔드포인트 없음, 현재 공간 권한 없음 | 콘솔에서 전체 ID 복사, 지역과 공간 확인 |
| 404 | ModelNotOpen |
계정에 모델이 아직 개통되지 않음 | 팡저우 콘솔에서 해당 모델 개통 |
| 404 | InvalidEndpointOrModel.ModelIDAccessDisabled |
계정이 모델 ID 직접 호출을 지원하지 않음 | 사용자 정의 엔드포인트를 만들고 ep- ID 사용 |
| 400 | InvalidParameter, MissingParameter |
파라미터 누락, 유형 오류, 모델이 파라미터 미지원 | 문서와 대조해 필드 수정, 최소 요청부터 실행 |
| 403 | AccessDenied, AccountOverdueError |
권한 없음 또는 계정 잔액 마이너스 | 키와 공간 권한 확인, 연체 처리 |
| 429 | RateLimitExceeded.* |
RPM/TPM 초과 또는 트래픽 급증 | 동시성 축소, 지수 백오프, 필요 시 증액 신청 |
| 500/503 | InternalServiceError, ServerOverloaded |
업스트림 이상 또는 과부하 | 잠시 대기 후 재시도, 대체 경로 준비 |
9. 마이그레이션 체크리스트
공식 직접 연결에서 게이트웨이로, 또는 한 게이트웨이에서 다른 게이트웨이로 옮길 때 다음 순서로 확인하세요.
- 실제 프롬프트 세트를 고정하고 현재 출력, 소요 시간,
usage를 비교 기준선으로 기록합니다. base_url과api_key를 동시에 교체하고 하나만 바꾸지 않습니다.- 사이트 내 모델 가격에 맞춰 모델명 매핑을 다시 확인하고 이전 이름을 쓰지 않습니다.
- 스트리밍 출력을 검증합니다. 첫 토큰 시간,
delta.content,finish_reason,[DONE]을 확인합니다. - 도구 호출을 검증합니다.
tools,tool_calls,tool_call_id반환, 멀티턴 순서를 확인합니다. thinking,reasoning_effort등 확장 파라미터 지원 여부와 기본값 차이를 검증합니다.- 연결·읽기 타임아웃을 설정하고 429, 5xx, 타임아웃에만 재시도하며 쓰기 작업은 멱등하게 처리합니다.
- 먼저 5%~10% 트래픽으로 3~7일 관찰하고 공식 직접 연결을 롤백 경로로 유지합니다.
통합 진입점이 필요하면 가입 페이지에서 테스트 키를 만들 수 있고, 모델 범위와 과금 기준은 모델 가격에서 확인하세요. ZSQH AI 게이트웨이는 독립 제3자 서비스이며 바이트댄스와 소속 관계가 없습니다.
관련 글:
자주 묻는 질문
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가 반환되면 키를 바꿀 것이 아니라 엔드포인트를 만들어야 한다는 뜻입니다.
첫 호출에서 깊은 사고를 켜야 하나요?
먼저 끄고 가장 짧은 요청으로 경로를 확인하는 편이 좋습니다. 일부 더우바오 모델은 깊은 사고가 기본 활성화되어 출력이 먼저 사고 내용을 생성하며 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별로 파라미터를 누적해야 합니다.
